sandboxels/archive/third-version.html

2242 lines
87 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<html>
<head>
<meta charset="utf-8">
<title>Sandbox Game</title>
<link href="https://fonts.googleapis.com/css?family=Press+Start+2P" rel="stylesheet">
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
html, body {
width: 100%;
height: 100%;
margin: 0;
}
body {
font-family: 'Press Start 2P';
background-color: #000000;
color: #ffffff;
}
h1 {
padding: 10px;
}
#gameDiv { /*game canvas*/
border: 1px solid #ffffff;
position: absolute;
left: 50%;
transform: translate(-50%, -0%);
-webkit-touch-callout: none; /* iOS Safari */
-webkit-user-select: none; /* Safari */
-khtml-user-select: none; /* Konqueror HTML */
-moz-user-select: none; /* Old versions of Firefox */
-ms-user-select: none; /* Internet Explorer/Edge */
user-select: none;
}
button, input[type="submit"], input[type="reset"] {
background: none;
color: inherit;
border: none;
padding: 0;
font: inherit;
cursor: pointer;
outline: inherit;
}
#underBox {
position: absolute;
left: 50%;
transform: translate(-50%, -0%);
margin-top: 10px;
width: 100%;
}
#controls button {
padding: 5px 10px;
border-radius: 5px;
font-size: 1em;
text-shadow: 0.5px 1px 4px #000000;
color: rgba(255, 255, 255, 0.75);
border: 1px solid #797979;
margin: 0px 5px 5px 5px;
font-variant: small-caps;
}
#controls button.bright {
text-shadow: 0.5px 1px 4px #ffffff;
color: rgba(0, 0, 0, 0.75);
}
/*Darken when active*/
#controls button:active, #controls button:active:hover {
filter: brightness(60%);
}
#controls button:hover {
filter: brightness(90%);
}
#controls button:disabled {
cursor: not-allowed;
}
#controls button[current="true"], #controls button[on="true"] {
border: 1px solid #ffffff;
filter: brightness(110%);
box-shadow: 0 5px 15px rgba(255, 255, 255, .4);
color: rgba(255, 255, 255, 1);
}
#controls button.bright[current="true"] {
color: rgba(0, 0, 0, 1);
}
#controls button[on="true"] {
border-color:lime;
color:lime;
}
#controls div {
display:block;
}
.stat {
margin-right: 25px;
margin-bottom: 5px;
float:right;
}
#stats {
margin: 0px 5px 5px 5px;
font-size: 0.75em;
height: 2em;
}
#stat-pos, #stat-pixels, #stat-shift, #stat-tps, #stat-ticks {
float:left;
}
.categoryName {
font-size: 0.75em;
text-transform: uppercase;
margin-left: 5px;
vertical-align: middle;
}
#extraInfo {
margin:5px
}
</style>
<script>
//replaceAll polyfill
if (!String.prototype.replaceAll) {String.prototype.replaceAll = function(str, newStr){if (Object.prototype.toString.call(str).toLowerCase() === '[object regexp]') {return this.replace(str, newStr);}return this.replace(new RegExp(str, 'g'), newStr);};}
</script>
<script>
behaviors = {
"POWDER": [
"XX|XX|XX",
"XX|XX|XX",
"M2|M1|M2",
],
"AGPOWDER": [
"M2|M1|M2",
"XX|XX|XX",
"XX|XX|XX",
],
"LIQUID": [
"XX|XX|XX",
"M2|XX|M2",
"M1|M1|M1",
],
"AGLIQUID": [
"M1|M1|M1",
"M2|XX|M2",
"XX|XX|XX",
],
"WALL": [
"XX|XX|XX",
"XX|XX|XX",
"XX|XX|XX",
],
"UL_UR": [
"M1|M1|M1",
"M2|XX|M2",
"XX|XX|XX",
],
"GAS": [
"M2|M1|M2",
"M1|XX|M1",
"M2|M1|M2",
],
"DGAS": [
"M2|M1|M2",
"M1|DL%5|M1",
"M2|M1|M2",
],
"SUPPORT": [
"XX|XX|XX",
"SP|XX|SP",
"XX|M1|XX",
],
"SUPPORTPOWDER": [
"XX|XX|XX",
"SP|XX|SP",
"M2|M1|M2",
],
"DELETE": [
"XX|DL|XX",
"DL|XX|DL",
"XX|DL|XX",
],
"FILL": [
"XX|CL|XX",
"CL|XX|CL",
"XX|CL|XX",
],
"STURDYPOWDER": [
"XX|XX|XX",
"XX|XX|XX",
"XX|M1|XX",
],
"SELFDELETE": [
"XX|XX|XX",
"XX|DL|XX",
"XX|XX|XX",
],
"FOAM": [
"SW:water%10|SW:water%10|SW:water%10",
"XX|DL%5|XX",
"M2%25|M1%25|M2%25",
],
"BUBBLE": [
"SW:water%25|SW:water%25|SW:water%25",
"M1%5|DL%0.25|M1%5",
"M1%2|M1%1|M1%2",
],
"STICKY": [
"XX|ST|XX",
"ST|XX|ST",
"XX|ST AND M1|XX",
],
}
airDensity = 1.225; // kg/m^3
airTemp = 20; // Celsius
// Object storing various powders, called elements
// name - name of the element
// color - color of the element's pixel
// density - density of the element [unused currently] (kg/m^3)
// behavior - behavior of the element
// flam - temperature of combustion [unused currently]
// temp - default temperature of the element (Celsius)
// tempHigh - highest temperature before state change
// tempLow - lowest temperature before state change
// stateHigh - element transformed into when tempHigh is reached
// stateLow - element transformed into when tempLow is reached
// viscosity - how slow a liquid will move (higher = slower) (cps)
// [Future]
// changeAfter - seconds before element dissolves
// change - element transformed into after time
elements = {
"heat": {
"name": "heat",
"color": "#ff0000",
"behavior": behaviors.WALL,
"temp": 2,
"category": "tools",
},
"cool": {
"name": "cool",
"color": "#0000ff",
"behavior": behaviors.WALL,
"temp": -2,
"category": "tools",
},
"sand": {
"name": "sand",
"color": "#e6d577",
"behavior": behaviors.POWDER,
"density": 1602,
"tempHigh": 1700,
"stateHigh": "molten_glass",
"category": "land",
},
"water": {
"name": "water",
"color": "#2167ff",
"behavior": behaviors.LIQUID,
"density": 997,
"tempHigh": 100,
"stateHigh": "steam",
"tempLow": 0,
"stateLow": "ice",
"viscosity": 1,
"category": "liquids",
},
"dirt": {
"name": "dirt",
"color": ["#9e6b4b","#9e6b4b","#9e6b4b","#9e6b4b","#9e6b4b","#9e6b4b","#9e6b4b","#9e6b4b","#9e6b4b","#9e6b4b","#9e6b4b","#9e6b4b","#9e6b4b","#9e6b4b","#b0a39b"],
"behavior": behaviors.POWDER,
"tempHigh":1200,
"category":"land",
},
"plant": {
"name": "plant",
"color": "#00bf00",
"behavior": [
"XX|DL:carbon_dioxide%50|XX",
"DL:carbon_dioxide%50|XX|DL:carbon_dioxide%50",
"XX|DL:carbon_dioxide%50|XX",
],
"category":"life",
"burn":65,
"burnTime":50,
},
"grass": {
"name": "grass",
"color": "#00ff00",
"behavior": [
"XX|XX|XX",
"XX|XX|XX",
"XX|M1|XX",
],
"tempHigh": 400,
"stateHigh": "fire",
"burn":50,
"burnTime":20,
"category":"land",
},
"wall": {
"name": "wall",
"color": "#808080",
"behavior": behaviors.WALL,
"density": 0,
"category": "solids",
},
"fire": {
"name": "fire",
"color": ["#ff6b21","#ffa600","#ff4000"],
"behavior": behaviors.UL_UR,
"density": 1300,
"temp":400,
"tempLow":350,
"stateLow": "smoke",
"tempHigh": 6095,
"stateHigh": "plasma",
"category": "energy",
"burning": true,
"burnTime": 25,
"burnInto": "smoke",
},
"steam": {
"name": "steam",
"color": "#abd6ff",
"behavior": behaviors.GAS,
"density": 0.013,
"temp": 100,
"tempLow": 95,
"stateLow": "water",
"category": "gases",
},
"ice": {
"name": "ice",
"color": "#c5e9f0",
"behavior": behaviors.WALL,
"density": 917,
"temp": 0,
"tempHigh": 5,
"stateHigh": "water",
"category": "solids",
},
"snow": {
"name": "snow",
"color": "#e1f8fc",
"behavior": behaviors.POWDER,
"density": 100,
"temp": 0,
"tempHigh": 5,
"stateHigh": "water",
"category": "land",
},
"packed_snow": {
"name": "packed snow",
"color": "#bcdde3",
"behavior": behaviors.SUPPORTPOWDER,
"density": 100,
"temp": 0,
"tempHigh": 20,
"stateHigh": "water",
"category": "land",
},
"wood": {
"name": "wood",
"color": "#a0522d",
"behavior": behaviors.WALL,
"density": 745,
"tempHigh": 400,
"stateHigh": "fire",
"category": "solids",
"burn": 5,
"burnTime": 300,
"burnInto": ["ash","charcoal"],
},
"smoke": {
"name": "smoke",
"color": "#383838",
"behavior": behaviors.DGAS,
"density": 1180,
"temp": 114,
"tempHigh": 605,
"stateHigh": "fire",
"deleteAfter": 100,
"category": "gases",
},
"rock": {
"name": "rock",
"color": ["#808080","#4f4f4f","#949494"],
"behavior": behaviors.POWDER,
"density": 2700,
"tempHigh": 950,
"stateHigh": "magma",
"category": "land",
},
"magma": {
"name": "magma",
"color": ["#ff6f00","#ff8c00","#ff4d00"],
"behavior": [
"XX|CR:fire%2.5|XX",
"M2|XX|M2",
"M1|M1|M1",
],
"temp": 950,
"tempLow": 800,
"stateLow": "rock",
"viscosity": 10000,
"category": "liquids",
},
"concrete": {
"name": "concrete",
"color": "#ababab",
"behavior": behaviors.SUPPORT,
"density": 2400,
"tempHigh": 1500,
"stateHigh": "magma",
"category": "structure",
},
"plasma": {
"name": "plasma",
"color": ["#8800ff","#b184d9","#8800ff"],
"behavior": behaviors.GAS,
"temp":12750,
"tempChange":-100,
"tempLow":10000,
"stateLow": "fire",
"category": "energy",
"burning": true,
},
"iron": {
"name": "iron",
"color": "#cbcdcd",
"behavior": behaviors.WALL,
"tempHigh": 1538,
"category": "solids",
},
"glass": {
"name": "glass",
"color": "#5e7c80",
"behavior": behaviors.WALL,
"tempHigh": 1500,
"category": "solids",
},
"glass_shard": {
"name": "glass shard",
"color": "#5e7c80",
"behavior": behaviors.POWDER,
"tempHigh": 1500,
"stateHigh": "molten_glass",
"category": "structure",
},
"blood": {
"name": "blood",
"color": "#ff0000",
"behavior": behaviors.LIQUID,
"viscosity": 10,
"category":"liquids",
},
"honey": {
"name": "honey",
"color": ["#ffd700","#ffae00"],
"behavior": behaviors.LIQUID,
"viscosity": 10000,
"category":"liquids",
},
"ketchup": {
"name": "ketchup",
"color": "#ff3119",
"behavior": behaviors.LIQUID,
"viscosity": 50000,
"category":"liquids",
},
"molasses": {
"name": "molasses",
"color": "#240f00",
"behavior": behaviors.LIQUID,
"viscosity": 7500,
"category":"liquids",
},
"filler": {
"name": "filler",
"color": "#ae4cd9",
"behavior": behaviors.FILL,
"category":"special",
},
"gravel": {
"name": "gravel",
"color": "#454b51",
"behavior": behaviors.POWDER,
"category":"land",
},
"slime": {
"name": "slime",
"color": "#81cf63",
"behavior": behaviors.LIQUID,
"viscosity": 5000,
"category":"liquids",
},
"ruins": {
"name": "ruins",
"color": "#5c5c5c",
"behavior": [
"XX|SP|XX",
"XX|XX|XX",
"M2%1|M1|M2%1"
],
"category":"structure",
},
"void": {
"name": "void",
"color": "#262626",
"behavior": behaviors.DELETE,
"category":"special",
},
"flea": {
"name": "flea",
"color": "#9e4732",
"behavior": [
"M2|XX|M2",
"DL:blood|XX|DL:blood",
"M2|M1|M2",
],
"tempHigh": 35,
"stateHigh": "ash",
"category":"life",
"burn":95,
"burnTime":25,
},
"ant": {
"name": "ant",
"color": "#4a0903",
"behavior": [
"SW:dirt,sand,gravel%5|XX|SW:dirt,sand,gravel%5",
"M2|XX|M2",
"SW:dirt,sand,gravel%5|M1|SW:dirt,sand,gravel%5",
],
"tempHigh": 53.7,
"stateHigh": "ash",
"category":"life",
"burn":95,
"burnTime":25,
},
"fly": {
"name": "fly",
"color": "#303012",
"behavior": [
"M1|XX|M1",
"DL:plant,meat,cooked_meat|XX|DL:plant,meat,cooked_meat",
"M1|M2|M1",
],
"tempHigh": 47,
"stateHigh": "ash",
"category":"life",
"burn":95,
"burnTime":25,
},
"frog": {
"name": "frog",
"color": "#607300",
"behavior": [
"M2%3|CR:slime%0.01|M2%3",
"DL:fly|XX|DL:fly",
"XX|M1|XX",
],
"temp": 19.1,
"tempHigh": 41,
"stateHigh": "meat",
"tempLow": -18,
"stateLow": "frozen_frog",
"category":"life",
"burn":75,
"burnTime":30,
},
"frozen_frog": {
"name": "frozen frog",
"color": "#007349",
"behavior": behaviors.STURDYPOWDER,
"temp": -18,
"tempHigh": 0,
"stateHigh": "frog",
"tempLow": -18,
"stateLow": "frozen_frog",
"category":"life",
"hidden": true,
},
"fish": {
"name": "fish",
"color": "#ac8650",
"behavior": [
"SW:water%6|M2%5|SW:water%6",
"DL:algae|XX|DL:algae",
"SW:water%8|M1|SW:water%8",
],
"temp": 20,
"tempHigh": 32,
"stateHigh": "meat",
"category":"life",
"burn":40,
"burnTime":100,
},
"algae": {
"name": "algae",
"color": "#00870e",
"behavior": [
"XX|XX|XX",
"SW:water%1|XX|SW:water%1",
"SW:water%10|M1|SW:water%18",
],
"category":"life",
"burn":95,
"burnTime":20,
},
"heater": {
"name": "heater",
"color": "#881111",
"behavior": [
"XX|HT|XX",
"HT|XX|HT",
"XX|HT|XX",
],
"category":"machines",
},
"cooler": {
"name": "cooler",
"color": "#111188",
"behavior": [
"XX|CO|XX",
"CO|XX|CO",
"XX|CO|XX",
],
"category":"machines",
},
"superheater": {
"name": "superheater",
"color": "#dd1111",
"behavior": [
"XX|HT:10|XX",
"HT:10|XX|HT:10",
"XX|HT:10|XX",
],
"category":"machines",
},
"freezer": {
"name": "freezer",
"color": "#1111dd",
"behavior": [
"XX|CO:10|XX",
"CO:10|XX|CO:10",
"XX|CO:10|XX",
],
"category":"machines",
},
"smoke_grenade": {
"name": "smoke grenade",
"color": "#2b382c",
"behavior": [
"XX|CR:smoke|XX",
"XX|XX|XX",
"M2|M1|M2",
],
"category":"weapons",
},
"torch": {
"name": "torch",
"color": "#d68542",
"behavior": [
"XX|CR:fire|XX",
"XX|XX|XX",
"XX|XX|XX",
],
"category":"special",
},
"water_spout": {
"name": "water spout",
"color": "#606378",
"behavior": [
"XX|CR:water|XX",
"CR:water|XX|CR:water",
"XX|CR:water|XX",
],
"category":"special",
},
"bone_marrow": {
"name": "bone marrow",
"color": "#c97265",
"behavior": [
"XX|CR:blood,bone,bone%5|XX",
"CR:blood,bone,bone%5|XX|CR:blood,bone,bone%5",
"XX|CR:blood,bone,bone%5|XX",
],
"category":"life",
"hidden": true,
},
"bone": {
"name": "bone",
"color": "#d9d9d9",
"behavior": behaviors.SUPPORT,
"category":"life",
},
"anti_powder": {
"name": "anti powder",
"color": "#ebd1d8",
"behavior": behaviors.AGPOWDER,
"category":"special",
},
"anti_fluid": {
"name": "anti fluid",
"color": "#d1dbeb",
"behavior": behaviors.AGLIQUID,
"category":"special",
},
"vertical": {
"name": "vertical",
"color": "#d9d9d9",
"behavior": [
"XX|M1|XX",
"CR:wall|XX|CR:wall",
"XX|XX|XX",
],
"category":"special",
"hidden":true,
},
"horizontal": {
"name": "horizontal",
"color": "#d9d9d9",
"behavior": [
"XX|CR:wall|XX",
"XX|XX|M1",
"XX|CR:wall|XX",
],
"category":"special",
"hidden":true,
},
"rain_cloud": {
"name": "rain cloud",
"color": "#636b78",
"behavior": [
"XX|XX|XX",
"XX|XX|XX",
"XX|CR:water%1|XX",
],
"category":"special",
},
"snow_cloud": {
"name": "snow cloud",
"color": "#7e8691",
"behavior": [
"XX|XX|XX",
"XX|XX|XX",
"XX|CR:packed_snow%1|XX",
],
"category":"special",
},
"rocket": {
"name": "rocket",
"color": "#ff0000",
"behavior": [
"XX|M1|XX",
"XX|XX|XX",
"XX|CR:fire|XX",
],
"category":"weapons",
"hidden":true,
},
"ash": {
"name": "ash",
"color": ["#8c8c8c","#9c9c9c"],
"behavior": behaviors.POWDER,
"category":"energy",
},
"meat": {
"name": "meat",
"color": "#b86565",
"behavior": behaviors.SUPPORT,
"tempHigh": 80,
"stateHigh": "cooked_meat",
"tempLow": -18,
"stateLow": "frozen_meat",
"category":"life",
"burn":25,
"burnTime":200,
"burnInto":"cooked_meat",
},
"cooked_meat": {
"name": "cooked meat",
"color": "#61361c",
"behavior": behaviors.STURDYPOWDER,
"tempHigh": 120,
"stateHigh": "ash",
"category":"life",
"hidden":true,
"burn":25,
"burnTime":200,
"burnInto": "ash",
},
"frozen_meat": {
"name": "frozen meat",
"color": "#65b8aa",
"behavior": behaviors.STURDYPOWDER,
"temp": -18,
"tempHigh": 0,
"stateHigh": "meat",
"category":"life",
"hidden":true,
},
"light": {
"name": "light",
"color": "#ffffa8",
"behavior": behaviors.SELFDELETE,
"category":"energy",
},
"pointer": {
"name": "pointer",
"color": "#ff0000",
"behavior": behaviors.SELFDELETE,
"category":"tools",
"hidden":true,
},
"charcoal": {
"name": "charcoal",
"color": "#2b2b2b",
"behavior": behaviors.POWDER,
"burn": 25,
"burnTime": 1000,
"burnInto": "carbon_dioxide",
"category": "energy",
},
"carbon_dioxide": {
"name": "carbon dioxide",
"color": "#2f2f2f",
"behavior": behaviors.GAS,
"category": "gases",
"tempLow": -78.5,
"stateLow": "dry_ice",
},
"dry_ice": {
"name": "dry ice",
"color": "#e6e6e6",
"behavior": behaviors.WALL,
"category": "solids",
"temp": -78.5,
"tempHigh": -78.5,
"stateHigh": "carbon_dioxide",
},
"oil": {
"name": "oil",
"color": "#300000",
"behavior": behaviors.LIQUID,
"category": "liquids",
"burn": 100,
"burnTime": 30,
"burnInto": ["carbon_dioxide","fire"],
"viscosity": 250,
},
"lamp_oil": {
"name": "lamp oil",
"color": "#1a0803",
"behavior": behaviors.LIQUID,
"category": "liquids",
"burn": 95,
"burnTime": 2000,
"burnInto": ["carbon_dioxide","fire"],
"viscosity": 3,
},
"propane": {
"name": "propane",
"color": "#cfcfcf",
"behavior": behaviors.GAS,
"category": "gases",
"burn": 100,
"burnTime": 5,
"fireColor": ["#00ffff","#00ffdd"],
},
"methane": {
"name": "methane",
"color": "#9f9f9f",
"behavior": behaviors.GAS,
"category": "gases",
"burn": 85,
"burnTime": 5,
"fireColor": ["#00ffff","#00ffdd"],
},
"oxygen": {
"name": "oxygen",
"color": "#99c7ff",
"behavior": behaviors.GAS,
"category": "gases",
"burn": 100,
"burnTime": 2,
"tempLow": -183.94,
"stateLow": "liquid_oxygen",
},
"liquid_oxygen": {
"name": "liquid oxygen",
"color": "#00ad99",
"behavior": behaviors.LIQUID,
"category": "liquids",
"burn": 100,
"burnTime": 2,
"temp":-182.94,
"tempHigh": -182.94,
"stateHigh": "oxygen",
},
"tinder": {
"name": "tinder",
"color": ["#917256","#87684F","#735F4A","#5D4C3E","#4B3A2E"],
"behavior": behaviors.STURDYPOWDER,
"category": "energy",
"burn": 50,
"burnTime": 100,
},
"static": {
"name": "static",
"color": ["#ffffff","#888888","#000000"],
"behavior": [
"XX|XX|XX",
"XX|CC:ffffff,9c9c9c,454545|XX",
"XX|XX|XX",
],
"category": "special",
},
"borax": {
"name": "borax",
"color": "#ffffff",
"behavior": behaviors.POWDER,
"burn": 15,
"burnTime": 200,
"fireColor": ["#34eb67","#5ceb34"],
"tempHigh": 743,
},
"bamboo": {
"name": "bamboo",
"color": "#336600",
"behavior": behaviors.WALL,
"tempHigh": 400,
"stateHigh": "fire",
"burn": 10,
"burnTime": 200,
"category": "life",
},
"bamboo_plant": {
"name": "bamboo plant",
"color": "#b4c781",
"behavior": [
"XX|M2%0.25|XX",
"XX|L2:bamboo AND CH:bamboo%0.025|XX",
"XX|M1|XX",
],
"tempHigh": 400,
"stateHigh": "fire",
"burn": 30,
"burnTime": 100,
"category": "life",
},
"clay": {
"name": "clay",
"color": "#d4c59c",
"behavior": behaviors.SUPPORT,
"tempHigh": 135,
"stateHigh": "baked_clay",
"category": "land",
},
"baked_clay": {
"name": "baked clay",
"color": "#b85746",
"behavior": behaviors.SUPPORT,
"category": "structure",
},
"brick": {
"name": "brick",
"color": "#cb4141",
"behavior": behaviors.WALL,
"category": "structure",
"tempHigh": 1540,
},
"sapling": {
"name": "sapling",
"color": "#3e9c3e",
"behavior": [
"XX|M2%2|XX",
"XX|L2:wood,tree_branch AND CH:wood%0.25|XX",
"XX|M1|XX",
],
"tempHigh": 400,
"stateHigh": "fire",
"burn": 65,
"burnTime": 15,
"category": "life",
},
"grass_seed": {
"name": "grass seed",
"color": "#00ff00",
"behavior": [
"XX|M2%0.05|XX",
"XX|L2:grass|XX",
"XX|M1|XX",
],
"tempHigh": 400,
"stateHigh": "fire",
"burn": 50,
"burnTime": 20,
"category": "life",
},
"wheat_seed": {
"name": "wheat seed",
"color": "#b6c981",
"behavior": [
"XX|M2%0.25|XX",
"CR:wheat%0.05|L2:wheat|CR:wheat%0.05",
"XX|M1|XX",
],
"tempHigh": 400,
"stateHigh": "fire",
"burn": 50,
"burnTime": 20,
"category": "life",
},
"wheat": {
"name": "wheat",
"color": "#c9bc81",
"behavior": behaviors.WALL,
"tempHigh": 400,
"stateHigh": "fire",
"burn":40,
"burnTime":25,
"category":"life",
"hidden":true,
},
"straw": {
"name": "straw",
"color": "#c9bc81",
"behavior": behaviors.SUPPORT,
"tempHigh": 400,
"stateHigh": "fire",
"burn": 60,
"burnTime": 40,
"category": "structure",
},
"flower_seed": {
"name": "flower seed",
"color": "#0e990e",
"behavior": [
"XX|M2%1.5|XX",
"XX|L2:grass AND CH:pistil%0.3|XX",
"XX|M1|XX",
],
"tempHigh": 400,
"stateHigh": "fire",
"burn":50,
"burnTime":20,
"category":"life",
},
"pistil": {
"name": "petal",
"color": "#734e39",
"behavior": [
"XX|CR:petal%10|XX",
"CR:petal%10|XX|CR:petal%10",
"XX|M1|XX",
],
"tempHigh": 400,
"stateHigh": "fire",
"burn":50,
"burnTime":20,
"category":"life",
"hidden": true,
},
"petal": {
"name": "petal",
"color": "#ff0000",
"behavior": behaviors.WALL,
"tempHigh": 400,
"stateHigh": "fire",
"burn":50,
"burnTime":20,
"category":"life",
"hidden": true,
},
"tree_branch": {
"name": "tree branch",
"color": "#a0522d",
"behavior": [
"CR:plant,tree_branch%2|CR:plant,plant,plant,tree_branch%2|CR:plant,tree_branch%2",
"XX|XX|XX",
"XX|XX|XX",
],
"density": 745,
"tempHigh": 400,
"stateHigh": "fire",
"category": "solids",
"burn": 40,
"burnTime": 50,
"burnInto": ["ash","charcoal"],
"hidden": true,
},
"vine": {
"name": "vine",
"color": "#005900",
"behavior": [
"XX|XX|XX",
"XX|XX|XX",
"XX|CL%2.5|XX",
],
"tempHigh": 400,
"stateHigh": "fire",
"burn": 35,
"burnTime": 100,
"category": "life",
},
"burner": {
"name": "burner",
"color": "#d6baa9",
"behavior": [
"CR:propane|CR:propane|CR:propane",
"XX|XX|XX",
"XX|XX|XX",
],
"category": "machines"
},
"foam": {
"name": "foam",
"color": "#cad2e3",
"behavior": behaviors.FOAM,
"category": "liquids"
},
"bubble": {
"name": "bubble",
"color": "#afc7fa",
"behavior": behaviors.BUBBLE,
"category": "liquids"
},
"rainbow": {
"name": "rainbow",
"color": ["#ff0000","#ff8800","#ffff00","#00ff00","#00ffff","#0000ff","#ff00ff"],
"behavior": [
"XX|XX|XX",
"XX|CC:ff0000,ff8800,ffff00,00ff00,00ffff,0000ff,ff00ff|XX",
"XX|XX|XX",
],
"category": "special",
},
"copper": {
"name": "copper",
"color": "#e08128",
"behavior": [
"XX|XX|XX",
"XX|CH:oxidized_copper%0.001|XX",
"XX|XX|XX",
],
"category": "solids",
"tempHigh": 1085,
},
"oxidized_copper": {
"name": "oxidized copper",
"color": "#7f9c21",
"behavior": [
"XX|XX|XX",
"XX|XX|XX",
"XX|XX|XX",
],
"category": "solids",
"hidden": true,
"tempHigh": 1085,
"stateHigh": "molten_copper",
},
"acid": {
"name": "acid",
"color": ["#b5cf91","#a1ff5e","#288f2a"],
"behavior": [
"XX|DL%5|XX",
"DL%5 AND M2|XX|DL%5 AND M2",
"DL%5 AND M2|DL%10 AND M1|DL%5 AND M2",
],
"category": "liquids",
"burn": 30,
"burnTime": 1,
},
"dust": {
"name": "dust",
"color": "#666666",
"behavior": behaviors.POWDER,
"burn": 10,
"burnTime": 1,
},
"glue": {
"name": "glue",
"color": "#f0f0f0",
"behavior": behaviors.STICKY,
"category": "liquids",
},
"gray_goo": {
"name": "gray goo",
"color": "#c0c0c0",
"behavior": [
"XX|CH:gray_goo|XX",
"CH:gray_goo|DL%5|CH:gray_goo",
"XX|CH:gray_goo AND M1|XX",
],
"category": "machines",
},
"virus": {
"name": "virus",
"color": "#cc00ff",
"behavior": [
"XX|CH:virus%25|XX",
"CH:virus%25|XX|CH:virus%25",
"XX|CH:virus%25 AND M1|XX",
],
"category": "special",
},
"worm": {
"name": "worm",
"color": "#402208",
"behavior": [
"DL:ash%1 AND SW:dirt,sand,gravel,ash%3|XX|DL:ash%1 AND SW:dirt,sand,gravel,ash%3",
"DL:ash%1 AND M2%10|XX|DL:ash%1 AND M2%10",
"DL:ash%1 AND SW:dirt,sand,gravel,ash%3|M1|DL:ash%1 AND SW:dirt,sand,gravel,ash%3",
],
"tempHigh": 40,
"stateHigh": "ash",
"category":"life",
"burn":95,
"burnTime":25,
},
"termite": {
"name": "termite",
"color": "#f5a056",
"behavior": [
"SW:wood,tree_branch%5|XX|SW:wood,tree_branch%5",
"DL:wood%4 AND M2%15|XX|DL:wood%4 AND M2%15",
"SW:wood,tree_branch%5|M1|SW:wood,tree_branch%5",
],
"tempHigh": 37.78,
"stateHigh": "ash",
"category":"life",
"burn":95,
"burnTime":25,
},
}
// Loop through behaviors and each behavior, if it is a string, split the items and replace the value with the array
for (var behavior in behaviors) {
if (typeof behaviors[behavior][0] === "string") {
var newbehavior = [];
for (var i = 0; i < behaviors[behavior].length; i++) {
newbehavior.push(behaviors[behavior][i].split("|"));
}
behaviors[behavior] = newbehavior;
}
}
// Loop through each element. If it has a tempHigh, but not a stateHigh, create a new molten element
for (element in elements) {
if (elements[element].tempHigh && !elements[element].stateHigh) {
var newname = "molten_"+element;
elements[element].stateHigh = newname;
elements[newname] = {
"name": newname.replaceAll("_"," "),
"color": ["#ff6f00","#ff8c00","#ff4d00"],
"behavior": behaviors.LIQUID,
"density": elements[element].density/2,
"temp": elements[element].tempHigh,
"tempLow": elements[element].tempHigh-100,
"stateLow": element,
"viscosity": 10000,
"hidden": true,
}
}
if (elements[element].behavior && typeof elements[element].behavior[0] === "string") {
var newbehavior = [];
for (var i = 0; i < elements[element].behavior.length; i++) {
newbehavior.push(elements[element].behavior[i].split("|"));
}
elements[element].behavior = newbehavior;
}
}
function hexToRGB(hex) {
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result ? {
r: parseInt(result[1], 16),
g: parseInt(result[2], 16),
b: parseInt(result[3], 16)
} : null;
}
// convert every color in the elements object to rgb
for (var key in elements) {
if (elements.hasOwnProperty(key)) {
// if the color is an array, loop over each one
if (elements[key].color instanceof Array) {
var rgbs = [];
var rgbos = [];
for (var i = 0; i < elements[key].color.length; i++) {
var c = elements[key].color[i];
if (c.startsWith("#")) {
var rgb = hexToRGB(c);
rgbs.push("rgb("+rgb.r+","+rgb.g+","+rgb.b+")");
rgbos.push(rgb);
}
else {
rgbs.push(c);
}
}
elements[key].color = rgbs;
elements[key].colorObject = rgbos;
} else {
// if elements[key].color starts with #
if (elements[key].color.startsWith("#")) {
var rgb = hexToRGB(elements[key].color);
elements[key].color = "rgb("+rgb.r+","+rgb.g+","+rgb.b+")";
elements[key].colorObject = rgb;
}
}
}
}
currentPixels = [];
currentID = 0;
// Pixel class, with attributes such as x, y, and element
class Pixel {
constructor(x, y, element) {
this.x = x;
this.y = y;
this.element = element;
var elementInfo = elements[element];
this.color = pixelColorPick(this);
// If element doesn't have temp attribute, set temp to airTemp
if (elementInfo.temp==undefined) {
this.temp = airTemp;
} else {
this.temp = elementInfo.temp;
}
this.start = pixelTicks;
this.id = currentID;
if (elementInfo.burning) {
this.burning = true;
this.burnStart = pixelTicks;
}
else {
this.burning = false;
}
currentID++;
pixelMap[x][y] = this;
}
}
pixelSize = 10;
function outOfBounds(x,y) {
// Returns true if the pixel is out of bounds
return y > height-1 || y < 1 || x > width-1 || x < 1
}
function isEmpty(x, y, ignoreBounds=false) {
if (outOfBounds(x,y)) {
return ignoreBounds;
}
return pixelMap[x][y] == undefined;
}
function canMove(pixel,x,y) {
if (isEmpty(x,y)) {
return true;
}
}
function movePixel(pixel,x,y,leaveBehind=null) {
// if the pixel isn't in currentPixels, return
if (!currentPixels.includes(pixel)) {
return;
}
// Delete the pixel from the old position
delete pixelMap[pixel.x][pixel.y];
if (leaveBehind != null && isEmpty(pixel.x,pixel.y)) { createPixel(leaveBehind,pixel.x,pixel.y); }
pixel.x = x;
pixel.y = y;
pixelMap[x][y] = pixel;
}
function clonePixel(pixel,x,y) {
currentPixels.push(new Pixel(x, y, pixel.element));
}
function createPixel(element,x,y) {
currentPixels.push(new Pixel(x, y, element));
}
function deletePixel(x,y,id=null) {
delete pixelMap[x][y];
if (id != null) {
for (var i = 0; i < currentPixels.length; i++) {
if (currentPixels[i].id == id) {
currentPixels.splice(i, 1);
return;
}
}
}
for (var i = 0; i < currentPixels.length; i++) {
if (currentPixels[i].x == x && currentPixels[i].y == y) {
currentPixels.splice(i, 1);
break;
}
}
}
function swapPixels(pixel1,pixel2) {
var tempX = pixel1.x;
var tempY = pixel1.y;
pixel1.x = pixel2.x;
pixel1.y = pixel2.y;
pixel2.x = tempX;
pixel2.y = tempY;
pixelMap[pixel1.x][pixel1.y] = pixel1;
pixelMap[pixel2.x][pixel2.y] = pixel2;
}
function behaviorCoords(x,y,bx,by) {
bx -= 1;
by -= 1;
x += bx;
y += by;
return {x:x,y:y};
}
/* New Behavior Example (Sand)
[
["XX","XX","XX"],
["XX","XX","XX"],
["M2","M1","M2"]
] */
/* Behavior Rules
XX = Ignore
M1 = Move (First Priority)
M2 = Move (Second Priority)
SP = Support (Doesn't move if all aren't empty)
DL = Delete
CL = Clone
CH = Change
RP = Replace (Move to an existing element)
CR:element_name = Create a pixel of element_name
LB:element_name = Leave behind a pixel of element_name when moved (Must be center cell)
%(num) = Chance of rule happening
L1:element_name = Leave behind only on M1 moves
L2:element_name = Leave behind only on M2 moves
SW = Swap
HT = Heat
CO = Cool
CC = Change Color (Hexadecimal)
ST = Stick
*/
function pixelTick(pixel) {
if (pixel.start == pixelTicks) {return}
var info = elements[pixel.element];
var behavior = info.behavior;
var x = pixel.x;
var y = pixel.y;
var move1Spots = [];
var move2Spots = [];
var supportSpots = [];
var replaceSpots = [];
var swapSpots = [];
var sticking = false;
var deleted = false;
var leaveBehind = null;
var leaveBehind1 = null;
var leaveBehind2 = null;
// Parse behavior
for (var by = 0; by < behavior.length; by++) {
for (var bx = 0; bx < behavior[by].length; bx++) {
var b0 = behavior[by][bx].trim();
//if (b.includes(" OR ")) {
// b = b.split(" OR ")[Math.floor(Math.random()*b.split(" OR ").length)];
//}
// Loop through b0.split(" AND ")
for (var i = 0; i < b0.split(" AND ").length; i++) {
var b = b0.split(" AND ")[i];
var arg = null;
if (b.includes(":")) {
arg = b.split(":")[1].split(/[\:\%]/)[0];
if (!b.includes("%")) {
b = b.split(/[\:\%]/)[0];
}
}
// If b has "%" followed by a number in it, it's a chance to move
if (b.includes("%")) {
// Split the string at the "%" and use the second half as the chance (float)
var chance = parseFloat(b.split("%")[1]);
//console.log(b+": "+(Math.random()*100 < chance));
b = b.split(/[\:\%]/)[0];
}
else { var chance = 100; }
if (b == "XX" || b=="") {continue}
if (Math.random()*100 < chance) {
var newCoords = behaviorCoords(x,y,bx,by);
if (b == "M1") {
if (!((Math.random()*100) < 100 / ((info.viscosity || 1) ** 0.25))) {
newCoords.x = x;
}
move1Spots.push(newCoords);
}
else if (b == "M2") {
if (!((Math.random()*100) < 100 / ((info.viscosity || 1) ** 0.25))) {
newCoords.x = x;
}
move2Spots.push(newCoords);
}
else if (b == "SP") {
supportSpots.push({x:newCoords.x,y:newCoords.y,arg:arg});
}
else if (b == "DL") {
if ((!isEmpty(newCoords.x,newCoords.y)) && !outOfBounds(newCoords.x,newCoords.y)) {
// if the pixel at newCoords is the same element as the pixel, ignore
newPixel = pixelMap[newCoords.x][newCoords.y];
if ((!(newPixel.element == pixel.element)) || (newCoords.x == x && newCoords.y == y)) {
if (arg != null) { var args = arg.split(","); }
if (arg == null || args.includes(newPixel.element)) {
deletePixel(newCoords.x,newCoords.y);
if (newCoords.x == x && newCoords.y == y) {
deleted = true;
}
}
}
}
}
else if (b == "CL") {
if (isEmpty(newCoords.x,newCoords.y)) {
clonePixel(pixel,newCoords.x,newCoords.y);
}
}
//Change pixel
else if (b == "CH") {
if (!isEmpty(newCoords.x,newCoords.y) && !outOfBounds(newCoords.x,newCoords.y)) {
var newPixel = pixelMap[newCoords.x][newCoords.y];
if (newPixel.element != arg) {
newPixel.element = arg;
newPixel.color = pixelColorPick(newPixel);
newPixel.start = pixelTicks;
}
}
}
//Replace pixel
/*else if (b == "RP") {
replaceSpots.push({x:newCoords.x,y:newCoords.y,arg:arg});
}*/
//Create pixel
else if (b == "CR") {
if (isEmpty(newCoords.x,newCoords.y)) {
if (arg == null) {
arg = pixel.element;
}
else if (arg.includes(",")) {
arg = arg.split(",")[Math.floor(Math.random()*arg.split(",").length)];
}
createPixel(arg,newCoords.x,newCoords.y);
}
}
//Leave behind element
else if (b == "LB" || b == "L1" || b == "L2") {
if (arg != null && arg.includes(",")) {
arg = arg.split(",")[Math.floor(Math.random()*arg.split(",").length)];
}
if (b=="LB") {leaveBehind = arg;}
else if (b=="L1") {leaveBehind1 = arg;}
else if (b=="L2") {leaveBehind2 = arg;}
}
//Swap
else if (b == "SW") {
if (!isEmpty(newCoords.x,newCoords.y) && !outOfBounds(newCoords.x,newCoords.y)) {
var newPixel = pixelMap[newCoords.x][newCoords.y];
if (arg != null) { var args = arg.split(","); }
if (arg == null || args.includes(newPixel.element)) {
swapSpots.push({x:newCoords.x,y:newCoords.y});
}
}
}
//Heat
else if (b == "HT") {
if (!isEmpty(newCoords.x,newCoords.y) && !outOfBounds(newCoords.x,newCoords.y)) {
var newPixel = pixelMap[newCoords.x][newCoords.y];
if (arg != null) {arg = parseFloat(arg)}
else {arg = 1}
if (arg == NaN) {arg = 1}
newPixel.temp += arg;
pixelTempCheck(newPixel);
}
}
//Cool
else if (b == "CO") {
if (!isEmpty(newCoords.x,newCoords.y) && !outOfBounds(newCoords.x,newCoords.y)) {
var newPixel = pixelMap[newCoords.x][newCoords.y];
if (arg != null) {arg = parseFloat(arg)}
else {arg = 1}
if (arg == NaN) {arg = 1}
newPixel.temp -= arg;
pixelTempCheck(newPixel);
}
}
//Change color
else if (b == "CC") {
if (!isEmpty(newCoords.x,newCoords.y) && !outOfBounds(newCoords.x,newCoords.y)) {
var newPixel = pixelMap[newCoords.x][newCoords.y];
if (arg == null) {arg = newPixel.colorObject}
else {
if (arg.includes(",")) {
arg = arg.split(",")[Math.floor(Math.random()*arg.split(",").length)];
}
if (!arg.startsWith("#")) {
arg = "#" + arg;
}
}
newPixel.color = pixelColorPick(newPixel,arg);
}
}
//Stick
else if (b=="ST") {
if (!isEmpty(newCoords.x,newCoords.y) && !outOfBounds(newCoords.x,newCoords.y)) {
var newPixel = pixelMap[newCoords.x][newCoords.y];
if (newPixel.element != pixel.element && (arg == null || newPixel.element == arg)) {
sticking = true
}
}
}
}
}
}
}
if (deleted) {return;}
var move = true;
if (supportSpots.length > 0) {
var supportCount = 0;
var allEmpty = true;
for (var i = 0; i < supportSpots.length; i++) {
var bx = supportSpots[i].x;
var by = supportSpots[i].y;
var arg = supportSpots[i].arg;
if ((!isEmpty(bx,by)) && !outOfBounds(bx,by)) {
if (pixelMap[bx][by].element == arg || arg == null) {
supportCount++;
}
}
}
if (supportCount == supportSpots.length) {
move = false;
}
}
/*replaceSpotsCopy = replaceSpots.slice();
if (replaceSpots.length > 0) {
while (move1Spots.length > 0) {
// coords = random item of replaceSpots
var newCoords = replaceSpots[Math.floor(Math.random()*replaceSpots.length)];
if (!isEmpty(newCoords.x,newCoords.y) && !outOfBounds(newCoords.x,newCoords.y)) {
if (newPixel.element == newCoords.arg || newCoords.arg == null) {
var newPixel = pixelMap[newCoords.x][newCoords.y];
var newCoords = replaceSpots[Math.floor(Math.random()*replaceSpots.length)];
deletePixel(newCoords.x,newCoords.y);
pixelMap[newCoords.x][newCoords.y] = pixel;
pixel.x = newCoords.x;
pixel.y = newCoords.y;
moved = true;
replaceSpotsCopy.splice(replaceSpotsCopy.indexOf(newCoords),1);
break;
}
}
replaceSpots.splice(replaceSpots.indexOf(newCoords),1);
}
}
if (replaceSpotsCopy.length > 0 && !moved) {
// combine replaceSpotsCopy and move2Spots
move2Spots = move2Spots.concat(replaceSpotsCopy);
}*/
var moved = false;
if (swapSpots.length > 0) {
var coords = swapSpots[Math.floor(Math.random()*swapSpots.length)];
swapPixels(pixel,pixelMap[coords.x][coords.y]);
move = false;
moved = true;
}
if (sticking) {
move = false;
}
// Move First Priority
if (move) {
if (move1Spots.length > 0) {
// While move1Spots is not empty
while (move1Spots.length > 0) {
// coords = random item of move1Spots
var coords = move1Spots[Math.floor(Math.random()*move1Spots.length)];
var nx = coords.x;
var ny = coords.y;
if (isEmpty(nx,ny)) { // If coords is empty, move to coords
movePixel(pixel,nx,ny,leaveBehind1 || leaveBehind);
moved = true;
break;
}
else { // Remove from move1Spots
move1Spots.splice(move1Spots.indexOf(coords),1);
}
}
}
// Move Second Priority
if (!moved && move2Spots.length > 0) {
// While move2Spots is not empty
while (move2Spots.length > 0) {
// coords = random item of move2Spots
var coords = move2Spots[Math.floor(Math.random()*move2Spots.length)];
var nx = coords.x;
var ny = coords.y;
if (isEmpty(nx,ny)) { // If coords is empty, move to coords
movePixel(pixel,nx,ny,leaveBehind2 || leaveBehind);
moved = true;
break;
}
else { // Remove from move2Spots
move2Spots.splice(move2Spots.indexOf(coords),1);
}
}
}
}
if (pixel.burning) {
pixel.temp += 1;
pixelTempCheck(pixel);
var burnSpots = [
{x:pixel.x+1,y:pixel.y},
{x:pixel.x-1,y:pixel.y},
{x:pixel.x,y:pixel.y+1},
{x:pixel.x,y:pixel.y-1},
];
// loop through burnspots
for (var i = 0; i < burnSpots.length; i++) {
var burnSpot = burnSpots[i];
if (!isEmpty(burnSpot.x,burnSpot.y) && !outOfBounds(burnSpot.x,burnSpot.y)) {
var newPixel = pixelMap[burnSpot.x][burnSpot.y];
if (elements[newPixel.element].burn && !newPixel.burning) {
if (Math.floor(Math.random()*100) < elements[newPixel.element].burn) {
newPixel.burning = true;
newPixel.burnStart = pixelTicks;
}
}
}
}
if ((pixelTicks - pixel.burnStart > (info.burnTime || 200)) && Math.floor(Math.random()*100)<(info.burn || 10)) {
var burnInto = info.burnInto;
if (burnInto == undefined) {
burnInto = 'fire';
}
else if (burnInto instanceof Array) {
burnInto = burnInto[Math.floor(Math.random()*burnInto.length)];
}
pixel.element = burnInto;
if (info.fireColor != undefined && burnInto == "fire") {
pixel.color = pixelColorPick(pixel,info.fireColor);
}
else {
pixel.color = pixelColorPick(pixel)
}
pixel.start = pixelTicks;
if (elements[burnInto].burning != true) {
pixel.burning = false;
delete pixel.burnStart;
}
else {
pixel.burning = true;
pixel.burnStart = pixelTicks;
}
if (elements[burnInto].temp != undefined) {
pixel.temp = elements[burnInto].temp;
pixelTempCheck(pixel)
}
}
else if (pixel.element != "fire" && isEmpty(pixel.x,pixel.y-1) && Math.floor(Math.random()*100)<10) {
createPixel("fire",pixel.x,pixel.y-1);
pixelMap[pixel.x][pixel.y-1].temp = pixel.temp+(pixelTicks - pixel.burnStart);
if (info.fireColor != undefined) {
pixelMap[pixel.x][pixel.y-1].color = pixelColorPick(pixelMap[pixel.x][pixel.y-1],info.fireColor);
}
}
}
if (info.tempChange != undefined) {
pixel.temp += info.tempChange;
pixelTempCheck(pixel);
}
}
function pixelColorPick(pixel,customColor=null) {
var element = pixel.element;
var elementInfo = elements[element];
//if (elementInfo.behavior instanceof Array) {
if (customColor != null) {
if (Array.isArray(customColor)) {
customColor = customColor[Math.floor(Math.random() * customColor.length)];
}
if (customColor.startsWith("#")) {
customColor = hexToRGB(customColor);
}
var rgb = customColor;
}
else {
var rgb = elements[element].colorObject; // {r, g, b}
// If rgb is an array, choose a random item
if (Array.isArray(rgb)) {
rgb = rgb[Math.floor(Math.random() * rgb.length)];
}
}
// Randomly darken or lighten the RGB color
var coloroffset = Math.floor(Math.random() * (Math.random() > 0.5 ? -1 : 1) * Math.random() * 15);
var r = rgb.r + coloroffset;
var g = rgb.g + coloroffset;
var b = rgb.b + coloroffset;
// Make sure the color is within the RGB range
r = Math.max(0, Math.min(255, r));
g = Math.max(0, Math.min(255, g));
b = Math.max(0, Math.min(255, b));
var color = "rgb("+r+","+g+","+b+")";
/*}
else {
var color = elementInfo.color;
if (Array.isArray(color)) {
color = color[Math.floor(Math.random() * color.length)];
}
}*/
return color;
}
function pixelTempCheck(pixel) {
var elementInfo = elements[pixel.element];
// If the pixel's temp >= the elementInfo tempHigh, change pixel.element to elementInfo.stateHigh
if (pixel.temp >= elementInfo.tempHigh) {
pixel.element = elementInfo.stateHigh;
pixel.color = pixelColorPick(pixel);
if (pixel.burning) {
pixel.burning = false;
delete pixel.burnStart;
}
if (elements[elementInfo.stateHigh].burning == true) {
pixel.burning = true;
pixel.burnStart = pixelTicks;
}
}
// If the pixel's temp <= the elementInfo tempLow, change pixel.element to elementInfo.stateLow
else if (pixel.temp <= elementInfo.tempLow) {
pixel.element = elementInfo.stateLow;
pixel.color = pixelColorPick(pixel);
if (pixel.burning) {
pixel.burning = false;
delete pixel.burnStart;
}
if (elements[elementInfo.stateLow].burning) {
pixel.burning = true;
pixel.burnStart = pixelTicks;
}
}
}
function getNeighbors(pixel) {
var neighbors = [];
var x = pixel.x;
var y = pixel.y;
if (!isEmpty(x-1,y,true)) { neighbors.push(pixelMap[x-1][y]); }
if (!isEmpty(x+1,y,true)) { neighbors.push(pixelMap[x+1][y]); }
if (!isEmpty(x,y-1,true)) { neighbors.push(pixelMap[x][y-1]); }
if (!isEmpty(x,y+1,true)) { neighbors.push(pixelMap[x][y+1]); }
return neighbors;
}
hiding = false;
function drawPixels(forceTick=false) {
if (!hiding) {
var canvas = document.getElementById("game");
var ctx = canvas.getContext("2d");
}
// Draw the current pixels
// newCurrentPixels = shuffled currentPixels
var newCurrentPixels = currentPixels.slice();
newCurrentPixels.sort(function() {return 0.5 - Math.random()});
for (var i = 0; i < newCurrentPixels.length; i++) {
pixel = newCurrentPixels[i];
if (pixelMap[pixel.x][pixel.y] == undefined) {continue}
if ((!paused) || forceTick) {pixelTick(pixel);};
if (!hiding) {
ctx.fillStyle = pixel.color;
ctx.fillRect(pixel.x*10, pixel.y*10, 10, 10);
}
}
if ((!paused) || forceTick) {pixelTicks++};
}
function tick() {
// If mouseIsDown, do mouseAction
if (mouseIsDown) {
mouseAction(null,mousePos.x,mousePos.y);
}
// Get the canvas
var canvas = document.getElementById("game");
var ctx = canvas.getContext("2d");
// Clear the canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawPixels();
var mouseOffset = Math.trunc(mouseSize/2);
var topLeft = [mousePos.x-mouseOffset,mousePos.y-mouseOffset];
var bottomRight = [mousePos.x+mouseOffset,mousePos.y+mouseOffset];
// Draw a rectangle around the mouse
ctx.strokeStyle = "white";
ctx.strokeRect(topLeft[0]*10,topLeft[1]*10,(bottomRight[0]-topLeft[0]+1)*10,(bottomRight[1]-topLeft[1]+1)*10);
updateStats();
ticks ++;
}
currentElement = "sand";
mouseIsDown = false;
mouseType = null;
function mouseClick(e) {
mouseIsDown = true;
// If it's a left click
if (e.button == 0) {
mouseType = "left";
}
else if (e.button == 2) {
mouseType = "right";
}
mouseMove(e);
}
function mouseUp(e) {
mouseIsDown = false;
}
function getMousePos(canvas, evt) {
// If evt.touches is defined, use the first touch
if (evt.touches) {
evt = evt.touches[0];
}
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)
};
}
function mouseMove(e) {
if (mouseIsDown) {
mouseAction(e);
}
else {
var canvas = document.getElementById("game");
mousePos = getMousePos(canvas, e);
}
}
function mouseAction(e,mouseX=undefined,mouseY=undefined) {
if (mouseType == "left") { mouse1Action(e,mouseX,mouseY); }
else if (mouseType == "right") { mouse2Action(e,mouseX,mouseY); }
}
mouseSize = 5;
mousePos = {x:0,y:0};
function mouseRange(mouseX,mouseY) {
var coords = [];
var mouseOffset = Math.trunc(mouseSize/2);
var topLeft = [mouseX-mouseOffset,mouseY-mouseOffset];
var bottomRight = [mouseX+mouseOffset,mouseY+mouseOffset];
// Starting at the top left, go through each pixel
for (var x = topLeft[0]; x <= bottomRight[0]; x++) {
for (var y = topLeft[1]; y <= bottomRight[1]; y++) {
// If the pixel is empty, add it to coords
coords.push([x,y]);
}
}
return coords;
}
function mouse1Action(e,mouseX=undefined,mouseY=undefined) {
// If x and y are undefined, get the mouse position
if (mouseX == undefined && mouseY == undefined) {
var canvas = document.getElementById("game");
var ctx = canvas.getContext("2d");
mousePos = getMousePos(canvas, e);
var mouseX = mousePos.x;
var mouseY = mousePos.y;
}
var coords = mouseRange(mouseX,mouseY);
var element = elements[currentElement];
// For each x,y in coords
for (var i = 0; i < coords.length; i++) {
var x = coords[i][0];
var y = coords[i][1];
// If element name is heat or cool
if (currentElement == "heat" || currentElement == "cool") {
if (!isEmpty(x,y,false)) {
if (outOfBounds(x,y)) {
continue;
}
var pixel = pixelMap[x][y];
if (shiftDown) {pixel.temp += element.temp*10;}
else {pixel.temp += element.temp;}
pixelTempCheck(pixel);
}
}
else if (placeMode == "replace") {
if (outOfBounds(x,y)) {
continue;
}
// Remove pixel at position from currentPixels
var index = currentPixels.indexOf(pixelMap[x][y]);
if (index > -1) {
currentPixels.splice(index, 1);
}
currentPixels.push(new Pixel(x, y, currentElement));
}
else if (isEmpty(x, y)) {
currentPixels.push(new Pixel(x, y, currentElement));
}
}
}
function mouse2Action(e,mouseX=undefined,mouseY=undefined) {
// Erase pixel at mouse position
if (mouseX == undefined && mouseY == undefined) {
var canvas = document.getElementById("game");
var ctx = canvas.getContext("2d");
mousePos = getMousePos(canvas, e);
var mouseX = mousePos.x;
var mouseY = mousePos.y;
}
var coords = mouseRange(mouseX,mouseY);
// For each x,y in coords
for (var i = 0; i < coords.length; i++) {
var x = coords[i][0];
var y = coords[i][1];
if (!isEmpty(x, y)) {
if (outOfBounds(x,y)) {
continue
}
var pixel = pixelMap[x][y];
delete pixelMap[x][y];
// Remove pixel from currentPixels
for (var j = 0; j < currentPixels.length; j++) {
if (currentPixels[j].x == x && currentPixels[j].y == y) {
currentPixels.splice(j, 1);
break;
}
}
}
}
}
function selectElement(element) {
var e1 = document.getElementById("elementButton-"+currentElement)
if (e1 != null) { e1.setAttribute("current","false"); }
currentElement = element;
var e2 = document.getElementById("elementButton-"+element)
if (e2 != null) { e2.setAttribute("current","true"); }
}
function clearAll() {
currentPixels = [];
pixelMap = [];
for (var i = 0; i < width; i++) {
pixelMap[i] = [];
for (var j = 0; j < height; j++) {
pixelMap[i][j] = undefined;
}
}
pixelTicks = 0;
}
// Update stats
function updateStats() {
var statsDiv = document.getElementById("stats");
statsDiv.innerHTML = "<span id='stat-pos' class='stat'>x"+mousePos.x+",y"+mousePos.y+"</span>";
statsDiv.innerHTML += "<span id='stat-pixels' class='stat'>Pxls:" + currentPixels.length+"</span>";
statsDiv.innerHTML += "<span id='stat-tps' class='stat'>" + tps+"tps</span>";
statsDiv.innerHTML += "<span id='stat-ticks' class='stat'>" + pixelTicks+"</span>";
if (pixelMap[mousePos.x] != undefined) {
var currentPixel = pixelMap[mousePos.x][mousePos.y];
if (currentPixel != undefined) {
statsDiv.innerHTML += "<span id='stat-element' class='stat'>Elem:"+currentPixel.element.toUpperCase()+"</span>";
statsDiv.innerHTML += "<span id='stat-temperature' class='stat'>Temp:"+currentPixel.temp+"°C</span>";
if (currentPixel.burning) {
statsDiv.innerHTML += "<span id='stat-burning' class='stat'>Burning</span>";
}
}
if (shiftDown) {
statsDiv.innerHTML += "<span id='stat-shift' class='stat'>[↑ ]</span>";
}
}
}
// On window load, run tick() 20 times per second
tps = 30;
tickInterval = window.setInterval(tick, 1000/tps);
function resetInterval(newtps=30) {
window.clearInterval(tickInterval);
tickInterval = window.setInterval(tick, 1000/newtps);
}
ticks = 0;
pixelTicks = 0;
placeMode = null;
paused = false;
function focusGame() { document.getElementById("game").focus(); }
//on window load
window.onload = function() {
// While the mouse is down, run mouseDown()
var gameCanvas = document.getElementById("game");
// Get context
var ctx = gameCanvas.getContext("2d");
var newWidth = Math.ceil(window.innerWidth*0.9 / 10) * 10;
var newHeight = Math.ceil(window.innerHeight*0.675 / 10) * 10;
// If the new width is greater than 800, set it to 800
if (newWidth > 1000) { newWidth = 1000; }
// If we are on a desktop and the new height is greater than 600, set it to 600
if (window.innerWidth > 1000 && newHeight > 600) { newHeight = 600; }
ctx.canvas.width = newWidth;
ctx.canvas.height = newHeight;
width = Math.round(newWidth/pixelSize)-1;
height = Math.round(newHeight/pixelSize)-1;
// Object with width arrays of pixels starting at 0
pixelMap = {};
for (var i = 0; i < width; i++) {
pixelMap[i] = [];
}
//...drawing code...
gameCanvas.addEventListener("mousedown", mouseClick);
gameCanvas.addEventListener("touchstart", mouseClick);
gameCanvas.addEventListener("mouseup", mouseUp);
gameCanvas.addEventListener("touchend", mouseUp);
gameCanvas.addEventListener("mousemove", mouseMove);
gameCanvas.addEventListener("touchmove", mouseMove);
gameCanvas.ontouchstart = function(e) {
if (e.touches) e = e.touches[0];
return false;
}
shiftDown = false;
// If the user presses [ or -, decrease the mouse size by 2
document.addEventListener("keydown", function(e) {
if (e.keyCode == 219 || e.keyCode == 189) {
if (shiftDown) {mouseSize = 1}
else {
mouseSize -= 2;
if (mouseSize < 1) { mouseSize = 1; }
}
}
// If the user presses ] or =, increase the mouse size by 2
if (e.keyCode == 221 || e.keyCode == 187) {
if (shiftDown) {mouseSize = (mouseSize+15)-((mouseSize+15) % 15)}
else {mouseSize += 2;}
// if height>width and mouseSize>height, set mouseSize to height, if width>height and mouseSize>width, set mouseSize to width
if (mouseSize > (height > width ? height : width)) { mouseSize = (height > width ? height : width); }
}
else if (e.keyCode == 16 || e.keyCode == 18) { shiftDown = true; }
// p or spacebar = pause
else if (e.keyCode == 80 || e.keyCode == 32) {
e.preventDefault();
paused = !paused;
if (paused) {
document.getElementById("pauseButton").setAttribute("on","true");
}
else {
document.getElementById("pauseButton").setAttribute("on","false");
}
}
});
// If the user releases either shift
document.addEventListener("keyup", function(e) {
if (e.keyCode == 16 || e.keyCode == 18) { shiftDown = false; }
});
// Create buttons for elements
// For each element type in elements, create a button in controls that sets the current element to that type
// Alphabetically sort and loop through dictionary named "elements"
elementCount = 0;
hiddenCount = 0;
for (var element in elements) {
elementCount++;
if (elements[element].hidden) { hiddenCount++; continue; }
var category = elements[element].category;
if (category==null) {category="Other"}
var categoryDiv = document.getElementById("category-"+category);
if (categoryDiv == null) {
categoryDiv = document.createElement("div");
categoryDiv.innerHTML = "<span class='categoryName'>"+category+"</span>";
categoryDiv.setAttribute("id","category-"+category);
categoryDiv.setAttribute("class","category");
document.getElementById("elementControls").appendChild(categoryDiv);
}
var button = document.createElement("button");
button.innerHTML = elements[element].name
//capitalize first letter of each word
button.innerHTML = button.innerHTML.replace("."," ").replace(/\w\S*/g, function(txt){return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();}).replace(" ",".").replace(/ /g, "");
//set attribute of element to the element
button.setAttribute("element", element);
button.setAttribute("current", "false");
button.className = "elementButton";
//color of the element
// if the element color is an array, make a gradient background color, otherwise, set the background color to the element color
if (elements[element].color instanceof Array) {
button.style.backgroundImage = "linear-gradient(to bottom right, "+elements[element].color.join(", ")+")";
}
else {
button.style.background = elements[element].color;
var colorObject = elements[element].colorObject;
if ((colorObject.r+colorObject.g+colorObject.b)/3 > 200) {
button.className += " bright"
}
}
button.id = "elementButton-" + element;
button.onclick = function() {
selectElement(this.getAttribute("element"));
}
categoryDiv.appendChild(button);
}
document.getElementById("extraInfo").innerHTML = "<small>There are " + elementCount + " elements, including " + hiddenCount + " hidden ones.</small>";
selectElement(currentElement);
focusGame();
// For every button element, onkeyup="event.preventDefault()"
var buttonElements = document.getElementsByTagName("button");
for (var i = 0; i < buttonElements.length; i++) {
buttonElements[i].onkeyup = function(e) {
e.preventDefault();
}
}
}
</script>
</head>
<body>
<h1>Sandbox Game</h1>
<div id="gameDiv">
<canvas id="game" width="800" height="600" oncontextmenu="return false;">
Your browser does not support the HTML5 canvas tag.
</canvas>
<div id="underDiv">
<div id="stats"></div>
<div id="controls">
<div id="toolControls">
<button id="pauseButton" title="Pause/play the simulation" class="controlButton" onclick='if (paused) {paused = false;this.setAttribute("on","false");}else {paused = true;this.setAttribute("on","true");};focusGame();' on="false">Pause</button><button id="frameButton" title="Pause and play one frame" class="controlButton" onclick='if (!paused) {paused=true;document.getElementById("pauseButton").setAttribute("on","true");}drawPixels(true);focusGame();' on="false">Step</button><button id="sizeUpButton" title="Increase the brush size" class="controlButton" onclick="mouseSize+=2;focusGame();"></button><button id="sizeDownButton" title="Decrease the brush size" class="controlButton" onclick="mouseSize-=2;focusGame();"></button><button id="resetButton" title="Clear the entire scene" class="controlButton" onclick="if (confirm('Are you sure you want to clear the whole scene?')) {clearAll();};focusGame();">Reset</button><button id="replaceButton" title="Override existing pixels when placing" class="controlButton" onclick='if (placeMode == "replace") {placeMode = null;this.setAttribute("on","false");}else {placeMode = "replace";this.setAttribute("on","true");};focusGame();' on="false">Replace</button><button id="elemSelectButton" title="Select an element by ID" class="controlButton" onclick='var e = prompt("Enter the elements ID").replaceAll(" ","_");if (elements[e] != undefined) {currentElement = e;var btn = document.getElementById("elementButton-"+e);if (btn != null) {btn.setAttribute("current","true");}};focusGame();'>E</button><button id="tpsButton" title="Change the simulation Ticks Per Second (TPS)" class="controlButton" onclick='var newtps = prompt("Enter the new simulation Ticks Per Second (TPS). This is how many updates per second the simulation will run.\n\nThe default is 30.\n\nThe current TPS is " + tps + ".");if (newtps == null || newtps == "" || newtps == "0") {alert("You did not enter a valid TPS. The TPS will be reset.");tps = 30;}else if (newtps > 1000) {alert("You entered a TPS that is too high. The TPS will be set to the maximum, 1000.");tps = 1000;}else {tps = parseInt(newtps);if (isNaN(tps)) {alert("You did not enter a valid TPS. The TPS will be reset.");tps = 30;}}resetInterval(tps);focusGame();'>Tps</button><button id="hideButton" title="Stops updating the scene to allow more resources towards simulation" class="controlButton" onclick='if (hiding) {hiding = false;this.setAttribute("on","false");}else {hiding = true;this.setAttribute("on","true");};focusGame();' on="false">Hide</button>
</div>
<div id="elementControls"></div>
</div>
<div id="extraInfo"></div>
</div>
</div>
</body>
</html>