sandboxels/mods/cubesstuff.js

2462 lines
76 KiB
JavaScript

// TypeScript integration for Sandboxels modding
// Enables function autocomplete & element definition hints
/// <reference path="./sandboxels-types.d.ts" />
// Get the file here: https://github.com/Cube14yt/sandboxels-types
// Changelog
// Starts at version 3
/*
V3
Tools: RGB LED, Dice, Custom Bomb
Life: Pineapple Plants (seed, stem, fruit)
Hazards: Lithium Battery, Lithium, Rubidium, Asbestos
Minerals: Chalk, Chalk Powder, Lapis Lazuli
Light: UV Light, Phosphor, Neon Tube
Extras: Realistic Ball
V3.1
Bug Fixes
Chalk powder, Wet chalk poeder, and Obsidian shard can now be glued back as intended.
Dog can now be smashed correctly.
Steam support with promptInput() instead of prompt()
V3.2
Machines: Robot, Adjustable heater/cooler
Bug Fixes
Fixed compatibility issue with nousersthings.js
V4
Machines: Paper filter, Indestructable filter, and Note block
Life: Cacao Plants (seed, stem, fruit)
Tools: Polish
Extras: 2 ways to make an element with no name
Special: Black hole
Building Materials: Roman concrete/cement
*/
elements.button = {
color: "#970000",
conduct: 1,
charge: 0,
category: "machines",
behavior: behaviors.WALL,
state: "solid",
onSelect: function () {
logMessage("Click the button with no elements equipped to charge the button.")
},
properties: {
clicked: false,
clickTime: 1,
},
onClicked: function (pixel) {
pixel.clicked = true
pixel.clickTime = 1
},
tick: function (pixel) {
if (pixel.clicked == true && pixel.clickTime > 0) {
pixel.charge = 1
pixel.clickTime--
}
else if (pixel.clicked == true && pixel.clickTime <= 0) {
pixel.clicked = false
pixel.charge = 0
}
}
}
function isPressable(pixel) {
if (elements[pixel.element].pressInto !== undefined) return true;
}
elements.aerogel = {
color: "#79ffff",
category: "solids",
behavior: behaviors.WALL,
state: "solid",
tempHigh: 1200,
stateHigh: "ash",
insulate: true,
density: 0.2,
hardness: 0.1,
breakInto: "dust",
onPlace: function (pixel) {
if (pixel.alpha === undefined) { pixel.alpha = Math.random() * (0.5 - 0.4) + 0.4 }
}
}
let oldCopperReactions = elements.copper.reactions
elements.molten_copper.reactions.molten_aluminum = { elem1: "molten_nordic_gold", elem2: null, chance: 0.5 }
elements.acid.ignore.push("nordic_gold")
elements.acid.ignore.push("nordic_gold_coin")
elements.nordic_gold = {
color: ["#f1db7c", "#e5c34b", "#d2a742", "#b98c31", "#a47320"],
tempHigh: 1060,
behavior: behaviors.WALL,
category: "solids",
state: "solid",
density: 8800,
conduct: 0.90,
breakInto: "nordic_gold_coin"
}
elements.nordic_gold_coin = {
color: ["#f1db7c", "#e5c34b", "#d2a742", "#b98c31", "#a47320"],
tempHigh: 1060,
stateHigh: "molten_nordic_gold",
behavior: behaviors.POWDER,
category: "powders",
state: "solid",
density: 8800,
conduct: 0.90,
alias: "euro_coin",
reactions: {
"glue": { elem1: "nordic_gold", elem2: null }
}
}
function randomColor() {
const letters = "0123456789ABCDEF";
let color = "#";
for (let i = 0; i < 6; i++) {
color += letters[Math.floor(Math.random() * 16)];
}
return color;
}
elements.disco_ball = {
color: "#ebebc3",
buttonColor: ["#ff0000", "#ff8800", "#ffff00", "#00ff00", "#00ffff", "#0000ff", "#ff00ff"],
renderer: renderPresets.LED,
behavior: behaviors.WALL,
category: "machines",
tempHigh: 1500,
stateHigh: ["molten_glass", "molten_glass", "molten_copper"],
conduct: 1,
breakInto: "glass_shard",
forceSaveColor: true,
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 (pixel.charge > 0) {
pixel.color = randomColor()
if (isEmpty(x, y)) {
createPixel("light", x, y)
let p = getPixel(x, y)
if (p !== null && p.element == "light") {
p.color = pixel.color
}
}
}
else { pixel.color = "#ebebc3" }
}
},
state: "solid"
}
elements.molten_iron.reactions.sulfur = { elem1: "pyrite", elem2: null, chance: 0.25 }
elements.molten_iron.reactions.molten_sulfur = { elem1: "pyrite", elem2: null, chance: 0.25 }
elements.molten_iron.reactions.sulfur_gas = { elem1: "pyrite", elem2: null, chance: 0.25 }
elements.pyrite = {
color: ["#d8c25e", "#bbaa49", "#998f3e"],
alias: ["fools_gold", "Iron Disulfide"],
density: 5000,
tempHigh: 1177,
stateHigh: ["iron", "molten_sulfur"],
grain: 0.4,
state: "solid",
behavior: behaviors.WALL,
category: "solids"
}
elements.fire_extinguisher_powder = {
color: "#ececec",
behavior: [
"XX|XX|XX",
"XX|DL%1|XX",
"M2%30|M1%30|M2%30"
],
extinguish: true,
tick: function (pixel) {
for (var i = 0; i < adjacentCoords.length; i++) {
var coords = adjacentCoords[i];
var x = pixel.x + coords[0];
var y = pixel.y + coords[1];
if (getPixel(x, y)?.burning === true) {
let elem = getPixel(x, y)
elem.burning = false
}
}
},
tool: function (pixel) {
if (pixel.burning === true) {
delete pixel.burning;
delete pixel.burnStart;
}
},
canPlace: true,
category: "powders",
state: "solid"
}
elements.pie_crust = {
color: "#f1f192",
breakInto: "crumb",
tempHigh: 500,
stateHigh: "ash",
burn: 5,
burnTime: 400,
burnInto: ["smoke", "smoke", "smoke", "ash"],
category: "food",
state: "solid",
behavior: behaviors.STURDYPOWDER,
isFood: true,
hidden: true,
density: 230,
reactions: {
"pumpkin": { elem1: "pie", elem2: null, tempMin: 200 },
"cooked_meat": { elem1: "pie", elem2: null, tempMin: 200 },
"meat": { elem1: "pie", elem2: null, tempMin: 200 },
"potato": { elem1: "pie", elem2: null, tempMin: 200 },
"mashed_potato": { elem1: "pie", elem2: null, tempMin: 200 },
"baked_potato": { elem1: "pie", elem2: null, tempMin: 200 },
"grape": { elem1: "pie", elem2: null, tempMin: 200, func: function (pixel) { pixel.originColor = "#8200fc" } },
"pineapple": { elem1: "pie", elem2: null, tempMin: 200, func: function (pixel) { pixel.originColor = "#ffd900" } }
}
}
elements.pie = {
color: "#fac145",
darkText: false,
behavior: behaviors.STURDYPOWDER,
category: "food",
isFood: true,
density: 240,
burn: 5,
burnTime: 400,
burnInto: ["smoke", "smoke", "smoke", "ash"],
state: "solid",
tempHigh: 500,
stateHigh: "ash",
breakInto: "sauce",
breakIntoColor: ["#ff822e", "#ff8c2e"],
tick: function (pixel) {
if (pixel.originColor) {
pixel.breakIntoColor = pixel.originColor
}
}
}
elements.gasoline = {
color: ["#ffff66", "#ffff55", "#ffff44"],
behavior: behaviors.LIQUID,
burn: 80,
burnTime: 100,
burnInto: ["fire", "fire", "fire", "explosion"],
viscosity: 0.7,
density: 750,
category: "liquids",
state: "liquid",
conduct: 0.02,
behaviorOn: [
"XX|XX|XX",
"XX|EX:10|XX",
"XX|XX|XX"
]
}
// Make molten sulfur stinky
elements.molten_sulfur.tick = function (pixel) {
for (var i = 0; i < adjacentCoords.length; i++) {
var coords = adjacentCoords[i];
var x = pixel.x + coords[0];
var y = pixel.y + coords[1];
if (isEmpty(x, y) && Math.random() <= 0.0005) {
createPixel("stench", x, y)
p = getPixel(x, y)
if (p !== null && p.element == "stench") {
p.temp = pixel.temp
}
}
}
}
elements.disco_floor = {
color: ["#ff0000", "#ff8800", "#ffff00", "#00ff00", "#00ffff", "#0000ff", "#ff00ff"],
breakInto: "glass_shard",
category: "machines",
forceSaveColor: true,
conduct: 1,
behavior: behaviors.WALL,
state: "solid",
tick: function (pixel) {
pixel.changeCd ??= 20;
pixel.changeCd--;
if (pixel.changeCd <= 0) {
let colors = elements.disco_floor.color;
pixel.color = colors[Math.floor(Math.random() * colors.length)];
pixel.changeCd = 20;
}
}
};
elements.moss = {
color: ["#007900", "#006000", "#008300"],
behavior: behaviors.POWDER,
tick: function (pixel) {
for (var i = 0; i < squareCoords.length; i++) {
var coords = squareCoords[i];
var x = pixel.x + coords[0];
var y = pixel.y + coords[1];
if (isEmpty(x, y) && Math.random() <= 0.01 && getPixel(pixel.x, pixel.y + 1) && getPixel(pixel.x, pixel.y + 1).element !== "moss") {
createPixel('moss', x, y)
}
}
},
tempHigh: 70,
stateHigh: "dead_plant",
extraTempHigh: {
"100": ["dead_plant", "dead_plant", "steam"]
},
tempLow: -10,
stateLow: "frozen_plant",
burn: 50,
burnTime: 30,
reactions: {
"carbon_dioxide": { elem2: "oxygen", chance: 0.1 },
"rock": { elem2: "dirt", chance: 0.0025 },
"rock_wall": { elem2: "dirt", chance: 0.0025 },
"gravel": { elem2: "dirt", chance: 0.0025 },
},
category: "life",
state: "solid"
}
elements.magma.extraTempLow = { "700": "obsidian" }
elements.obsidian = {
color: ["#1f1f1f", "#1f1f1f", "#1f1f1f", "#1f1f1f", "#292929"],
buttonColor: ["#1a1a1a", "#2b2b2b", "#3b3b3b"],
colorPattern: textures.GLASS,
colorKey: {
"g": "#1f1f1f",
"s": "#292929",
"S": "#252525"
},
grain: 0,
behavior: behaviors.WALL,
category: "solids",
tempHigh: 1200,
stateHigh: "magma",
state: "solid",
density: 2500,
breakInto: "obsidian_shard"
}
elements.obsidian_shard = {
color: ["#1f1f1f", "#1f1f1f", "#1f1f1f", "#1f1f1f", "#292929"],
behavior: behaviors.POWDER,
grain: 0,
category: "powders",
tempHigh: 1200,
stateHigh: "magma",
state: "solid",
density: 2500,
reactions: {
"glue": { elem1: "obsidian", elem2: null }
}
}
elements.cardboard = {
color: ["#9E6B34"],
burn: 70,
burnTime: 300,
burnInto: ["fire", "fire", "fire", "fire", "fire", "ash"],
behavior: behaviors.WALL,
reactions: {
"water": { elem1: "cellulose", elem2: null },
"dirty_water": { elem1: "cellulose", elem2: null },
"salt_water": { elem1: "cellulose", elem2: null },
"sugar_water": { elem1: "cellulose", elem2: null },
"seltzer": { elem1: "cellulose", elem2: null },
"soda": { elem1: "cellulose", elem2: null },
"blood": { elem1: "cellulose", elem2: null },
"foam": { elem1: "cellulose", elem2: null },
"bubble": { elem1: "cellulose", elem2: null },
"oil": { elem1: "cellulose", elem2: null },
"alcohol": { elem1: "cellulose", elem2: null },
"vinegar": { elem1: "cellulose", elem2: null }
},
category: "solids",
tempHigh: 248,
stateHigh: ["fire", "fire", "fire", "fire", "fire", "ash"],
state: "solid",
density: 1200
}
elements.paper.pressInto = "cardboard"
function pressPixel(pixel) {
if (elements[pixel.element].pressInto === undefined) { return; }
// if it is an array, choose a random item, else just use the value
let result;
if (elements[pixel.element].pressInto !== undefined) {
if (Array.isArray(elements[pixel.element].pressInto)) {
result = elements[pixel.element].pressInto[Math.floor(Math.random() * elements[pixel.element].pressInto.length)];
}
else {
result = elements[pixel.element].pressInto;
}
}
// change the pixel to the result
if (result === null) {
deletePixel(pixel.x, pixel.y);
return;
}
else if (result !== undefined) {
changePixel(pixel, result);
}
}
elements.press = {
color: ["#999999", "#c0c0c0", "#999999"],
category: "tools",
tool: function (pixel) {
// edited smash code
if (isPressable(pixel)) {
let old = pixel.element
if (Math.random() < (1 - (elements[pixel.element].resistPress || 0)) / (shiftDown ? 1 : 4)) {
pressPixel(pixel)
}
else if (old === pixel.element && elements[pixel.element].movable && !isEmpty(pixel.x, pixel.y + 1) && !paused) {
let x = 0; let y = 0;
if (Math.random() < 0.66) x = Math.random() < 0.5 ? 1 : -1;
if (Math.random() < 0.66) y = Math.random() < 0.5 ? 1 : -1;
tryMove(pixel, pixel.x + x, pixel.y + y)
}
}
}
}
elements.malware.reactions.wire = { elem2: [null, "faulty_wire"], chance: 0.01 };
elements.faulty_wire = {
color: ["#4d0a03", "#4d0a03", "#4d0a03", "#4d0a03", "#4d0a03", "#4d0a03", "#4d0a03", "#4d0a03", "#4d0a03", "#a95232",],
buttonColor: "#4d0a03",
behavior: behaviors.WALL,
behaviorOn: [
"XX|CR:electric,flash%1|XX",
"CR:electric,flash%1|XX|CR:electric,flash%1",
"XX|CR:electric,flash%1|XX"
],
category: "machines",
insulate: true,
conduct: 0.7,
noMix: true,
state: "solid"
}
elements.lighter_fluid = {
color: "#f1e185",
behavior: [
"XX|XX|XX",
"M2 AND SW:lighter_fluid_gas|XX|M2 AND SW:lighter_fluid_gas",
"M1 AND SW:lighter_fluid_gas|M1 AND SW:lighter_fluid_gas|M1 AND SW:lighter_fluid_gas"
],
category: "liquids",
state: "liquid",
density: 750,
tick: function (pixel) {
pixel.gasMade ??= 0
for (var i = 0; i < squareCoordsShuffle.length; i++) {
var coord = squareCoordsShuffle[i];
var x = pixel.x + coord[0];
var y = pixel.y + coord[1];
if (isEmpty(x, y) && Math.random() >= 0.75) {
createPixel("lighter_fluid_gas", x, y)
pixel.gasMade += 1
}
}
if (pixel.gasMade > 100 && Math.random() <= 0.01) {
deletePixel(pixel.x, pixel.y)
}
}
}
elements.lighter_fluid_gas = {
color: "#f1e185",
alpha: 0.1,
behavior: behaviors.GAS,
category: "gases",
state: "gas",
burn: 100,
isGas: true,
hidden: true,
density: 20,
tick: function (pixel) {
if (Math.random() <= 0.1) {
deletePixel(pixel.x, pixel.y)
}
}
}
elements.mold = {
color: "#054e05",
category: "life",
behavior: behaviors.POWDER,
state: "solid",
burn: 70,
burnTime: 100,
tempHigh: 200,
stateHigh: "fire",
tempLow: -10,
stateLow: "frozen_plant",
reactions: {
"meat": { elem1: "rotten_meat", elem2: "rotten_meat" },
"cheese": { elem1: "rotten_cheese", elem2: "rotten_cheese" }
},
tick: function (pixel) {
let moldable = ["meat", "cheese", "rotten_meat", "rotten_cheese"]
for (var i = 0; i < squareCoords.length; i++) {
var coords = squareCoords[i];
var x = pixel.x + coords[0];
var y = pixel.y + coords[1];
if (isEmpty(x, y) && Math.random() <= 0.01 && getPixel(pixel.x, pixel.y + 1) && moldable.includes(getPixel(pixel.x, pixel.y + 1).element)) {
createPixel('mold', x, y)
}
}
}
}
elements.glow_stick = {
color: ["#00ff00", "#ea00ff", "#00eeff"],
glow: true,
behavior: behaviors.POWDER,
breakInto: "glow_stick_liquid",
category: "powders",
tempHigh: 300,
stateHigh: ["molten_plastic", "stench"],
state: "solid"
}
elements.glow_stick_liquid = {
color: ["#00ff00", "#ea00ff", "#00eeff"],
glow: true,
behavior: behaviors.LIQUID,
category: "liquids",
hidden: true,
tempHigh: 300,
stateHigh: "stench",
tempLow: -5,
stateLow: "glow_stick_ice",
state: "solid"
}
elements.glow_stick_ice = {
color: ["#00ff00", "#ea00ff", "#00eeff"],
behavior: behaviors.WALL,
category: "states",
hidden: true,
temp: -5,
tempHigh: -5,
stateHigh: "glow_stick_liquid",
state: "solid"
}
// Add TPS keybind
keybinds["KeyT"] = function () {
tpsPrompt()
}
function addRowWhenReady() {
const table = document.getElementById("controlsTable");
if (!table) {
// Table not ready yet, try again in 100ms
setTimeout(addRowWhenReady, 100);
return;
}
// Table exists, add the row
const rowCount = table.rows.length;
const newRow = table.insertRow(rowCount - 1);
const cell1 = newRow.insertCell(0);
const cell2 = newRow.insertCell(1);
cell1.textContent = "Change TPS";
cell2.innerHTML = "<kbd>T</kbd>";
console.log("Row added successfully!");
}
// Start the process
addRowWhenReady();
elements.randomizer = {
buttonColor: ["#ff0000", "#ff8800", "#ffff00", "#00ff00", "#00ffff", "#0000ff", "#ff00ff"],
excludeRandom: true,
onSelect: function () {
logMessage("Warning: It can fill up the screen with random elements")
},
tick: function (pixel) {
pixel.color = randomColor()
for (var i = 0; i < adjacentCoords.length; i++) {
var coords = adjacentCoords[i];
var x = pixel.x + coords[0];
var y = pixel.y + coords[1];
let p = getPixel(x, y)
if (!isEmpty(x, y) && !outOfBounds(x, y)) {
if (p.element !== "randomizer") {
changePixel(p, "random")
}
}
}
},
behavior: behaviors.WALL,
insulate: true,
hardness: 1,
category: "special",
state: "solid"
}
elements.cloner.ignore.push("randomizer")
elements.ecloner.ignore.push("randomizer")
elements.floating_cloner.ignore.push("randomizer")
elements.slow_cloner.ignore.push("randomizer")
elements.rocket.ignore.push("randomizer")
elements.antibomb.tick = function (pixel) {
doDefaults(pixel)
if (!tryMove(pixel, pixel.x, pixel.y + 1)) {
if (!outOfBounds(pixel.x, pixel.y + 1)) {
var elem = pixelMap[pixel.x][pixel.y + 1].element;
if (elements[elem].isGas) { return }
}
else {
var elem = "smoke";
}
if (elem !== "antibomb" && elem !== "randomizer") {
explodeAt(pixel.x, pixel.y, 8, elem)
}
}
}
function drawCircle(x0, y0, radius, element) {
for (let y = -radius; y <= radius; y++) {
for (let x = -radius; x <= radius; x++) {
if (x * x + y * y <= radius * radius) {
let px = x0 + x;
let py = y0 + y;
if (isEmpty(px, py)) {
createPixel(element, px, py);
}
}
}
}
}
let circleRad = 7;
let circle_element = "wood";
elements.circle = {
color: "#ffffff",
behavior: behaviors.WALL,
category: "special",
state: "solid",
onSelect: function () {
promptInput(
"Select the radius you want your circle to be:",
function (input1) {
let ans1 = Number(input1)
if (Number.isInteger(ans1) && ans1 > 0) {
circleRad = ans1
} else {
circleRad = 7
logMessage("Invalid radius, using default size: " + circleRad);
}
promptInput(
"Select the element you want your circle to be:",
function (ans2) {
let similar = mostSimilarElement(ans2);
if (similar && elements[similar]) {
circle_element = similar;
} else {
circle_element = "wood"
logMessage("Invalid element, using default element: " + circle_element);
}
},
"Element prompt",
"wood"
);
},
"Radius prompt",
"7"
);
},
onPlace: function (pixel) {
drawCircle(pixel.x, pixel.y, circleRad, circle_element);
changePixel(pixel, circle_element);
pixel.temp = (elements[circle_element].temp || 20)
},
maxSize: 1,
excludeRandom: true
};
runAfterReset(function () {
circleRad = 7;
circle_element = "wood";
})
function randomIntInRange(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
let r = randomIntInRange(0, 255);
let g = randomIntInRange(0, 255);
let b = randomIntInRange(0, 255);
elements.rgb_led = {
buttonColor: ["#ff0000", "#ff8800", "#ffff00", "#00ff00", "#00ffff", "#0000ff", "#ff00ff"],
behavior: behaviors.WALL,
desc: "Input the red, green, and blue value (not exceeding 255) and get the color.",
renderer: renderPresets.LED,
conduct: 1,
state: "solid",
breakInto: "glass_shard",
forceSaveColor: true,
reactions: {
"light": { charge1: 1, elem2: null },
"liquid_light": { charge1: 1, elem2: null }
},
category: "machines",
tempHigh: 1500,
stateHigh: ["molten_glass", "molten_glass", "molten_glass", "molten_gallium"],
onSelect: () => {
promptInput("Enter red value (0-255):", function (r_inp) {
r_inp = parseInt(r_inp);
if (r_inp > 255 || r_inp < 0 || isNaN(r_inp)) {
logMessage("Red value is invalid, using default/last red value: " + r);
} else {
r = r_inp;
}
promptInput("Enter green value (0-255):", function (g_inp) {
g_inp = parseInt(g_inp);
if (g_inp > 255 || g_inp < 0 || isNaN(g_inp)) {
logMessage("Green value is invalid, using default/last green value: " + g);
} else {
g = g_inp;
}
promptInput("Enter blue value (0-255):", function (b_inp) {
b_inp = parseInt(b_inp);
if (b_inp > 255 || b_inp < 0 || isNaN(b_inp)) {
logMessage("Blue value is invalid, using default/last blue value: " + b);
} else {
b = b_inp;
}
}, "Blue Value", b); // optional default input
}, "Green Value", g);
}, "Red Value", r);
},
onPlace: (pixel) => {
var ledColor = RGBToHex({ r: r, g: g, b: b });
pixel.color = ledColor;
}
};
runAfterReset(() => {
r = 100;
g = 100;
b = 100;
})
elements.malware.reactions.rgb_led = { elem2: ["led_r", "led_g", "led_b"], chance: 0.01 }
elements.malware.reactions.led_r = { elem2: ["rgb_led", "led_g", "led_b"], chance: 0.01 }
elements.malware.reactions.led_g = { elem2: ["rgb_led", "led_r", "led_b"], chance: 0.01 }
elements.malware.reactions.led_b = { elem2: ["rgb_led", "led_g", "led_r"], chance: 0.01 }
elements.dice = {
color: "#d5d5d5",
state: "solid",
onClicked(pixel) {
pixel.clicked = true;
},
tick(pixel) {
if (pixel.rolled === undefined) {
pixel.rolled = false;
}
if (pixel.clicked && !pixel.rolled) {
const roll = randomIntInRange(1, 6);
clearLog()
logMessage("Dice roll: " + roll);
pixel.rolled = true;
}
if (!pixel.clicked) {
pixel.rolled = false;
}
pixel.clicked = false;
},
behavior: behaviors.WALL,
density: 500,
category: "special",
onSelect: () => {
logMessage("Click The Dice to Roll")
logMessage("It is reccomended to roll only one dice at a time as it will only show one value")
}
}
elements.wood.pressInto = "plank";
elements.wood.resistPress = 0.5;
elements.plank = {
color: "#98633B",
behavior: behaviors.WALL,
renderer: renderPresets.WOODCHAR,
tempHigh: 400,
stateHigh: ["ember", "charcoal", "fire", "fire", "fire"],
category: "solids",
burn: 5,
burnTime: 300,
burnInto: ["ember", "charcoal", "fire"],
state: "solid",
hardness: 0.50,
breakInto: "sawdust",
forceSaveColor: true
}
elements.sand.pressInto = "packed_sand";
elements.sand.resistPress = 0.2
elements.snow.pressInto = "packed_snow";
elements.snow.resistPress = 0.2
elements.asbestos = {
color: "#f1f5f1",
behavior: [
"SP|XX|SP",
"XX|XX|XX",
"SA AND M2|M1|SP AND M2"
],
category: "powders",
state: "solid",
tempHigh: 800,
stateHigh: ["dust", "ash", "fire"],
breakInto: ["gravel", "sand", "dust"],
reactions: {
"body": { elem2: "cancer", chance: 0.5 },
"head": { elem2: "cancer", chance: 0.5 }
},
nocheer: true
}
elements.lithium_battery = {
color: "#616161",
behavior: [
"XX|SH|XX",
"SH|XX|SH",
"XX|SH|XX"
],
tempHigh: 1400,
stateHigh: ["explosion", "lithium", "acid_gas"],
hardness: 0.8,
breakInto: ["lithium", "acid"],
category: "machines",
alias: "lithium-ion battery",
state: "solid"
}
elements.lithium = {
color: "#acacac",
behavior: behaviors.POWDER,
reactions: {
"water": { elem1: ["pop", "pop", "pop", "hydrogen"], chance: 0.01, temp2: 200 },
"salt_water": { elem1: ["pop", "pop", "pop", "hydrogen"], chance: 0.01, temp2: 200 },
"pool_water": { elem1: ["pop", "pop", "pop", "hydrogen"], chance: 0.01, temp2: 200 },
"sugar_water": { elem1: ["pop", "pop", "pop", "hydrogen"], chance: 0.01, temp2: 200 },
"seltzer": { elem1: ["pop", "pop", "pop", "hydrogen"], chance: 0.01, temp2: 200 },
"dirty_water": { elem1: ["pop", "pop", "pop", "hydrogen"], chance: 0.01, temp2: 200 },
"primordial_soup": { elem1: ["pop", "pop", "pop", "hydrogen"], chance: 0.01, temp2: 200 },
"nut_milk": { elem1: ["pop", "pop", "pop", "hydrogen"], chance: 0.01, temp2: 200 }
},
density: 533.4,
tempHigh: 180.5,
conduct: 0.45,
category: "powders",
state: "solid"
}
elements.molten_lithium = {
tempHigh: 1344,
stateHigh: 'lithium_gas',
reactions: {
"water": { elem1: ["pop", "pop", "pop", "hydrogen"], chance: 0.01, temp2: 200 },
"salt_water": { elem1: ["pop", "pop", "pop", "hydrogen"], chance: 0.01, temp2: 200 },
"pool_water": { elem1: ["pop", "pop", "pop", "hydrogen"], chance: 0.01, temp2: 200 },
"sugar_water": { elem1: ["pop", "pop", "pop", "hydrogen"], chance: 0.01, temp2: 200 },
"seltzer": { elem1: ["pop", "pop", "pop", "hydrogen"], chance: 0.01, temp2: 200 },
"dirty_water": { elem1: ["pop", "pop", "pop", "hydrogen"], chance: 0.01, temp2: 200 },
"primordial_soup": { elem1: ["pop", "pop", "pop", "hydrogen"], chance: 0.01, temp2: 200 },
"nut_milk": { elem1: ["pop", "pop", "pop", "hydrogen"], chance: 0.01, temp2: 200 }
},
state: "liquid"
}
elements.lithium_gas = {
color: "#cccccc",
behavior: behaviors.GAS,
isGas: true,
tempLow: 1334,
stateLow: "molten_lithium",
density: 533.4,
conduct: 0.45,
temp: 1334,
category: "states",
hidden: true,
state: "gas"
}
elements.chalk = {
color: "#fff3ac",
behavior: behaviors.WALL,
stain: 0.25,
category: "land",
breakInto: "chalk_powder",
tempHigh: 1000,
density: 2700,
reactions: {
"water": { elem1: "wet_chalk", elem2: null, chance: 0.5 },
"salt_water": { elem1: "wet_chalk", elem2: null, chance: 0.5 },
"seltzer": { elem1: "wet_chalk", elem2: null, chance: 0.5 },
"dirty_water": { elem1: "wet_chalk", elem2: null, chance: 0.5 },
"sugar_water": { elem1: "wet_chalk", elem2: null, chance: 0.5 },
"pool_water": { elem1: "wet_chalk", elem2: null, chance: 0.5 },
"primordial_soup": { elem1: "wet_chalk", elem2: null, chance: 0.5 },
"nut_milk": { elem1: "wet_chalk", elem2: null, chance: 0.5 }
},
alias: "calcite",
state: "solid"
}
elements.chalk_powder = {
color: "#fff3ac",
behavior: behaviors.POWDER,
stain: 0.25,
category: "powders",
tempHigh: 1000,
stateHigh: "molten_chalk",
density: 2700,
reactions: {
"water": { elem1: "wet_chalk_powder", elem2: null, chance: 0.5 },
"salt_water": { elem1: "wet_chalk_powder", elem2: null, chance: 0.5 },
"seltzer": { elem1: "wet_chalk_powder", elem2: null, chance: 0.5 },
"dirty_water": { elem1: "wet_chalk_powder", elem2: null, chance: 0.5 },
"sugar_water": { elem1: "wet_chalk_powder", elem2: null, chance: 0.5 },
"pool_water": { elem1: "wet_chalk_powder", elem2: null, chance: 0.5 },
"primordial_soup": { elem1: "wet_chalk_powder", elem2: null, chance: 0.5 },
"nut_milk": { elem1: "wet_chalk_powder", elem2: null, chance: 0.5 },
"glue": { elem1: "chalk", elem2: null }
},
state: "solid"
}
elements.wet_chalk = {
color: "#bbb17b",
behavior: behaviors.WALL,
stain: 0.25,
category: "land",
breakInto: "wet_chalk_powder",
tempHigh: 100,
stateHigh: "chalk",
density: 2300,
onStateHigh: (pixel) => {
releaseElement(pixel, "steam")
},
state: "solid"
}
elements.wet_chalk_powder = {
color: "#bbb17b",
behavior: behaviors.POWDER,
stain: 0.25,
category: "powders",
tempHigh: 100,
stateHigh: "chalk_powder",
density: 2000,
onStateHigh: (pixel) => {
releaseElement(pixel, "steam")
},
state: "solid",
reactions: {
"glue": { elem1: "chalk", elem2: null }
}
}
elements.lapis_lazuli = {
color: ["#0000df", "#1212cc", "#120A8F", "#060080"],
behavior: behaviors.WALL,
breakInto: "lapis_lazuli_powder",
tempHigh: 1100,
stateHigh: 'magma',
state: "solid",
category: "solids"
}
elements.lapis_lazuli_powder = {
color: "#120A8F",
behavior: behaviors.POWDER,
tempHigh: 1100,
stateHigh: "magma",
alias: "Ultramarine",
category: "powders",
state: "solid"
}
elements.uv_light = {
color: "#440088",
behavior: [
"XX|XX|XX",
"XX|DL%1|XX",
"XX|XX|XX"
],
glow: true,
tick: behaviors.BOUNCY,
temp: 35,
tempLow: -273,
stateLow: ["liquid_light", null],
stateLowColorMultiplier: 0.8,
breakInto: "light",
breakIntoColor: "#ffcfcf",
category: "energy",
state: "gas",
density: 0.00001,
ignoreAir: true,
reactions: {
"ozone": { elem1: null, chance: 0.9 },
}
};
if (!settings.cheerful) {
elements.uv_light.reactions.cell = { elem2: "cancer", chance: 0.05 },
elements.uv_light.reactions.skin = { elem2: "cancer", chance: 0.005 }
}
else {
if (elements.uv_light.reactions.cell) {
delete elements.uv_light.reactions.cell
}
if (elements.uv_light.reactions.skin) {
delete elements.uv_light.reactions.skin
}
}
function fadeColor(base, glow, ratio) {
// ratio 1 = full glow, 0 = base color
let r1 = parseInt(glow.substr(1, 2), 16);
let g1 = parseInt(glow.substr(3, 2), 16);
let b1 = parseInt(glow.substr(5, 2), 16);
let r2 = parseInt(base.substr(1, 2), 16);
let g2 = parseInt(base.substr(3, 2), 16);
let b2 = parseInt(base.substr(5, 2), 16);
let r = Math.round(r1 * ratio + r2 * (1 - ratio));
let g = Math.round(g1 * ratio + g2 * (1 - ratio));
let b = Math.round(b1 * ratio + b2 * (1 - ratio));
return `#${r.toString(16).padStart(2, "0")}${g.toString(16).padStart(2, "0")}${b.toString(16).padStart(2, "0")}`;
}
elements.phosphor = {
color: "#fffbe7",
behavior: behaviors.POWDER,
tick: function (pixel) {
let nearUV = false;
for (let i = 0; i < adjacentCoords.length; i++) {
let x = pixel.x + adjacentCoords[i][0];
let y = pixel.y + adjacentCoords[i][1];
let p = getPixel(x, y);
if (p && p.element == "uv_light") {
nearUV = true;
break;
}
}
if (nearUV) {
pixel.colorStay = 60; // longer for realism
}
if (pixel.colorStay > 0) {
let ratio = pixel.colorStay / 60; // exponential fade would be even better
pixel.color = fadeColor("#fffbe7", "#00c500", ratio);
pixel.colorStay--;
} else {
pixel.color = "#fffbe7"
}
if (enabledMods.includes("mods/glow.js")) {
if (pixel.color !== "#fffbe7") {
pixel.emit = pixel.colorStay / 10
}
}
},
category: "powders",
state: "solid",
};
elements.packed_sand.pressInto = "sandstone"
elements.packed_sand.resistPress = 0.7
elements.packed_snow.pressInto = "ice"
elements.packed_snow.resistPress = 0.7
elements.sandstone = {
color: "#d3bc56",
behavior: behaviors.WALL,
breakInto: "sand",
hardness: 0.5,
state: "solid",
stateHigh: "glass",
tempHigh: 1700,
category: "solids"
}
// Glow.js integrtion
if (enabledMods.includes("mods/glow.js")) {
elements.uv_light.emit = true
elements.glow_stick.emit = true
delete elements.glow_stick.glow
elements.glow_stick_liquid.emit = true
delete elements.glow_stick_liquid.glow
}
let neonTubeChoice = "#00ff00"; // default color
elements.neon_tube = {
color: "#d1d1b5",
renderer: renderPresets.BORDER,
alpha: 0.25,
conduct: 1,
behavior: behaviors.WALL,
breakInto: ["neon", "glass_shard"],
grain: 0,
category: "machines",
state: "solid",
tick: (pixel) => {
pixel.def_color ??= pixel.color;
pixel.glow_color ??= neonTubeChoice;
if (pixel.charge) {
pixel.alpha = 1;
pixel.color = pixel.glow_color;
} else {
pixel.alpha = 0.25;
pixel.color = pixel.def_color;
}
if (enabledMods.includes("mods/glow.js")) {
if (pixel.charge) {
pixel.emit = true;
pixel.emitColor = pixel.glow_color;
} else {
delete pixel.emit;
delete pixel.emitColor;
}
}
},
// Broken since yellow stacks above color
// only green is good
/*
onSelect() {
promptChoose(
"Pick a color for your neon tube:",
["Red", "Green", "Blue", "Pink"],
function(choice) {
let colors = {
"Red": "#ff0000",
"Green": "#00ff00",
"Blue": "#0000ff",
"Pink": "#ff66cc"
};
neonTubeChoice = colors[choice] || "#00ff00";
logMessage("Neon tube color set to " + choice);
},
"Neon Tube Setup"
);
}
*/
};
elements.realistic_ball = {
color: "#e35693",
tempHigh: 250,
stateHigh: "molten_plastic",
category: "special",
state: "solid",
density: 1052,
hidden: true,
tick: function (pixel) {
// initialize velocity
if (pixel.vy === undefined) {
pixel.vy = 0;
}
// gravity
pixel.vy += 0.3;
// predict next position
let nextY = pixel.y + Math.sign(pixel.vy);
if (isEmpty(pixel.x, nextY)) {
// free space → move
tryMove(pixel, pixel.x, pixel.y + Math.sign(pixel.vy));
} else {
// collision: reverse velocity (bounce)
pixel.vy *= -0.7;
// stop very small bounces
if (Math.abs(pixel.vy) < 0.5) {
pixel.vy = 0;
}
}
}
}
explodeElem = "fire"
elements.custom_bomb = {
color: "#49443b",
category: "weapons",
state: "solid",
density: 1300,
excludeRandom: true,
cooldown: defaultCooldown,
behavior: behaviors.STURDYPOWDER,
onSelect: function () {
promptInput(
"Input the element you want your bomb to explode into",
function (input) {
pr1 = mostSimilarElement(input)
if (elements[pr1]) {
if (pr1 === "custom_bomb") {
explodeElem = 'fire'
logMessage("Element cannot explode to itself. Using default: fire")
}
else { explodeElem = pr1 }
}
else {
explodeElem = 'fire'
logMessage("Invalid element. Using default: fire")
}
},
"Element prompt",
"fire"
)
},
tick: function (pixel) {
let belowPixel = getPixel(pixel.x, pixel.y + 1);
// If pixel is at the bottom or resting on a solid
if (outOfBounds(pixel.x, pixel.y + 1) || (belowPixel && belowPixel.element !== "custom_bomb" && !isEmpty(pixel.x, pixel.y + 1) && belowPixel.element !== "fire" && belowPixel.element !== "smoke")) {
explodeAt(pixel.x, pixel.y, 10, explodeElem);
deletePixel(pixel.x, pixel.y);
}
}
}
runAfterReset(() => {
explodeElem = 'fire'
})
elements.pineapple = {
color: "#ffd900",
isFood: true,
behavior: behaviors.STURDYPOWDER,
breakInto: ["juice", "juice", "juice", "juice", "juice", "pineapple_seed"],
breakIntoColor: "#fffc00",
category: "food",
state: "solid",
seed: "pineapple_seed"
}
elements.pineapple_seed = {
color: "#695531",
behavior: behaviors.STURDYPOWDER,
cooldown: defaultCooldown,
category: "life",
tempHigh: 400,
stateHigh: "fire",
tempLow: -2,
stateLow: "frozen_plant",
burn: 50,
burnTime: 20,
state: "solid",
tick: function (pixel) {
let belowPixel = getPixel(pixel.x, pixel.y + 1)
if (
!isEmpty(pixel.x, pixel.y + 1) &&
belowPixel &&
Math.random() <= 0.05
) {
changePixel(pixel, "pineapple_stem")
pixel.growthState = 1
pixel.growDiagLeft = true
pixel.growDiagRight = true
}
},
seed: true
}
eListAdd("SEEDS", "pineapple_seed")
elements.pineapple_stem = {
color: "#3aab11",
behavior: behaviors.WALL,
movable: false,
category: "life",
tempHigh: 100,
stateHigh: "dead_plant",
tempLow: -1.66,
stateLow: "frozen_plant",
burn: 15,
burnTime: 60,
burnInto: "dead_plant",
breakInto: "dead_plant",
tick: function (pixel) {
let left = pixel.x - 1
let right = pixel.x + 1
let up = pixel.y - 1
let down = pixel.y + 1
let belowPixel = getPixel(pixel.x, down)
pixel.fruitCD ??= 60
if (!outOfBounds(pixel.x, down) && belowPixel && eLists.SOIL.includes(belowPixel.element) && pixel.growthState === 1) {
changePixel(belowPixel, "root")
}
if (pixel.growthState === 1 && Math.random() <= 0.05) {
if (isEmpty(left, up) && pixel.growDiagLeft === true) {
createPixel("pineapple_stem", left, up)
let newPixel = getPixel(left, up)
if (newPixel) {
newPixel.growthState = 2
newPixel.growDiagLeft = true
delete newPixel.growDiagRight
}
}
else {
delete pixel.growDiagLeft
}
if (isEmpty(right, up) && pixel.growDiagRight === true) {
createPixel("pineapple_stem", right, up)
let newPixel = getPixel(right, up)
if (newPixel) {
newPixel.growthState = 2
newPixel.growDiagRight = true
delete newPixel.growDiagLeft
}
}
else {
delete pixel.growDiagRight
}
}
if (!pixel.growDiagLeft && !pixel.growDiagRight && pixel.growthState === 1) {
if (Math.random() <= 0.05 && isEmpty(pixel.x, up) && !pixel.fruitCD && !pixel.pineappleGrown) {
createPixel("pineapple", pixel.x, up)
pixel.fruitCD = 60
pixel.pineappleGrown = true
}
if (pixel.fruitCD && !pixel.pineappleGrown) {
pixel.fruitCD--
}
}
if (isEmpty(pixel.x, up)) {
pixel.pineappleGrown = false
}
if (pixel.growthState === 2 && Math.random() <= 0.05) {
if (isEmpty(left, up) && pixel.growDiagLeft === true) {
createPixel("pineapple_stem", left, up)
}
if (isEmpty(right, up) && pixel.growDiagRight === true) {
createPixel("pineapple_stem", right, up)
}
}
}
}
// making redstone conductive bc idk why its not
if (enabledMods.includes("mods/minecraft.js")) {
runAfterLoad(() => {
elements.redstone_dust.conduct = 1
})
}
elements.rubidium = {
color: "#c0c0c0",
state: "solid",
behavior: behaviors.POWDER,
tempHigh: 39.30,
stateHigh: "liquid_rubidium",
density: 1534,
conduct: 0.6,
reactions: {
"water": { elem1: ["explosion", "explosion", "hydrogen"], temp2: 200 },
"salt_water": { elem1: ["explosion", "explosion", "hydrogen"], temp2: 200 },
"sugar_water": { elem1: ["explosion", "explosion", "hydrogen"], temp2: 200 },
"pool_water": { elem1: ["explosion", "explosion", "hydrogen"], temp2: 200 },
"dirty_water": { elem1: ["explosion", "explosion", "hydrogen"], temp2: 200 },
"salt_water": { elem1: ["explosion", "explosion", "hydrogen"], temp2: 200 },
"seltzer": { elem1: ["explosion", "explosion", "hydrogen"], temp2: 200 },
"primordial_soup": { elem1: ["explosion", "explosion", "hydrogen"], temp2: 200 },
"nut_milk": { elem1: ["explosion", "explosion", "hydrogen"], temp2: 200 }
},
category: "powders"
}
elements.liquid_rubidium = {
color: "#d0d0d0",
state: "liquid",
behavior: behaviors.LIQUID,
tempHigh: 688,
density: 1460,
conduct: 0.6,
tempLow: 39.30,
stateLow: "rubidium",
temp: 40,
reactions: {
"water": { elem1: ["explosion", "explosion", "hydrogen"], temp2: 200 },
"salt_water": { elem1: ["explosion", "explosion", "hydrogen"], temp2: 200 },
"sugar_water": { elem1: ["explosion", "explosion", "hydrogen"], temp2: 200 },
"pool_water": { elem1: ["explosion", "explosion", "hydrogen"], temp2: 200 },
"dirty_water": { elem1: ["explosion", "explosion", "hydrogen"], temp2: 200 },
"salt_water": { elem1: ["explosion", "explosion", "hydrogen"], temp2: 200 },
"seltzer": { elem1: ["explosion", "explosion", "hydrogen"], temp2: 200 },
"primordial_soup": { elem1: ["explosion", "explosion", "hydrogen"], temp2: 200 },
"nut_milk": { elem1: ["explosion", "explosion", "hydrogen"], temp2: 200 }
},
category: "states",
hidden: true
}
elements.dog = {
color: ["#221d0c", "#5c4300", "#7c5b00", "#fcfceb"],
behavior: [
"XX|XX|M2%3",
"XX|FX%5|M2%5",
"XX|M1|XX"
],
tempHigh: 100,
stateHigh: "cooked_meat",
tempLow: -10,
stateLow: "frozen_meat",
category: "life",
state: "solid",
burn: 30,
burnTime: 50,
burnInto: ["cooked_meat", "smoke"],
breakInto: ["meat", "blood"],
reactions: {
"meat": { elem2: null, chance: 0.5, func: behaviors.FEEDPIXEL },
"egg": { elem2: null, chance: 0.5, func: behaviors.FEEDPIXEL },
"yolk": { elem2: null, chance: 0.5, func: behaviors.FEEDPIXEL },
"cheese": { elem2: null, chance: 0.5, func: behaviors.FEEDPIXEL },
"cooked_meat": { elem2: null, chance: 0.5, func: behaviors.FEEDPIXEL },
"chocolate": { elem2: null, chance: 0.2, func: behaviors.FEEDPIXEL, elem1: "rotten_meat" },
"grape": { elem2: null, chance: 0.2, func: behaviors.FEEDPIXEL, elem1: "rotten_meat" },
"rat": { elem2: null, chance: 0.3, func: behaviors.FEEDPIXEL },
"nut_butter": { elem2: null, chance: 0.5, func: behaviors.FEEDPIXEL },
},
egg: "dog",
}
// Keyboard state tracking
const robotKeys = {
left: false,
right: false,
jump: false
};
// Set up keyboard listeners
window.addEventListener('keydown', (e) => {
const key = e.key.toLowerCase();
if (key === 'a' || key === 'arrowleft') robotKeys.left = true;
if (key === 'd' || key === 'arrowright') robotKeys.right = true;
if (key === 'w' || key === 'arrowup') robotKeys.jump = true;
});
window.addEventListener('keyup', (e) => {
const key = e.key.toLowerCase();
if (key === 'a' || key === 'arrowleft') robotKeys.left = false;
if (key === 'd' || key === 'arrowright') robotKeys.right = false;
if (key === 'w' || key === 'arrowup') robotKeys.jump = false;
});
// Helper function for movement
function tryMoveRobot(headPixel, direction) {
const newX = headPixel.x + direction;
const body = getPixel(headPixel.x, headPixel.y + 1);
if (body && body.element === "robot_body" &&
isEmpty(newX, headPixel.y) &&
isEmpty(newX, body.y)) {
movePixel(body, newX, body.y);
movePixel(headPixel, newX, headPixel.y);
return true;
}
return false;
}
function tryJump(headPixel) {
const body = getPixel(headPixel.x, headPixel.y + 1);
if (!body || body.element !== "robot_body") return false;
// Check if grounded (on solid surface or bottom of screen)
const underBody = getPixel(body.x, body.y + 1);
const isGrounded = (!isEmpty(body.x, body.y + 1) || outOfBounds(body.x, body.y + 1));
if (isGrounded) {
// Check space above
if (isEmpty(headPixel.x, headPixel.y - 1) &&
isEmpty(headPixel.x, headPixel.y - 2)) {
// Two-stage jump animation
pixelTicks = 0;
headPixel.jumping = true;
// First frame - small hop
movePixel(headPixel, headPixel.x, headPixel.y - 1);
movePixel(body, body.x, headPixel.y + 1);
// Second frame - complete jump (after small delay)
setTimeout(() => {
if (headPixel.jumping) { // Only if still jumping
movePixel(headPixel, headPixel.x, headPixel.y - 1);
movePixel(body, body.x, headPixel.y + 1);
headPixel.jumping = false;
}
}, 100); // 100ms delay for smoother animation
return true;
}
}
return false;
}
// Robot elements
elements.robot_head = {
color: "#d9d9d9",
category: "machines",
state: "solid",
tick(pixel) {
const body = getPixel(pixel.x, pixel.y + 1);
if (body && body.element === "robot_body") {
pixel.connected = true;
body.connected = true;
// Controlled movement
if (pixel.mode === "Controlled") {
if (robotKeys.left) {
tryMoveRobot(pixel, -1);
}
else if (robotKeys.right) {
tryMoveRobot(pixel, 1);
}
if (robotKeys.jump && !pixel.jumping) {
tryJump(pixel);
}
}
// Aimless wandering
else if (pixel.mode === "Aimless" && Math.random() < 0.02) {
pixel.dir = pixel.dir || (Math.random() < 0.5 ? -1 : 1);
if (!tryMoveRobot(pixel, pixel.dir)) {
pixel.dir *= -1;
}
}
}
else {
pixel.connected = false;
tryMove(pixel, pixel.x, pixel.y + 1);
}
}
};
elements.robot_body = {
color: "#b1b1b1",
category: "machines",
state: "solid",
tick(pixel) {
const head = getPixel(pixel.x, pixel.y - 1);
if (head && head.element === "robot_head") {
pixel.connected = true;
head.connected = true;
// Gravity - move down if space below
if (isEmpty(pixel.x, pixel.y + 1)) {
let oldY = pixel.y;
movePixel(pixel, pixel.x, pixel.y + 1);
movePixel(head, head.x, oldY);
}
}
else {
pixel.connected = false;
tryMove(pixel, pixel.x, pixel.y + 1);
}
}
};
// Robot creator element
elements.robot = {
color: "#b1b1b1",
category: "machines",
state: "solid",
onSelect() {
promptChoose(
"Choose robot mode",
["Aimless", "Controlled"],
(choice) => {
if (choice === "Controlled" && isMobile) {
logMessage("Controlled mode doesn't work on mobile");
mode = "Aimless";
} else {
mode = choice || "Aimless";
if (mode === "Controlled") {
logMessage("Controls: A/D to move and W to jump or (Not reccomended) ←/→ to move, and ↑ to jump");
}
}
},
"Robot Mode"
);
},
onPlace(pixel) {
// Try to create head above
if (isEmpty(pixel.x, pixel.y - 1)) {
createPixel("robot_head", pixel.x, pixel.y - 1);
const head = getPixel(pixel.x, pixel.y - 1);
head.mode = mode;
changePixel(pixel, "robot_body");
pixel.mode = mode;
}
// Try to create body below if above is blocked
else if (isEmpty(pixel.x, pixel.y + 1)) {
createPixel("robot_body", pixel.x, pixel.y + 1);
const body = getPixel(pixel.x, pixel.y + 1);
body.mode = mode;
changePixel(pixel, "robot_head");
pixel.mode = mode;
}
// Delete if no space
else {
deletePixel(pixel.x, pixel.y);
}
},
cooldown: defaultCooldown
};
elements.mercury_gas.behaviorOn = [
"M2|CR:uv_light%10 AND M1|M2",
"CR:uv_light%10 AND M1|XX|CR:uv_light%10 AND M1",
"M2|CR:uv_light%10 AND M1|M2"
]
adjusted_heater_temp = 100
elements.broken_adjustable_heater = {
color: "#ff0000",
category: "extras",
insulate: true,
behavior: behaviors.WALL,
onSelect() {
promptInput(
"Select the temperature you want to adjust to",
function (choice) {
if (choice && !isNaN(Number(choice))) {
adjusted_heater_temp = choice
logMessage("Occasionally creates superheated pixels")
}
},
"Temperature Prompt", adjusted_heater_temp
)
},
tick(pixel) {
pixel.heat_temp ??= adjusted_heater_temp
for (let i = 0; i < adjacentCoords.length; i++) {
let x = pixel.x + adjacentCoords[i][0];
let y = pixel.y + adjacentCoords[i][1];
let current_pixel = getPixel(x, y);
if (
current_pixel &&
!elements[current_pixel.element]?.insulate
&& current_pixel.temp < pixel.heat_temp
) {
current_pixel.temp = Math.min(current_pixel.temp + 2, pixel.heat_temp);
}
}
}
};
adjusted_temp = 100
heatAmount = 2
elements.adjustable_heater = {
color: "#ff0000",
category: "machines",
insulate: true,
behavior: behaviors.WALL,
onSelect() {
promptInput(
"Select the temperature you want to adjust to",
function (choice) {
if (choice && !isNaN(Number(choice))) {
adjusted_temp = Number(choice);
}
},
"Temperature Prompt", adjusted_temp
);
},
tick(pixel) {
for (let i = 0; i < adjacentCoords.length; i++) {
let x = pixel.x + adjacentCoords[i][0];
let y = pixel.y + adjacentCoords[i][1];
let current_pixel = getPixel(x, y);
if (
current_pixel &&
!elements[current_pixel.element]?.insulate
) {
// Heat or cool toward the adjusted temp
if (current_pixel.temp < adjusted_temp) {
current_pixel.temp = Math.min(current_pixel.temp + heatAmount, adjusted_temp);
} else if (current_pixel.temp > adjusted_temp) {
current_pixel.temp = Math.max(current_pixel.temp - heatAmount, adjusted_temp);
}
pixelTempCheck(current_pixel)
}
}
}
};
adjusted_cooler_temp = 0; // default cooling target
elements.broken_adjustable_cooler = {
color: "#0000ff",
category: "extras",
insulate: true,
behavior: behaviors.WALL,
onSelect() {
promptInput(
"Select the temperature you want to cool to",
function (choice) {
if (choice && !isNaN(Number(choice))) {
adjusted_cooler_temp = Number(choice);
logMessage("Occasionally creates supercooled pixels");
}
},
"Temperature Prompt", adjusted_cooler_temp
);
},
tick(pixel) {
pixel.cool_temp ??= adjusted_cooler_temp;
for (let i = 0; i < adjacentCoords.length; i++) {
let x = pixel.x + adjacentCoords[i][0];
let y = pixel.y + adjacentCoords[i][1];
let current_pixel = getPixel(x, y);
if (
current_pixel &&
!elements[current_pixel.element]?.insulate &&
current_pixel.temp > pixel.cool_temp
) {
// Cool the pixel toward the target
current_pixel.temp = Math.max(current_pixel.temp - 2, pixel.cool_temp);
}
}
}
};
adjusted_cool_temp = 0; // default cooling target
coolAmount = 2; // adjustable step
elements.adjustable_cooler = {
color: "#0000ff",
category: "machines",
insulate: true,
behavior: behaviors.WALL,
onSelect() {
promptInput(
"Select the temperature you want to cool to",
function (choice) {
if (choice && !isNaN(Number(choice))) {
adjusted_cool_temp = Number(choice);
}
},
"Temperature Prompt", adjusted_cool_temp
);
},
tick(pixel) {
for (let i = 0; i < adjacentCoords.length; i++) {
let x = pixel.x + adjacentCoords[i][0];
let y = pixel.y + adjacentCoords[i][1];
let current_pixel = getPixel(x, y);
if (
current_pixel &&
!elements[current_pixel.element]?.insulate
) {
// Cool or heat toward target (mirrors fixed heater logic)
if (current_pixel.temp > adjusted_cool_temp) {
current_pixel.temp = Math.max(current_pixel.temp - coolAmount, adjusted_cool_temp);
} else if (current_pixel.temp < adjusted_cool_temp) {
current_pixel.temp = Math.min(current_pixel.temp + coolAmount, adjusted_cool_temp);
}
pixelTempCheck(current_pixel)
}
}
}
};
let polishedList = new Set()
elements.polish = {
category: "tools",
color: ["#a0dff0", "#c0e8f8", "#e0f5ff"],
tool(pixel) {
let element = pixel.element
if ((elements[pixel.element].colorPattern && !polishedList.has(`${pixel.x}, ${pixel.y}`)) || shiftDown) {
deletePixel(pixel.x, pixel.y)
createPixel(element, pixel.x, pixel.y)
polishedList.add(`${pixel.x}, ${pixel.y}`)
}
},
onUnselect() {
polishedList.clear()
}
}
elements[" "] = {
category: "extras",
onSelect() {
logMessage("This Element has weird properties since its a space ' '")
},
alias: "space"
}
elements.paper_filter = {
desc: "Filters solids from liquids",
color: "#ececec",
behavior: behaviors.WALL,
reactions: {
"light": { stain1: "#ebdfa7" },
"oxygen": { stain1: "#ebdfa7" }
},
tempHigh: 248,
stateHigh: ["fire", "fire", "fire", "fire", "fire", "ash"],
burn: 70,
burnTime: 300,
burnInto: ["fire", "fire", "fire", "fire", "fire", "ash"],
category: "machines",
density: 1201,
breakInto: "confetti",
breakIntoColor: ["#ffffff", "#e6e6e6", "#dbdbdb"],
tick(pixel) {
let upPixel = getPixel(pixel.x, pixel.y - 1)
if (upPixel && elements[upPixel.element].state == "liquid" && !pixel.con) {
deletePixel(pixel.x, pixel.y - 1)
pixel.con = upPixel
}
if (upPixel && (upPixel.element === "paper_filter" || upPixel.element === "indestructable_filter") && upPixel.con && !pixel.con) {
let liquid = upPixel.con
let viscMove = true
if (elements[liquid.element].viscosity) {
viscMove = (Math.random() * 100) < (100 / Math.pow(elements[liquid.element].viscosity, 0.5))
}
if (viscMove) {
pixel.con = liquid
delete upPixel.con
}
}
if (isEmpty(pixel.x, pixel.y + 1) && !outOfBounds(pixel.x, pixel.y + 1) && pixel.con) {
let liquid = pixel.con
let viscExit = true
if (elements[liquid.element].viscosity) {
viscExit = (Math.random() * 100) < (100 / Math.pow(elements[liquid.element].viscosity, 0.5))
}
if (viscExit) {
createPixel(liquid.element, pixel.x, pixel.y + 1)
delete pixel.con
}
}
}
}
elements.indestructable_filter = {
desc: "Filters solids from liquids",
color: "#aaaaaa",
behavior: behaviors.WALL,
category: "machines",
state: "solid",
movable: false,
tick(pixel) {
let upPixel = getPixel(pixel.x, pixel.y - 1)
let belowPixel = getPixel(pixel.x, pixel.y + 1)
if (upPixel && elements[upPixel.element].state == "liquid" && !pixel.con) {
deletePixel(pixel.x, pixel.y - 1)
pixel.con = upPixel
}
if (upPixel && (upPixel.element === "indestructable_filter" || upPixel.element === "paper_filter") && upPixel.con && !pixel.con) {
let liquid = upPixel.con
let viscMove = true
if (elements[liquid.element].viscosity) {
viscMove = (Math.random() * 100) < (100 / Math.pow(elements[liquid.element].viscosity, 0.5))
}
if (viscMove) {
pixel.con = liquid
delete upPixel.con
}
}
if (isEmpty(pixel.x, pixel.y + 1) && !outOfBounds(pixel.x, pixel.y + 1) && pixel.con) {
let liquid = pixel.con
let viscExit = true
if (elements[liquid.element].viscosity) {
viscExit = (Math.random() * 100) < (100 / Math.pow(elements[liquid.element].viscosity, 0.5))
}
if (viscExit) {
createPixel(liquid.element, pixel.x, pixel.y + 1)
delete pixel.con
}
}
}
}
let black_hole_expand = false
elements.black_hole = {
color: "#111111",
hardness: 1,
category: "special",
properties: {
absorbed: 0
},
renderer: function (pixel, ctx) {
if (!viewInfo[view].colorEffects) { drawDefault(ctx, pixel); return }
renderPresets.HEATGLOW(pixel, ctx);
if (pixel.alpha === 0) return;
let edge = false;
pixel.edge = false;
pixel.color = "#111111";
for (var i = 0; i < adjacentCoords.length; i++) {
var coords = adjacentCoords[i];
var x = pixel.x + coords[0];
var y = pixel.y + coords[1];
if (!outOfBounds(x, y)) {
let neighbor = getPixel(x, y);
if (!neighbor || elements[neighbor.element].movable !== elements[pixel.element].movable) {
edge = true;
break;
}
}
}
if (edge) { pixel.color = "#ffae00"; pixel.edge = true }
},
tick(pixel) {
// Glow effect
if (pixel.edge) {
pixel.glow = true;
if (enabledMods.includes("mods/glow.js")) {
pixel.emit = 10;
}
}
else {
pixel.glow = false;
if (enabledMods.includes("mods/glow.js")) {
pixel.emit = 0;
}
}
// Suction physics
let radius = 20; // how far the suction reaches
for (let dx = -radius; dx <= radius; dx++) {
for (let dy = -radius; dy <= radius; dy++) {
if (dx === 0 && dy === 0) continue;
let x = pixel.x + dx;
let y = pixel.y + dy;
if (!outOfBounds(x, y)) {
let other = getPixel(x, y);
if (other && other !== pixel) {
let elemDef = elements[other.element];
// Skip if indestructible
if (elemDef.hardness === 1) continue;
// Distance to black hole
let dist = Math.sqrt(dx * dx + dy * dy);
if (dist <= radius) {
// Suction chance: closer = stronger pull
let chance = 1 / dist;
if (Math.random() < chance) {
let stepX = Math.sign(pixel.x - x);
let stepY = Math.sign(pixel.y - y);
let newX = x + stepX;
let newY = y + stepY;
if (isEmpty(newX, newY) && !outOfBounds(newX, newY)) {
movePixel(other, newX, newY);
}
else if (dist <= 1.5) {
deletePixel(x, y); // absorb it
pixel.absorbed++
}
}
}
}
}
}
if (black_hole_expand) {
for (var i = 0; i < adjacentCoords.length; i++) {
var x = pixel.x + adjacentCoords[i][0];
var y = pixel.y + adjacentCoords[i][1];
if (pixel.absorbed >= 30 && isEmpty(x, y)) {
createPixel("black_hole", x, y)
pixel.absorbed = 0
}
}
}
}
},
forceSaveColor: true,
onSelect() {
promptChoose(
"Do you want the black hole to grow?",
["Yes", "No"],
(choice) => {
if (!choice) {
choice = false
}
if (choice == "Yes") {
black_hole_expand = true
}
else {
black_hole_expand = false
}
}
)
}
};
elements.cacao_fruit = {
color: "#854700",
behavior: [
"XX|ST:cacao_stem|XX",
"ST:cacao_stem|XX|ST:cacao_stem",
"XX|ST:cacao_stem AND M1|XX"
],
isFood: true,
burn: 10,
burnTime: 100,
burnInto: "ash",
breakInto: "cacao_bean",
category: "food",
state: "solid",
density: 1000
}
elements.cacao_bean = {
color: "#ffe7ba",
isFood: true,
behavior: [
"XX|XX|XX",
"XX|XX|XX",
"M2%10|M1|M2%10"
],
tempHigh: 100,
stateHigh: "dried_cacao_bean",
onStateHigh(pixel) { releaseElement(pixel, "steam") },
state: "solid",
category: "food",
density: 1000
}
elements.dried_cacao_bean = {
color: "#61321e",
behavior: behaviors.POWDER,
reactions: {
"sugar_water": { elem2: "melted_chocolate", tempMin: 65 },
"water": { elem2: "melted_chocolate", tempMin: 65 }
},
tempHigh: 400,
stateHigh: "ash",
isFood: true,
category: "food",
state: "solid",
density: 1000
}
elements.coffee_bean.reactions.sugar_water = { elem2: "coffee", tempMin: 80 }
elements.coffee.reactions.sugar_water = { elem2: "coffee", tempMin: 70, chance: 0.2 }
elements.coffee_ground.reactions.sugar_water = elements.coffee_ground.reactions.water
elements._ = {
category: "extras",
onSelect() {
logMessage("Another way to make an element with no name \"_\"")
},
alias: ["underscore"]
}
elements.cacao_seed = {
color: "#8b3f00",
behavior: behaviors.STURDYPOWDER,
cooldown: defaultCooldown,
category: "life",
tempHigh: 400,
stateHigh: "fire",
tempLow: -2,
stateLow: "frozen_plant",
burn: 50,
burnTime: 20,
state: "solid",
tick(pixel) {
let belowPixel = getPixel(pixel.x, pixel.y + 1)
if ((!isEmpty(pixel.x, pixel.y + 1) && belowPixel) || outOfBounds(pixel.x, pixel.y + 1) && Math.random() <= 0.005) {
changePixel(pixel, "cacao_stem")
pixel.stage = 1
}
}
}
elements.cacao_stem = {
color: "#916a00",
renderer: renderPresets.WOODCHAR,
movable: false,
tempHigh: 100,
stateHigh: "wood",
tempLow: -30,
stateLow: "wood",
category: "life",
burn: 2,
burnTime: 300,
burnInto: ["sap", "ember", "charcoal", "smoke"],
state: "solid",
density: 1500,
hardness: 0.15,
breakInto: ["sap", "sawdust"],
seed: "cacao_seed",
forceSaveColor: true,
stateHighColorMultiplier: 0.95,
onPlace(pixel) {
pixel.stage = 1
},
hoverStat(pixel) {
if (pixel.stage) return pixel.stage;
else return 0;
},
tick(pixel) {
// 1 = trunk
// 2 = spread
// 3 = stop
if (pixel.stage === 1 && isEmpty(pixel.x, pixel.y - 1) && Math.random() <= 0.05) {
tryMove(pixel, pixel.x, pixel.y - 1, "cacao_stem")
let oldPixel = getPixel(pixel.x, pixel.y + 1)
delete oldPixel.stage
if (Math.random() <= 0.3) {
pixel.stage = 2
}
}
if (pixel.stage === 2) {
let rand = Math.random()
let nx;
if (rand < 0.4) {
nx = 1
}
else if (rand < 0.8) {
nx = -1
}
else nx = 0;
if (isEmpty(pixel.x + nx, pixel.y - 1) && Math.random() <= 0.05) {
createPixel(["cacao_stem", "plant"], pixel.x + nx, pixel.y - 1)
newPixel = getPixel(pixel.x + nx, pixel.y - 1)
if (Math.random() <= 0.2) {
newPixel.stage = 3
}
else newPixel.stage = 2;
}
if (!isEmpty(pixel.x + 1, pixel.y - 1) && !isEmpty(pixel.x, pixel.y - 1) && !isEmpty(pixel.x - 1, pixel.y - 1) && Math.random() <= 0.005) {
shuffleArray(adjacentCoordsShuffle)
for (var i = 0; i < adjacentCoordsShuffle.length; i++) {
var x = pixel.x + adjacentCoordsShuffle[i][0];
var y = pixel.y + adjacentCoordsShuffle[i][1];
if (isEmpty(x, y) && !pixel.fruitMade) {
createPixel("cacao_fruit", x, y)
pixel.fruitMade = true
pixel.fruitCoordsx = x
pixel.fruitCoordsy = y
break
}
}
}
}
if (pixel.fruitCoordsx && pixel.fruitCoordsy) {
if (getPixel(pixel.fruitCoordsx, pixel.fruitCoordsy) && getPixel(pixel.fruitCoordsx, pixel.fruitCoordsy).element === "cacao_fruit") return;
pixel.fruitMade = false
delete pixel.fruitCoordsx
delete pixel.fruitCoordsy
}
}
}
// --- audio setup ---
const audioCtx = new (window.AudioContext || window.webkitAudioContext)();
function playNote(frequency, duration = 1, type = "sine", volume = 0.1) {
if (!Number.isFinite(frequency)) {
console.error("Invalid frequency:", frequency);
return;
}
const osc = audioCtx.createOscillator();
const gain = audioCtx.createGain();
osc.type = type;
osc.frequency.value = frequency;
gain.gain.setValueAtTime(volume, audioCtx.currentTime);
osc.connect(gain);
gain.connect(audioCtx.destination);
gain.gain.exponentialRampToValueAtTime(0.001, audioCtx.currentTime + duration);
osc.start();
osc.stop(audioCtx.currentTime + duration);
}
const pianoFrequencies = {
"A0": 27.500, "A#0": 29.135, "BB0": 29.135, "B0": 30.868,
"C1": 32.703, "C#1": 34.648, "DB1": 34.648, "D1": 36.708,
"D#1": 38.891, "EB1": 38.891, "E1": 41.203, "F1": 43.654,
"F#1": 46.249, "GB1": 46.249, "G1": 48.999, "G#1": 51.913,
"AB1": 51.913, "A1": 55.000, "A#1": 58.270, "BB1": 58.270,
"B1": 61.735,
"C2": 65.406, "C#2": 69.296, "DB2": 69.296, "D2": 73.416,
"D#2": 77.782, "EB2": 77.782, "E2": 82.407, "F2": 87.307,
"F#2": 92.499, "GB2": 92.499, "G2": 97.999, "G#2": 103.826,
"AB2": 103.826, "A2": 110.000, "A#2": 116.541, "BB2": 116.541,
"B2": 123.471,
"C3": 130.813, "C#3": 138.591, "DB3": 138.591, "D3": 146.832,
"D#3": 155.563, "EB3": 155.563, "E3": 164.814, "F3": 174.614,
"F#3": 184.997, "GB3": 184.997, "G3": 195.998, "G#3": 207.652,
"AB3": 207.652, "A3": 220.000, "A#3": 233.082, "BB3": 233.082,
"B3": 246.942,
"C4": 261.626, "C#4": 277.183, "DB4": 277.183, "D4": 293.665,
"D#4": 311.127, "EB4": 311.127, "E4": 329.628, "F4": 349.228,
"F#4": 369.994, "GB4": 369.994, "G4": 391.995, "G#4": 415.305,
"AB4": 415.305, "A4": 440.000, "A#4": 466.164, "BB4": 466.164,
"B4": 493.883,
"C5": 523.251, "C#5": 554.365, "DB5": 554.365, "D5": 587.330,
"D#5": 622.254, "EB5": 622.254, "E5": 659.255, "F5": 698.456,
"F#5": 739.989, "GB5": 739.989, "G5": 783.991, "G#5": 830.609,
"AB5": 830.609, "A5": 880.000, "A#5": 932.328, "BB5": 932.328,
"B5": 987.767,
"C6": 1046.502, "C#6": 1108.731, "DB6": 1108.731, "D6": 1174.659,
"D#6": 1244.508, "EB6": 1244.508, "E6": 1318.510, "F6": 1396.913,
"F#6": 1479.978, "GB6": 1479.978, "G6": 1567.982, "G#6": 1661.219,
"AB6": 1661.219, "A6": 1760.000, "A#6": 1864.655, "BB6": 1864.655,
"B6": 1975.533,
"C7": 2093.005, "C#7": 2217.461, "DB7": 2217.461, "D7": 2349.318,
"D#7": 2489.016, "EB7": 2489.016, "E7": 2637.020, "F7": 2793.826,
"F#7": 2959.955, "GB7": 2959.955, "G7": 3135.963, "G#7": 3322.438,
"AB7": 3322.438, "A7": 3520.000, "A#7": 3729.310, "BB7": 3729.310,
"B7": 3951.066,
"C8": 4186.009
};
let note = 261.626; // default C4
let notesToPlay = [];
function flushNotes() {
if (notesToPlay.length === 0) return;
let baseVolume = 0.2;
let volume = baseVolume / Math.sqrt(notesToPlay.length);
for (let f of notesToPlay) {
playNote(f, 1, "sine", volume);
}
notesToPlay = [];
}
elements.note_block = {
color: "#965500",
behavior: behaviors.WALL,
onSelect() {
promptInput(
"Select the note this note block should be",
function (choice) {
if (!choice) {
if (!note) { note = 261.626; }
return;
}
let key = choice.toUpperCase();
if (key in pianoFrequencies) {
note = pianoFrequencies[key];
} else {
note = 261.626; // fallback = C4
}
},
"Note prompt"
);
},
onPlace(pixel) {
pixel.note = note;
},
tick(pixel) {
if (pixel.charge) {
notesToPlay.push(Number(pixel.note));
}
},
conduct: 1,
category: "machines"
};
runEveryTick(function () { flushNotes() });
/*
elements.uncook = {
color: ["#4dcdff", "#70ddff", "#bcddff", "#ffffff"],
category: "tools",
tool(pixel) {
if (!pixel || !pixel.element) return;
// 1) If the current element itself defines stateLow, use it (common case)
const cur = elements[pixel.element];
if (cur && cur.stateLow !== undefined) {
const low = cur.stateLow;
pixel.element = Array.isArray(low) ? low[Math.floor(Math.random() * low.length)] : low;
if (typeof pixel.temp === "number") pixel.temp = Math.max(0, pixel.temp - 1);
return; // done
}
// 2) Otherwise search for an element whose stateHigh === the current element
for (const key in elements) {
const el = elements[key];
if (!el) continue;
if (el.stateHigh === pixel.element) {
// 'key' is the low-state element name
changePixel(pixel, key)
if (typeof pixel.temp === "number") pixel.temp = Math.max(0, pixel.temp - 1);
return; // done
}
// If el.stateHigh can be an array of high-state names:
if (Array.isArray(el.stateHigh) && el.stateHigh.includes(pixel.element)) {
changePixel(pixel, key)
if (typeof pixel.temp === "number") pixel.temp = Math.max(0, pixel.temp - 1);
return;
}
}
}
};
*/
elements.roman_cement = {
color: "#b8b8b8",
behavior: behaviors.LIQUID,
category: "liquids",
viscosity: 1000,
density: 1400,
state: "solid",
tempLow: -10,
stateLow: "roman_concrete",
tempHigh: 1550,
stateHigh: "magma",
tick(pixel) {
if (pixelTicks - pixel.start > 100 && Math.random() <= 0.1) {
changePixel(pixel, "roman_concrete")
}
}
}
elements.roman_concrete = {
color: "#ababab",
behavior: behaviors.SUPPORT,
tempHigh: 1500,
stateHigh: "magma",
category: "powders",
state: "solid",
density: 2400,
hardness: 0.5,
breakInto: "dust",
darkText: true
}
/**
*
* @param {string} element
* @param {object} reaction
* @returns {void}
*/
function doWaterReactions(element, reaction) {
if (!elements[element].reactions) {
elements[element].reactions = {}
}
elements[element].reactions.water = reaction
elements[element].reactions.salt_water = reaction
elements[element].reactions.pool_water = reaction
elements[element].reactions.sugar_water = reaction
elements[element].reactions.dirty_water = reaction
elements[element].reactions.selter = reaction
elements[element].reactions.primordial_soup = reaction
elements[element].reactions.nut_milk = reaction
}
doWaterReactions("slaked_lime", {elem1:"roman_cement", elem2: null, chance: 0.25})