Merge pull request #1 from R74nCom/main

Merge update
This commit is contained in:
lllllllllwith10ls 2023-07-20 18:48:26 -05:00 committed by GitHub
commit bad272e853
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 1023 additions and 72 deletions

View File

@ -105,6 +105,20 @@
<p>The original <a href="https://sandboxels.r74n.com/changelog.txt">plain text version</a> of this is still maintained.</p>
</div>
<h2 id="1.8.4">[Version 1.8.4 - July 11, 2023]</h2>
<ul>
<li>+ Image placing<ul>
<li>+ Image tool in Special</li>
<li>+ Select any image from your computer</li>
<li>+ Place it on the canvas at any scale</li>
<li>+ Choose its element or disable smoothing in Settings</li>
<li>+ Burn it, blow it up, or make it a powder!</li></ul></li>
<li>+ Paste or Drag-and-Drop images!</li></ul></li>
<li>[Stay tuned for bigger updates in the coming months!]</li>
<li>[Bug Fixes]</li>
<li>~ Fixed: Old browsers that don't support ECMAScript 2016 crash</li>
</ul>
<h2 id="1.8.3">[Version 1.8.3 - May 16, 2023 - Deserted Update]</h2>
<ul>
<li>+ Cactus</li>

View File

@ -7,6 +7,18 @@ See sneak peaks for upcoming updates on the Discord: https://discord.gg/ejUc6YPQ
A fancier version of this changelog can be found here: https://sandboxels.r74n.com/changelog
[Version 1.8.4 - July 11, 2023]
+ Image placing
+ Image tool in Special
+ Select any image from your computer
+ Place it on the canvas at any scale
+ Choose its element or disable smoothing in Settings
+ Burn it, blow it up, or make it a powder!
+ Paste or Drag-and-Drop images!
[Bug Fixes]
~ Fixed: Old browsers that don't support ECMAScript 2016 crash
Stay tuned for bigger updates in the coming months!
[Version 1.8.3 - May 26, 2023 - Deserted Update]
+ Cactus
+ Fancy Changelog and Controls pages

View File

@ -101,6 +101,7 @@
<tr><td>Smooth view (Low performance)</td> <td><kbd>4</kbd></td></tr>
<tr><td>Toggle GUI</td> <td><kbd>F1</kbd></td></tr>
<tr><td>Capture screenshot</td> <td><kbd>C</kbd> or <kbd>F2</kbd></td></tr>
<tr><td>Paste Image</td> <td><kbd>Ctrl</kbd> + <kbd>V</kbd> or Drag and Drop</td></tr>
</table>
<h2>Button Info</h2>

View File

@ -44,7 +44,7 @@
<meta name="twitter:creator:id" content="1436857621827530753">
<script> // versioning info
currentversion = "1.8.3"
currentversion = "1.8.4"
</script>
<style>
.setting-span {
@ -190,7 +190,7 @@
if (pixel.charge && elements[pixel.element].behaviorOn) {
pixelTick(pixel)
}
if (elements[pixel.element].viscosity && (!((Math.random()*100) < 100 / ((elements[pixel.element].viscosity) ** 0.25)))) {
if (elements[pixel.element].viscosity && (!((Math.random()*100) < 100 / Math.pow(elements[pixel.element].viscosity, 0.25)))) {
var move1Spots = [
[pixel.x, pixel.y+1]
]
@ -209,7 +209,7 @@
else { move1Spots.splice(move1Spots.indexOf(coords), 1); }
}
if (!moved) {
if (elements[pixel.element].viscosity===undefined || !(!((Math.random()*100) < 100 / ((elements[pixel.element].viscosity) ** 0.25)))) {
if (elements[pixel.element].viscosity===undefined || !(!((Math.random()*100) < 100 / Math.pow(elements[pixel.element].viscosity, 0.25)))) {
if (Math.random() < 0.5) {
if (!tryMove(pixel, pixel.x+1, pixel.y)) {
tryMove(pixel, pixel.x-1, pixel.y);
@ -1717,6 +1717,32 @@
category: "special",
excludeRandom: true
},
"image": {
color: ["#78bbff","#5bb81a"],
onSelect: function() {
// prompt to upload an image file, then store the image in placingImage
var file = document.createElement("input");
file.type = "file";
file.accept = "image/*";
file.onchange = function() {
var reader = new FileReader();
reader.onload = function(e) {
var img = new Image();
img.onload = function() {
placingImage = img;
}
img.src = e.target.result;
}
reader.readAsDataURL(file.files[0]);
}
file.click();
},
onUnselect: function() {
placingImage = null;
},
tool: function() {},
category: "special",
},
"unpaint": {
color: ["#ffffff","#000000"],
tool: function(pixel) {
@ -9451,7 +9477,7 @@
default: break;
case "M1":
if (info.viscosity !== undefined) {
if (!((Math.random()*100) < 100 / ((info.viscosity) ** 0.25))) {
if (!((Math.random()*100) < 100 / Math.pow(info.viscosity, 0.25))) {
newCoords.x = x;
}
}
@ -9459,7 +9485,7 @@
break;
case "M2":
if (info.viscosity !== undefined) {
if (!((Math.random()*100) < 100 / ((info.viscosity) ** 0.25))) {
if (!((Math.random()*100) < 100 / Math.pow(info.viscosity, 0.25))) {
newCoords.x = x;
}
}
@ -10420,7 +10446,7 @@
if (info.hardness) { // lower damage depending on hardness(0-1)
if (info.hardness < 1) {
// more hardness = less damage, logarithmic
damage *= (1-info.hardness)**info.hardness;
damage *= Math.pow((1-info.hardness),info.hardness);
}
else { damage = 0; }
}
@ -10664,13 +10690,63 @@
else {
mouseType = "left";
}
if (shiftDown && e.button !== 1 && !((elements[currentElement].tool || elements[currentElement].category==="tools") && mouseType==="left")) {
if ((e.button === 0 || e.touches) && placingImage) {
if (e.touches) { mouseMove(e); }
placeImage();
return false;
}
else if (shiftDown && e.button !== 1 && !((elements[currentElement].tool || elements[currentElement].category==="tools") && mouseType==="left")) {
shaping = 1;
shapeStart = mousePos;
}
mouseMove(e);
return false;
}
function placeImage(placementX,placementY,scale) {
if (!scale) { scale = mouseSize }
// downscale the <img to mouseSize x mouseSize and draw it
var canvas = document.createElement("canvas");
// set width or height proportional to mouseSize
if (placingImage.width > placingImage.height) {
canvas.width = mouseSize;
canvas.height = Math.round(placingImage.height/placingImage.width*mouseSize);
}
else {
canvas.height = mouseSize;
canvas.width = Math.round(placingImage.width/placingImage.height*mouseSize);
}
var newWidth = canvas.width;
var newHeight = canvas.height;
var ctx = canvas.getContext("2d");
if (settings.imagesmooth === 0) {
ctx.webkitImageSmoothingEnabled = false;
ctx.mozImageSmoothingEnabled = false;
ctx.imageSmoothingEnabled = false;
}
ctx.drawImage(placingImage,0,0,newWidth,newHeight);
var newImage = ctx.getImageData(0,0,newWidth,newHeight);
// loop through each pixel in the ImageData
for (var x = 0; x < newWidth; x++) {
for (var y = 0; y < newHeight; y++) {
var i = (y*newWidth+x)*4;
var r = newImage.data[i];
var g = newImage.data[i+1];
var b = newImage.data[i+2];
var a = newImage.data[i+3];
if (a > 0.33) {
// mousePos is the center of the image
var pixelX = (placementX||mousePos.x) - Math.round(newWidth/2) + x+1;
var pixelY = (placementY||mousePos.y) - Math.round(newHeight/2) + y+1;
if (isEmpty(pixelX,pixelY)) {
var elem = (settings.imageelem || "wood");
if (!elements[elem]) { elem = "wood";}
createPixel(elem,pixelX,pixelY);
pixelMap[pixelX][pixelY].color = pixelColorPick(pixelMap[pixelX][pixelY], RGBToHex([r,g,b]));
}
}
}
}
}
function mouseUp(e) {
mouseIsDown = false;
if (shaping) {
@ -10697,7 +10773,7 @@
};
}
function mouseMove(e) {
if (mouseIsDown && !shaping) {
if (mouseIsDown && !shaping && !placingImage) {
mouseAction(e);
}
else {
@ -10986,6 +11062,9 @@
function selectElement(element) {
var e1 = document.getElementById("elementButton-"+currentElement);
if (e1 != null) { e1.setAttribute("current","false"); }
if (elements[currentElement].onUnselect) {
elements[currentElement].onUnselect();
}
currentElement = element;
if (elements[element].customColor) {
// show the colorSelector
@ -10995,6 +11074,9 @@
// hide the colorSelector
document.getElementById("colorSelector").style.display = "none";
}
if (elements[element].onSelect) {
elements[element].onSelect();
}
var e2 = document.getElementById("elementButton-"+element);
if (!e2) { return; }
e2.setAttribute("current","true");
@ -11980,6 +12062,7 @@ for (var k = 0; k < b0.split(" AND ").length; k++) {
shiftDown = 0;
shaping = 0;
shapeStart = null;
placingImage = null;
// On window load, run tick() 20 times per second
tps = 30;
tickInterval = window.setInterval(tick, 1000/tps);
@ -12548,6 +12631,55 @@ for (var k = 0; k < b0.split(" AND ").length; k++) {
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;
// for img file(s), read the file & draw to canvas
} else {
console.log(e.dataTransfer.files)
if (!e.dataTransfer.files || e.dataTransfer.files.length === 0) { return; }
var file = e.dataTransfer.files[0];
if (file.type.indexOf('image/') === -1) { return; }
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);
}
}, false);
// if pasted image, draw to canvas
window.addEventListener("paste", function(e){
if (e.clipboardData) {
var items = e.clipboardData.items;
if (!items) { return; }
var item = items[items.length-1];
if (item.type.indexOf('image/') === -1) { return; }
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;
}
}, 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?';
@ -12880,13 +13012,13 @@ for (var k = 0; k < b0.split(" AND ").length; k++) {
setSetting("lastversion",currentversion);
});
// wiki clicked check
if (!settings["clickedwiki"]) {
/*if (!settings["clickedwiki"]) {
document.getElementById("wikiButton").insertAdjacentHTML("beforeend",`<span style="color:red">(NEW)</span>`);
}
document.getElementById("wikiButton").addEventListener("click", function() {
document.getElementById("wikiButton").innerHTML = "Wiki";
setSetting("clickedwiki",true);
});
});*/
</script>
</div>
<div id="infoParent">
@ -12995,6 +13127,12 @@ Cancer, Landmine, Grenade, Smoke Grenade">?</span> <input type="button" value="O
</select>
<span onclick="clearAll();" style="font-style:italic;cursor:pointer"></span>
</span>
<span setting="imageelem" class="setting-span multisetting" title="Default: Wood">
Image Elem<input type="text" value="wood" onchange="if(this.value===''){this.value='wood'}; setSetting('imageelem',this.value.trim())">
</span>
<span setting="imagesmooth" class="setting-span multisetting" title="Default: ON">
Smoothing<input type="button" value="ON" class="toggleInput" onclick="toggleInput(this,'imagesmooth')" state="1">
</span>
</div>
</div>
</div>
@ -13003,6 +13141,10 @@ Cancer, Landmine, Grenade, Smoke Grenade">?</span> <input type="button" value="O
</div>
<div id="bottomInfoBox">
<div id="bottomTopBox">
<p>Want your ad here? Email us at <a href="mailto:contact@r74n.com">contact@r74n.com</a>!</p>
</div>
<div id="bottomLeftBox">
<h2>Sandboxels Introduction</h2>
<p><strong><def>Sandboxels</def></strong> is a free falling-sand simulator that can be played in your browser. It features heat simulation, electricity, density, chemical reactions, and fire spread. With over 400 unique elements to play with, Sandboxels is a great way to pass the time. There are thousands of interactions between the many materials, plants, animals, and fluids in the game.</p>
@ -13011,7 +13153,8 @@ Cancer, Landmine, Grenade, Smoke Grenade">?</span> <input type="button" value="O
<img src="icons/wallpaper.webp" style="width:100%" alt="Rainforest landscape made in Sandboxels" title="Rainforest landscape made in Sandboxels">
<p>Sandboxels has many applications in education. With a hands-on experience, it can give students an understanding of emerging phenomena in many fields, such as biology, chemistry, ecology, geology, and even virology. There are countless aspects to discover.</p>
<p>We have a thriving community on <a href="https://discord.gg/ejUc6YPQuS" target="_blank">Discord</a>! There you can post feedback or share your creations.</p>
<p>If you'd like to support us, consider donating on <a href="https://www.paypal.com/donate/?hosted_button_id=GCX4VHQ7SZWTN" target="_blank">PayPal</a> or <a href="https://cash.app/$emojiartist" target="_blank" title="$emojiartist">CashApp</a>, or subscribing on Discord.</p>
<!-- <p>If you'd like to support us, consider donating on <a href="https://www.paypal.com/donate/?hosted_button_id=GCX4VHQ7SZWTN" target="_blank">PayPal</a> or <a href="https://cash.app/$emojiartist" target="_blank" title="$emojiartist">CashApp</a>, or subscribing on Discord.</p> -->
<p>Business inquiries? Education stories? Help needed? Email us at <a href="mailto:contact@r74n.com">contact@r74n.com</a>!</p>
<p>Thanks to our Donators: Serioustar, Trent, u2ce</p>
<p>Sandboxels is developed by R74n. Check out <a href="https://r74n.com" target="_blank">our other projects</a>!</p>
<script>

View File

@ -6,11 +6,51 @@ let rPOWDER = behaviors.POWDER
console.log("Welcome to the console.");
console.log(rPOWDER);
elements.test = {
name: "Testium",
color: "#ff0000",
behavior: behaviors.POWDER,
category: "land",
state: "solid",
density: 15,
temp: 22,
tempHigh: 35,
stateHigh: "molten_testium",
reactions: {
"ilitium": { "elem1":"tralphium", "elem2":null },
"nickel": { "elem1":"iron", "elem2":null },
}
};
elements.molten_testium = {
name:"Liquid Testium",
color:"#0000ff",
behavior: behaviors.LIQUID,
category: "liquids",
state: "liquid",
density: 10,
temp: 50,
tempHigh: 450,
stateHigh: "testium_gas",
tempLow: 35,
stateLow: "test",
reactions: {
"ilitium": { "elem1":"tralphium", "elem2":null },
"molten_nickel": { "elem1":"molten_iron", "elem2":null },
},
};
elements.testium_gas = {
name:"Liquid Testium",
color:"#00ff00",
behavior: behaviors.GAS,
category: "gases",
state: "gas",
density: 5,
temp: 525,
tempLow: 450,
stateLow: "molten_testium",
reactions: {
"ilitium": { "elem1":"helium", "elem2":null },
},
};
elements.neutronium = {
name: "Neutronium",
@ -972,46 +1012,6 @@ tempLow: 1668,
temp: 2000,
viscosity: 10000
};
elements.toxin = {
color: "#07f71b",
category: "liquids",
state: "liquid",
behavior: behaviors.LIQUID,
stateLow: "toxic_ice",
tempLow: -10,
stateHigh: "toxic_gas",
tempHigh: 115,
reactions: {
"water": { "elem1":null, "elem2":"toxic_water" },
},
};
lifeArray = ["plant", "dead_plant", "frozen_plant", "grass", "algae", "cell", "cancer", "flea", "termite", "ant", "worm", "fly", "firefly", "bee", "human", "body", "head", "rat", "frog", "frozen_frog", "fish", "slug", "snail", "bone_marrow", "sapling", "seeds", "grass_seed", "wheat_seed", "wheat", "pollen", "flower_seed", "pistil", "petal", "vine", "bamboo", "bamboo_plant", "mushroom_spore", "mushroom_stalk", "mushroom_gills", "mushroom_cap", "hyphae", "lichen", "cellulose", "corn_seed", "potato_seed", "root", "berry_seed", "old_berry_leaf", "berry_leaf", "berry", "slime", "blood", "antibody", "infection", "meat", "rotten_meat", "frozen_meat", "yeast"]
if(!elements.toxin.reactions) {
elements.toxin.reactions = {}
}
for(i = 0; i < lifeArray.length; i++) {
elements.toxin.reactions[lifeArray[i]] = { "elem1":null, "elem2":"dead" }
};
/*
// (Ignore this comment if it's not in another comment anymore) this whole area is a comment only because I don't want the game to try using useless code and (possibly) error out.
if(!elements.toxic_gas.reactions) {
elements.toxic_gas.reactions = {}
}
for(i = 0; i < lifeArray.length; i++) {
elements.toxic_gas.reactions[lifeArray[i]] = { "elem1":null, "elem2":"dead" }
};
if(!elements.toxic_ice.reactions) {
elements.toxic_ice.reactions = {}
}
for(i = 0; i < lifeArray.length; i++) {
elements.toxic_ice.reactions[lifeArray[i]] = { "elem1":null, "elem2":"dead" }
};
if(!elements.toxic_water.reactions) {
elements.toxic_water.reactions = {}
}
for(i = 0; i < lifeArray.length; i++) {
elements.toxic_water.reactions[lifeArray[i]] = { "elem1":null, "elem2":"dead" }
}; */
elements.laser_emitter = {
color: "#8a8886",
category: "machines",
@ -1032,13 +1032,6 @@ behavior: behaviors.WALL,
behaviorOn: behaviors.LASEREMITTER,
conduct: 1,
};
elements.dead = {
color: "#a5a683",
category: "life",
state: "solid",
behavior: behaviors.POWDER,
density: 10,
};
elements.ilitium = {
color: "#97baa7",
category: "solids",

158
mods/betterModManager.js Normal file
View File

@ -0,0 +1,158 @@
var awaitingReload = [];
function updateModManager() {
const modManager = document.getElementById("modManager");
// make the title a bit better
// modManager.getElementsByClassName("menuTitle").item(0).innerHTML = '<span class="menuTitle">Mod Manager</span>';
document.getElementById("modManagerUrl").remove();
const button = document.createElement("button");
button.id = "modListOpen";
button.onclick = openModList;
button.innerText = "Open Mod List";
button.style.position = "absolute";
button.style.bottom = "5%";
button.style.width = "100%";
button.style.left = "0";
button.style.height = "50px";
button.style.backgroundColor = "#3c3c3c"
button.style.paddingLeft = "auto";
button.style.zIndex = 11;
modManager.appendChild(button);
const list = document.getElementById("modManagerList");
list.style.maxHeight = "60%";
list.style.overflowY = "scroll";
addModList();
}
function addModList() {
const parent = document.createElement("div");
parent.className = "menuParent"
parent.id = "modListParent";
parent.style.display = "none";
const modList = document.createElement("div");
modList.className = "menuScreen";
modList.id = "modList";
modList.innerHTML = `<button class="XButton" onclick="closeMenu();">-</button>
<span class="menuTitle">Mod List</span><br><br>
<span>NOTE: This list contains mods ONLY available on GitHub. For custom mods hosted elsewhere, enter JS url in the <a href="#modManagerUrl">.JS URL input box</a>.</span>`;
const modListUl = document.createElement("ul");
modListUl.id = "modListUl";
modListUl.style.maxHeight = "50%";
modListUl.style.overflowY = "scroll";
modList.appendChild(modListUl);
parent.appendChild(modList);
// add the modManagerUrl back, on a different screen
const modManagerUrl = document.createElement("input");
modManagerUrl.id = "modManagerUrl";
modManagerUrl.type = "text";
modManagerUrl.placeholder = ".JS URL...";
modManagerUrl.style.marginBottom = "10px";
modManagerUrl.onkeydown = (ev) => {
if (ev.key == "Enter") {
addMod(document.getElementById("modManagerUrl").value);
document.getElementById("modManagerUrl").value = '';
}
this.focus();
}
parent.appendChild(modManagerUrl);
document.getElementById("gameDiv").appendChild(parent);
const style = document.createElement("style");
style.innerHTML = `#modListUl { margin-top: 20px; }\n#modListUl li { position: relative; list-style-type: none; }\n#modListUl li::before { content: '•'; position: absolute; left: -1.5em; font-size: 1em; font-family: 'Press Start 2P'; }\n.addMod { color: #00cc00; }\n.awaitingReload { color: #666; }\n::-webkit-scrollbar { width: 10px; }\n::-webkit-scrollbar-track { background: #1f1f1f; }\n::-webkit-scrollbar-thumb { background: #aaa; }\n::-webkit-scrollbar-thumb:hover { background: #666; }`;
document.head.appendChild(style);
updateModList();
}
function updateModList() {
const modList = document.getElementById("modListUl");
// fetch all the mods from github
fetch("https://api.github.com/repos/R74nCom/sandboxels/git/trees/main?recursive=1").then(res => res.json())
.then(res => {
res.tree.filter(f => f.path.startsWith("mods/")).map(f => f.path).forEach(file => {
const link = document.createElement("a");
link.target = "_blank";
link.innerText = file.slice(5);
link.href = file;
const span = document.createElement("span");
if (awaitingReload.includes(file)) {
span.className = "awaitingReload";
span.innerText = " -";
} else if (enabledMods.includes(file)) {
span.className = "removeModX";
span.innerText = " X";
} else {
span.className = "addMod";
span.innerText = " +";
}
span.onclick = () => {
// makes sure that you cant modify mod state after its put into the awaiting reload group
// those mods will get updated when the site is reloaded and awaitingReload (because its not stored in localStorage) will get reset
if (awaitingReload.includes(file)) return;
if (enabledMods.includes(file)) {
// why on earth addMod adds "mods/" automatically and removeMod doesnt
removeMod(file.startsWith("mods/") ? file : "mods/" + file);
} else {
addMod(file.replace(/mods\//g, ""));
}
awaitingReload.push(file);
span.innerText = " -";
span.className = "awaitingReload";
}
const listItem = document.createElement("li");
listItem.appendChild(link);
listItem.appendChild(span);
modList.appendChild(listItem);
}
)})
}
function openModList() {
document.getElementById("modParent").style.display = "none";
const modParent = document.getElementById("modListParent");
modParent.style.display = "block";
showingMenu = "modList";
}
runAfterLoadList.push(updateModManager);
closeMenu = function() {
if (!showingMenu) { return; }
if (showingMenu == "info") {
var infoParent = document.getElementById("infoParent");
var infoSearch = document.getElementById("infoSearch");
infoParent.style.display = "none";
infoSearch.value = "";
showingMenu = false;
infoHistory = [];
}
else if (showingMenu == "mods") {
var modParent = document.getElementById("modParent");
var modManagerUrl = document.getElementById("modManagerUrl");
modParent.style.display = "none";
modManagerUrl.value = "";
showingMenu = false;
}
else if (showingMenu == "modList") {
var modParent = document.getElementById("modListParent");
var modManagerUrl = document.getElementById("modManagerUrl");
modParent.style.display = "none";
modManagerUrl.value = "";
showingMenu = false;
// open mod manager again so the mod list menu looks like a submenu
showModManager();
}
else if (showingMenu == "settings") {
var settingsParent = document.getElementById("settingsParent");
settingsParent.style.display = "none";
showingMenu = false;
}
else {
// do it to all elements with the class "menuParent"
var menuParents = document.getElementsByClassName("menuParent");
for (var i = 0; i < menuParents.length; i++) {
menuParents[i].style.display = "none";
}
showingMenu = false;
}
}

17
mods/descriptions.js Normal file
View File

@ -0,0 +1,17 @@
fetch("https://eoynkne6jon7kld.m.pipedream.net/descriptions-analytics").catch(_=>{});
console.log("descriptions.js: Loading vanilla descriptions...");
fetch (
"https://mollthecoder.github.io/Sandboxels-Descriptions/descriptions/vanilla.json",
{
referrer: "https://sandboxels.r74n.com/mods/descriptions.js"
}
).then(res=>{
res.json().then(json=>{
for(const element in json) {
// If the element doesn't exist (for example, nocancer2.js) then don't try to change it.
if(!elements.hasOwnProperty(element)) continue;
elements[element].desc = json[element];
}
console.log("descriptions.js: Loaded vanilla descriptions!");
});
});

8
mods/infLiqLight.js Normal file
View File

@ -0,0 +1,8 @@
let oldLiqLightTick = elements.liquid_light.tick;
let oldDelPixel = deletePixel;
elements.liquid_light.tick = (pixel)=>{
deletePixel = ()=>{};
oldLiqLightTick(pixel);
deletePixel = oldDelPixel;
}
window.addEventListener("load", ()=>{});

View File

@ -1,13 +1,15 @@
clearInterval(tickInterval);
const oldTick = tick;
let __registeredTickCallbacks = [];
window.addEventListener("load", ()=>{
let oldTick = tick;
clearInterval(tickInterval);
tick = function(){
oldTick();
__registeredTickCallbacks.forEach(func=>{
func();
});
}
tickInterval = setInterval(tick, 1000/tps);
});
function everyTick(callback){
__registeredTickCallbacks.push(callback);
}
tick = function(){
oldTick();
__registeredTickCallbacks.forEach(func=>{
func();
});
}
tickInterval = setInterval(tick, 1000/tps);

23
mods/oldTooltip.js Normal file
View File

@ -0,0 +1,23 @@
const defaultTooltip = "---";
let tooltipEle;
window.addEventListener("load", ()=>{
tooltipEle = document.createElement("p");
tooltipEle.innerHTML = defaultTooltip;
setTimeout(()=>{
document.getElementById("extraInfo").children[1].appendChild(tooltipEle);
let buttons = document.getElementsByClassName("elementButton");
[...buttons].forEach(button=>{
let ele = button.getAttribute("element");
button.addEventListener("mouseenter", e=>{
if(elements.hasOwnProperty(ele)) {
if(elements[ele].hasOwnProperty("desc")) {
tooltipEle.innerHTML = elements[ele].desc;
}
}
});
button.addEventListener("mouseleave", e=>{
tooltipEle.innerHTML = defaultTooltip;
});
});
});
});

View File

@ -1197,8 +1197,8 @@ Make sure to save your command in a file if you want to add this preset again.`
document.addEventListener("keydown", function(e) { //prop prompt listener
// , = propPrompt()
if (e.keyCode == 49) { //!
if(shiftDown) { funniPrompt() };
if ([1,3].includes(shiftDown) && e.keyCode == 49) { //either shift + 1
funniPrompt();
};
});

View File

@ -16,6 +16,7 @@ elements.legendary_energy = {
reactions: {
"magma": { "elem1": "armageddon", "elem2": null },
"void": { "elem1": "light", "elm2": null },
},
}
runAfterLoad(function() {
@ -33,10 +34,11 @@ runAfterLoad(function() {
state: "liquid",
density: 2000,
excludeRandom: true,
reactions" {
reactions: {
"magma": { "elem1": "armageddon", "elem2": null },
"void": { "elem1": "light", "elm2": null }
}
"void": { "elem1": "light", "elm2": null },
},
},
elements.banana_juice = {
name: "banana juice",
color: "#e0f542",

32
mods/tooltip.js Normal file
View File

@ -0,0 +1,32 @@
const devMode = false;
// Tippy depends on popper
const popperUrl = devMode ? "https://unpkg.com/@popperjs/core@2/dist/umd/popper.min.js" : "https://unpkg.com/@popperjs/core@2";
const tippyUrl = devMode ? "https://unpkg.com/tippy.js@6/dist/tippy-bundle.umd.js" : "https://unpkg.com/tippy.js@6";
window.addEventListener("load", ()=>{
let popper = document.createElement("script");
popper.src = popperUrl;
popper.addEventListener("load", () => {
let tippyScr = document.createElement("script");
tippyScr.src = tippyUrl;
tippyScr.addEventListener("load", ()=>main(), {passive: true});
document.body.appendChild(tippyScr);
}, {passive: true});
document.body.appendChild(popper);
}, {passive: true});
function main() {
[...document.getElementsByClassName("elementButton")].forEach(button=>{
let ele = elements[button.getAttribute("element")];
// if(ele === undefined || ele === null) return;
if(ele.desc) {
button.setAttribute("data-tippy-content", `<div>${ele.desc}</div>`);
if(ele.desc.includes("<!--INTERACTIVE-->")) {
button.setAttribute("data-tippy-interactive", true);
}
}
});
tippy("[data-tippy-content]", {
allowHTML: true,
duration: 0,
placement: "bottom"
});
}

543
mods/yumcherries.js. Normal file
View File

@ -0,0 +1,543 @@
var modName = "mods/cherries.js";
var onTryMoveIntoMod = "mods/onTryMoveInto.js";
var libraryMod = "mods/code_library.js";
if(enabledMods.includes(onTryMoveIntoMod) && enabledMods.includes(libraryMod)) {
randomNumberFromOneToThree = function() {
return 1 + Math.floor(Math.random() * 3)
};
debugSpeedGrowth = false;
logLeaves = false;
cherryAttachWhitelist = ["cherry_log","cherry_branch_1","cherry_branch_2","blossom","cherry_leaf","cherry_plant_top","cherry"];
cherryDirtElements = ["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.cherry_seed = {
color: "#8b4513",
tick: function(pixel) {
if(pixel.cherryRange === null) {
pixel.cherryRange = randomNumberFromOneToThree();
};
if (isEmpty(pixel.x,pixel.y+1)) {
movePixel(pixel,pixel.x,pixel.y+1);
} else {
if (Math.random() < (debugSpeedGrowth ? 0.09 : 0.03) && pixel.age > (debugSpeedGrowth ? 20 : 50) && pixel.temp < 100) {
if (!outOfBounds(pixel.x,pixel.y+1)) {
var dirtPixel = pixelMap[pixel.x][pixel.y+1];
if (cherryDirtElements.includes(dirtPixel.element)) {
changePixel(dirtPixel,"root");
};
};
if (isEmpty(pixel.x,pixel.y-1)) {
movePixel(pixel,pixel.x,pixel.y-1);
createPixel("cherry_log",pixel.x,pixel.y+1);
pixelMap[pixel.x][pixel.y+1].cherryRange = pixel.cherryRange; //pass cherry range down to log
};
} else if (pixel.age > (debugSpeedGrowth ? 500 : 1000)) {
changePixel(pixel,"cherry_plant_top");
};
pixel.age++;
};
if(Math.random() < 0.01 && pixel.age > 200) {
changePixel(pixel,"cherry_plant_top");
};
doDefaults(pixel);
},
properties: {
"age": 0,
//"cherryRange": null, //apparently this is suddenly, in an illogical, never-before-seen, completely new, unprecedented incident of bad behavior, evaluated before being put into the property database, so RNG has to be done in tick
"cherryRange": 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.cherry_log = {
hidden: true,
color: "#310a0b",
tick: function(pixel) {
if(pixel.cherryRange === null) {
pixel.cherryRange = 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("cherry_branch_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].cherryRange = pixel.cherryRange; //pass cherry range down to branch
if(Math.random() < 0.8) { pixel.grewPeduncle = true; } //20% chance to not mark as true, allowing for a chance to try another branch
};
};
};
};
pixel.age++;
doDefaults(pixel);
},
properties: {
"age": 0,
"grewPeduncle": false,
"cherryRange": 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.cherry = {
color: "#f7022a",
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(cherryAttachWhitelist.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_cherry");
};
};
},
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.cherry_branch_1 = {
hidden: true,
name: "cherry branch (offshoot)",
color: "#310a0b",
tick: function(pixel) {
if(pixel.cherryRange === null) {
pixel.cherryRange = 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]].cherryRange = pixel.cherryRange; //pass cherry range down to next pixel of branch horizontal
} else {
createPixel("cherry_branch_2",peduncleCoords2[0],peduncleCoords2[1]);
pixelMap[peduncleCoords2[0]][peduncleCoords2[1]].cherryRange = pixel.cherryRange; //pass cherry range down to diagonal offshoot
};
};
};
pixel.age++;
doDefaults(pixel);
},
properties: {
"dir": (!Math.floor(Math.random() * 2)) ? 1 : -1,
"age": 0,
//"cherryRange": (1 + (Math.floor(Math.random() * 3))), //1-3
"cherryRange": 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.cherry_branch_2 = {
hidden: true,
name: "cherry branch (hanging)",
color: "#310a0b",
tick: function(pixel) {
if(pixel.cherryRange === null) {
pixel.cherryRange = 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]].cherryRange = pixel.cherryRange; //pass cherry range down to next pixel of branch vertical
} else {
createPixel("blossom",...growthCoords); // cherry flower
};
};
};
//Make cherries
if (pixel.age > 40 && pixel.temp < 100) {
var cherryOffsets = [-1, 1]; //placed to the left, placed to the right
for(i = 0; i < cherryOffsets.length; i++) {
//console.log(`Looping through left and right positions: ${cherryOffsets}`);
for(j = 1; j < pixel.cherryRange + 1; j++) { //for max cherry distance, using the cherry range
//console.log(`Looping through cherry offset multipliers: ${j}`);
if (isEmpty(pixel.x+(j * cherryOffsets[i]),pixel.y,false)) { //if there's an empty space
//console.log(`Cherry position is empty: [${j * cherryOffsets[i]}, 0]\nTrying cherry at (${pixel.x+(j * cherryOffsets[i])},${pixel.y})`);
if (Math.random() < (debugSpeedGrowth ? 0.05 : 0.005)) { //try to place the cherry
//console.log(`Placing cherry`);
createPixel("cherry",pixel.x+(j * cherryOffsets[i]),pixel.y);
pixelMap[pixel.x+(j * cherryOffsets[i])][pixel.y].attached = true;
pixelMap[pixel.x+(j * cherryOffsets[i])][pixel.y].attachDirection = -1 * Math.sign(cherryOffsets[i]); //attach dir is the opposite of placement dir so it attaches towards the stem
} else {
//console.log(`NOT placing cherry`);
};
//console.log(`Cherry tried, stopping iteration`);
break; //and then stop iteration
} else {
//console.log(`Cherry position is NOT empty: [${j * cherryOffsets[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 cherry iterator<<<<`);
};
pixel.age++;
doDefaults(pixel);
//console.log(`\nEnd of branch tick\n`);
},
properties: {
"age": 0,
//"cherryRange": (1 + (Math.floor(Math.random() * 3))), //1-3
"cherryRange": 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_cherry = {
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_cherry = { "elem2":null, chance:0.15, func:behaviors.FEEDPIXEL };
elements.cherry_leaf = {
hidden: true,
color: "#9df24e",
tick: function(pixel) {
if(pixel.cherryRange === null) {
pixel.cherryRange = 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],
"cherryRange": 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 cherry leaves") }; //yes, this code is for cherry 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.cherry_leaf = { "elem2": "dead_plant" };*/
elements.cherry_plant_top = {
hidden: true,
color: "#310a0b",
tick: function(pixel) {
if(pixel.cherryRange === null) {
pixel.cherryRange = 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 cherry_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 cherry_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("cherry_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].cherryRange = pixel.cherryRange;
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 cherry_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 cherry_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("cherry_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].cherryRange = pixel.cherryRange;
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,
"cherryRange": 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: functi
};*/
} else {
enabledMods.splice(enabledMods.indexOf(modName),0,onTryMoveIntoMod);
enabledMods.splice(enabledMods.indexOf(modName),0,libraryMod);
localStorage.setItem("enabledMods", JSON.stringify(enabledMods));
alert(`The ${onTryMoveIntoMod} mod and ${libraryMod} mods are required and have been automatically inserted (reload for this to take effect).`);
};

View File

@ -41,6 +41,9 @@ a:active, a:hover:active {filter: brightness(275%);}
margin: 1.5em 50px 0px 50px;
font-family: 'Arial';
}
#bottomTopBox {
text-align: center;
}
#bottomLeftBox {
float: left;
width: 50%;