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
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 ##
const whiteColor = {r: 255, g: 255, b: 255};
const blackColor = {r: 0, g: 0, b: 0};
canvas = document.getElementsByTagName("canvas")[0];
ctx = canvas.getContext("2d");
//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)}`);
};
};
//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
}
//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) { //turns rgb() to {r,g,b} with no bounds checking
//console.log("Splitting string into object");
string = string.split(",");
var red = parseFloat(string[0].substring(4));
var green = parseFloat(string[1]);
var blue = parseFloat(string[2].slice(0,-1));
//console.log("String split: outputs " + red + ", " + green + ", " + blue + ".");
return {r: red, g: green, b: blue};
};
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) {
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].slice(0,-2));
//NaN checking
var hueNaN,saturationNaN,lightnessNaN;
isNaN(hue) ? hueNaN = true : hueNaN = false;
isNaN(saturation) ? saturationNaN = true : saturationNaN = false;
isNaN(lightness) ? lightnessNaN = true : lightnessNaN = false;
var NanErrorString = "One or more colors are NaN:"
if(hueNaN) { NanErrorString += " hue" };
if(saturationNaN) { NanErrorString += " saturation" };
if(lightnessNaN) { NanErrorString += " lightness" };
if(hueNaN || saturationNaN || lightnessNaN) { throw new Error(NanErrorString) };
return {h: hue, s: saturation, l: lightness};
};
function rgbToHex(color) {
//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;
//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})`);
red = red.toString(16);
green = green.toString(16);
blue = blue.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(`Colors padded to (0x${red}, 0x${green}, 0x${blue}), concatenating...`);
return "#" + red + green + blue;
} 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);
red = color.r;
green = color.g;
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})`);
red = red.toString(16);
green = green.toString(16);
blue = blue.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(`Colors padded to (0x${red}, 0x${green}, 0x${blue}), concatenating...`);
return "#" + red + green + blue;
} 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%,luminance%)"*/);
};
};
} 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);
//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.log(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);
//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") {
//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 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)", "rgb(0,0,0)", "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") { //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 = 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) {
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 ? `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 ? {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"); //make any RGBs rgb()
color = rgbStringToHSL(color,"json"); //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) //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 changeLuminance(color,luminanceChange,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 += luminanceChange;
break;
case "-":
case "subtract":
color.l -= luminanceChange;
break;
case "*":
case "x":
case "×":
case "multiply":
color.l *= luminanceChange;
break;
case "/":
case "÷":
case "divide":
color.l /= luminanceChange;
break;
case "=":
case "set":
color.l = luminanceChange;
break;
case ">":
case ">=":
case "min":
color.l = Math.max(color.l,luminanceChange);
break;
case "<":
case "<=":
case "max":
color.l = Math.min(color.l,luminanceChange);
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 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.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) {
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.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(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 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: "#ffffff",
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: "#ffffff",
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)
};
//COLOR MANIPULATION TOOLS ##
var colorToolCounter = 0;
saturationAmount = 1;
saturationOp = "add";
luminanceAmount = 1;
luminanceOp = "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 luminancePrompt() {
var preLuminance = 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(preLuminance))) {
if(preLuminance === "" || preLuminance === null) {
alert("No value was specified! Defaulting to 1");
preLuminance = 1;
} else {
alert("Invalid value! The value must be a number (defaulting to 1)");
preLuminance = 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";
};
};
preLuminance = parseFloat(preLuminance)
luminanceAmount = preLuminance;
luminanceOp = preLumOp;
return [preLuminance, 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.luminance = {
color: ["#000000","#333333","#666666","#999999","#cccccc","#ffffff"],
tool: function(pixel) {
var element = pixel.element;
if( colorToolElementFilter === "none" || ( (typeof(colorToolElementFilter) === "string" && element === colorToolElementFilter) || (Array.isArray(colorToolElementFilter) && colorToolElementFilter.includes(element)) ) ) {
pixel.color = changeLuminance(pixel.color,luminanceAmount,luminanceOp,"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: "rgb(0,0,0)",
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]) {
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:
color1: The first color of the stripe
color2: The second color of the stripe (defaults to black)
scale: Relative width of the stripes, compared to the default
phase: Offset in the position of the stripes (π/2 = 1 stripe width)
angle: Angle in degrees
0 = vertical line
45 = bottom left to top right
90 = horizontal line
135 = top left to bottom right...
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,
scale: 1,
phase: 0,
angle: 0
},
stain: elements.dye.stain,
tick: function(pixel) {
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"];
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";
pixelSize = newPixelSize;
pixelSizeHalf = newPixelSize/2;
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(); }
}
autoResizeCanvas = function(clear) {
pixelSize = 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; }
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`);
rgbos.push("rgb(255,255,255)")
} else if (c.startsWith("#")) {
var rgb = hexToRGB(c);
if(rgb == null) { console.log(key,c); rgb = {r: 255, g: 255, b: 255} };
rgbs.push("rgb("+rgb.r+","+rgb.g+","+rgb.b+")");
rgbos.push(rgb);
}
else {
rgbs.push(c);
}
}
elements[key].color = rgbs;
elements[key].colorObject = rgbos;
} else {
// if elements[key].color starts with #
if (elements[key].color.startsWith("#")) {
var rgb = hexToRGB(elements[key].color);
elements[key].color = "rgb("+rgb.r+","+rgb.g+","+rgb.b+")";
elements[key].colorObject = rgb;
}
}
}
}
}
// 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;
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.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" }
};
}*/
}
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,"evaporate");
}
}
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++;
// 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 = "rgb(255,255,255)";
elements[key].colorObject = {r:255,g:255,b:255};
}
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 !== "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];
if (elements[key].colorKey[char]) {
if (elements[key].colorKey[char].startsWith("#")) {
var rgb = hexToRGB(elements[key].colorKey[char]);
elements[key].colorKey[char] = "rgb("+rgb.r+","+rgb.g+","+rgb.b+")";
}
newPattern[i].push(elements[key].colorKey[char]);
}
else {
newPattern[i].push("rgb(255,255,255)");
}
}
}
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 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 settingElements = settingSpans[i].getElementsByTagName("select") || settingSpans[i].getElementsByTagName("input");
if (settingElements.length > 0) {
settingElements[0].value = settingValue;
}
}
}
};
function setCanvasWidthAndHeight(ctx) {
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 800, set it to 800
if (newWidth > 1000) { newWidth = 1000; }
// If we are on a desktop and the new height is greater than 600, set it to 600
if (window.innerWidth > 1000 && newHeight > 500) { newHeight = 500; }
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("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.ontouchstart = function(e) {
if (e.touches) e = e.touches[0];
return false;
}
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];
console.log(item);
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?';
}
};
};
function generateModManagerList() {
if (enabledMods.length > 0) {
modManagerList = document.getElementById("modManagerList");
for (var i = 0; i < enabledMods.length; i++) {
var mod = enabledMods[i];
// modName is the last part of the mod's path
var modName = mod.split("/").pop();
modManagerList.innerHTML += "
");
selectElement(currentElement);
focusGame();
};
window.onload = function() {
// 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]();
}
// 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()
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",
},
"evaporate": { // Liquid -> Gas
rgb: [ [1.5,1.5,1.5] ],
behavior: behaviors.GAS,
type: "high",
hidden: true,
state: "gas",
}
}
// Loop through each element. If it has a tempHigh, but not a stateHigh, create a new molten element
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();
validateWorldGenSelection();
// 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.
loadSettings();
//scared to touch this because ctx is pretty important
var gameCanvas = document.getElementById("game");
// Get context
var ctx = gameCanvas.getContext("2d");
setCanvasWidthAndHeight(ctx);
mousePos = {x:width/2,y:height/2};
definePixelMap();
// randomChoices = the keys of "elements" with any element with the category "tools" or the property excludeRandom set to true removed
setRandomChoices();
addCanvasAndWindowListeners(gameCanvas);
gameCanvas.ontouchstart = function(e) {
if (e.touches) e = e.touches[0];
return false;
}
window.onbeforeunload = function(){ // Confirm leaving page if there are pixels on-screen
if (currentPixels.length > 0){
return 'Are you sure you want to leave?';
}
};
// If enabledMods has items, add an li to modManagerList for each item with the href to the item, target blank, and the item's name, with "X" after the link
generateModManagerList();
document.getElementById("game").oncontextmenu = function(e) { e.preventDefault(); return false; }
// If the user presses [ or -, decrease the mouse size by 2
addKeyboardListeners();
// If the user releases either shift
addUnshiftListeners();
// Create buttons for elements
// For each element type in elements, create a button in controls that sets the current element to that type
// Alphabetically sort and loop through dictionary named "elements"
createButtonsAndCountElements();
for (var i = 0; i < runAfterButtonsList.length; i++) {
runAfterButtonsList[i]();
};
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();
}
}
if (window.self !== window.top && !location.ancestorOrigins[0].includes("itch.io")) {
// Open a message that tells the user they aren't on the real website
var menuParent = document.createElement("div");
menuParent.className = "menuParent";
menuParent.style.display = "block";
menuParent.innerHTML = `
Sandboxels
You may be on a website that has embedded our game involuntarily.
Please use the main website to support us instead.
You can also join our Discord if that isn't possible.
`
document.body.appendChild(menuParent);
showingMenu = "alert";
}
//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"));
gameLoaded = true
};
//MORE CONFIGURABLE EXPLOSIONS (explodeAtPlus) ##
velocityBlacklist = [];
function explodeAtPlus(x,y,radius,firee="fire",smokee="smoke",beforeFunction=null,afterFunction=null,changeTemp=true) {
var message = "Explosion ";
var 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;
//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 };
if (r.setting && !(settings[r.setting])) {
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.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) {
return false;
}
if (r.chance !== undefined && Math.random() > r.chance) {
return false;
}
if (r.y !== undefined && (pixel1.y < r.y[0] || pixel1.y > r.y[1])) {
return false;
}
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(pixel1 && pixel2) {
if (r.func) { 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
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];
pixel.temp += burnTempChange;
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 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) {
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
if (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;
};
var maxColorOffset = Math.floor(Math.random() * (Math.random() > 0.5 ? -1 : 1) * Math.random() * offsetAmount);
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)];
}
}*/
return color;
}
//FIND MODE, PIXEL PROPERTIES LINKED TO SPECIAL CODE, CONFIGURABLE VISUAL DISTORTION AND VISUAL PIXEL SHAPE SETTINGS (acid_and_shapes.js) ##
//two separate things i.e. not "pixel properties linked to special code, configurable visual distortion, and visual pixel shape settings" though there's basically no semantic difference
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 keybindsIf 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: ["#000000", "#000000", "#000000", "#000000", "#ff0000", "#ff0000", "#ff0000", "#ff0000"],
name: "find toggle (look at info)",
behavior: behaviors.SELFDELETE,
category: "special",
excludeRandom: true,
desc: `
I'm running out of keybindsIf 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"];
settings.shapeMode ??= 0;
settings.doacid ??= false;
settings.acidFunction ??= "none";
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;
//I hate overwriting drawPixels
runAfterAutogen(function() {
//rAA because velocity.js already puts its redef in a rAL and rAA comes after that
drawPixels = function(forceTick=false) {
// Draw the current pixels
var canvas = document.getElementById("game");
var ctx = canvas.getContext("2d");
// newCurrentPixels = shuffled currentPixels
var newCurrentPixels = currentPixels.slice();
var pixelsFirst = [];
var pixelsLast = [];
if (!paused || forceTick) {
shuffleArray(newCurrentPixels);
}
/*{newCurrentPixels.sort(function(p) { // shuffle the pixels but keep elements[p.element].isGas last
return 0.5 - Math.random();
})} // shuffle the pixels if not paused*/
for (var i = 0; i < newCurrentPixels.length; i++) {
pixel = newCurrentPixels[i];
if(typeof(elements[pixel.element]) == "undefined") { continue };
if(typeof(pixel) == "undefined") { continue };
//if (pixelMap[pixel.x][pixel.y] == undefined || currentPixels.indexOf(pixel) == -1) {continue}
if (pixel.del) {continue}
if (!paused || forceTick) {
if(typeof(elements[pixel.element]) == "undefined") { continue };
doVelocity(pixel);
if (elements[pixel.element].tick) { // Run tick function if it exists
elements[pixel.element].tick(pixel);
}
if (pixel.del) {continue}
if (elements[pixel.element].behavior) { // Parse behavior if it exists
pixelTick(pixel);
}
};
if (elements[pixel.element].isGas) {
pixelsLast.push(pixel);
}
else {
pixelsFirst.push(pixel);
}
}
ctx.clearRect(0, 0, canvas.width, canvas.height);
if (!settings["bg"]) {ctx.clearRect(0, 0, canvas.width, canvas.height)}
else {
if(settings["bg"] instanceof Array) {
settings.bgAngle ??= 90;
var angle = (settings.bgAngle) * Math.PI / 180;
ctx.fillStyle = ctx.createLinearGradient(
0,
0,
canvas.width * Math.cos(angle) + 0,
canvas.height * Math.sin(angle)
);
var colors = settings["bg"];
for(i = 0; i < colors.length; i++) {
var color = colors[i];
var position = i / (colors.length - 1);
ctx.fillStyle.addColorStop(position,color);
};
} else {
ctx.fillStyle = settings["bg"];
};
ctx.fillRect(0, 0, canvas.width, canvas.height);
}
var pixelDrawList = pixelsFirst.concat(pixelsLast);
for (var i = 0; i < pixelDrawList.length; i++) {
var pixel = pixelDrawList[i];
if (pixelMap[pixel.x][pixel.y] == undefined) {continue}
if (pixel.con) { pixel = pixel.con }
if (view===null || view===3) {
var colorOut = pixel.color;
for(var sry4thelag in specialProperties) {
if(pixel[sry4thelag] !== undefined && specialProperties[sry4thelag].specialColorFunction) {
colorOut = specialProperties[sry4thelag].specialColorFunction(pixel,oldColor=colorOut)
}
}
ctx.fillStyle = colorOut;
}
else if (view === 2) { // thermal view
// set the color to pixel.temp, from hottest at -66 (294.1875) hue to coldest 265 hue, with the minimum being -273, max being 7755
var temp = pixel.temp;
temp = Math.min(Math.max(temp + 900,(settings.abszero ?? -273.15)),55530);
var hue,sat,lig;
sat = 100;
lig = 50;
if(temp <= 7755) {
hue = 265 - (Math.min(7755,temp)/6000)*265;
if (hue < 0) {hue += (360 * Math.ceil(hue / -360))}
if (temp < 0 && hue > 285) {hue = 285}
} else if(temp <= 9255) {
hue = 294.1875;
lig = 50 + (Math.max(0,temp - 7755) * (50/1500));
} else if(temp <= 11255) {
hue = 294.1875;
sat = 0;
lig = 100 - (Math.max(0,temp - 9255) * (100 / 2000));
} else if(temp <= 11755) {
hue = 265;
lig = (Math.max(0,temp - 11255) * (25 / 500));
} else if(temp <= 19510) {
hue = 265 - (Math.min(19510,Math.max(0,temp - 11755))/6000)*265;
if (hue < 0) {hue += (360 * Math.ceil(hue / -360))}
lig = 25;
} else if(temp <= 20510) {
hue = 294.1875
//lig = scale(temp,19510,20010,25,75);
//hue = scale(temp,19510,20010,294.1875,585) % 360;
sat = scale(temp,19510,20510,100,50);
lig = scale(temp,19510,20510,25,75);
} else if(temp <= 28265) {
hue = scale(temp,20510,28265,294.1875,585) % 360;
sat = 50;
lig = 75;
} else if(temp <= 29265) {
hue = 265;
sat = scale(temp,28265,29265,50,40);
lig = scale(temp,28265,29265,75,87.5);
} else if(temp <= 37020) {
hue = scale(temp,29265,37020,265,654.1875) % 360;
sat = 40;
lig = 87.5;
} else if(temp <= 39020) {
hue = 294.1875;
sat = 40;
lig = scale(temp,37020,39020,87.5,50);
} else if(temp <= 46775) { //46775
hue = scale(temp,39020,46775,294.1875,585) % 360;
sat = 40;
lig = 50;
} else if(temp <= 47775) {
hue = 265;
sat = scale(temp,46775,47775,40,20);
lig = 50;
} else { //55530
hue = scale(temp,47775,55530,265,654.1875) % 360;
sat = 20;
lig = 50;
};
ctx.fillStyle = "hsl("+hue+","+sat+"%,"+lig+"%)";
}
else if (view === 4) { // smooth view, average of surrounding pixels
// E/N: i'm too scared to do smooth view
var colorlist = [];
// check adjacent coords on the pixelMap, add the color to the list if the pixel is not empty and the color indexOf "rgb" is not -1
for (var j = 0; j < biCoords.length; j++) {
var x = pixel.x + biCoords[j][0];
var y = pixel.y + biCoords[j][1];
if (isEmpty(x,y,true) || elements[pixelMap[x][y].element].state !== elements[pixel.element].state) {continue}
var color = pixelMap[x][y].color;
if (color.indexOf("rgb") !== -1) {
colorlist.push(color.match(/\d+/g));
}
}
if (colorlist.length === 0) {
ctx.fillStyle = pixel.color;
}
else {
ctx.fillStyle = averageRGB(colorlist);
}
}
else if (view === 5) { // 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) {
ctx.fillStyle = "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);
ctx.fillStyle = "hsl("+hue+","+sat+"%,"+lig+"%)";
}
}
if(find) { //if find and matching, override fill style with the find coloration
if(findElement instanceof Array ? findElement.includes(pixel.element) : pixel.element === findElement) {
ctx.fillStyle = "rgb(255," + marasi(findColorPulseTimer / 10) + ",0)";
}
};
var mode = getShapeMode();
settings.acidFunction ??= "none";
var acidFunction;
if([false,"false"].includes(settings.doacid)) {
acidFunction = acidFunctions.none
} else {
acidFunction = acidFunctions[settings.acidFunction ?? "none"]
};
var acidOffset1,acidOffset2;
if(settings.doacid && settings.acidFunction != "none" && !!acidFunction) {
acidOffset1 = (settings.doacid ?? false) * (18*acidFunction((pixel.y+incrementt)/4.4));
acidOffset2 = (settings.doacid ?? false) * (18*acidFunction((pixel.x+incrementt)/4.4))
} else {
acidOffset1 = 0;
acidOffset2 = 0
};
if ((view === null || view === 4) && elements[pixel.element].isGas) {
//gas rendering
switch(mode) {
case "circles":
ctx.globalAlpha = 0.66;
ctx.beginPath();
ctx.arc((pixel.x+0.5)*pixelSize+acidOffset1, (pixel.y+0.5)*pixelSize+acidOffset2, pixelSize, 0, 2 * Math.PI, false);
ctx.fill();
ctx.globalAlpha = 1;
break;
case "triangles":
ctx.globalAlpha = 0.66;
ctx.beginPath();
ctx.moveTo((pixel.x-0.75)*pixelSize+acidOffset1,(pixel.y+1.5)*pixelSize+acidOffset2);
ctx.lineTo((pixel.x+0.5)*pixelSize+acidOffset1,(pixel.y-1)*pixelSize+acidOffset2);
ctx.lineTo((pixel.x+1.75)*pixelSize+acidOffset1,(pixel.y+1.5)*pixelSize+acidOffset2);
ctx.fill();
ctx.globalAlpha = 1;
break;
default:
ctx.globalAlpha = 0.5;
ctx.fillRect((pixel.x-1)*pixelSize+acidOffset1, (pixel.y)*pixelSize+acidOffset2, pixelSize*3, pixelSize);
ctx.fillRect((pixel.x)*pixelSize+acidOffset1, (pixel.y-1)*pixelSize+acidOffset2, pixelSize, pixelSize*3);
ctx.globalAlpha = 1;
break;
};
}
else { // draw the pixel (default)
switch(mode) {
case "circles":
ctx.beginPath();
ctx.arc((pixel.x+0.5)*pixelSize+acidOffset1, (pixel.y+0.5)*pixelSize+acidOffset2, pixelSize/2, 0, 2 * Math.PI, false);
ctx.fill();
ctx.globalAlpha = 1;
break;
case "triangles":
ctx.beginPath();
ctx.moveTo(pixel.x*pixelSize+acidOffset1,(pixel.y+1)*pixelSize+acidOffset2);
ctx.lineTo((pixel.x+0.5)*pixelSize+acidOffset1,(pixel.y)*pixelSize+acidOffset2);
ctx.lineTo((pixel.x+1)*pixelSize+acidOffset1,(pixel.y+1)*pixelSize+acidOffset2);
ctx.fill();
ctx.globalAlpha = 1;
break;
default:
ctx.fillRect(pixel.x*pixelSize+acidOffset1, pixel.y*pixelSize+acidOffset2, pixelSize, pixelSize);
break;
};
}
if (pixel.charge && settings.shockoverlay && view !== 2) { // Yellow glow on charge
if (!elements[pixel.element].colorOn) {
ctx.fillStyle = "rgba(255,255,0,0.5)";
switch(mode) {
case "circles":
ctx.beginPath();
ctx.arc((pixel.x+0.5)*pixelSize+acidOffset1, (pixel.y+0.5)*pixelSize+acidOffset2, pixelSize/2, 0, 2 * Math.PI, false);
ctx.fill();
ctx.globalAlpha = 1;
break;
case "triangles":
ctx.beginPath();
ctx.moveTo(pixel.x*pixelSize+acidOffset1,(pixel.y+1)*pixelSize+acidOffset2);
ctx.lineTo((pixel.x+0.5)*pixelSize+acidOffset1,(pixel.y)*pixelSize+acidOffset2);
ctx.lineTo((pixel.x+1)*pixelSize+acidOffset1,(pixel.y+1)*pixelSize+acidOffset2);
ctx.fill();
ctx.globalAlpha = 1;
break;
default:
ctx.fillRect(pixel.x*pixelSize+acidOffset1, pixel.y*pixelSize+acidOffset2, pixelSize, pixelSize);
break;
};
}
}
if (pixel.burning && settings.burnoverlay && view !== 2) { // Red glow on burn
if (!elements[pixel.element].colorOn) {
ctx.fillStyle = "rgba(255,0,0,0.5)";
switch(mode) {
case "circles":
ctx.beginPath();
ctx.arc((pixel.x+0.5)*pixelSize+acidOffset1, (pixel.y+0.5)*pixelSize+acidOffset2, pixelSize/2, 0, 2 * Math.PI, false);
ctx.fill();
ctx.globalAlpha = 1;
break;
case "triangles":
ctx.beginPath();
ctx.moveTo(pixel.x*pixelSize+acidOffset1,(pixel.y+1)*pixelSize+acidOffset2);
ctx.lineTo((pixel.x+0.5)*pixelSize+acidOffset1,(pixel.y)*pixelSize+acidOffset2);
ctx.lineTo((pixel.x+1)*pixelSize+acidOffset1,(pixel.y+1)*pixelSize+acidOffset2);
ctx.fill();
ctx.globalAlpha = 1;
break;
default:
ctx.fillRect(pixel.x*pixelSize+acidOffset1, pixel.y*pixelSize+acidOffset2, pixelSize, pixelSize);
break;
};
}
}
};
if (ctx.globalAlpha < 1) {
ctx.globalAlpha = 1;
};
if (elements[currentElement].maxSize < mouseSize) {
var mouseOffset = Math.trunc(elements[currentElement].maxSize/2);
}
else {
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
ctx.strokeStyle = "white";
ctx.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) {
ctx.fillStyle = "rgba(255,255,255,0.5)";
ctx.fillRect(mousePos.x*pixelSize,mousePos.y*pixelSize,pixelSize,pixelSize);
}
if ((!paused) || forceTick) {pixelTicks++};
findColorPulseTimerSubTimer++;
if(findColorPulseTimerSubTimer >= 2) {
findColorPulseTimer++;
findColorPulseTimerSubTimer = 0;
};
}
});
//I hate overwriting drawPixels
viewKey = {
2: "thermal",
3: "basic",
4: "smooth",
5: "velocity"
};
function setView(n) {
if (viewKey[n]) { // range of number keys with valid views
view = n;
}
else { // reset view
view = null;
}
};
runAfterLoad(function() {
//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 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 = showSettings;
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);
};
}
})
});
//FUNCTION EXECUTION WHEN A PIXEL TRIES TO MOVE INTO ANOTHER (onTryMoveInto) ##
elements.on_try_move_into_test = {
color: "#ffffff",
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 (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) {
tryMove(pixel,pixel.x,pixel.y-1);
},
category: "movement tools",
excludeRandom: true,
},
elements.move_down = {
color: "#000038",
tool: function(pixel) {
tryMove(pixel,pixel.x,pixel.y+1);
},
category: "movement tools",
excludeRandom: true,
},
elements.move_left = {
color: "#007000",
tool: function(pixel) {
tryMove(pixel,pixel.x-1,pixel.y);
},
category: "movement tools",
excludeRandom: true,
},
elements.move_right = {
color: "#000E00",
tool: function(pixel) {
tryMove(pixel,pixel.x+1,pixel.y);
},
category: "movement tools",
excludeRandom: true,
},
elements.move_up_left = {
color: "#E00000",
tool: function(pixel) {
tryMove(pixel,pixel.x-1,pixel.y-1);
},
category: "movement tools",
excludeRandom: true,
},
elements.move_down_left = {
color: "#0001C0",
tool: function(pixel) {
tryMove(pixel,pixel.x-1,pixel.y+1);
},
category: "movement tools",
excludeRandom: true,
},
elements.move_up_right = {
color: "#038000",
tool: function(pixel) {
tryMove(pixel,pixel.x+1,pixel.y-1);
},
category: "movement tools",
excludeRandom: true,
},
elements.move_down_right = {
color: "#000007",
tool: function(pixel) {
tryMove(pixel,pixel.x+1,pixel.y+1);
},
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", "#000000", "#000000", "#000000", "#000000"],
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: ["#000000", "#ff00ff", "#000000", "#ff00ff"],
tool: function(pixel) {
pixel.temp = NaN;
pixelTempCheck(pixel)
},
category: "cursed tools",
};
elements.inf_temp = {
color: ["#ff0000", "#ffffff", "#ff0000", "#ffffff", "#ff0000", "#ffffff", "#ff0000", "#ffffff", "#ff0000", "#ffffff", "#ff0000", "#ffffff", "#ff0000", "#ffffff", "#ff0000", "#ffffff", "#ff0000", "#ffffff", "#ff0000", "#ffffff", "#ff0000", "#ffffff", "#ff0000", "#ffffff", "#ff0000", "#ffffff", "#ff0000", "#ffffff", "#ff0000", "#ffffff", "#ff0000", "#ffffff", "#ff0000", "#ffffff", "#ff0000", "#ffffff", "#ff0000", "#ffffff", "#ff0000", "#ffffff"],
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_1 = {
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_2 = {
color: "#eeeeee",
tick: function(pixel) {
var target = randomChoice(currentPixels);
target.color = "rgb(0,0,0)"
},
category: "troll machines",
insulate: true,
state: "solid",
excludeRandom: true,
desc: "Paints random pixels black"
},
elements.troll_3 = {
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_4 = {
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: ["#000000", "#ff00ff", "#000000", "#ff00ff", "#000000", "#ff00ff", "#000000", "#ff00ff"],
tool: function(pixel) {
tryMove(pixel,pixel.x,pixel.y+0.25);
pixelTempCheck(pixel)
},
category: "cursed tools",
},
elements.offset_half_y = {
color: ["#000000", "#ff00ff", "#000000", "#ff00ff", "#000000", "#ff00ff", "#000000", "#ff00ff"],
tool: function(pixel) {
tryMove(pixel,pixel.x,pixel.y+0.5);
pixelTempCheck(pixel)
},
category: "cursed tools",
},
elements.offset_three_fourth_y = {
color: ["#000000", "#ff00ff", "#000000", "#ff00ff", "#000000", "#ff00ff", "#000000", "#ff00ff"],
tool: function(pixel) {
tryMove(pixel,pixel.x,pixel.y+0.75);
pixelTempCheck(pixel)
},
category: "cursed tools",
},
elements.troll_5 = {
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_6 = {
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_7 = {
color: "#eeeeee",
tick: function(pixel) {
var target = randomChoice(currentPixels);
if(target && !(isEmpty(target.x,target.y))) {
target.temp += (Math.floor(Math.random() * 500 + 1) - 250)
}
},
category: "troll machines",
insulate: true,
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"] }
},
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: "#FFFFFF",
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); //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",
"tick": function(pixel) {
pixel.r ??= 0;
if(isNaN(pixel.r)) { return false };
pixel.rSpeed ??= -0.03;
pixel.beamLength ??= 10;
pixel.beamTemp ??= 2000;
pixel.brevity ??= 5;
pixel.beamColor ??= "#FF0000";
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 += 0.5) { //twice the tries to try to reduce gaps in the beam
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
};
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.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: "#000000",
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: "#000000",
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: "#000000",
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: "#000000",
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: "#000000",
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: "#000000",
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: "#000000",
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: "#000000",
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: "#000000",
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: "#000000",
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: "#000000",
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: "#000000",
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: ["#ffffff","#000000"],
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: ["#000000","#000000","#7f7f7f","#ffffff","#ffffff"],
behavior: behaviors.POWDER,
category: "powders",
state: "solid",
density: 1400,
tick: function(pixel) {
if(pixel.y % 6 == 0) {
if(pixel.x % 6 == 0) {
pixel.color = "rgb(255,255,255)"
} else {
if(!settings.bg || settings.bg == "#000000") {
pixel.color = "rgb(15,15,15)"
} else {
pixel.color = "rgb(0,0,0)"
}
}
} else if((pixel.y + 3) % 6 == 0) {
if((pixel.x + 3) % 6 == 0) {
pixel.color = "rgb(255,255,255)"
} else {
if(!settings.bg || settings.bg == "#000000") {
pixel.color = "rgb(15,15,15)"
} else {
pixel.color = "rgb(0,0,0)"
}
}
} else {
if(!settings.bg || settings.bg == "#000000") {
pixel.color = "rgb(15,15,15)"
} else {
pixel.color = "rgb(0,0,0)"
}
}
},
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","#ffffff","#ffffff"],
behavior: behaviors.GAS,
category: "gases",
state: "gas",
density: 550,
tick: function(pixel) {
if(pixel.y % 6 == 0) {
if(pixel.x % 6 == 0) {
pixel.color = "rgb(255,255,255)"
} 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 = "rgb(255,255,255)"
} 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","#ffffff","#ffffff"],
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 = "rgb(255,255,255)"
} 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 = "rgb(255,255,255)"
} 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 = "#000000";
}
}
},
color: ["#000000", "#000000", "#000000", "#000000", "#000000", "#000000", "#000000", "#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 = changeLuminance(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", "#000000"],
["white", "#FFFFFF"],
["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 ?? changeLuminance(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", "#FFFFFF"], //white (cursed)
["gy", "#7F7F7F"], //gray (more cursed)
["bl", "#000000", "#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 }
};
//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: ["#000000","#ff0000","#000000","#ff8800","#000000","#ffff00","#000000","#00ff00","#000000","#00ffff","#000000","#0000ff","#000000","#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,whiteColor,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: ["#ffffff","#202020","#ffffff","#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,
}
//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", "#000000", "#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", "#000000", "#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) {
var bottomFortyPercent = Math.round(height * 0.6);
var bottomTwentyPercent = Math.round(height * 0.8);
var bottomTenPercent = Math.round(height * 0.9);
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
if(!isEmpty(x,y,true)) {
pixelMap[x][y].vy ??= 0;
pixelMap[x][y].vy -= 20;
};
if(y > bottomTenPercent && !isEmpty(x,y,true)) {
pixelMap[x][y].temp += 2000;
};
if(Math.random() < chance) {
explodeAt(x,y,radius,"plasma");
};
};
};
if(doColorChange) {
settings.bg = ["#000000","#000000","#000000","#000000","#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();
pixel.active = false;
changePixel(pixel,"metal_scrap");
};
if(pixel.active) {
var pixelDistanceFromBottom = height - pixel.y;
var counterDistanceFromBottom = height - (pixel.y + pixel.counter);
var closenessToBottom = 1 - (counterDistanceFromBottom / pixelDistanceFromBottom);
//console.log(closenessToBottom);
var finalRadius = Math.round(((1 + closenessToBottom) ** 2) * 6);
if(typeof(explodeAtPlus) === "function") {
explodeAtPlus(pixel.x,pixel.y+pixel.counter,finalRadius,"plasma","fire",null,planetCrackerHeat);
} else {
explodeAt(pixel.x,pixel.y+pixel.counter,finalRadius,"plasma");
};
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: "#FFFFFF",
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 = "rgb(255,255,255)";
}
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 = "rgb(255,255,255)";
}
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 = "rgb(255,255,255)";
}
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: "#ffffff",
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)" ]];
aa = (0 - (Math.floor(pixel.arr[0].length / 2))) //Center align code
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++) { //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
};
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) {
clearAll();
rebuildCurrentPixels();
var save = localStorage.getItem("quicksave");
if(!save) {
if(doFailAlert) { alert("No save exists") };
return false;
} else {
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;
if(json.settings) {
settings = json.settings;
};
//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) {
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 simulationAlternatively, copy simulation JSONNo file loader status
One file, please:
Or paste JSONNo text loader statusNo size setter status
Pixel size (rendering only): (Use if the save looks cut off)
`;
elements.save_loader = {
behavior: behaviors.SELFDELETE,
excludeRandom: true,
color: "#FFFFFF",
desc: saveLoaderDescription,
};
//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 ((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:"+formatTemp(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 (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 += ""+viewKey[view]+"";
}
statsDiv.innerHTML = stats;
}
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) { //prop prompt listener
quickSlDetectorLastKeys.push(e.key);
if(quickSlDetectorLastKeys.length > 3) {
quickSlDetectorLastKeys.shift();
};
justPromptedQuickSL = false;
});
document.addEventListener("keydown", function(e) { //prop prompt listener
if (quickSlDetectorLastKeys.join(",") == "(,(,L") {
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 = [];
};
});
} 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,luminance=50) {
saturation = Math.max(0,Math.min(100,saturation))
luminance = Math.max(0,Math.min(100,luminance))
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}%,${luminance}%)`;
} 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}%,${luminance}%)`;
};
};
};
};
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 = {r: 0, g: 0, b: 0};
pinkObject = {r: 255, g: 148, b: 255};
elements.black_pink_test = {
color: ["#000000","#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 || 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 ??= "#000000";
function getBackgroundColorOrAverageAsJSON() {
if(!(settings?.bg)) {
return {r: 0, g: 0, b: 0};
} 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.bioooze = {
color: ["#53FF4F", "#53FF4F", "#06DE00", "#04A600", "#036E00"],
behavior: behaviors.LIQUID,
tempHigh: 100,
stateHigh: ["plague","slime","steam","poison"],
//tempLow: -4,
//stateLow: "bioooze_ice",
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": ["bioooze","bioooze","poison","slime",null], "elem2": "infection" },
"soap": { "elem1": "slime", "chance": 0.02 },
"plant": { "elem1": ["bioooze","bioooze","poison","slime",null], "elem2": "dead_plant" },
"grass": { "elem1": ["bioooze","bioooze","poison","slime",null], "elem2": "dead_plant" },
"algae": { "elem1": ["bioooze","bioooze","poison","slime",null], "elem2": "dead_plant" },
"mushroom_spore": { "elem1": ["bioooze","bioooze","poison","slime",null], "elem2": "dead_plant" },
"lichen": { "elem1": ["bioooze","bioooze","poison","slime",null], "elem2": "dead_plant" },
"rat": { "elem1": ["bioooze","bioooze","poison","slime",null], "elem2": "rotten_meat" },
"frog": { "elem1": ["bioooze","bioooze","poison","slime",null], "elem2": "rotten_meat" },
"fish": { "elem1": ["bioooze","bioooze","poison","slime",null], "elem2": "rotten_meat" },
"bird": { "elem1": ["bioooze","bioooze","poison","slime",null], "elem2": "rotten_meat" },
"head": { "elem1": ["bioooze","bioooze","poison","slime",null], "elem2": "rotten_meat" },
"body": { "elem1": ["bioooze","bioooze","poison","slime",null], "elem2": "rotten_meat" },
"ant": { "elem1": ["bioooze","bioooze","bioooze","bioooze","poison","slime",null], "elem2": "dust" },
"worm": { "elem1": ["bioooze","bioooze","bioooze","bioooze","poison","slime",null], "elem2": "dust" },
"fly": { "elem1": ["bioooze","bioooze","bioooze","bioooze","poison","slime",null], "elem2": "dust" },
"firefly": { "elem1": ["bioooze","bioooze","bioooze","bioooze","poison","slime",null], "elem2": "dust" },
"bee": { "elem1": ["bioooze","bioooze","bioooze","bioooze","poison","slime",null], "elem2": "dust" },
"slug": { "elem1": ["bioooze","bioooze","bioooze","bioooze","poison","slime",null], "elem2": "dust" },
"snail": { "elem1": ["bioooze","bioooze","bioooze","bioooze","poison","slime",null], "elem2": "calcium" },
"sapling": { "elem1": ["bioooze","bioooze","poison","slime",null], "elem2": "dead_plant" },
"root": { "elem1": ["bioooze","bioooze","poison","slime",null], "elem2": "dead_plant" },
"flower_seed": { "elem1": ["bioooze","bioooze","poison","slime",null], "elem2": "dead_plant" },
"pistil": { "elem1": ["bioooze","bioooze","poison","slime",null], "elem2": "dead_plant" },
"petal": { "elem1": ["bioooze","bioooze","poison","slime",null], "elem2": "dead_plant" },
"grass_seed": { "elem1": ["bioooze","bioooze","poison","slime",null], "elem2": "dead_plant" },
"meat": { "elem1": ["bioooze","bioooze","poison","slime",null], "elem2": "rotten_meat" },
"wood": { "elem1": ["bioooze","bioooze","poison","slime",null], "elem2": "sawdust", "chance": 0.25 }
},
/*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 ?? "#000000");
settingPicker.setAttribute("onchange","settings.pageBG = this.value; document.body.style.background = this.value; saveSettings();");
bgSettingSpan.appendChild(settingPicker);
settingsMenu.appendChild(bgSettingSpan);
});
settings.pageBG ??= "#000000"; 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"
},
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 = "#000000"
}
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) {
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,
},
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,
},
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,
},
//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,
},
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,
},
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,
},
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,
},
//Gold exists
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 = ["bismuth","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.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 !== "bismuth") {
pixel.temp -= 0.05; //incentivize cooling
pixel.element = "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.bismuth.tempHigh) {
newPixel.temp -= 0.05;
newPixel.element = "bismuth";
newPixel.crystalColorCounter = (pixel.crystalColorCounter + 1) % 8;
newPixel.color = pixelColorPick(pixel,bismuthCrystalColorArray[pixel.crystalColorCounter % 8]);
};
};
};
};
};
//mooreDoHeat(pixel);
};
elements.molten_bismuth = {
color: "#d1c6b0", //not really hot enough to be red
behavior: behaviors.LIQUID,
tempLow: -Infinity, //suppress normal freezing mechanism
stateLow: "molten_bismuth",
tick: function(pixel) {
bismuthCrystallization(pixel);
},
density: 10050,
state: "liquid",
category: "liquids",
temp: 300,
tempHigh: 1560,
fireColor: "#4275db",
};
runAfterAutogen(function() {
delete elements.molten_bismuth.tempLow;
delete elements.molten_bismuth.stateLow;
});
elements.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.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.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 = ["bismuth","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",
"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 reactionStealerImmutableElem2(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 ?? 800;
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", "#ffffff", "#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]) || !reactionStealerImmutableElem2(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) { reactionStealerImmutableElem2(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;
};
reactionStealerImmutableElem2(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: "rgb(0,0,0)", 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: "rgb(0,0,0)", 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: "rgb(255,255,255)", 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: "rgb(0,0,0)", 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: "rgb(255,255,255)", group: "NewJeans"} ],
"17-05": { member: "JiU", color: "rgb(255,255,255)", 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: "rgb(255,255,255)", 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: "rgb(0,0,0)", 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: "rgb(255,255,255)", 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: "rgb(255,255,255)", 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 = "rgb(255,255,255)";
};
//console.log(name);
//console.log(color);
color == "rgb(0,0,0)" ? 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
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 luminance
for(i = 0; i < finalColor.length; i+=3) {
finalColor[i] = changeLuminance(finalColor[i],1.25,"multiply","hsljson");
};
//leave offset-1 colors as-is
for(i = 2; i < finalColor.length; i+=3) {
finalColor[i] = changeLuminance(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);
};
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(changeLuminance(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";
elements.concrete.tick = 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))) { //decresingly 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 = changeLuminance(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 = changeLuminance(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 = changeLuminance(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 = changeLuminance(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 = changeLuminance(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 = changeLuminance(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 = changeLuminance(pixel.color,6,"+",colorWasHSL ? "hsl" : "rgb");
newPixel.wet++;
var newColorWasHSL = newPixel.color.startsWith("hsl");
newPixel.color = changeLuminance(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;
};
};
};
newConcreteTick = elements.concrete.tick;
runAfterLoad(function() { //mamma mia that's some tasty spaghetti
if(elements.hanging_concrete) {
elements.hanging_concrete.tick = function(pixel) {
newConcreteTick(pixel);
};
};
if(elements.attach_concrete) {
oldAttachConcreteTick = elements.attach_concrete.tick ;
elements.attach_concrete.tick = function(pixel) {
oldAttachConcreteTick(pixel);
newConcreteTick(pixel);
};
};
if(elements.crumbling_concrete) {
oldCrumblingConcreteTick = elements.crumbling_concrete.tick ;
newConcreteTick = elements.concrete.tick ;
elements.crumbling_concrete.tick = function(pixel) {
oldCrumblingConcreteTick(pixel);
newConcreteTick(pixel);
}
}
});
//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); x.l *= (26/15); x.l += 5 }); 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)) && ["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: 1e12,
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 = [
"XX|XX|XX",
"XX|EX:80>plasma,plasma,plasma,plasma,plasma,plasma,plasma,plasma,plasma,plasma,molten_iron,molten_uranium,molten_lead AND CH:neutron_star,neutron_star,neutron_star,neutronium,quark_matter,void|XX",
"XX|XX|XX",
]
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,"#ffffff"); 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", "#ffffff", "#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", "#ffffff"],
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 rgbStringToUnvalidatedObject(string) {
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) {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) {
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) {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 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 !!"#FFFFFF".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 "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: ["#000000","#00ff00","#000000","#00ff00","#000000","#00ff00","#000000","#00ff00","#000000","#00ff00"],
behavior: behaviors.SELFDELETE,
desc: "Click here or press Shift+1 to open the command prompt.",
category:"special",
};
//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) {
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;
}
}
}
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.bioooze = { 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",
properties: {
range: 10,
pushStrength: 1
},
tick: function(pixel) {
pixel.range ??= 10;
pixel.pushStrength ??= 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",
properties: {
range: 10,
pushStrength: 1
},
tick: function(pixel) {
pixel.range ??= 10;
pixel.pushStrength ??= 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",
properties: {
range: 10,
pushStrength: 1
},
tick: function(pixel) {
pixel.range ??= 10;
pixel.pushStrength ??= 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",
properties: {
range: 10,
pushStrength: 1
},
tick: function(pixel) {
pixel.range ??= 10;
pixel.pushStrength ??= 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: {
range: 10,
pushTime: 0,
pushLength: 5,
pushStrength: 1
},
tick: function(pixel) {
pixel.range ??= 10;
pixel.pushTime ??= 0;
pixel.pushLength ??= 5;
pixel.pushStrength ??= 1;
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: {
range: 10,
pushTime: 0,
pushLength: 5,
pushStrength: 1
},
tick: function(pixel) {
pixel.range ??= 10;
pixel.pushTime ??= 0;
pixel.pushLength ??= 5;
pixel.pushStrength ??= 1;
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: {
range: 10,
pushTime: 0,
pushLength: 5,
pushStrength: 1
},
tick: function(pixel) {
pixel.range ??= 10;
pixel.pushTime ??= 0;
pixel.pushLength ??= 5;
pixel.pushStrength ??= 1;
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: {
range: 10,
pushTime: 0,
pushLength: 5,
pushStrength: 1
},
tick: function(pixel) {
pixel.range ??= 10;
pixel.pushTime ??= 0;
pixel.pushLength ??= 5;
pixel.pushStrength ??= 1;
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 luminance = 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);
luminance = slBound(luminance + 20);
color = `hsl(${hue},${saturation}%,${luminance}%)`;
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 luminance = parseFloat(color[2].slice(0,-2));
//console.log("the j");
luminance = slBound(luminance + 1.176);
//console.log(luminance);
color = `hsl(${hue},${saturation}%,${luminance}%)`;
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 luminance = 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);
luminance = slBound(luminance + 20);
color = `hsl(${hue},${saturation}%,${luminance}%)`;
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 luminance = parseFloat(color[2].slice(0,-2));
luminance = slBound(luminance + 1.176);
color = `hsl(${hue},${saturation}%,${luminance}%)`;
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 luminance = 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);
luminance = slBound(luminance + 20);
color = `hsl(${hue},${saturation}%,${luminance}%)`;
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 luminance = parseFloat(color[2].slice(0,-2));
luminance = slBound(luminance + (2 * 1.176));
color = `hsl(${hue},${saturation}%,${luminance}%)`;
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 luminance = 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);
luminance = slBound(luminance + 20);
color = `hsl(${hue},${saturation}%,${luminance}%)`;
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 luminance = parseFloat(color[2].slice(0,-2));
//console.log("the j");
luminance = slBound(luminance + 1.176);
//console.log(luminance);
color = `hsl(${hue},${saturation}%,${luminance}%)`;
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 luminance = 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);
luminance = slBound(luminance + 20);
color = `hsl(${hue},${saturation}%,${luminance}%)`;
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 luminance = parseFloat(color[2].slice(0,-2));
luminance = slBound(luminance + 1.176);
color = `hsl(${hue},${saturation}%,${luminance}%)`;
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 luminance = 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);
luminance = slBound(luminance + 20);
color = `hsl(${hue},${saturation}%,${luminance}%)`;
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 luminance = parseFloat(color[2].slice(0,-2));
//console.log("the j");
luminance = slBound(luminance + 1.176);
//console.log(luminance);
color = `hsl(${hue},${saturation}%,${luminance}%)`;
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 luminance = 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);
luminance = slBound(luminance + 20);
color = `hsl(${hue},${saturation}%,${luminance}%)`;
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 luminance = parseFloat(color[2].slice(0,-2));
luminance = slBound(luminance + 1.176);
color = `hsl(${hue},${saturation}%,${luminance}%)`;
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 luminance = 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);
luminance = slBound(luminance + 20);
color = `hsl(${hue},${saturation}%,${luminance}%)`;
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 luminance = parseFloat(color[2].slice(0,-2));
//console.log("the j");
luminance = slBound(luminance + 1.176);
//console.log(luminance);
color = `hsl(${hue},${saturation}%,${luminance}%)`;
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 luminance = 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);
luminance = slBound(luminance + 20);
color = `hsl(${hue},${saturation}%,${luminance}%)`;
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 luminance = parseFloat(color[2].slice(0,-2));
luminance = slBound(luminance + 1.176);
color = `hsl(${hue},${saturation}%,${luminance}%)`;
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.");
};
},
};
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: panic set to 0");
pixel.mood = 0;
};
//console.log(`Bounding code running from value of ${pixel.panic}`);
pixel.mood = Math.max(-3,Math.min(3,pixel.mood));
//console.log(`Validation result: Panic set to ${pixel.panic}`);
};
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
}; //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 += 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 += 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 amongst themselves)
["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"]]
];
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
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) • Feedback • Wiki • YouTube • Discord • Install Offline
v${currentversion} • ${elementCount} elements, with ${hiddenCount} hidden.