sandboxels/mods/anmm.js

567 lines
20 KiB
JavaScript
Raw Permalink Normal View History

urlParams = new URLSearchParams(window.location.search);
function elementExists(elementName) {
return typeof(elements[elementName]) === "object";
};
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)
};
};
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];
};
window.addEventListener("load",function() {
currentElement = urlParams.get("currentElement") ?? "sand";
if(!elementExists(currentElement)) {
currentElement = "sand"
};
selectElement(currentElement);
var size = urlParams.get("mouseSize") ?? 5;
if(isNaN(size)) {
size = 5;
};
mouseSize = size
var _tps = urlParams.get("tps") ?? "30";
if(isNaN(parseInt(_tps))) {
_tps = 30;
};
tps = parseInt(_tps);
if(tps !== 30) {
resetInterval(tps)
};
if(enabledMods.includes("mods/cursor_shapes.js")) {
var shape = urlParams.get("currentShape") ?? "square";
if(shapeOrder.indexOf(shape) == -1) {
shape = "square"
};
currentShape = shape
};
/*if(urlParams.get("pause") !== null) {
paused = true;
document.getElementById("pauseButton").setAttribute("on","true")
};*/
});
2025-02-04 19:40:52 -05:00
function getEmptyVonNeumannNeighbors(pixel,returnOffsets=false) {
var neighbors = [];
var x = pixel.x;
var y = pixel.y;
for(var i = 0; i < adjacentCoords.length; i++) {
2025-02-04 19:40:52 -05:00
var offsetX = adjacentCoords[i][0];
var offsetY = adjacentCoords[i][1];
var finalX = pixel.x + offsetX;
var finalY = pixel.y + offsetY;
if(isEmpty(finalX,finalY,false)) {
2025-02-04 19:40:52 -05:00
neighbors.push(returnOffsets ? [offsetX,offsetY] : [finalX,finalY])
};
};
return neighbors
};
mooreDonutCoords = [[-1, -1], [0, -1], [1, -1], [-1, 0], [1, 0], [-1, 1], [0, 1], [1, 1]]; //Moore neighborhood (includes corners) minus center, as opposed to the von Neumann neighborhood which is the + shape.
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
};
2025-02-04 19:40:52 -05:00
function getEmptyMooreNeighbors(pixel,returnOffsets=false) {
var neighbors = [];
var x = pixel.x;
var y = pixel.y;
for(var i = 0; i < mooreDonutCoords.length; i++) {
2025-02-04 19:40:52 -05:00
var offsetX = mooreDonutCoords[i][0];
var offsetY = mooreDonutCoords[i][1];
var finalX = pixel.x + offsetX;
var finalY = pixel.y + offsetY;
if(isEmpty(finalX,finalY,false)) {
2025-02-04 19:40:52 -05:00
neighbors.push(returnOffsets ? [offsetX,offsetY] : [finalX,finalY])
};
};
return neighbors
};
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;
};
var dw = "dirty_water";
elements.mud.reactions.water = {elem1: ["mud","mud","mud","muddy_water"], elem2: "muddy_water", chance: 0.01 };
elements.muddy_water = {
color: "#2D468B",
behavior: behaviors.LIQUID,
tempHigh: 100,
stateHigh: ["steam","steam","steam","steam","mudstone"],
tempLow: 0,
stateLow: ["ice","ice","ice","ice","permafrost"],
category: "liquids",
heatCapacity: 4.184,
reactions: {
"muddy_water": {elem1: ["water","water","water","water","mud"], elem2: ["water","water","water","water","mud"], chance: 0.0005},
//everything else
"dust": { elem1: dw, elem2: null },
"ash": { elem1: dw, elem2: null },
"cyanide": { elem1: dw, elem2: null },
"cyanide_gas": { elem1: dw, elem2: null },
"sulfur": { elem1: dw, elem2: null },
"rat": { elem1: dw, chance:0.005 },
"plague": { elem1: dw, elem2: null },
"rust": { elem1: dw, chance:0.005 },
"lead": { elem1: dw, chance:0.005 },
"solder": { elem1: dw, chance:0.005 },
"fallout": { elem1: dw, chance:0.25 },
"radiation": { elem1: dw, chance:0.25 },
"uranium": { elem1: dw, chance:0.25 },
"rad_steam": { elem1: dw, chance:0.02 },
"rad_glass": { elem1: dw, chance:0.005 },
"rad_shard": { elem1: dw, chance:0.01 },
"rotten_meat": { elem1: dw, chance:0.25 },
"rotten_cheese": { elem1: dw, chance:0.25 },
"cancer": { elem1: dw, chance:0.25 },
"oil": { elem1: dw, chance:0.005 },
"dioxin": { elem1: dw, chance:0.1 },
"rock": { elem2: "wet_sand", chance: 0.00035 },
"limestone": { elem2: "wet_sand", chance: 0.00035 },
"tuff": { elem2: "wet_sand", color2:"#7a6b5c", chance: 0.00035 },
"ruins": { elem2: "rock", chance: 0.00035 },
"mudstone": { elem2: "mud", chance: 0.00015 },
"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.125, oneway:true },
"firefly": { elem2:"dead_bug", chance:0.125, oneway:true },
"bee": { elem2:"dead_bug", chance:0.0625, oneway:true },
"stink_bug": { elem2:"dead_bug", chance:0.125, oneway:true },
"cured_meat": { elem1:"dirty_water", elem2:"meat" },
//can't bubble because that would require arrays to work on attr2
},
state: "liquid",
density: 1100,
conduct: 0.02,
stain: -0.5,
extinguish: true
}
elements.glass_shard.reactions ??= {};
elements.rad_shard.reactions ??= {};
elements.glass_shard.reactions.body = {elem2: ["meat","meat","meat","meat","blood"], chance: 0.05};
elements.rad_shard.reactions.body = {elem2: ["meat","meat","cooked_meat","ash","blood","cancer"], chance: 0.05};
elements.glass_shard.reactions.head = {elem2: ["meat","meat","bone","meat","bone","blood"], chance: 0.025};
elements.rad_shard.reactions.head = {elem2: ["meat","meat","bone","cooked_meat","ash","blood","bone","cancer"], chance: 0.025};
var hasArgon = (elementExists("argon") && elementExists("liquid_argon"));
var nitrogenChance = ((1.204 * 0.7808) / elements.liquid_nitrogen.density);
var oxygenChance = ((1.204 * 0.2095) / elements.liquid_oxygen.density);
var snowChance = (0.00933 / elements.snow.density); //based on New York City in May https://www.omnicalculator.com/physics/absolute-humidity https://weather-and-climate.com/average-monthly-Humidity-perc,New-York,United-States-of-America https://weather-and-climate.com/average-monthly-min-max-Temperature,New-York,United-States-of-America
var argonChance; if(hasArgon == true) { argonChance = ((1.204 * 0.0093) / elements.liquid_argon?.density) } else { argonChance = 0 }; // is it just me or has JavaScript almost entirely restricted the use of ternary operators?
var condensationMultiplierScaling = Math.abs(-273.15 - (-190));
2025-02-04 19:40:52 -05:00
function glaciliteBase(pixel,coolingAmount,maximumTemperature,cmsScaling=1.5) {
if(pixel.temp > maximumTemperature) {
if(pixel.temp - coolingAmount <= maximumTemperature) {
pixel.temp = maximumTemperature
} else {
pixel.temp = pixel.temp - coolingAmount;
}
};
var condensationMultiplier = 1;
2025-02-04 19:40:52 -05:00
if(pixel.temp < -190) { //NOT scaled to maximum temperature OR absolute zero
var distanceFromNegative190 = Math.abs((-190) - pixel.temp);
condensationMultiplier = Math.max(6,1 + (distanceFromNegative190 / (condensationMultiplierScaling / cmsScaling)));
}
var emptyNeighbors = getEmptyVonNeumannNeighbors(pixel);
if(emptyNeighbors.length > 0) {
if((pixel.temp < elements.nitrogen.tempLow) && (Math.random() < (nitrogenChance * condensationMultiplier))) {
var randomEmptySpace = randomChoice(emptyNeighbors);
let np = createPixel("liquid_nitrogen",...randomEmptySpace); if(np) { np.temp = pixel.temp };
emptyNeighbors = getEmptyVonNeumannNeighbors(pixel)
}
};
if(emptyNeighbors.length > 0) {
if((pixel.temp < elements.oxygen.tempLow) && (Math.random() < (oxygenChance * condensationMultiplier))) {
var randomEmptySpace = randomChoice(emptyNeighbors);
let np = createPixelReturn("liquid_oxygen",...randomEmptySpace); if(np) { np.temp = pixel.temp };
emptyNeighbors = getEmptyVonNeumannNeighbors(pixel)
}
};
if((emptyNeighbors.length > 0)) {
if((pixel.temp < elements.water.tempLow) && (Math.random() < (snowChance * condensationMultiplier))) {
var randomEmptySpace = randomChoice(emptyNeighbors);
let np = createPixelReturn("snow",...randomEmptySpace); if(np) { np.temp = pixel.temp };
emptyNeighbors = getEmptyVonNeumannNeighbors(pixel)
}
}
if(hasArgon && (emptyNeighbors.length > 0)) {
if((pixel.temp < elements.argon.tempLow) && (Math.random() < (argonChance * condensationMultiplier))) {
var randomEmptySpace = randomChoice(emptyNeighbors);
let np = createPixelReturn("liquid_argon",...randomEmptySpace); if(np) { np.temp = pixel.temp };
emptyNeighbors = getEmptyVonNeumannNeighbors(pixel)
}
}
}
elements.glacilite = {
behavior: behaviors.WALL,
state: "solid",
category: "solids",
density: 7228,
hardness: 0.32,
temp: -190,
breakInto: "glacilite_shard",
color: ["#17BBF6","#70A4FE","#6ADCEE","#8EA9FF"],
tempHigh: 1200,
2025-02-04 19:40:52 -05:00
stateHigh: "liquified_glacilite",
tick: function(pixel) {
glaciliteBase(pixel,5,-190)
}
}
elements.glacilite_shard = {
behavior: behaviors.POWDER,
state: "solid",
category: "powders",
hidden: true,
density: 3819,
hardness: 0.89,
temp: -190,
color: ["#17BBF6","#70A4FE","#6ADCEE","#8EA9FF"],
tempHigh: 1200,
2025-02-04 19:40:52 -05:00
stateHigh: "liquified_glacilite",
tick: function(pixel) {
glaciliteBase(pixel,5,-190)
}
}
2025-02-04 19:40:52 -05:00
elements.liquified_glacilite = {
behavior: behaviors.LIQUID,
state: "liquid",
category: "liquids",
hidden: true,
density: 4171,
temp: -260,
hardness: 0.99,
color: ["#01BFE1","#2404C3","#0763DB","#04308F","#3AF0F3"],
tempHigh: 1200,
2025-02-04 19:40:52 -05:00
stateHigh: "liquified_glacilite",
tick: function(pixel) {
2025-02-04 19:40:52 -05:00
var maximumTemperature = (settings.abszero ?? -273.15);
var coolingAmount = 14;
var cmsScaling = 3.5;
2025-02-04 19:40:52 -05:00
if(pixel.temp > maximumTemperature) {
if(pixel.temp - coolingAmount <= maximumTemperature) {
pixel.temp = maximumTemperature
} else {
pixel.temp = pixel.temp - coolingAmount;
}
};
var condensationMultiplier = 1;
2025-02-04 19:40:52 -05:00
if(pixel.temp < -150) { //NOT scaled to maximum temperature OR absolute zero
var distanceFromNegative190 = Math.abs((-190) - pixel.temp);
condensationMultiplier = Math.max(6,1 + (distanceFromNegative190 / (condensationMultiplierScaling / cmsScaling)));
};
var mooreNeighbors = getPixelMooreNeighbors(pixel);
for(let i = 0; i < mooreNeighbors.length; i++) {
if(Math.random() < (0.02 * condensationMultiplier)) {
let otherPixel = mooreNeighbors[i];
let otherElem = otherPixel.element;
let otherData = elements[otherElem];
let otherTL = otherData.tempLow;
let otherSL = otherData.stateLow;
if((otherTL == undefined) || (otherSL == undefined)) {
otherPixel.temp = pixel.temp;
} else if((typeof(otherTL) == "number") && (typeof(elements[otherSL]) == "string")) {
changePixel(otherPixel,otherSL,true);
otherPixel.temp = Math.min(pixel.temp,otherTL - 5);
pixelTempCheck(pixel);
}
}
};
var emptyNeighbors = getEmptyMooreNeighbors(pixel);
if(emptyNeighbors.length > 0) {
if((pixel.temp < elements.nitrogen.tempLow) && (Math.random() < (nitrogenChance * condensationMultiplier))) {
var randomEmptySpace = randomChoice(emptyNeighbors);
let np = createPixel("liquid_nitrogen",...randomEmptySpace); if(np) { np.temp = pixel.temp };
emptyNeighbors = getEmptyMooreNeighbors(pixel)
}
};
if(emptyNeighbors.length > 0) {
if((pixel.temp < elements.oxygen.tempLow) && (Math.random() < (oxygenChance * condensationMultiplier))) {
var randomEmptySpace = randomChoice(emptyNeighbors);
let np = createPixelReturn("liquid_oxygen",...randomEmptySpace); if(np) { np.temp = pixel.temp };
emptyNeighbors = getEmptyMooreNeighbors(pixel)
}
};
if((emptyNeighbors.length > 0)) {
if((pixel.temp < elements.water.tempLow) && (Math.random() < (snowChance * condensationMultiplier))) {
var randomEmptySpace = randomChoice(emptyNeighbors);
let np = createPixelReturn("snow",...randomEmptySpace); if(np) { np.temp = pixel.temp };
emptyNeighbors = getEmptyMooreNeighbors(pixel)
}
}
if(hasArgon && (emptyNeighbors.length > 0)) {
if((pixel.temp < elements.argon.tempLow) && (Math.random() < (argonChance * condensationMultiplier))) {
var randomEmptySpace = randomChoice(emptyNeighbors);
let np = createPixelReturn("liquid_argon",...randomEmptySpace); if(np) { np.temp = pixel.temp };
emptyNeighbors = getEmptyMooreNeighbors(pixel)
}
}
}
}
2025-02-04 19:40:52 -05:00
function igniscidBase(pixel,heatingAmount,minimumTemperature,nerfExponent) {
if(pixel.temp < minimumTemperature) {
if(pixel.temp + heatingAmount >= minimumTemperature) {
pixel.temp = minimumTemperature
} else {
pixel.temp = pixel.temp + heatingAmount;
}
};
var nerfFactor = 1;
if(pixel.temp > minimumTemperature) { nerfFactor = 1 / (Math.log10((pixel.temp - (minimumTemperature - 1))) + 1) ** nerfExponent };
if(Math.random() < (0.04 * nerfFactor)) { pixel.temp += heatingAmount };
var emptyNeighbors = getEmptyVonNeumannNeighbors(pixel,true);
var element = "fire";
var temp = pixel.temp;
if(pixel.temp >= 7000) {
element = "plasma";
temp = Math.max(temp,5000);
} else if(pixel.temp < 100) {
element = "smoke";
pixel.temp += 1.5;
};
for(var i = 0; i < emptyNeighbors.length; i++) {
var coords = emptyNeighbors[i];
var distance = coords.map(x => Math.abs(x)).reduce((a,b) => a + b);
coords = [coords[0]+pixel.x,coords[1]+pixel.y];
var chance = 1/distance;
var newPixel = createPixelReturn(element,...coords);
if(newPixel && (newPixel.temp < pixel.temp)) {
newPixel.temp = temp
}
}
}
elements.igniscid = {
behavior: behaviors.WALL,
state: "solid",
category: "solids",
density: 3300,
hardness: 0.44,
temp: 1200,
breakInto: "igniscid_shard",
color: ["#f68317","#f6b317","#eeea6a","#ffa38e"],
tempHigh: 11000,
stateHigh: "liquified_igniscid",
tick: function(pixel) {
igniscidBase(pixel,4,1200,1.2)
}
}
elements.igniscid_shard = {
behavior: behaviors.POWDER,
state: "solid",
category: "powders",
hidden: true,
density: 3100,
hardness: 0.89,
temp: 1200,
color: ["#f68317","#f6b317","#eeea6a","#ffa38e"],
tempHigh: 11000,
stateHigh: "liquified_igniscid",
tick: function(pixel) {
igniscidBase(pixel,4,1200,1.2)
}
}
elements.liquified_igniscid = {
behavior: behaviors.LIQUID,
state: "liquid",
category: "liquids",
hidden: true,
density: 2790,
temp: 12000,
hardness: 0.99,
color: ["#f77b0f","#e0410b","#db4a07","#f5ac7f","#f3593a"],
tick: function(pixel) {
igniscidBase(pixel,12,100000,0.5)
}
}
elements.frozen_fertilizer = {
color: ["#4d341d","#40301c","#3d2814"],
behavior: [
"CR:stench,stench,methane%0.25|CR:stench,stench,methane%0.5|CR:stench,stench,methane%0.25",
"CR:stench,stench,methane%0.5|XX|CR:stench,stench,methane%0.5",
"CR:stench,stench,methane%0.25|CR:stench,stench,methane%0.5|CR:stench,stench,methane%0.25"
],
behavior: behaviors.WALL,
tempHigh: 0,
stateHigh: "fertilizer",
category: "solids",
hardness: 0.35,
breakInto: "fertilizer",
state: "solid",
density: 1060,
stain: 0.01,
hidden: true,
},
elements.fertilizer = {
color: ["#451f0a","#542b07","#593317"],
behavior: [
"CR:stench,stench,methane%0.25|CR:stench,stench,methane%0.5|CR:stench,stench,methane%0.25",
"M2 AND CR:stench,stench,methane%0.5|XX|M2 AND CR:stench,stench,methane%0.5",
"M1|M1|M1"
],
viscosity: 8000,
tempHigh: 40,
stateHigh: ["poison","stench","methane","dried_fertilizer","dried_fertilizer","dried_fertilizer","dried_fertilizer","dried_fertilizer","dried_fertilizer","dried_fertilizer","dried_fertilizer"],
tempLow: 0,
stateLow: "frozen_fertilizer",
category: "solids",
2025-02-04 19:47:03 -05:00
state: "solid",
2025-02-04 19:40:52 -05:00
density: 1060,
stain: 0.07
},
elements.dried_fertilizer = {
color: ["#4f382b","#66462b","#704a2b"],
behavior: [
"CR:stench,stench,methane%0.15|CR:stench,stench,methane%0.3|CR:stench,stench,methane%0.15",
"CR:stench,stench,methane%0.3|XX|CR:stench,stench,methane%0.3",
"M2|M1|M2"
],
tempHigh: 100,
stateHigh: ["steam","steam","steam","steam","steam","steam","steam","steam","steam","steam","steam","steam","steam","steam","steam","steam","steam","steam","stench","poison","stench","poison","methane","methane","cooked_fertilizer","cooked_fertilizer","cooked_fertilizer","cooked_fertilizer","cooked_fertilizer","cooked_fertilizer","cooked_fertilizer","cooked_fertilizer","cooked_fertilizer","cooked_fertilizer","cooked_fertilizer","cooked_fertilizer",],
category: "solids",
state: "solid",
density: 1100,
stain: 0.045,
hidden: true
},
elements.cooked_fertilizer = {
color: ["#473931","#594636","#4f3621"],
behavior: [
"CR:stench,stench,methane%0.1|CR:stench,stench,methane%0.2|CR:stench,stench,methane%0.1",
"CR:stench,stench,methane%0.2|XX|CR:stench,stench,methane%0.2",
"M2|M1|M2"
],
tempHigh: 550,
stateHigh: ["charcoal","hydrogen","oxygen","nitrogen","sulfur_gas","metal_scrap","calcium","stench","poison_gas","steam","dust"],
category: "solids",
state: "solid",
density: 1240,
burn: 3,
burnTime: 260,
fireElement: ["stench","smoke","carbon_dioxide","poison_gas","fire","fire","fire","fire"],
burnInto: ["charcoal","hydrogen","oxygen","nitrogen","molten_sulfur","metal_scrap","calcium","stench","poison_gas","steam","dust","stench","poison_gas","smoke"],
hidden: true
},
elements.fartium = {
behavior: behaviors.LIQUID,
state: "liquid",
category: "liquids",
density: elements.water.density,
hardness: 0.4,
breakInto: ["fire","pop","stench","sulfur_gas"],
color: ["#9B8E66","#699941","#869637"],
tempHigh: 800,
stateHigh: ["fire","explosion","fire","stench","stench"],
burn: 0.8,
burnTime: 150,
2025-02-04 19:40:52 -05:00
burnInto: ["stench","stench","stench","stench","fire","fire","fire","fire","liquified_sulfur","explosion"],
tick: function(pixel) {
var chanceModifier = Math.min(0.22,(pixel.temp - 20) / 750);
if(chanceModifier < 0) { chanceModifier *= 2 };
chanceModifier = Math.max(-0.055,chanceModifier);
if(Math.random() < (0.06 + chanceModifier)) {
var emptyNeighbors = getEmptyMooreNeighbors(pixel);
var amountDecider = Math.random();
var amount;
if(amountDecider < 0.2) {
amount = 0
} else if(amountDecider < 0.3) {
amount = 1
} else if(amountDecider < 0.5) {
amount = 2
} else if(amountDecider < 0.75) {
amount = 3
} else if(amountDecider < 0.85) {
amount = 4
} else if(amountDecider < 0.95) {
amount = 5
} else {
amount = 6
};
for(let i = 0; i < amount; i++) {
var typeDecider = Math.random();
var type;
if(typeDecider < 0.8) {
type = "methane"
} else if(typeDecider < 0.995) {
type = "stench"
} else if(typeDecider < 0.998) {
type = Math.random() < 0.8 ? "mud" : "fertilizer";
} else {
type = "sulfur"
}
if(emptyNeighbors.length > 0) {
var randomEmptySpace = randomChoice(emptyNeighbors);
let np = createPixel(type,...randomEmptySpace); if(np) {
np.temp = pixel.temp; pixelTempCheck(np);
if(type == "fertilizer") { pixelColorPick(np,elements.mud.color) };
}
emptyNeighbors = getEmptyMooreNeighbors(pixel);
if(emptyNeighbors.length == 0) { break };
}
}
}
}
}
2025-02-04 19:47:03 -05:00