commit
bad272e853
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
165
index.html
165
index.html
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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!");
|
||||
});
|
||||
});
|
||||
|
|
@ -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", ()=>{});
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -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();
|
||||
};
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
});
|
||||
}
|
||||
|
|
@ -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).`);
|
||||
};
|
||||
Loading…
Reference in New Issue