diff --git a/CITATION.cff b/CITATION.cff index c87d93c7..b6cae29e 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -8,11 +8,35 @@ message: >- metadata from this file. type: software authors: - - given-names: R74n - email: contact@r74n.com - affiliation: R74n + - name: R74n + email: contact@R74n.com + date-start: '2017-05-02' + website: 'https://r74n.com/' +identifiers: + - type: doi + value: 10.17605/OSF.IO/H7TDY + - type: swh + value: 'swh:1:rev:5a5813a8f4f418540f1bdb765d293735541bf7fd' + - type: url + value: 'https://sandboxels.r74n.com/' + - type: doi + value: 10.5281/zenodo.10044909 + - type: other + value: /g/11spmybz10 + description: KGMID + - type: url + value: 'https://purl.org/r74n/sandboxels' + description: PURL + - type: url + value: 'https://w3id.org/r74n/sandboxels' + description: W3ID repository-code: 'https://github.com/R74nCom/sandboxels' url: 'https://sandboxels.r74n.com' +abstract: >- + Sandboxels is a free falling-sand simulator that can be + played in your browser. It features heat simulation, + electricity, density, chemical reactions, cooking, and + fire spread. keywords: - R74n - Sandboxels diff --git a/mod-list.html b/mod-list.html index 4242543b..1312c8fc 100644 --- a/mod-list.html +++ b/mod-list.html @@ -115,13 +115,13 @@ Tools & Settings adjustablepixelsize.jsAllows you to set the pixelSize with a URL parameterAlice +betaworldgen.jsadds a more advanced world generation to the gameAlex betterModManager.jsImprovements to the Mod Managerggod betterSettings.jsAdds additional settings and functionalityggod betterStats.jsSeparate “real” and “set” TPS, meaning you can see what the TPS actually is, instead of only seeing what it’s set tomollthecoder change.jsAdds a tool that only replaces existing pixelsAlice color_tools.jsAdds tools that manipulate colorsAlice controllable_pixel_test.jsAdds a pixel that can be controlled with the keyboard keys. Read the commit description for more info. [PC ONLY]Alice -customexplosion.jsAdded a custom explosion element and interface for it. check out its source code for how modders can use it.Alex cpt_alt.jsAdds a more destructive variant of the controllable pixelAlice debugRework.jsRevamps the Debug toolFioushemastor delete_all_of_element.jsAdds a tool that deletes every pixel of the element(s) the user clicks onAlice @@ -261,6 +261,7 @@ changeTempReactionParameter.jsAdds the changeTemp property to modded reactionsAlice code_library.jsAdds functions and variables common to some other modsAlice CrashTestDummy.jsOriginally a test to see if certain code broke the game, but now just adds a tool that turns things into sandStellarX20 +customexplosion.jsAdded a custom explosion element and interface for it. check out its source code for how modders can use it.Alex date_test.jsK-pop idol birthday testing stuffAlice drawPixels_change_test.jsA test of altering drawPixels(). Gives burning pixels a red overlay similar to the yellow overlay for charged pixelsAlice example_mod.jsAn example mod for new moddersR74n diff --git a/mods/betaworldgen.js b/mods/betaworldgen.js new file mode 100644 index 00000000..9c25dd1f --- /dev/null +++ b/mods/betaworldgen.js @@ -0,0 +1,128 @@ +//This mod was made by Alex the transfem, https://discord.com/users/778753696804765696 on discord and https://www.tiktok.com/@alextheagenenby?_t=8hoCVI3NRhu&_r=1 on tiktok. +function randomAlter(num, list){ + let r = Math.floor(Math.random() * list.length); + return (num + list[r]); +} +let avgheight = 0; +let seed = ""; +function getSeed(type = "plains", thickness = 15){ + seed = ""; + console.log(thickness) + if(thickness == 15){ + avgheight = Math.floor(Math.random() * (18 - 12 + 1)) + 12; + } else{ + avgheight = thickness; + } + console.log(avgheight) + + if(type == "plains"){ + let location = Math.floor(Math.random(0, pixelMap.length) * 100); + let i = 0; + while (i < pixelMap.length){ + if (i !== location){ + seed += `${randomAlter(avgheight, [0, 1, 1, 2, 0, 0])}|`; + i += 1; + } else if (i == location){ + let height = `${Math.floor(Math.random(40, pixelMap[i].length) * 10)}`; + let prevH = randomAlter(avgheight, [1, 1, 1, 2, 0, 0]); + while (height > prevH){ + prevH = randomAlter(prevH, [0, 1, 1, 2, 0, 0, 0, 1]); + seed += `${prevH}|`; + } + + i += 1; + + } + } + return seed; + } + if(type == "desert"){ + let i = 0; + while (i < pixelMap.length){ + seed += `${randomAlter(avgheight, [0, 1, 1, 2, 0, 0])}|`; + i += 1; + + } + } + return seed; + } +function spawnElements(seed, list, height2 = 1, condition = [1, 1, 0]){ + console.log(list); + let width = pixelMap.length - 1; + let element; + let height = pixelMap[1].length - 1; + console.log(seed); + let seedArray = seed.split("|"); + console.log(seedArray); + seedArray.splice(seedArray.indexOf(""), 1); + seedArray.splice(pixelMap.length); + console.log(seedArray); + let i = 0; + while (i < seedArray.length - 1){ + + let ii = 0; + while (ii < seedArray[i]){ + if((Math.floor(Math.random() * (condition[0] - condition[1] + 1))) == condition[2]){ + if (height2 != 1){ + element = list[Math.floor(Math.random() * list.length)]; + createPixel(element, (width - 1) - i, (height - (height2 + 1)) - ii); + } else{ + element = list[Math.floor(Math.random() * list.length)]; + createPixel(element, (width - 1) - i, (height - 1) - ii); + } + } + ii += 1; + } + + i += 1; + } +} + +function flat(){ + let iii = 0; + let flat = ""; + while (iii < pixelMap.length){ + flat += "1|"; + iii += 1; + } + return flat; +} +function processSeed(seed, type = "plains"){ + + console.log(flat()); + let seedsArray = seed.split(":"); + console.log(seedsArray); + if(type == "plains"){ + spawnElements(seedsArray[0],["rock","rock","rock","rock","rock","rock","metal_scrap","metal_scrap","metal_scrap","gold_coin","uranium","uranium","diamond","rock","iron","iron","iron","aluminum","aluminum","aluminum","aluminum","copper","copper","copper","zinc","zinc","rock","rock","rock","rock","rock","rock","rock","rock","rock","rock","rock","rock","rock","rock","rock"].sort(() => Math.random() - 0.5)); + setTimeout(function(){ spawnElements(seedsArray[1], ["dirt"], 24); }, 200); + setTimeout(function(){ spawnElements(flat(),["grass","grass","grass","sapling","flower_seed","grass","grass","pinecone","grass","grass","grass","grass","grass","grass","grass","grass","grass","grass"], 40); }, 300); + } else if(type == "desert"){ + spawnElements(seed, ["sand"]); + setTimeout(function(){ spawnElements(flat(), ["cactus"], 40, [6, 0, 3]); }, 100); + } +} +elements.worldGen = { + color: "#FFFFFF", + behavior: elements.erase.behavior, + temp: 2, + category: "tools", + insulate:true, + canPlace: false, + desc: "Generate worlds with random seeds or your own seeds.", + onSelect: function() { + let Seed = prompt("Enter desert or plains random generation! automatically set to plains."); + let regex = /[a-z]/; + if (regex.test(Seed)){ + if(Seed.toLowerCase() == "desert"){ + processSeed(getSeed("desert", 30), "desert"); + } + } else { + if (Seed == ""){ + seed = `${getSeed("plains", 20)}:${getSeed("plains", 8)}` + processSeed(seed); + } else{ + processSeed(Seed); + } + } + } +} diff --git a/mods/code_library.js b/mods/code_library.js index 6035ca3a..0afacccf 100644 --- a/mods/code_library.js +++ b/mods/code_library.js @@ -1,7 +1,15 @@ +libraryLoaded = true; +this.libraryLoaded = true; +window.libraryLoaded = true; + //URL urlParams = new URLSearchParams(window.location.search); +libraryLoaded = true; +this.libraryLoaded = true; +window.libraryLoaded = true; + //Objects //getKeyByValue code by SO UncleLaz: https://stackoverflow.com/questions/9907419/how-to-get-a-key-in-a-javascript-object-by-its-value @@ -10,6 +18,10 @@ return Object.keys(object).find(key => object[key] === value); }; +libraryLoaded = true; +this.libraryLoaded = true; +window.libraryLoaded = true; + //RNG //Random integer from 0 to n @@ -77,6 +89,10 @@ return Math.floor(randomFunction() * (max - min + 1)) + min }; +libraryLoaded = true; +this.libraryLoaded = true; +window.libraryLoaded = true; + //Arrays //Shallow array comparer by SO Tim Down: https://stackoverflow.com/a/10260204 @@ -158,6 +174,10 @@ }; }; +libraryLoaded = true; +this.libraryLoaded = true; +window.libraryLoaded = true; + //Checks //Element exists in the elements object @@ -260,6 +280,10 @@ return false; }; +libraryLoaded = true; +this.libraryLoaded = true; +window.libraryLoaded = true; + //Math(s) //Base n logarithm from https://stackoverflow.com/a/3019290 @@ -305,6 +329,10 @@ return (number - inMin) * (outMax - outMin) / (inMax - inMin) + outMin; } +libraryLoaded = true; +this.libraryLoaded = true; +window.libraryLoaded = true; + //Color function rgbStringToUnvalidatedObject(string) { //turns rgb() to {r,g,b} with no bounds checking @@ -1182,7 +1210,11 @@ //console.log(`Hexed to #${f(0)}${f(8)}${f(4)}`) return `#${f(0)}${f(8)}${f(4)}`; }; - + +libraryLoaded = true; +this.libraryLoaded = true; +window.libraryLoaded = true; + //Pixels function exposedToAir(pixel) { @@ -1540,6 +1572,10 @@ return true; }; +libraryLoaded = true; +this.libraryLoaded = true; +window.libraryLoaded = true; + //World function breakCircle(x,y,radius,respectHardness=false,changeTemp=false,defaultBreakIntoDust=false) { @@ -1639,6 +1675,10 @@ return true; }; +libraryLoaded = true; +this.libraryLoaded = true; +window.libraryLoaded = true; + //Logic function xor(c1,c2) { @@ -1651,6 +1691,10 @@ }; }; +libraryLoaded = true; +this.libraryLoaded = true; +window.libraryLoaded = true; + //currentPixels operations function findInCurrentPixels(x,y) { @@ -1710,6 +1754,10 @@ }; +libraryLoaded = true; +this.libraryLoaded = true; +window.libraryLoaded = true; + //Sugar functions function newElement(name="element_name",color="#FF00FF",otherProps={}) { @@ -1722,6 +1770,10 @@ return elements[name]; }; +libraryLoaded = true; +this.libraryLoaded = true; +window.libraryLoaded = true; + //Fixes //fix -1-caused ghost pixels @@ -1747,4 +1799,16 @@ } } }*/ + }; + +libraryLoaded = true; +this.libraryLoaded = true; +window.libraryLoaded = true; + +runAfterLoad(function() { + if(!libraryLoaded) { + libraryLoaded = true; + this.libraryLoaded = true; + window.libraryLoaded = true } +}) diff --git a/mods/heatglow.js b/mods/heatglow.js index d9b6e269..2fb1a58b 100644 --- a/mods/heatglow.js +++ b/mods/heatglow.js @@ -1,8 +1,9 @@ const heatfunc = function(pixel){ - if (pixel.ogR == null || pixel.ogG == null || pixel.ogB == null){ + if (pixel.ogR == null || pixel.ogG == null || pixel.ogB == null || !(pixel.element == pixel.ogElement)){ pixel.ogR = parseInt(pixel.color.slice(4, pixel.color.indexOf(',')), 10) pixel.ogG = parseInt(pixel.color.slice(pixel.color.indexOf(',') + 1, pixel.color.lastIndexOf(',')), 10) pixel.ogB = parseInt(pixel.color.slice(pixel.color.lastIndexOf(',') + 1, -1), 10) + pixel.ogElement = pixel.element }else{ pixel.gethigh = (elements[pixel.element].tempHigh) pixel.halftemp = ((20+pixel.gethigh)/2) @@ -24,7 +25,7 @@ const heatfunc = function(pixel){ } }; if (!eLists.metals) { eLists.metals = [] } - eLists.metals = eLists.metals.concat(["iron", "glass", "copper", "gold", "brass","steel","nickel","zinc","silver","aluminum","bronze","metal_scrap","oxidized_copper","tin","lead"]) + eLists.metals = eLists.metals.concat(["iron", "glass", "copper", "gold", "brass","steel","nickel","zinc","silver","aluminum","bronze","metal_scrap","oxidized_copper","tin","lead", "rose_gold"]) eLists.metals.forEach(metal => { const prefunc = elements[metal].tick; if (!prefunc){ diff --git a/mods/nousersthings.js b/mods/nousersthings.js index 883ebdb1..4f2d2288 100644 --- a/mods/nousersthings.js +++ b/mods/nousersthings.js @@ -1390,4 +1390,111 @@ elements.blackhole_storage = { }, movable: false, conduct: 1, +}, +elements.plutonium = { + color: ["#616161", "#4b4949", "#353232", "#211c1c"], + behavior: behaviors.STURDYPOWDER, + category: "powders", + tempHigh: 640, + stateHigh: "molten_plutonium", + state: "solid", + tick: function(pixel){ + if (Math.random() < 0.0007) { + changePixel(pixel, "neutron", false); + } else if (Math.random() < 0.0007) { + changePixel(pixel, "uranium", false); + } + }, + reactions: { + "neutron": { elem1:"pn_explosion", tempMin:400, chance:0.1 }, + "neutron": { temp1: 100, temp2: 100 }, + }, + density: 19186, +} +elements.molten_plutonium = { + color: ["#6b5133", "#743f26", "#7c2727"], + behavior: behaviors.LIQUID, + category: "states", + state: "liquid", + tempLow: 620, + stateLow: "plutonium", + tick: function(pixel){ + if (Math.random() < 0.0007) { + changePixel(pixel, "neutron", false); + } else if (Math.random() < 0.0007) { + changePixel(pixel, "uranium", false); + } + }, + reactions: { + "neutron": { elem1:"pn_explosion", tempMin:400, chance:0.1 }, + }, + density: 16629, +}, +elements.neutron.reactions = { + "uranium": { temp2:100 }, + "plutonium": { temp2: 100 } +}, +elements.pn_explosion = { + color: ["#ffb48f","#ffd991","#ffad91"], + behavior: [ + "XX|XX|XX", + "XX|EX:80>plasma,plasma,plasma,plasma,radiation,rad_steam,neutron|XX", + "XX|XX|XX", + ], + temp: 100000000, + category: "energy", + state: "gas", + density: 1000, + excludeRandom: true, + hidden: true, + alias: "plutonium nuclear explosion", + noMix: true +}, +elements.smasher = { + color: "#606060", + behavior: behaviors.WALL, + category: "machines", + tick: function(pixel){ + for (var i = 0; i < squareCoords.length; i++) { + var coord = squareCoords[i]; + var x = pixel.x+coord[0]; + var y = pixel.y+coord[1]; + if (!isEmpty(x,y)) { + var otherPixel = pixelMap[x][y]; + breakPixel(otherPixel); + } + } + }, + movable: false, +}, +elements.mixer = { + color: "#F0F0F0", + behavior: behaviors.WALL, + category: "machines", + tick: function(pixel){ + pixel.mixList = []; + for (var i = 0; i < squareCoords.length; i++) { + var coord = squareCoords[i]; + var x = pixel.x+coord[0]; + var y = pixel.y+coord[1]; + if (!isEmpty(x,y)) { + var otherPixel = pixelMap[x][y]; + pixel.mixList.push(otherPixel); + } + } + for (var i = 0; i < pixel.mixList.length; i++) { + var pixel1 = pixel.mixList[Math.floor(Math.random()*pixel.mixList.length)]; + var pixel2 = pixel.mixList[Math.floor(Math.random()*pixel.mixList.length)]; + swapPixels(pixel1,pixel2); + pixel.mixList.splice(pixel.mixList.indexOf(pixel1),1); + pixel.mixList.splice(pixel.mixList.indexOf(pixel2),1); + if (elements[pixel1.element].onMix) { + elements[pixel1.element].onMix(pixel1,pixel2); + } + if (elements[pixel2.element].onMix) { + elements[pixel2.element].onMix(pixel2,pixel1); + } + } + }, + movable: false, } diff --git a/mods/precisionDrawing.js b/mods/precisionDrawing.js new file mode 100644 index 00000000..5dbd5929 --- /dev/null +++ b/mods/precisionDrawing.js @@ -0,0 +1,58 @@ +let precisionDrawing = false +//mod made by feeshmaster + +//try out debugRework.js or any other mods made by me! +document.addEventListener("keydown", (e) => { + if (e.key == "w" && precisionDrawing) { + mousePos.y -= 1 + } else if (e.key == "a" && precisionDrawing) { + mousePos.x -= 1 + } else if (e.key == "s" && precisionDrawing) { + mousePos.y += 1 + } else if (e.key == "d" && precisionDrawing) { + mousePos.x += 1 + } else if (event.key === 'X' && event.shiftKey) { + console.log("precisionMode deactivated!") + precisionDrawing = !precisionDrawing + if (!precisionDrawing) { + mousePos = {x: "not", y: "updated"} + } + } +}) + +setTimeout(setUpOnStart, 1000) +function setUpOnStart() { + + getMousePos = (canvas, evt) => { + if (precisionDrawing) { + return mousePos; + } + // If evt.touches is defined, use the first touch + if (evt.touches) { + evt.preventDefault(); + evt = evt.touches[0]; + isMobile = true; + } + var rect = canvas.getBoundingClientRect(); + return { + // Round to nearest pixel + x: Math.round((evt.clientX - rect.left)/pixelSize-0.5), + y: Math.round((evt.clientY - rect.top)/pixelSize-0.5) + }; + } + +let controlsTable = document.getElementById("controlsTable"); +let row1 = document.createElement("tr"); +row1.innerHTML = `Mod controls`; +let row2 = document.createElement("tr"); +row2.innerHTML = `start precisionShift + X or toggle button(not released)`; +let row3 = document.createElement("tr"); +row3.innerHTML = `move in precisionW, A, S, D/arrows or buttons(not released)`; + + // Append the rows + controlsTable.appendChild(row1); + controlsTable.appendChild(row2); + controlsTable.appendChild(row3); + + +} diff --git a/mods/rays.js b/mods/rays.js index f862df47..0b039664 100644 --- a/mods/rays.js +++ b/mods/rays.js @@ -22,7 +22,7 @@ var runAfterAutogenMod = "mods/runAfterAutogen2.js"; var libraryMod = "mods/code_library.js"; if(enabledMods.includes(runAfterAutogenMod) && enabledMods.includes(libraryMod)) { -whenAvailable(["runAfterAutogen","libraryLoaded"], function() { +whenAvailable(["raaLoaded","libraryLoaded"], function() { runAfterAutogen(function() { snowAndIceCache = Object.keys(elements).filter(function(name) { return name.endsWith("snow") || name.endsWith("ice") || name == "rime" diff --git a/mods/runAfterAutogen2.js b/mods/runAfterAutogen2.js index 8ea63f87..8d3ea839 100644 --- a/mods/runAfterAutogen2.js +++ b/mods/runAfterAutogen2.js @@ -51,4 +51,6 @@ function createButtonsAndCountElements() { }, 10); }; -runAfterAutogen(createButtonsAndCountElements); \ No newline at end of file +runAfterAutogen(createButtonsAndCountElements); + +raaLoaded = true;