diff --git a/README.md b/README.md index 68beee50..dfa4a21d 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Play at [Sandboxels.R74n.com](https://sandboxels.r74n.com/). Join the [Discord server](https://discord.com/invite/ejUc6YPQuS). ![Landscape made in Sandboxels](https://raw.githubusercontent.com/R74nCom/sandboxels/main/icons/wallpaper.png) - + # Controls * Left Click = Draw pixels * Right Click = Erase pixels diff --git a/lang/cs.json b/lang/cs.json index 25a64d8f..6eb4758f 100644 --- a/lang/cs.json +++ b/lang/cs.json @@ -527,21 +527,21 @@ "molten_potassium_salt":"Rozteklá_Draselná_Sůl", "molten_sodium_acetate":"Rozteklý_Octan_Sodný", "frozen_nitro":"Zmrzlé_Nitro", -"cured_meat": "Uzené_Maso", -"nut_oil": "Ořechový_Olej", -"grease": "Tuk", -"fat": "Tuk", -"potassium": "Draslík", -"molten_potassium": "Rozteklý_Draslík", -"magnesium": "Magnézium", -"molten_magnesium": "Rozteklé_Magnézium", -"sandstorm": "Píšecná_Bouře", -"caustic_potash": "Potaš", -"antibomb": "Anti-bomba", -"tornado": "Tornádo", -"earthquake": "Zemětřesení", -"tsunami": "Tsunami", -"blaster": "Blaster", -"propane_ice": "Zrmzlý_Propan", -"molten_caustic_potash": "Rozteklý_Potaš" +"cured_meat":"Uzené_Maso", +"nut_oil":"Ořechový_Olej", +"grease":"Tuk", +"fat":"Tuk", +"potassium":"Draslík", +"molten_potassium":"Rozteklý_Draslík", +"magnesium":"Magnézium", +"molten_magnesium":"Rozteklé_Magnézium", +"sandstorm":"Píšecná_Bouře", +"caustic_potash":"Potaš", +"antibomb":"Anti-bomba", +"tornado":"Tornádo", +"earthquake":"Zemětřesení", +"tsunami":"Tsunami", +"blaster":"Blaster", +"propane_ice":"Zrmzlý_Propan", +"molten_caustic_potash":"Rozteklý_Potaš" } diff --git a/mod-list.html b/mod-list.html index b04ea04d..cbc7784f 100644 --- a/mod-list.html +++ b/mod-list.html @@ -177,7 +177,7 @@ Science & Chemistry alcohol.jsAdds methanol, (iso-)propanol, and butanolAlice alkahest.jsAdds the alkahest, a liquid which dissolves anythingAlice -aScientistsWish.jsAdds things that related to science, especially radiationCarbon Monoxide, salmonfishy +aScientistsWish.jsAdds several things that related to science and physics Carbon Monoxide, salmonfishy bettermetalscrap.jsAllows metal scrap to be melted back into its original materialnousernamefound bigger_star_spawners.jsAdds spawners for larger starsAlice bioooze_and_pyrogens.jsAdds Bio-Ooze from Frackin’ Universe and several heat-producing materials from various games’ modsAlice @@ -185,6 +185,7 @@ chalcopyrite.jsAdds the chalcopyrite oreSophie chem.jsAdds several chemistry and physics-related elementslllllllllwith10ls clf3.jsAdds Chlorine TrifluorideAlice +fire_extinguisher.jsAdds fire extinguisher blocks and realistic firefighting foam to put out nearly anythingDr_Lego fire_mod.jsAdds various properties to change fire behavior, & a radioactive version of fireAlice fire_slime.jsAdds a pyrogenic version of slimeAlice Gemstones.jsAdds more gemstonesSchweeny @@ -314,7 +315,7 @@ lone_urea.jsAdds urea without the rest of the piss modAlice maze.jsAdds a solvable maze generatorggod memelists.jsMakes it so you must select elements through a list at the bottom of the page.
We like listsmollthecoder -minecraft.jsAdds several things from MinecraftStellarX20 +minecraft.jsAdds several things from MinecraftStellarX20, nousernamefound minesweeper.jsA subpar implementation of MinesweeperAlice musicalfruit.jsHumans get gas from eating Beansmollthecoder prideflags.jsAdds some pride flags to the game.Adora diff --git a/mods/aScientistsWish.js b/mods/aScientistsWish.js index 258159bf..64769c97 100644 --- a/mods/aScientistsWish.js +++ b/mods/aScientistsWish.js @@ -1,580 +1,1159 @@ +//this mod is still on proggres (WIP) you can give feedback or report bug to these discord account. @salmonfishy or @carbon_monoxides6 + elements.carbon_monoxide = { - color: "#b5b5b5", - behavior: behaviors.GAS, - behaviorOn: [ - "XX|XX|XX", - "XX|CH:fire|XX", - "XX|XX|XX", - ], - category: "gases", - state: "gas", - density: 1.14, - burn: 75, - tempHigh: 609, - stateHigh: "fire", - tempLow: -192, - stateLow: "liquid_carbon_monoxide", - burntime: 5, - darkText: true, - fireColor: "#ebba34", - reactions: { - "head": { elem1:"rotten_meat", chance:0.5 }, - "body": { elem1:"rotten_meat", chance:0.5 }, - "human": { elem1:"rotten_meat", chance:0.5 }, - } + color: "#b5b5b5", + behavior: behaviors.GAS, + behaviorOn: [ + "XX|XX|XX", + "XX|CH:fire|XX", + "XX|XX|XX", +], + category: "gases", + state: "gas", + density: 1.14, + burn: 75, + tempHigh: 609, + stateHigh: "fire", + tempLow: -192, + stateLow: "liquid_carbon_monoxide", + burntime: 5, + darkText: true, + fireColor: "#ebba34", + reactions: { + "head": { elem2:"rotten_meat", chance:0.5 }, + "body": { elem2:"rotten_meat", chance:0.5 }, + "human": { elem2:"rotten_meat", chance:0.5 }, + } }; elements.liquid_carbon_monoxide = { - color: "#b5b5b5", - behavior: behaviors.LIQUID, - category: "liquids", - state: "liquid", - density: 1.14, - darkText: true, - tempHigh: -190, - temp: -192, - tempLow: -199, - hidden: true, - stateLow: "ice_carbon_monoxide", - stateHigh: "carbon_monoxide", + color: "#b5b5b5", + behavior: behaviors.LIQUID, + category: "liquids", + state: "liquid", + density: 1.14, + darkText: true, + tempHigh: -190, + temp: -192, + tempLow: -199, + hidden: true, + stateLow: "ice_carbon_monoxide", + stateHigh: "carbon_monoxide", }; elements.ice_carbon_monoxide = { - color: "#b5b5b5", - behavior: behaviors.WALL, - category: "solids", - state: "solid", - temp: -199, - density: 1.14, - tempHigh: -192, - darkText: true, - stateHigh: "liquid_carbon_monoxide", + color: "#b5b5b5", + behavior: behaviors.WALL, + category: "solids", + state: "solid", + temp: -199, + density: 1.14, + tempHigh: -192, + darkText: true, + stateHigh: "liquid_carbon_monoxide", }; elements.carbon_monoxide_detector = { - behavior: behaviors.WALL, - desc: "give red light and electric when found Carbon Monoxide touch", - color: "#ffffff", - reactions: { - "carbon_monoxide": {"charge1":1}, - }, - conduct: 1, - tempHigh: 1550, - stateHigh: ["molten_metal_scrap","electric","molten_plastic"], - colorOn: "#ff0000", - movable: false, - insulate: true, - noMix: true, - category:"machines", - darkText: true, - hardness: 1, + behavior: behaviors.WALL, + desc: "give red light and electric when found Carbon Monoxide touch", + color: "#ffffff", + reactions: { + "carbon_monoxide": {"charge1":1}, + }, + conduct: 1, + tempHigh: 1550, + stateHigh: ["molten_metal_scrap","electric","molten_plastic"], + colorOn: "#ff0000", + movable: false, + insulate: true, + noMix: true, + category:"machines", + darkText: true, + hardness: 1, }; elements.cpu = { - color: "#575757", - behavior: behaviors.SOLID, - category: "machines", - state: "solid", - insulate: true, - movable: false, - noMix: true, - density: 75, - tempHigh: 1414, - stateHigh: ["explosion","metal_scrap"], + color: "#575757", + behavior: behaviors.SOLID, + category: "machines", + state: "solid", + insulate: true, + movable: false, + noMix: true, + density: 75, + tempHigh: 1414, + stateHigh: ["explosion","metal_scrap"], reactions: { - "virus": { elem1 : null , elem2:"malware", chance:0.9 }, - "metal_scrap": { elem2:"computer" }, - - } + "virus": { elem1 : null , elem2:"malware", chance:0.9 }, + "metal_scrap": { elem2:"computer" }, + + } }; elements.computer = { - color: "#2b2b2a", - behavior: behaviors.SOLID, - category: "machines", - state: "solid", - density: 8908, - insulate: true, - noMix: true, - movable: false, - tempHigh: 1414, - stateHigh: ["explosion","metal_scrap"], + color: "#2b2b2a", + behavior: behaviors.SOLID, + category: "machines", + state: "solid", + density: 8908, + insulate: true, + noMix: true, + movable: false, + tempHigh: 1414, + stateHigh: ["explosion","metal_scrap"], reactions: { - "virus": { elem1 : null , elem2:"malware", chance:0.9 }, - "water": { elem1: null , elem2: "electric" }, - } + "virus": { elem1 : null , elem2:"malware", chance:0.9 }, + "water": { elem1: null , elem2: "electric" }, + } } elements.electrons = { - color: "#b80606", - behavior: [ - "XX|SH|XX", // shocks (adds charge) - "SH|DL%0.25|SH", - "XX|SH|XX", - ], - tick: behaviors.BOUNCY, - reactions: {}, - temp: 20, - category: "energy", - state: "gas", - density: 0.000003, - ignoreAir: true, +color: "#b80606", +behavior: [ + "XX|SH|XX", // shocks (adds charge) + "SH|DL%0.25|SH", + "XX|SH|XX", +], +tick: behaviors.BOUNCY, +temp: 20, +category: "energy", +state: "gas", +density: 0.000003, +ignoreAir: true, }; elements.gelatin = { - behavior: behaviors.SOLID, - category: "food", - state: "solid", - color: "#faf8ca", - breakInto: "gelatin_powder", - ignoreAir: true, - isFood: true, + behavior: behaviors.SOLID, + category: "food", + state: "solid", + color: "#faf8ca", + breakInto: "gelatin_powder", + ignoreAir: true, + isFood: true, }; elements.gelatin_powder = { - behavior: behaviors.POWDER, - category: "food", - state: "powder", - color: "#edeb9f", - hidden: true, - ignoreAir: true, - isFood: true, + behavior: behaviors.POWDER, + category: "food", + state: "powder", + color: "#edeb9f", + hidden: true, + ignoreAir: true, + isFood: true, }; elements.blueberries = { - behavior: behaviors.POWDER, - category: "food", - state: "solid", - color: ["#464196","#2e2b64"], - breakInto: "blueberries_juice", - ignoreAir: true, - isFood: true, - reactions: { - "sugar": { elem1: "blueberries_jam" }, - }, +behavior: behaviors.POWDER, +category: "food", +state: "solid", +color: ["#464196","#2e2b64"], +breakInto: "blueberries_juice", +ignoreAir: true, +isFood: true, +reactions: { +"sugar": { elem1: "blueberries_jam" }, +}, }; elements.blueberries_juice = { - behavior: behaviors.LIQUID, - category: "food", - state: "liquid", - color: "#1f1c42", - hidden: true, - tempHigh: 170, - stateHigh: ["steam","sugar"], - reactions: { - ignoreAir: true, - isFood: true, - "gelatin": { elem1: "blueberries_jelly", elem2: null }, - "gelatin_powder": { elem1: "blueberries_jelly", elem2: null }, - }, +behavior: behaviors.LIQUID, +category: "food", +state: "liquid", +color: "#1f1c42", +hidden: true, +tempHigh: 170, +stateHigh: ["steam","sugar"], +reactions: { +ignoreAir: true, +isFood: true, +"gelatin": { elem1: "blueberries_jelly", elem2: null }, +"gelatin_powder": { elem1: "blueberries_jelly", elem2: null }, +}, }; elements.blueberries_jam = { - behavior: behaviors.LIQUID, - category: "food", - viscosity: 5000, - state: "liquid", - tempHigh: 200, - stateHigh: ["smoke","sugar"], - color: "#080629", - hidden: true, - ignoreAir: true, - isFood: true, +behavior: behaviors.LIQUID, +category: "food", +viscosity: 5000, +state: "liquid", +tempHigh: 200, +stateHigh: ["smoke","sugar"], +color: "#080629", +hidden: true, +ignoreAir: true, +isFood: true, }; elements.blueberries_jelly = { - behavior: behaviors.LIQUID, - category: "food", - viscosity: 200000, - state: "liquid", - color: "#59559e", - hidden: true, - tempHigh: 200, - stateHigh: ["smoke","sugar"], - tempLow: -5, - stateLow: ["sugar_ice","sugar_ice","juice_ice"], - ignoreAir: true, - isFood: true, +behavior: behaviors.LIQUID, +category: "food", +viscosity: 200000, +state: "liquid", +color: "#59559e", +hidden: true, +tempHigh: 200, +stateHigh: ["smoke","sugar"], +tempLow: -5, +stateLow: ["sugar_ice","sugar_ice","juice_ice"], +ignoreAir: true, +isFood: true, }; elements.fallout_drum = { - behavior: behaviors.WALL, - category: "radiated", - state: "solid", - density: 9000, - color: "#e3cc34", - tempHigh: 2500, - stateHigh: ["aluminum","radiated_water","radiated_water","fallout"], - breakInto: ["fallout","fallout"], - reactions: { - "water": { elem1:"fallout_drum", elem2:"radiated_water" }, - } +behavior: behaviors.WALL, +category: "radiated", +state: "solid", +density: 9000, +color: "#e3cc34", +tempHigh: 2500, +stateHigh: ["aluminum","radiated_water","radiated_water","fallout"], +breakInto: ["fallout","fallout"], +reactions: { +"water": { elem1:"fallout_drum", elem2:"radiated_water" }, +} }; elements.radiated_water = { - behavior: behaviors.LIQUID, - category: "radiated", - state:"liquid", - density :1300, - color: ["#23d959","#29d65d"], - hidden: true, - tempHigh: 140, - stateHigh: "polluted_air", - tempLow: -6, - stateLow: "rad_ice", - reactions: { - "human": { elem2:"rotten_meat" }, - "body": { elem2:"rotten_meat" }, - "head": { elem2:"ash" }, - "bird": { elem2:"rotten_meat"}, - "cell": { elem2:"cancer"}, - } +behavior: behaviors.LIQUID, +category: "radiated", +state:"liquid", +density :1300, +color: ["#23d959","#29d65d"], +hidden: true, +tempHigh: 140, +stateHigh: "polluted_air", +tempLow: -6, +stateLow: "rad_ice", +reactions: { +"human": { elem2:"rotten_meat" }, +"body": { elem2:"rotten_meat" }, +"head": { elem2:"ash" }, +"bird": { elem2:"rotten_meat"}, +"cell": { elem2:"cancer"}, +"worm": { elem2:"rotten_meat"}, +} }; elements.polluted_air = { - behavior: behaviors.GAS, - category: "radiated", - state:"gas", - density :10, - color: ["#60f53b","#65ba50"], - reactions: { - "body": { elem2:"rotten_meat" }, - "head": { elem2:"rotten_meat" }, - "human": { elem2:"rotten_meat" }, - "bird": { elem2:"rotten_meat" }, - "cell": { elem2:"cancer" }, - "water": { elem1: null, elem2: "radiated_water" }, - "worm": { elem2: ["ash","cancer"] }, - "flea": { elem2: "ash" }, - "seed": {elem2: "dead_plant" }, - "plant": {elem1: null, chance:0.5, elem2: "dead_plant", chance:0.5 }, - }, +behavior: behaviors.DGAS, +category: "radiated", +state:"gas", +density :10, +color: ["#60f53b","#65ba50"], +reactions: { + "body": { elem2:"rotten_meat" }, + "head": { elem2:"rotten_meat" }, + "human": { elem2:"rotten_meat" }, + "bird": { elem2:"rotten_meat" }, + "cell": { elem2:"cancer" }, + "water": { elem1: null, elem2: "radiated_water" }, + "worm": { elem2: ["ash","cancer"] }, + "flea": { elem2: "ash" }, + "seed": {elem2: "dead_plant" }, + "plant": {elem1: null, chance:0.5, elem2: "dead_plant", chance:0.5 }, +}, }; elements.siren = { - desc: "Detecting Nuclear Radiation Residues", - behavior: behaviors.WALL, - category: "machines", - state:"solid", - density :500, - color: "#808080", - reactions: { - "fallout": {"charge1":1}, - "radiated_water": {"charge1":1}, - "polluted_air": {"charge1":1}, - } +desc: "Detecting Nuclear Radiation Residues", +behavior: behaviors.WALL, +category: "machines", +state:"solid", +density :500, +color: "#808080", +reactions: { + "fallout": {"charge1":1}, + "radiated_water": {"charge1":1}, + "polluted_air": {"charge1":1}, + "radiation": {"charge1":1}, + "rad_snow": {"charge1":1}, + "rad_rock": {"charge1":1}, +} }; elements.radiated_metal = { - behavior: behaviors.WALL, - category: "radiated", - state:"solid", - density :2045, - color: ["#5e705a","#83ab7b","#474747"], - tempHigh: 1440, - stateHigh: ["molten_nickel","molten_iron","molten_tin","fallout"], - reactions: { - "water": { elem2:"radiated_water", chance:0.7 }, - "foam": { elem1:["tin","nickel","iron"] }, - } +behavior: behaviors.WALL, +category: "radiated", +state:"solid", +density :2045, +color: ["#5e705a","#83ab7b","#474747"], +tempHigh: 1440, +stateHigh: ["molten_nickel","molten_iron","molten_tin","fallout"], +reactions: { + "water": { elem2:"radiated_water", chance:0.7 }, + "foam": { elem1:["tin","nickel","iron"] }, +} }; elements.rad_ice = { - behavior: behaviors.WALL, - category: "radiated", - state:"solid", - density: 1905, - color: ["#81d79c","#68b899","#68abb8"], - hidden: true, - temp: -6, - tempHigh: 5, - stateHigh: ["radiated_water","water"], - reactions: { - "snow": { elem2:"dirty_water" }, - "water": { elem2:"radiated_water" }, - } +behavior: behaviors.WALL, +category: "radiated", +state:"solid", +density: 1905, +color: ["#81d79c","#68b899","#68abb8"], +hidden: true, +temp: -6, +tempHigh: 5, +stateHigh: ["radiated_water","water"], +reactions: { + "snow": { elem2:"dirty_water" }, + "water": { elem2:"radiated_water" }, +} } elements.rad_snow = { - behavior: behaviors.POWDER, - category: "radiated", - state:"powder", - density: 1500, - color: ["#9effe4","#b5fffd","#d4fff1"], - temp: -2, - tempHigh: 21, - stateHigh: "radiated_water", +behavior: behaviors.POWDER, +category: "radiated", +state:"powder", +density: 1500, +color: ["#9effe4","#b5fffd","#d4fff1"], +temp: -2, +tempHigh: 21, +stateHigh: "radiated_water", }; elements.rad_rock = { - behavior: behaviors.POWDER, - category: "land", - state: "powder", - density: 2790, - color: ["#34382d","#3f4633","#595a4d"], - tempHigh: 1200, - stateHigh: ["magma","fallout"], - reactions: { - "water": { elem2:"dirty_water" }, - "salt_water": { elem2:"dirty_water" }, - "sugar_water": { elem2:"dirty_water" }, - "seltzer": { elem2:"dirty_water" }, - "bleach": {elem2: "rock", chance:0.1 }, - "rad_cleaner": { elem1:"rock" }, - "foam": { elem1:"rock" }, - "juice": { elem2: null }, - "blood": { elem2:"infection" }, - "grass": { elem2:"dead_plant" }, - "plant": { elem2:"dead_plant" }, - "cell": { elem2:"cancer" }, - "worm": { elem2:"ash" }, - "glass": { elem2:"rad_glass"}, - "glass_shard": { elem2:"rad_shard" }, - } +behavior: behaviors.POWDER, +category: "land", +state: "powder", +density: 2790, +color: ["#34382d","#3f4633","#595a4d"], +tempHigh: 1200, +stateHigh: ["magma","fallout"], +reactions: { + "water": { elem2:"dirty_water" }, + "salt_water": { elem2:"dirty_water" }, + "sugar_water": { elem2:"dirty_water" }, + "seltzer": { elem2:"dirty_water" }, + "bleach": {elem2: "rock", chance:0.1 }, + "rad_cleaner": { elem1:"rock" }, + "foam": { elem1:"rock" }, + "juice": { elem2: null }, + "blood": { elem2:"infection" }, + "grass": { elem2:"dead_plant" }, + "plant": { elem2:"dead_plant" }, + "cell": { elem2:"cancer" }, + "worm": { elem2:"ash" }, + "glass": { elem2:"rad_glass"}, + "glass_shard": { elem2:"rad_shard" }, +} }; elements.rad_cleaner = { - behavior: behaviors.WALL, - category: "machines", - state: "solid", - density: 2500, - color: ["#383838","#212220"], - desc: "clean radiated elements", - tempHigh: 1250, - stateHigh: ["fallout","molten_plastic","steam"], - reactions: { - "radiated_water": {elem2:"water" }, - "polluted_air": {elem2: "oxygen"}, - "rad_snow": {elem2: "snow"}, - "rad_rock": {elem2: "rock"}, - } - }; +behavior: behaviors.WALL, +category: "machines", +state: "solid", +density: 2500, +color: ["#383838","#212220"], +desc: "clean radiated elements", +tempHigh: 1250, +stateHigh: ["fallout","molten_plastic","steam"], +reactions: { + "radiated_water": {elem2:"water" }, + "polluted_air": {elem2: "oxygen"}, + "rad_snow": {elem2: "snow"}, + "rad_rock": {elem2: "rock"}, + "radiated_metal": {elem2: ["nickel","tin","iron"], }, + "fallout": {elem2: "rock", }, +} +}; elements.rad_meat = { - behavior: behaviors.STURDYPOWDER, - category: "food", - state: "solid", - density: 1500, - color: ["#e8fc03","#f0b446","#92f046"], - tempHigh: 120, - stateHigh: ["rotten_meat","fallout"], - tempLow:-12, - stateLow: ["frozen_meat","radiation"], - reactions: { - "water": {elem2: "radiated_water", chance:0.4}, - "salt_water": { elem2: "radiated_water" }, - } +behavior: behaviors.STURDYPOWDER, +category: "food", +state: "solid", +density: 1500, +color: ["#e8fc03","#f0b446","#92f046"], +tempHigh: 120, +stateHigh: ["rotten_meat","fallout"], +tempLow:-12, +stateLow: ["frozen_meat","radiation"], +reactions: { + "water": {elem2: "radiated_water", chance:0.4}, + "salt_water": { elem2: "radiated_water" }, +} }; elements.lemon = { - behavior: behaviors.POWDER, - category: "food", - state: "powder", - density: 800, - color: ["#f9f934", "#f1ee20",], - tempHigh: 90, - stateHigh: "hot_lemon", - tempLow: -2, - stateLow: "wrinkled_lemon", - burn: 120, - burntime: 600, - burnInto: "ash", - breakInto: [ "lemon_water", "lemon_zest", ], - reactions: { - "water": { elem2: "lemon_water", chance:0.2}, - "salt_water": { elem2: [ "lemon_water", "water",] }, - "dirty_water": { elem1: "rotten_lemon", }, - "soda": { elem2: "lemon_soda", }, - "head": { elem1: "saliva", chance:0.75}, - "milk": { elem2: "lemon_milk", }, - "tea": { elem2: "lemon_tea", }, - "poison": { elem2: "rotten_lemon", }, - } +behavior: behaviors.POWDER, +category: "food", +state: "powder", +density: 800, +color: ["#f9f934", "#f1ee20",], +tempHigh: 90, +stateHigh: "hot_lemon", +tempLow: -2, +stateLow: "wrinkled_lemon", +burn: 120, +burntime: 600, +burnInto: "ash", +breakInto: [ "lemon_water", "lemon_zest", ], +reactions: { + "water": { elem2: "lemon_water", chance:0.2}, + "salt_water": { elem2: [ "lemon_water", "water",] }, + "dirty_water": { elem1: "rotten_lemon", }, + "soda": { elem2: "lemon_soda", }, + "head": { elem1: "saliva", chance:0.75}, + "milk": { elem2: "lemon_milk", }, + "tea": { elem2: "lemon_tea", }, + "poison": { elem2: "rotten_lemon", }, +} }; elements.hot_lemon = { - behavior: behaviors.POWDER, - state: "powder", - category: "food", - hidden: true, - density: 820, - color: ["#8a6d1e","#70661a",], - hidden: true, - temp: 90, - tempHigh: 200, - stateHigh: "fire", - tempLow: 30, - stateLow: "wrinkled_lemon", - burn: 120, - burntime: 600, - burnInto: "ash", - reactions: { - "water": { elem2: "lemon_water", }, - } +behavior: behaviors.POWDER, +state: "powder", +category: "food", +hidden: true, +density: 820, +color: ["#8a6d1e","#70661a",], +hidden: true, +temp: 90, +tempHigh: 200, +stateHigh: "fire", +tempLow: 30, +stateLow: "wrinkled_lemon", +burn: 120, +burntime: 600, +burnInto: "ash", +reactions: { + "water": { elem2: "lemon_water", }, +} }; elements.leather = { - behavior: behaviors.SUPPORTPOWDER, - color: ["#3f261d","#664f40",], - state: "powder", - category: "powders", - tempHigh: 200, - stateHigh: "fire", - breakInto: "dust", - burn: 20, - burntime: 200, - burnInto: "ash", +behavior: behaviors.SUPPORTPOWDER, +color: ["#3f261d","#664f40",], +state: "powder", +category: "powders", +tempHigh: 200, +stateHigh: "fire", +breakInto: "dust", +burn: 20, +burntime: 200, +burnInto: "ash", }; elements.wrinkled_lemon = { - behavior: behaviors.POWDER, - color: ["#999543","#a6a03a",], - state: "powder", - category: "food", - tempHigh: 90, - stateHigh: "hot_lemon", - hidden: true, - burn: 120, - burntime: 600, - burnInto: "ash", - reactions: { - "water": { elem2: "lemon_water", chance: 0.2, - } - } +behavior: behaviors.POWDER, +color: ["#999543","#a6a03a",], +state: "powder", +category: "food", +tempHigh: 90, +stateHigh: "hot_lemon", +hidden: true, +burn: 120, +burntime: 600, +burnInto: "ash", +reactions: { +"water": { elem2: "lemon_water", chance: 0.2, +} +} }; elements.coolant = { - color: "#0eebeb", - behavior: [ - "XX|CO:4|XX", - "CO:4|HT:1|CO:4", - "XX|CO:4|XX", - ], - category: "liquids", - state: "liquid", - insulate: true, - density: 1000, - darkText: false, - tempHigh: 500, - hidden: true, - stateHigh: "steam", +color: "#0eebeb", +behavior: [ + "XX|CO:4|XX", + "CO:4|HT:1|CO:4", + "XX|CO:4|XX", +], +category: "liquids", +state: "liquid", +insulate: true, +density: 1000, +darkText: false, +tempHigh: 500, +hidden: true, +stateHigh: "steam", }; elements.arkycite = { - color: "#46ab63", - behavior: behaviors.LIQUID, - category: "liquids", - state: "liquid", - density: 997, - darkText: false, - tempHigh: 400, - hidden: true, - burn: 40, - stateHigh: "fire", - burntime: 500, +color: "#46ab63", +behavior: behaviors.LIQUID, +category: "liquids", +state: "liquid", +density: 997, +darkText: false, +tempHigh: 400, +hidden: true, +burn: 40, +stateHigh: "fire", +burntime: 500, }; elements.lemon_water = { - color: ["#faec52","#faee69",], - behavior: behaviors.LIQUID, - category: "liquids", - state: "liquid", - density: 900, - tempHigh: 100, - stateHigh: ["steam","fragrance",], - hidden: true, - tempLow: -10, - stateLow: "lemon_ice", - reactions: { - "balloon": { elem2: "helium", chance: 0.5, }, - "head": { elem1: "saliva", chance: 0.2, }, - } +color: ["#faec52","#faee69",], +behavior: behaviors.LIQUID, +category: "liquids", +state: "liquid", +density: 900, +tempHigh: 100, +stateHigh: ["steam","fragrance",], +hidden: true, +tempLow: -10, +stateLow: "lemon_ice", +reactions: { +"balloon": { elem2: "helium", chance: 0.5, }, +"head": { elem1: "saliva", chance: 0.2, }, +} }; elements.lemon_zest = { - color: ["#ded254","#ccc03d",], - behavior: behaviors.POWDER, - category: "trashes", - state: "powder", - density: 1000, - hidden: true, - tempHigh: 350, - stateHigh: "fire", - breakInto: "lemon_water", - burn: 60, - burntime: 200, - burnInto: "smoke" +color: ["#ded254","#ccc03d",], +behavior: behaviors.POWDER, +category: "trashes", +state: "powder", +density: 1000, +hidden: true, +tempHigh: 350, +stateHigh: "fire", +breakInto: "lemon_water", +burn: 60, +burntime: 200, +burnInto: "smoke" }; elements.saliva = { - color: ["#a6f5f0","#b6f0ec",], - behavior: behaviors.LIQUID, - category: "liquids", - state: "liquid", - density: 1280, - tempHigh: 105, - stateHigh: ["steam","fragrance"], - tempLow: -5, - stateHigh: "saliva_ice", - reactions: { - "water": { elem1: null, chance: 0.5, elem2: "dirty_water", chance: 0.5, - } - } +color: ["#a6f5f0","#b6f0ec",], +behavior: behaviors.LIQUID, +category: "liquids", +state: "liquid", +density: 1280, +tempHigh: 105, +stateHigh: ["steam","fragrance"], +tempLow: -5, +stateHigh: "saliva_ice", +reactions: { +"water": { elem1: null, chance: 0.5, elem2: "dirty_water", chance: 0.5, +} +} }; elements.lemon_milk = { - color: ["#f5f2c4","#f7f4b2",], - behavior: behaviors.LIQUID, - category: "liquids", - state: "liquid", - density: 1002, - tempHigh: 500, - stateHigh: "smoke", - tempLow: -2, - stateLow: "frozen_yogurt", - stateLowColor: ["#f5f3cb","#f7f5bc"], - reactions: { - "bacteria": { elem1: "yogurt", }, - } +color: ["#f5f2c4","#f7f4b2",], +behavior: behaviors.LIQUID, +category: "liquids", +state: "liquid", +density: 1002, +tempHigh: 500, +stateHigh: "smoke", +tempLow: -2, +stateLow: "frozen_yogurt", +stateLowColor: ["#f5f3cb","#f7f5bc"], +reactions: { +"bacteria": { elem1: "yogurt", }, +} }; elements.lemon_soda = { - color: ["#f5c842","#edcc68",], - behavior: behaviors.LIQUID, - category: "liquids", - state: "liquid", - density: 1240, - tempHigh: 140, - stateHigh: ["carbon_dioxide", "smoke",], - reactions: { - "poison": { elem1: null, } - } +color: ["#f5c842","#edcc68",], +behavior: behaviors.LIQUID, +category: "liquids", +state: "liquid", +density: 1240, +tempHigh: 140, +stateHigh: ["carbon_dioxide", "smoke",], +reactions: { +"poison": { elem1: null, } +} }; elements.saliva_ice = { - color: ["#97fcfb","#bcf7f6",], - behavior: behaviors.SOLID, - category: "states", - state: "solid", - density: 1300, - tempHigh: 5, - stateHigh: "saliva", +color: ["#97fcfb","#bcf7f6",], +behavior: behaviors.SOLID, +category: "states", +state: "solid", +density: 1300, +tempHigh: 5, +stateHigh: "saliva", }; elements.lemon_tea = { - color: ["#dec85b","#edd351",], - behavior: behaviors.LIQUID, - category: "liquids", - state: "liquid", - density: 1580, - tempHigh: 280, - stateHigh: ["fragrance","smoke","smoke",], - tempLow: -2, - stateLowColor: ["#e8cf8b","#f0dca5",], - stateLow: "tea_ice", - reactions: { - "sugar": { elem2: null, }, - } +color: ["#dec85b","#edd351",], +behavior: behaviors.LIQUID, +category: "liquids", +state: "liquid", +density: 1580, +tempHigh: 280, +stateHigh: ["fragrance","smoke","smoke",], +tempLow: -2, +stateLowColor: ["#e8cf8b","#f0dca5",], +stateLow: "tea_ice", +reactions: { +"sugar": { elem2: null, }, +} }; elements.rotten_lemon = { - color: ["#e3f283","#cdcf6b"], - behavior: behaviors.POWDER, - category: "food", - state: "powder", - density: 1890, - tempHigh: 200, - stateHigh: ["stench","ash",], - reactions: { - "water": { elem2: "dirty_water" }, - } +color: ["#e3f283","#cdcf6b"], +behavior: behaviors.POWDER, +category: "food", +state: "powder", +density: 1890, +tempHigh: 200, +stateHigh: ["stench","ash",], +reactions: { +"water": { elem2: "dirty_water" }, } +}; + +elements.cow = { + color: ["#d9d9d9","#616161",], + behavior: [ + "XX|XX|XX", + "XX|FX%5.0|M2%5.0 AND BO", + "XX|M1|XX",], + category: "life", + state: "powder", + density: 2800, + tempHigh: 250, + stateHigh: "cooked_meat", + tempLow: -10, + stateLow: "frozen_meat", + reactions: { + "water": { elem2: null, chance: 0.2,}, + "milk": { elem2: null, chance: 0.1, }, + "alcohol": { elem2: null, chance: 0.21, }, + "poison": { elem1: "rotten_meat", elem2: null, }, + "grass": { elem2: null, }, + "plague": { elem1: "rotten_meat", chance: 0.3, }, + "worm": { elem2: null, }, + "flea": { elem2: null, }, + } +}; + +elements.hazmat_head = { + color: ["#404040","#1a1a1a","#737373"], + category: "life", + hidden: true, + density: 1080, + state: "solid", + conduct: .05, + temp: 40, + tempHigh: 3500, + stateHigh: ["ash","iron",], + tempLow: -180, + stateLow: "frozen_meat", + breakInto: ["blood","meat","bone","metal_scrap"], + forceSaveColor: true, + reactions: { + "oxygen": { elem2:"carbon_dioxide", chance:0.5 }, + "meat": { elem2:null, chance:0.1 }, + "cooked_meat": { elem2:null, chance:0.1 }, + "cured_meat": { elem2:null, chance:0.1 }, + "sugar": { elem2:null, chance:0.1 }, + "broth": { elem2:null, chance:0.2 }, + "yolk": { elem2:null, chance:0.1 }, + "hard_yolk": { elem2:null, chance:0.1 }, + "dough": { elem2:null, chance:0.1 }, + "batter": { elem2:null, chance:0.2 }, + "butter": { elem2:null, chance:0.1 }, + "melted_butter": { elem2:null, chance:0.2 }, + "chocolate": { elem2:null, chance:0.2 }, + "melted_chocolate": { elem2:null, chance:0.3 }, + "grape": { elem2:null, chance:0.1 }, + "tomato": { elem2:null, chance:0.1 }, + "herb": { elem2:null, chance:0.1 }, + "lettuce": { elem2:null, chance:0.1 }, + "corn": { elem2:null, chance:0.1 }, + "popcorn": { elem2:null, chance:0.15 }, + "potato": { elem2:null, chance:0.1 }, + "baked_potato": { elem2:null, chance:0.15 }, + "bread": { elem2:null, chance:0.1 }, + "toast": { elem2:null, chance:0.1 }, + "gingerbread": { elem2:null, chance:0.1 }, + "baked_batter": { elem2:null, chance:0.2 }, + "wheat": { elem2:null, chance:0.1 }, + "candy": { elem2:null, chance:0.1 }, + "yogurt": { elem2:null, chance:0.2 }, + "frozen_yogurt": { elem2:null, chance:0.1 }, + "ice_cream": { elem2:null, chance:0.2 }, + "beans": { elem2:[null,null,null,null,null,null,null,null,"stench"], chance:0.2 }, + "tea": { elem2:null, chance:0.2 }, + "coffee": { elem2:null, chance:0.2 }, + "milk": { elem2:null, chance:0.2 }, + "cream": { elem2:null, chance:0.2 }, + "soda": { elem2:null, chance:0.2 }, + "chocolate_milk": { elem2:null, chance:0.2 }, + "fruit_milk": { elem2:null, chance:0.2 }, + "pilk": { elem2:null, chance:0.2 }, + "eggnog": { elem2:null, chance:0.2 }, + "juice": { elem2:null, chance:0.2 }, + "cheese": { elem2:null, chance:0.1 }, + "melted_cheese": { elem2:null, chance:0.2 }, + "alcohol": { elem2:null, chance:0.2 }, + "antidote": { elem2:null, chance:0.2 }, + "honey": { elem2:null, chance:0.2 }, + "caramel": { elem2:null, chance:0.2 }, + "molasses": { elem2:null, chance:0.05 }, + "ketchup": { elem2:null, chance:0.1 }, + "pumpkin_seed": { elem2:null, chance:0.1 }, + "nut": { elem2:null, chance:0.1 }, + "nut_meat": { elem2:null, chance:0.1 }, + "nut_butter": { elem2:null, chance:0.1 }, + "nut_milk": { elem2:null, chance:0.2 }, + "jelly": { elem2:null, chance:0.2 }, + "mayo": { elem2:null, chance:0.2 }, + "mashed_potato": { elem2:null, chance:0.2 }, + "sauce": { elem2:null, chance:0.2 }, + "pickle": { elem2:null, chance:0.1 }, + "light": { stain1:"#fff154" }, + }, + properties: { + dead: false + }, + tick: function(pixel) { + doHeat(pixel); + doBurning(pixel); + doElectricity(pixel); + if (pixel.dead) { + // Turn into rotten_meat if pixelTicks-dead > 500 + if (pixelTicks-pixel.dead > 200 && Math.random() < 0.1) { + changePixel(pixel,"rotten_meat"); + return + } + } + + // Find the body + if (!isEmpty(pixel.x, pixel.y+1, true) && pixelMap[pixel.x][pixel.y+1].element == "hazmat_body") { + var body = pixelMap[pixel.x][pixel.y+1]; + if (body.dead) { // If body is dead, kill head + pixel.dead = body.dead; + } + } + else { var body = null } + + if (tryMove(pixel, pixel.x, pixel.y+1)) { + // create blood if severed 10% chance + if (isEmpty(pixel.x, pixel.y+1) && !pixel.dead && Math.random() < 0.1 && !pixel.charge) { + createPixel("blood", pixel.x, pixel.y+1); + // set dead to true 15% chance + if (Math.random() < 0.15) { + pixel.dead = pixelTicks; + } + } + } + // homeostasis + if (pixel.temp > 40) { pixel.temp -= 1; } + else if (pixel.temp < 40) { pixel.temp += 1; } + } +}; + +elements.hazmat_body = { + color: ["#ffdf4f","#e8c00e","#ffd412"], + category: "life", + hidden: true, + density: 1570, + state: "solid", + conduct: .25, + temp: 39, + tempHigh: 3500, + stateHigh: ["metal_scrap","ash"], + tempLow: -180, + stateLow: "frozen_meat", + breakInto: ["blood","meat","bone","metal_scrap","radiation","fallout",], + forceSaveColor: true, + reactions: { + "egg": { elem2:"yolk", chance:0.5, oneway:true }, + "grape": { elem2:"juice", chance:0.5, color2:"#291824", oneway:true }, + "ant": { elem2:"dead_bug", chance:0.05, oneway:true }, + "fly": { elem2:"dead_bug", oneway:true }, + "firefly": { elem2:"dead_bug", oneway:true }, + "bee": { elem2:"dead_bug", oneway:true }, + "flea": { elem2:"dead_bug", oneway:true }, + "termite": { elem2:"dead_bug", oneway:true }, + "worm": { elem2:"slime", chance:0.05, oneway:true }, + "stink_bug": { elem2:"stench", oneway:true }, + "grass_seed": { elem2:null, chance:0.05 }, + "gold_coin": { elem2:null, chance:0.05 }, + "diamond": { elem2:null, chance:0.05 }, + }, + properties: { + dead: false, + dir: 1, + panic: 0 + }, + tick: function(pixel) { + if (tryMove(pixel, pixel.x, pixel.y+1)) { // Fall + if (!isEmpty(pixel.x, pixel.y-2, true)) { // Drag head down + var headpixel = pixelMap[pixel.x][pixel.y-2]; + if (headpixel.element == "hazmat_head") { + if (isEmpty(pixel.x, pixel.y-1)) { + movePixel(pixelMap[pixel.x][pixel.y-2], pixel.x, pixel.y-1); + } + else { + swapPixels(pixelMap[pixel.x][pixel.y-2], pixelMap[pixel.x][pixel.y-1]); + } + } + } + } + doHeat(pixel); + doBurning(pixel); + doElectricity(pixel); + if (pixel.dead) { + // Turn into rotten_meat if pixelTicks-dead > 500 + if (pixelTicks-pixel.dead > 200 && Math.random() < 0.1) { + changePixel(pixel,"rotten_meat"); + } + return + } + + // Find the head + if (!isEmpty(pixel.x, pixel.y-1, true) && pixelMap[pixel.x][pixel.y-1].element == "hazmat_head") { + var head = pixelMap[pixel.x][pixel.y-1]; + if (head.dead) { // If head is dead, kill body + pixel.dead = head.dead; + } + } + else { var head = null } + if (pixel.burning) { + pixel.panic += 0.1; + if (head && pixelTicks-pixel.burnStart > 240) { + pixel.color = head.color; + } + } + else if (pixel.panic > 0) { + pixel.panic -= 0.1; + } + + if (isEmpty(pixel.x, pixel.y-1)) { + // create blood if decapitated 10% chance + if (Math.random() < 0.1 && !pixel.charge) { + createPixel("blood", pixel.x, pixel.y-1); + // set dead to true 15% chance + if (Math.random() < 0.15) { + pixel.dead = pixelTicks; + } + } + } + else if (head == null) { return } + else if (Math.random() < 0.1*(isEmpty(pixel.x, pixel.y+1) ? 1 : pixel.panic+1)) { // Move 10% chance + var movesToTry = [ + [1*pixel.dir,0], + [1*pixel.dir,-1], + ]; + // While movesToTry is not empty, tryMove(pixel, x, y) with a random move, then remove it. if tryMove returns true, break. + while (movesToTry.length > 0) { + var move = movesToTry.splice(Math.floor(Math.random() * movesToTry.length), 1)[0]; + if (isEmpty(pixel.x+move[0], pixel.y+move[1]-1)) { + var origx = pixel.x+move[0]; + var origy = pixel.y+move[1]; + if (tryMove(pixel, pixel.x+move[0], pixel.y+move[1]) && pixel.x===origx && pixel.y===origy) { + movePixel(head, head.x+move[0], head.y+move[1]); + break; + } + } + } + // 15% chance to change direction + if (Math.random() < 0.15) { + pixel.dir *= -1; + } + // homeostasis + if (pixel.temp > 39) { pixel.temp -= 1; } + else if (pixel.temp < 39) { pixel.temp += 1; } + } + + } +}; + +elements.hazmat_human = { + // color: ["#404040","#1a1a1a","#737373"], + color: ["#404040","#1a1a1a","#737373"], + category: "life", + properties: { + dead: false, + dir: 1, + panic: 0 + }, + tick: function(pixel) { + if (isEmpty(pixel.x, pixel.y+1)) { + createPixel("hazmat_body", pixel.x, pixel.y+1); + pixel.element = "hazmat_head"; + } + else if (isEmpty(pixel.x, pixel.y-1)) { + createPixel("hazmat_head", pixel.x, pixel.y-1); + pixelMap[pixel.x][pixel.y-1].color = pixel.color; + pixel.element = "hazmat_body"; + pixel.color = pixelColorPick(pixel) + } + else { + deletePixel(pixel.x, pixel.y); + } + }, + related: ["hazmat_body","hazmat_head"], + cooldown: defaultCooldown, + forceSaveColor: true, +}; + +elements.zombie_head = { + color: ["#57f542","#43de2f","#46c435"], + category: "life", + hidden: true, + density: 1025, + state: "solid", + conduct: .07, + temp: 28, + tempHigh: 320, + stateHigh: ["ash","zombie_virus",], + tempLow: -45, + stateLow: ["frozen_meat","zombie_virus",], + breakInto: ["infection","rotten_meat","bone","zombie_virus",], + forceSaveColor: true, + reactions: { + "head": { elem2: ["rotten_meat","zombie",], chance:0.8, }, + "body": { elem2: ["rotten_meat","zombie",], chance:0.5, }, + "oxygen": { elem2:"carbon_dioxide", chance:0.5 }, + "rotten_meat": { elem2: null, chance:0.5 }, + "meat": { elem2:null, chance:0.1 }, + "cooked_meat": { elem2:null, chance:0.1 }, + "cured_meat": { elem2:null, chance:0.1 }, + "light": { stain1:"#45eb2f" }, + }, + properties: { + dead: false + }, + tick: function(pixel) { + doHeat(pixel); + doBurning(pixel); + doElectricity(pixel); + if (pixel.dead) { + // Turn into rotten_meat if pixelTicks-dead > 500 + if (pixelTicks-pixel.dead > 200 && Math.random() < 0.1) { + changePixel(pixel,"rotten_meat"); + return + } + } + + // Find the body + if (!isEmpty(pixel.x, pixel.y+1, true) && pixelMap[pixel.x][pixel.y+1].element == "zombie_body") { + var body = pixelMap[pixel.x][pixel.y+1]; + if (body.dead) { // If body is dead, kill head + pixel.dead = body.dead; + } + } + else { var body = null } + + if (tryMove(pixel, pixel.x, pixel.y+1)) { + // create blood if severed 10% chance + if (isEmpty(pixel.x, pixel.y+1) && !pixel.dead && Math.random() < 0.1 && !pixel.charge) { + createPixel("blood", pixel.x, pixel.y+1); + // set dead to true 15% chance + if (Math.random() < 0.15) { + pixel.dead = pixelTicks; + } + } + } + // homeostasis + if (pixel.temp > 28) { pixel.temp -= 1; } + else if (pixel.temp < 28) { pixel.temp += 1; } + } +}; + +elements.zombie_body = { + color: ["#2d7ecf","#4d94db","#65a175",], + category: "life", + hidden: true, + density: 1520, + state: "solid", + conduct: .29, + temp: 29, + tempHigh: 350, + stateHigh: ["zombie_virus","ash"], + tempLow: -180, + stateLow: ["frozen_meat","zombie_virus",], + breakInto: ["infection","rotten_meat","bone","zombie_virus",], + forceSaveColor: true, + reactions: { + "head": { elem2: "zombie", }, + "body": { elem2: "zombie", }, + }, + + properties: { + dead: false, + dir: 1, + panic: 0 + }, + tick: function(pixel) { + if (tryMove(pixel, pixel.x, pixel.y+1)) { // Fall + if (!isEmpty(pixel.x, pixel.y-2, true)) { // Drag head down + var headpixel = pixelMap[pixel.x][pixel.y-2]; + if (headpixel.element == "zombie_head") { + if (isEmpty(pixel.x, pixel.y-1)) { + movePixel(pixelMap[pixel.x][pixel.y-2], pixel.x, pixel.y-1); + } + else { + swapPixels(pixelMap[pixel.x][pixel.y-2], pixelMap[pixel.x][pixel.y-1]); + } + } + } + } + doHeat(pixel); + doBurning(pixel); + doElectricity(pixel); + if (pixel.dead) { + // Turn into rotten_meat if pixelTicks-dead > 500 + if (pixelTicks-pixel.dead > 200 && Math.random() < 0.1) { + changePixel(pixel,"rotten_meat"); + } + return + } + + // Find the head + if (!isEmpty(pixel.x, pixel.y-1, true) && pixelMap[pixel.x][pixel.y-1].element == "zombie_head") { + var head = pixelMap[pixel.x][pixel.y-1]; + if (head.dead) { // If head is dead, kill body + pixel.dead = head.dead; + } + } + else { var head = null } + if (pixel.burning) { + pixel.panic += 0.1; + if (head && pixelTicks-pixel.burnStart > 240) { + pixel.color = head.color; + } + } + else if (pixel.panic > 0) { + pixel.panic -= 0.1; + } + + if (isEmpty(pixel.x, pixel.y-1)) { + // create blood if decapitated 10% chance + if (Math.random() < 0.1 && !pixel.charge) { + createPixel("blood", pixel.x, pixel.y-1); + // set dead to true 15% chance + if (Math.random() < 0.15) { + pixel.dead = pixelTicks; + } + } + } + else if (head == null) { return } + else if (Math.random() < 0.1*(isEmpty(pixel.x, pixel.y+1) ? 1 : pixel.panic+1)) { // Move 10% chance + var movesToTry = [ + [1*pixel.dir,0], + [1*pixel.dir,-1], + ]; + // While movesToTry is not empty, tryMove(pixel, x, y) with a random move, then remove it. if tryMove returns true, break. + while (movesToTry.length > 0) { + var move = movesToTry.splice(Math.floor(Math.random() * movesToTry.length), 1)[0]; + if (isEmpty(pixel.x+move[0], pixel.y+move[1]-1)) { + var origx = pixel.x+move[0]; + var origy = pixel.y+move[1]; + if (tryMove(pixel, pixel.x+move[0], pixel.y+move[1]) && pixel.x===origx && pixel.y===origy) { + movePixel(head, head.x+move[0], head.y+move[1]); + break; + } + } + } + // 15% chance to change direction + if (Math.random() < 0.15) { + pixel.dir *= -1; + } + // homeostasis + if (pixel.temp > 29) { pixel.temp -= 1; } + else if (pixel.temp < 29) { pixel.temp += 1; } + } + + } +}; + +elements.zombie = { + // color: ["#404040","#1a1a1a","#737373"], + color: ["#57f542","#43de2f","#46c435"], + category: "life", + properties: { + dead: false, + dir: 1, + panic: 0, + }, + tick: function(pixel) { + if (isEmpty(pixel.x, pixel.y+1)) { + createPixel("zombie_body", pixel.x, pixel.y+1); + pixel.element = "zombie_head"; + } + else if (isEmpty(pixel.x, pixel.y-1)) { + createPixel("zombie_head", pixel.x, pixel.y-1); + pixelMap[pixel.x][pixel.y-1].color = pixel.color; + pixel.element = "zombie_body"; + pixel.color = pixelColorPick(pixel) + } + else { + deletePixel(pixel.x, pixel.y); + } + }, + related: ["zombie_body","zombie_head"], + cooldown: defaultCooldown, + forceSaveColor: true, +}; + +elements.zombie_virus = { + behavior: behaviors.DGAS, + color: ["#660266","#bd06bd","#f041f0",], + category: "special", + density: 30, + state: "gas", + reactions: { + "head": { elem2: ["zombie","rotten_meat",], chance: 0.2, }, + "body": { elem2: ["zombie","rotten_meat",], chance: 0.2, }, + } +} + +elements.matter = { + behavior: behaviors.GAS, + color: ["#c4f8ff","#b0f6ff","#9ccfd6",], + darkText: true, + category: "energy", + density: 2.20, + state: "gas", + reactions: { + "antimatter": { elem1: "explosion", }, + "positron": { elem1: "explosion", }, + "electron": { elem1: "explosion", }, + } +}; +elements.particle_accelerator_left = { + behavior: behaviors.SOLID, + color: ["#363aa3","#858585","#d1d1d1"], + density: 2400, + category: "machines", + state: "solid", + reactions: { + "matter": { elem2: ["accelerated_matter_left",] }, + } +}; +elements.particle_accelerator_right = { + behavior: behaviors.SOLID, + color: ["#363aa3","#858585","#d1d1d1"], + density: 2400, + category: "machines", + state: "solid", + reactions: { + "matter": { elem2: ["accelerated_matter_right",] }, + } +}; + +elements.accelerated_matter_left = { + color: ["#c0ecf0","#a8f8ff",], + behavior: [ + "M2|XX|XX", + "M1 AND XX|XX|XX", + "M2|XX|XX", + ], + hidden: true, + state: "gas", + category: "energy", + density: 2.20, + reactions: { + "accelerated_matter_right": { elem1: ["antimatter","pop",], chance: 0.3, }, + "accelerated_matter_left": { elem1: ["antimatter","pop",], chance: 0.3, }, + "antimatter": { elem1: "pop", chance: 0.01, }, + } + +}; +elements.accelerated_matter_right = { + color: ["#c0ecf0","#a8f8ff",], + behavior: [ + "XX|XX|M2", + "XX|XX|M1 AND XX", + "XX|XX|M2", + ], + hidden: true, + state: "gas", + category:"energy", + density: 2.20, + reactions: { + "accelerated_matter_left": { elem1: ["antimatter","pop",], chance: 0.3, }, + "accelerated_matter_right": { elem1: ["antimatter","pop",], chance: 0.3, }, + "antimatter": { elem1: "pop", chance: 0.01, }, + + } + +}; diff --git a/mods/circuitcore.js b/mods/circuitcore.js new file mode 100644 index 00000000..41903bd4 --- /dev/null +++ b/mods/circuitcore.js @@ -0,0 +1,1555 @@ +// CircuitCore: adds circuits to sandboxels, logicgates.js is required + +/*if (!enabledMods.includes("mods/betterSettings.js")) { enabledMods.unshift("mods/betterSettings.js"); localStorage.setItem("enabledMods", JSON.stringify(enabledMods)); window.location.reload() }; + +var cc_settingsTab = new SettingsTab("CircuitCore"); +var cc_setting1 = new Setting("Default Memory Fill; 0=empty,1=random,2=fill custom,3=custom data", "mem_fill", settingType.NUMBER, false, "The default method used to initialize ROM/RAM memory devices"); +var cc_setting2 = new Setting("Example Setting 2", "element", settingType.TEXT, false, "2"); +cc_settingsTab.registerSettings("Default Memory Fill", cc_setting1); +cc_settingsTab.registerSettings("Setting 2", cc_setting2); +settingsManager.registerTab(cc_settingsTab);*/ + +var dataVisualizationPalette16 = [ + "#000000", "#ff0000", "#ff7700", "#ffff00", "#00ff00", "#00ffff", "#0000ff", "#ff00ff", + "#777777", "#770000", "#773300", "#777700", "#007700", "#007777", "#000077", "#770077", +]; + +function binaryArrayToNumber(binaryArray) { + return binaryArray.reduce((acc, bit, index) => acc + bit * Math.pow(2, (binaryArray.length - 1) - index), 0); +} + +function hexToPixelGrid(hex) { + var hexDigitStrings = [ + "111101101101111", // 0 + "001001001001001", // 1 + "111001111100111", // 2 + "111001111001111", // 3 + "101101111001001", // 4 + "111100111001111", // 5 + "111100111101111", // 6 + "111001001001001", // 7 + "111101111101111", // 8 + "111101111001111", // 9 + "111101111101101", // A + "100100111101111", // B + "111100100100111", // C + "001001111101111", // D + "111100111100111", // E + "111100111100100" // F + ]; + + if (hex < 0 || hex > 15 || isNaN(hex)) { + console.log("inputted value: ", hex); + throw new Error("Input must be a valid 1-digit hexadecimal number."); + } + + const binaryString = hexDigitStrings[hex]; + const hexDigitArray = []; + for (let i = 0; i < 5; i++) { + hexDigitArray.push(binaryString.slice(i * 3, i * 3 + 3).split('').map(Number)); + } + return hexDigitArray; +} + +// Function to rotate a coordinate around the origin +function rotateCoordinate(x, y, angle) { + var radians = angle * (Math.PI / 180); + var cos = Math.cos(radians); + var sin = Math.sin(radians); + + var nx = Math.round(x * cos - y * sin); + var ny = Math.round(x * sin + y * cos); + + return [nx, ny]; +} + +// Initialize the circuit with optional rotation +function initializeCircuit(pixel, pins, w, h, center=true, rotation=circuitRotation, callback=()=>{}) { + if (pixel.hasGenerated) {return;} + + if (!center) {rotation = 0;} // non-centered circuits don't support rotation yet + pixel.circuitRotation = rotation; + + createCircuitFrame(pixel, w, h, center, rotation); + createPins(pixel, pins, rotation); + callback(pixel, pins, w, h); + + pixel.hasGenerated = true; +} + +function createCircuitFrame(pixel, width_, height_, center=true, rotation=0) { + var halfHeight = Math.floor(height_ / 2); + var halfWidth = Math.floor(width_ / 2); + + var a = -halfHeight; + var b = halfHeight; + var c = -halfWidth; + var d = halfWidth; + + if (!center) { + a = 0; + b = height_ - 1; + c = 0; + d = width_ - 1; + } + + for (var y = a; y <= b; y++) { + for (var x = c; x <= d; x++) { + var [rx, ry] = rotateCoordinate(x, y, rotation); + var px = pixel.x + rx; + var py = pixel.y + ry; + if (!(0 <= px && px < width && 0 <= py && py < height)) {continue;} + + if (!pixelMap[px] || pixelMap[px][py] === undefined) { + createPixel("circuit_material", px, py); + } + } + } +} + +function createPins(pixel, pins, rotation=0) { + for (var i = 0; i < pins.length; i++) { + var [rx, ry] = rotateCoordinate(pins[i][0], pins[i][1], rotation); + var px = pixel.x + rx; + var py = pixel.y + ry; + if (!(0 <= px && px < width && 0 <= py && py < height)) {continue;} + + if (!pixelMap[px] || pixelMap[px][py] == undefined) { + var pinType = pins[i][2] ? "input_pin" : "output_pin"; + createPixel(pinType, px, py); + } + } +} + +function checkPin(pixel, pins, index, rotation=pixel.circuitRotation) { + var [rx, ry] = rotateCoordinate(pins[index][0], pins[index][1], rotation); + var px = pixel.x + rx; + var py = pixel.y + ry; + if (!(0 <= px && px < width && 0 <= py && py < height)) {return;} + + return pixelMap[px][py] && pixelMap[px][py].active; +} + +function setPin(pixel, pins, index, value, rotation=pixel.circuitRotation) { + var [rx, ry] = rotateCoordinate(pins[index][0], pins[index][1], rotation); + var px = pixel.x + rx; + var py = pixel.y + ry; + if (!(0 <= px && px < width && 0 <= py && py < height)) {return;} + + if (pixelMap[px][py] && pixelMap[px][py].element == "output_pin") { + pixelMap[px][py].active = value; + } +} + +// Circuits +elements.four_bit_enabler_circuit = { + centered: true, + tick: function(pixel) { + var pins = [ + // Data inputs (D0-D3) + [-3, -2, true], // D0 + [-1, -2, true], // D1 + [1, -2, true], // D2 + [3, -2, true], // D3 + + // Enable input (E) + [5, 0, true], // Enable (E) + + // Enable mirror (E2) + [-5, 0, false], + + // Outputs (Q0-Q3) + [-3, 2, false], // Q0 + [-1, 2, false], // Q1 + [1, 2, false], // Q2 + [3, 2, false] // Q3 + ]; + + var elementData = elements[pixel.element]; + initializeCircuit(pixel, pins, 9, 3, elementData.centered); + + // Read inputs + var D = [ + checkPin(pixel, pins, 0), + checkPin(pixel, pins, 1), + checkPin(pixel, pins, 2), + checkPin(pixel, pins, 3) + ]; + + var C = checkPin(pixel, pins, 4); // Control input + setPin(pixel, pins, 5, C); + + // Previous state initialization + if (pixel._state === undefined) { + pixel._state = [false, false, false, false]; + } + + // Update latch state based on control input + if (C) { + pixel._state = [D[0], D[1], D[2], D[3]]; // Update latch state with data inputs + } else { + pixel._state = [false, false, false, false]; + } + + // Output the latch state + setPin(pixel, pins, 6, pixel._state[0]); // Q0 + setPin(pixel, pins, 7, pixel._state[1]); // Q1 + setPin(pixel, pins, 8, pixel._state[2]); // Q2 + setPin(pixel, pins, 9, pixel._state[3]); // Q3 + } +}; + +elements.randomizer = { + color: "#FFCCFF", + tick: function(pixel) { + for (var i = 0; i < adjacentCoords.length; i++) { + var coord = adjacentCoords[i]; + var x = pixel.x + coord[0]; + var y = pixel.y + coord[1]; + if (!isEmpty(x, y, true)) { + if (pixelMap[x][y].element == "logic_wire") { + if (Math.random() < 0.5){ + pixelMap[x][y].lstate = 2 + pixelMap[x][y].color = pixelColorPick(pixelMap[x][y], "#ffe49c") + } else { + pixelMap[x][y].lstate = -2 + pixelMap[x][y].color = pixelColorPick(pixelMap[x][y], "#3d4d2c") + } + } + } + } + }, +} + +elements.four_bit_randomizer_circuit = { + tick: function(pixel) { + var pins = [ + // Clock input + [0, -2, true], // Clock + + // Outputs (Q0-Q3) + [-3, 2, false], // Q0 + [-1, 2, false], // Q1 + [1, 2, false], // Q2 + [3, 2, false] // Q3 + ]; + + initializeCircuit(pixel, pins, 9, 3); + + // Read clock input + var clock = checkPin(pixel, pins, 0); + + // Initialize the state if not already done + if (pixel._state === undefined) { + pixel._state = [false, false, false, false]; + pixel.prevClock = false; + } + + // Detect the positive edge on the clock pin + if (clock && !pixel.prevClock) { + // Generate a new 4-bit random number + var randomValue = Math.floor(Math.random() * 16); + + // Update the state with the new random value + pixel._state = [ + (randomValue & 1) !== 0, + (randomValue & 2) !== 0, + (randomValue & 4) !== 0, + (randomValue & 8) !== 0 + ]; + } + + // Output the current state + setPin(pixel, pins, 1, pixel._state[0]); // Q0 + setPin(pixel, pins, 2, pixel._state[1]); // Q1 + setPin(pixel, pins, 3, pixel._state[2]); // Q2 + setPin(pixel, pins, 4, pixel._state[3]); // Q3 + + // Update previous state of clock input + pixel.prevClock = clock; + } +}; + +/*elements.ROM_circuit = { + previewSize: false, + tick: function(pixel) { + var romWidth = 16; + var romHeight = 16; + + var pins = [ + // Address inputs (D0-D7) + [-1, 1, true], // D0 + [-1, 3, true], // D1 + [-1, 5, true], // D2 + [-1, 7, true], // D3 + [-1, 9, true], // D4 + [-1, 11, true], // D5 + [-1, 13, true], // D6 + [-1, 15, true], // D7 + + // ROM output + [romWidth + 2, 1, false], + [romWidth + 2, 3, false], + [romWidth + 2, 5, false], + [romWidth + 2, 7, false], + ]; + + initializeCircuit(pixel, pins, romWidth + 2, romHeight + 2, false, pixel.circuitRotation, addDisplayCallback); + +// if (!pixel.romData) {pixel.romData = new Array(64).fill(0);} + if (!pixel.romData) { + pixel.romData = Array.from({length: romWidth * romHeight}, () => Array.from({length: 4}, () => Math.floor(Math.random() * 2))); + } + + // Read inputs + var D = [ + checkPin(pixel, pins, 0), + checkPin(pixel, pins, 1), + checkPin(pixel, pins, 2), + checkPin(pixel, pins, 3), + checkPin(pixel, pins, 4), + checkPin(pixel, pins, 5), + checkPin(pixel, pins, 6), + checkPin(pixel, pins, 7) + ]; + + var address = binaryArrayToNumber(D); + if (isNaN(address)) {return;} + + // Draw the ROM data + for (var y = 1; y <= romWidth; y++) { + for (var x = 1; x <= romHeight; x++) { + var px = pixel.x + x; + var py = pixel.y + y; + var cellAddress = ((y - 1) * romWidth) + x - 1; + var cellData = pixel.romData[cellAddress]; + if (!(0 <= px && px < width && 0 <= py && py < height)) {continue;} + + if (pixelMap[px][py] && pixelMap[px][py].element == "art") { +// if (address == cellAddress) {} + pixelMap[px][py].color = dataVisualizationPalette16[binaryArrayToNumber(cellData)]; + } + } + } + + var output = pixel.romData[address]; + + setPin(pixel, pins, 8, output[0]); + setPin(pixel, pins, 9, output[1]); + setPin(pixel, pins, 10, output[2]); + setPin(pixel, pins, 11, output[3]); + } +};*/ + +function general_encoder(inputBits) { + return function(pixel) { + var pins = []; + var outputBits = Math.ceil(Math.log2(inputBits)); + var circuitWidth = (inputBits * 2) + 1; + var circuitHeight = (outputBits * 2) + 1; + + // Define input pins + for (var i = 0; i < inputBits; i++) { + pins.push([-Math.floor(circuitWidth / 2) + 1 + 2 * i, outputBits + 1, true]); + } + + // Define output pins + for (var i = 0; i < outputBits; i++) { + pins.push([Math.floor(circuitWidth / 2) + 1, -outputBits + 1 + 2 * i, false]); // Right outputs + } + + for (var i = 0; i < outputBits; i++) { + pins.push([-Math.floor(circuitWidth / 2) - 1, -outputBits + 1 + 2 * i, false]); // Left outputs + } + + initializeCircuit(pixel, pins, circuitWidth, circuitHeight); + + // Determine which input is active (priority encoder) + var activeInput = -1; + for (var i = inputBits - 1; i >= 0; i--) { + if (checkPin(pixel, pins, i)) { + activeInput = i; + break; + } + } + + // Set output values based on active input + for (var i = 0; i < outputBits; i++) { + var outputValue = activeInput >= 0 ? ((activeInput >> i) & 1) : false; + setPin(pixel, pins, inputBits + i, outputValue); // Right outputs + setPin(pixel, pins, inputBits + outputBits + i, outputValue); // Left outputs + } + }; +} + +// Define a 2-to-1 encoder using the general_encoder function +elements.two_to_one_encoder_circuit = { + tick: general_encoder(2) +}; + +// Define a 4-to-2 encoder using the general_encoder function +elements.four_to_two_encoder_circuit = { + tick: general_encoder(4) +}; + +// Define an 8-to-3 encoder using the general_encoder function +elements.eight_to_three_encoder_circuit = { + tick: general_encoder(8) +}; + +// Define a 16-to-4 encoder using the general_encoder function +elements.sixteen_to_four_encoder_circuit = { + tick: general_encoder(16) +}; + +function general_demultiplexer(selectorBits) { + return function(pixel) { + var pins = []; + var outputCount = Math.pow(2, selectorBits); + var circuitWidth = 3; + var circuitHeight = (outputCount * 2) + 1; + + // Define the input pin + pins.push([0, Math.floor(circuitHeight / 2) + 1, true]); + + // Define selector pins + for (var i = 0; i < selectorBits; i++) { + pins.push([-2, (Math.floor(circuitHeight / 2) - 1) - (2 * i), true]); + } + + // Define output pins + for (var i = 0; i < outputCount; i++) { + pins.push([Math.floor(circuitWidth / 2) + 1, -Math.floor(circuitHeight / 2) + 1 + 2 * i, false]); + } + + initializeCircuit(pixel, pins, circuitWidth, circuitHeight); + + // Read input and selector values + var input = checkPin(pixel, pins, 0); + var selector = 0; + for (var i = 0; i < selectorBits; i++) { + if (checkPin(pixel, pins, 1 + i)) { + selector += Math.pow(2, i); + } + } + + // Set output values based on selector + for (var i = 0; i < outputCount; i++) { + setPin(pixel, pins, 1 + selectorBits + i, i === selector ? input : false); + } + }; +} + +// Define a 1-to-2 demultiplexer using the general_demultiplexer function +elements.one_to_two_demultiplexer_circuit = { + tick: general_demultiplexer(1) +}; + +// Define a 1-to-4 demultiplexer using the general_demultiplexer function +elements.one_to_four_demultiplexer_circuit = { + tick: general_demultiplexer(2) +}; + +// Define a 1-to-8 demultiplexer using the general_demultiplexer function +elements.one_to_eight_demultiplexer_circuit = { + tick: general_demultiplexer(3) +}; + +// Define a 1-to-16 demultiplexer using the general_demultiplexer function +elements.one_to_sixteen_demultiplexer_circuit = { + tick: general_demultiplexer(4) +}; + +function general_decoder(inputBits) { + return function(pixel) { + var pins = []; + var outputCount = Math.pow(2, inputBits); + var circuitWidth = (inputBits * 2) + 1; + var circuitHeight = (outputCount * 2) + 1; + + // Define input pins + for (var i = 0; i < inputBits; i++) { + pins.push([-Math.floor(circuitWidth / 2) + 1 + 2 * i, outputCount + 1, true]); + } + + // Define output pins + for (var i = 0; i < outputCount; i++) { + pins.push([Math.floor(circuitWidth / 2) + 1, -outputCount + 1 + 2 * i, false]); // Right outputs + } + + for (var i = 0; i < outputCount; i++) { + pins.push([-Math.floor(circuitWidth / 2) - 1, -outputCount + 1 + 2 * i, false]); // Left outputs + } + + initializeCircuit(pixel, pins, circuitWidth, circuitHeight); + + // Read input values + var input = 0; + for (var i = 0; i < inputBits; i++) { + if (checkPin(pixel, pins, i)) { + input += Math.pow(2, i); + } + } + + // Set output values + for (var i = 0; i < outputCount; i++) { + var outputValue = (i === input); + setPin(pixel, pins, inputBits + i, outputValue); // Right outputs + setPin(pixel, pins, inputBits + outputCount + i, outputValue); // Left outputs + } + }; +} + +elements.one_to_two_decoder_circuit = { + tick: general_decoder(1) +}; + +elements.two_to_four_decoder_circuit = { + tick: general_decoder(2) +}; + +elements.three_to_eight_decoder_circuit = { + tick: general_decoder(3) +}; + +elements.four_to_sixteen_decoder_circuit = { + tick: general_decoder(4) +}; + +function general_multiplexer(inputLines) { + return function(pixel) { + var pins = []; + var selectorBits = Math.ceil(Math.log2(inputLines)); + var circuitWidth = (selectorBits * 2) + 1; + var circuitHeight = (inputLines * 2) + 1; + + // Define selector pins + for (var i = 0; i < selectorBits; i++) { + pins.push([-Math.floor(circuitWidth / 2) + 1 + 2 * i, inputLines + 1, true]); + } + + // Define input data pins + for (var i = 0; i < inputLines; i++) { + pins.push([-Math.floor(circuitWidth / 2) - 1, -inputLines + 1 + 2 * i, true]); + } + + // Define output pin + pins.push([Math.floor(circuitWidth / 2) + 1, 0, false]); + + initializeCircuit(pixel, pins, circuitWidth, circuitHeight); + + // Read selector input + var selector = 0; + for (var i = 0; i < selectorBits; i++) { + if (checkPin(pixel, pins, i)) { + selector += Math.pow(2, i); + } + } + + setPin(pixel, pins, selectorBits + inputLines, checkPin(pixel, pins, selector + selectorBits)); // Output pin + }; +} + +// Define a 2-input multiplexer using the general_multiplexer function +elements.two_to_one_multiplexer_circuit = { + tick: general_multiplexer(2) +}; + +// Define a 4-input multiplexer using the general_multiplexer function +elements.four_to_one_multiplexer_circuit = { + tick: general_multiplexer(4) +}; + +// Define an 8-input multiplexer using the general_multiplexer function +elements.eight_to_one_multiplexer_circuit = { + tick: general_multiplexer(8) +}; + +// Define an 8-input multiplexer using the general_multiplexer function +elements.sixteen_to_one_multiplexer_circuit = { + tick: general_multiplexer(16) +}; + +elements.four_bit_PISO_shift_register_circuit = { + tick: function(pixel) { + var pins = [ + // Data inputs (D0-D3) + [-3, -3, true], // D0 + [-1, -3, true], // D1 + [1, -3, true], // D2 + [3, -3, true], // D3 + + // Control input (Load/Shift Enable) + [-5, -1, true], // Load/Shift Enable + + // Clock input + [-5, 1, true], // Clock + + // Serial output + [5, -1, false], // Serial Out (Q) + [5, 1, false] // Transmission Flag + ]; + + initializeCircuit(pixel, pins, 9, 5); + + // Read data inputs + var D = [ + checkPin(pixel, pins, 0), + checkPin(pixel, pins, 1), + checkPin(pixel, pins, 2), + checkPin(pixel, pins, 3) + ]; + + // Read control and clock inputs + var loadShiftEnable = checkPin(pixel, pins, 4); + var clock = checkPin(pixel, pins, 5); + + // Initialize the state if not already done + if (pixel._state === undefined) { + pixel._state = [false, false, false, false]; + pixel.bitIndex = 0; + pixel.prevLoadShiftEnable = false; + pixel.prevClock = false; + } + + // Detect the positive edge on the control pin + if (loadShiftEnable && !pixel.prevLoadShiftEnable) { + // Load the data into the register on the first positive edge + pixel._state = [D[0], D[1], D[2], D[3]]; + pixel.bitIndex = 0; + } + + // Detect the positive edge on the clock pin + if (clock && !pixel.prevClock) { + if (pixel.bitIndex < 4) { + // Shift the register and output the next bit + var serialOut = pixel._state[0]; + for (var i = 0; i < 3; i++) { + pixel._state[i] = pixel._state[i + 1]; + } + pixel._state[3] = false; // Clear the last bit after shifting + + // Output the serial value + setPin(pixel, pins, 6, serialOut); + + // Update bit index + pixel.bitIndex++; + } + } + + // Set the transmission flag + var transmitting = pixel.bitIndex < 4 && loadShiftEnable; + setPin(pixel, pins, 7, transmitting); + + // Update previous state of control and clock inputs + pixel.prevLoadShiftEnable = loadShiftEnable; + pixel.prevClock = clock; + } +}; + +elements.four_bit_SIPO_shift_register_circuit = { + tick: function(pixel) { + var pins = [ + // Serial input (Data In) + [-2, -3, true], // Data In + + // Clock input + [-2, -1, true], // Clock + + // Parallel outputs (Q0-Q3) + [2, -3, false], // Q0 + [2, -1, false], // Q1 + [2, 1, false], // Q2 + [2, 3, false] // Q3 + ]; + + initializeCircuit(pixel, pins, 3, 9); + + // Read serial and clock input + var serialIn = checkPin(pixel, pins, 0); + var clock = checkPin(pixel, pins, 1); + + // Initialize the state if not already done + if (pixel._state === undefined) { + pixel._state = [false, false, false, false]; + pixel.prevClock = false; + } + + // Detect the positive edge on the clock pin + if (clock && !pixel.prevClock) { + pixel._state = [serialIn, pixel._state[0], pixel._state[1], pixel._state[2]]; + } + + // Output the parallel values + for (var i = 0; i < 4; i++) { + setPin(pixel, pins, 2 + i, pixel._state[i]); + } + + // Update previous state of control and clock inputs + pixel.prevClock = clock; + } +}; + +elements.four_bit_program_counter_circuit = { + tick: function(pixel) { + var pins = [ + // Data inputs (D0-D3) + [-3, -3, true], // D0 + [-1, -3, true], // D1 + [1, -3, true], // D2 + [3, -3, true], // D3 + + // Control inputs (Increment, Write Enable) + [5, -1, true], // Increment + [5, 1, true], // Write Enable + + // Outputs (Q0-Q3) + [-3, 3, false], // Q0 + [-1, 3, false], // Q1 + [1, 3, false], // Q2 + [3, 3, false], // Q3 + ]; + + initializeCircuit(pixel, pins, 9, 5); + + // Read data inputs + var D = [ + checkPin(pixel, pins, 0), + checkPin(pixel, pins, 1), + checkPin(pixel, pins, 2), + checkPin(pixel, pins, 3) + ]; + + // Read control inputs + var Increment = checkPin(pixel, pins, 4); + var WriteEnable = checkPin(pixel, pins, 5); + + // Initialize the state if not already done + if (pixel._state === undefined) { + pixel._state = [false, false, false, false]; + pixel.prevIncrement = false; // Previous state of Increment pin + } + + // Convert the state to a 4-bit binary number + var stateValue = binaryArrayToNumber(pixel._state); + + // Detect the positive edge on the Increment pin + if (Increment && !pixel.prevIncrement) { + stateValue = (stateValue + 1) % 16; // Ensure the value wraps around at 4 bits + } + + // Update the register state if WriteEnable is active + if (WriteEnable) { + stateValue = binaryArrayToNumber(D); // Load data inputs into state + } + + // Update the state + pixel._state = [ + (stateValue & 8) !== 0, + (stateValue & 4) !== 0, + (stateValue & 2) !== 0, + (stateValue & 1) !== 0 + ]; + + // Output the register state + setPin(pixel, pins, 6, pixel._state[0]); // Q0 + setPin(pixel, pins, 7, pixel._state[1]); // Q1 + setPin(pixel, pins, 8, pixel._state[2]); // Q2 + setPin(pixel, pins, 9, pixel._state[3]); // Q3 + + // Update previous state of Increment pin + pixel.prevIncrement = Increment; + } +}; + +elements.four_bit_register_circuit = { + tick: function(pixel) { + var pins = [ + // Data inputs (D0-D3) + [-3, -3, true], // D0 + [-1, -3, true], // D1 + [1, -3, true], // D2 + [3, -3, true], // D3 + + // Control inputs (Enable, Write Enable) + [5, -1, true], // Enable + [5, 1, true], // Write Enable + + // Outputs (Q0-Q3) + [-3, 3, false], // Q0 + [-1, 3, false], // Q1 + [1, 3, false], // Q2 + [3, 3, false], // Q3 + ]; + + initializeCircuit(pixel, pins, 9, 5); + + // Read data inputs + var D = [ + checkPin(pixel, pins, 0), + checkPin(pixel, pins, 1), + checkPin(pixel, pins, 2), + checkPin(pixel, pins, 3) + ]; + + // Read control inputs + var Enable = checkPin(pixel, pins, 4); + var WriteEnable = checkPin(pixel, pins, 5); + + // Initialize the state if not already done + if (pixel._state === undefined) { + pixel._state = [false, false, false, false]; + } + + // Update the register state if WriteEnable is active + if (WriteEnable && Enable) { + pixel._state = [D[0], D[1], D[2], D[3]]; + } + + // Output the register state if Enable is active + if (Enable) { + setPin(pixel, pins, 6, pixel._state[0]); // Q0 + setPin(pixel, pins, 7, pixel._state[1]); // Q1 + setPin(pixel, pins, 8, pixel._state[2]); // Q2 + setPin(pixel, pins, 9, pixel._state[3]); // Q3 + } else { + // Disable outputs if Enable is not active + setPin(pixel, pins, 6, false); // Q0 + setPin(pixel, pins, 7, false); // Q1 + setPin(pixel, pins, 8, false); // Q2 + setPin(pixel, pins, 9, false); // Q3 + } + } +}; + +elements.SR_latch_circuit = { + tick: function(pixel) { + var pins = [ + [0, -2, true], // Input: Set + [0, 2, true], // Input: Reset + [2, 0, false], // Output + [-2, 0, false] // Output + ]; + initializeCircuit(pixel, pins, 3, 3); + + if (checkPin(pixel, pins, 0)) {pixel._state = true;} // Set + if (checkPin(pixel, pins, 1)) {pixel._state = false;} // Reset + + setPin(pixel, pins, 2, pixel._state); + setPin(pixel, pins, 3, pixel._state); + } +}; + +elements.T_flip_flop_circuit = { + tick: function(pixel) { + var pins = [ + [0, -2, true], // Input: Toggle (T) + [2, 0, false], // Output (Q) + [-2, 0, false] // Output (not Q) - Optional + ]; + + initializeCircuit(pixel, pins, 3, 3); + + // Check the current state of the Toggle (T) input + var T = checkPin(pixel, pins, 0); + + // Initialize the previous state of T if not already done + if (pixel.prevT === undefined) { + pixel.prevT = false; + } + + // Detect the positive edge + if (T && !pixel.prevT) { + pixel._state = !pixel._state; // Toggle state on positive edge + } + + // Update the previous state of T + pixel.prevT = T; + + setPin(pixel, pins, 1, pixel._state); + setPin(pixel, pins, 2, pixel._state); + } +}; + +elements.D_latch_circuit = { + tick: function(pixel) { + var pins = [ + [0, -2, true], // Input: Data + [2, 0, true], // Input: Enable + [0, 2, false], // Output + [-2, 0, false] // Output + ]; + initializeCircuit(pixel, pins, 3, 3); + + var D = checkPin(pixel, pins, 0); // Data input + var E = checkPin(pixel, pins, 1); // Enable input + + if (E) { + pixel._state = D; // Q follows D when E is active + } + + setPin(pixel, pins, 2, pixel._state); + setPin(pixel, pins, 3, pixel._state); + } +}; + +elements.D_flip_flop_circuit = { + tick: function(pixel) { + var pins = [ + [0, -2, true], // Input: Data + [2, 0, true], // Input: Enable + [0, 2, false], // Output + [-2, 0, false] // Output + ]; + + initializeCircuit(pixel, pins, 3, 3); + + // Read inputs + var D = checkPin(pixel, pins, 0); // Data input + var C = checkPin(pixel, pins, 1); // Control input + + // Initialize previous state of control input if not already done + if (pixel.prevC === undefined) { + pixel.prevC = false; + } + + // Previous state initialization + if (pixel._state === undefined) { + pixel._state = false; + } + + // Update flip-flop state on positive edge of control input + if (C && !pixel.prevC) { + pixel._state = D; // Q follows D on positive edge of C + } + + // Update the previous state of control input + pixel.prevC = C; + + // Output the flip-flop state + setPin(pixel, pins, 2, pixel._state); + setPin(pixel, pins, 3, pixel._state); + } +}; + +elements.four_bit_D_latch_circuit = { + tick: function(pixel) { + var pins = [ + // Data inputs (D0-D3) + [-3, -2, true], // D0 + [-1, -2, true], // D1 + [1, -2, true], // D2 + [3, -2, true], // D3 + + // Control input (C) + [5, 0, true], // Control (C) + + // Outputs (Q0-Q3) + [-3, 2, false], // Q0 + [-1, 2, false], // Q1 + [1, 2, false], // Q2 + [3, 2, false] // Q3 + ]; + + initializeCircuit(pixel, pins, 9, 3); + + // Read inputs + var D = [ + checkPin(pixel, pins, 0), + checkPin(pixel, pins, 1), + checkPin(pixel, pins, 2), + checkPin(pixel, pins, 3) + ]; + + var C = checkPin(pixel, pins, 4); // Control input + + // Previous state initialization + if (pixel._state === undefined) { + pixel._state = [false, false, false, false]; + } + + // Update latch state based on control input + if (C) { + pixel._state = [D[0], D[1], D[2], D[3]]; // Update latch state with data inputs + } + + // Output the latch state + setPin(pixel, pins, 5, pixel._state[0]); // Q0 + setPin(pixel, pins, 6, pixel._state[1]); // Q1 + setPin(pixel, pins, 7, pixel._state[2]); // Q2 + setPin(pixel, pins, 8, pixel._state[3]); // Q3 + } +}; + +elements.four_bit_D_flip_flop_circuit = { + tick: function(pixel) { + var pins = [ + // Data inputs (D0-D3) + [-3, -2, true], // D0 + [-1, -2, true], // D1 + [1, -2, true], // D2 + [3, -2, true], // D3 + + // Control input (C) + [5, 0, true], // Control (C) + + // Outputs (Q0-Q3) + [-3, 2, false], // Q0 + [-1, 2, false], // Q1 + [1, 2, false], // Q2 + [3, 2, false] // Q3 + ]; + + initializeCircuit(pixel, pins, 9, 3); + + // Read inputs + var D = [ + checkPin(pixel, pins, 0), + checkPin(pixel, pins, 1), + checkPin(pixel, pins, 2), + checkPin(pixel, pins, 3) + ]; + + var C = checkPin(pixel, pins, 4); // Control input + + // Initialize previous state of control input if not already done + if (pixel.prevC === undefined) { + pixel.prevC = false; + } + + // Previous state initialization + if (pixel._state === undefined) { + pixel._state = [false, false, false, false]; + } + + // Update flip-flop state on positive edge of control input + if (C && !pixel.prevC) { + pixel._state = [D[0], D[1], D[2], D[3]]; // Update flip-flop state with data inputs + } + + // Update the previous state of control input + pixel.prevC = C; + + // Output the flip-flop state + setPin(pixel, pins, 5, pixel._state[0]); // Q0 + setPin(pixel, pins, 6, pixel._state[1]); // Q1 + setPin(pixel, pins, 7, pixel._state[2]); // Q2 + setPin(pixel, pins, 8, pixel._state[3]); // Q3 + } +}; + +elements.four_bit_incrementer_circuit = { + tick: function(pixel) { + var pins = [ + // 4-bit number inputs (N0-N3) + [-3, -2, true], // N0 + [-1, -2, true], // N1 + [1, -2, true], // N2 + [3, -2, true], // N3 + + // Increment control input (INC) + [-5, 0, true], // Increment (INC) + + // Outputs (Q0-Q3) + [-3, 2, false], // Q0 + [-1, 2, false], // Q1 + [1, 2, false], // Q2 + [3, 2, false], // Q3 + + // Carry out + [5, 0, false] // Carry out (COUT) + ]; + + initializeCircuit(pixel, pins, 9, 3); + + // Read inputs + var N = [ + checkPin(pixel, pins, 0), + checkPin(pixel, pins, 1), + checkPin(pixel, pins, 2), + checkPin(pixel, pins, 3) + ]; + + var INC = checkPin(pixel, pins, 4); // Increment control input + + // Calculate the incremented value when control is active + var carry = 0; + var result = []; + + if (INC) { + carry = 1; // Start with a carry of 1 to increment by 1 + } + + for (var i = 0; i < 4; i++) { + var sum = N[i] + carry; + result[i] = sum % 2; // Current bit sum + carry = Math.floor(sum / 2); // Carry for next bit + } + + // Output the incremented value and carry out + setPin(pixel, pins, 5, result[0]); // Q0 + setPin(pixel, pins, 6, result[1]); // Q1 + setPin(pixel, pins, 7, result[2]); // Q2 + setPin(pixel, pins, 8, result[3]); // Q3 + setPin(pixel, pins, 9, carry); // Carry out (COUT) + } +}; + +elements.four_bit_adder_circuit = { + tick: function(pixel) { + var pins = [ + // First 4-bit number (A) + [-7, -2, true], // A0 + [-5, -2, true], // A1 + [-3, -2, true], // A2 + [-1, -2, true], // A3 + + // Second 4-bit number (B) + [1, -2, true], // B0 + [3, -2, true], // B1 + [5, -2, true], // B2 + [7, -2, true], // B3 + + // Carry-in (C_in) + [9, 0, true], // Carry-in (C_in) + + // Output sum (S) + [-7, 2, false], // S0 + [-5, 2, false], // S1 + [-3, 2, false], // S2 + [-1, 2, false], // S3 + [1, 2, false], // Carry Out (C4) + ]; + + initializeCircuit(pixel, pins, 17, 3); + + // Read inputs + var A = [ + checkPin(pixel, pins, 0), + checkPin(pixel, pins, 1), + checkPin(pixel, pins, 2), + checkPin(pixel, pins, 3) + ]; + + var B = [ + checkPin(pixel, pins, 4), + checkPin(pixel, pins, 5), + checkPin(pixel, pins, 6), + checkPin(pixel, pins, 7) + ]; + + var C_in = checkPin(pixel, pins, 8); // Carry-in + + // Calculate the sum and carry + var sum = []; + var carry = C_in; + + for (var i = 0; i < 4; i++) { + var bitSum = A[i] + B[i] + carry; + sum[i] = bitSum % 2; // Current bit sum + carry = Math.floor(bitSum / 2); // Carry for next bit + } + + // Output the sum + setPin(pixel, pins, 9, sum[0]); // S0 + setPin(pixel, pins, 10, sum[1]); // S1 + setPin(pixel, pins, 11, sum[2]); // S2 + setPin(pixel, pins, 12, sum[3]); // S3 + setPin(pixel, pins, 13, carry); // Carry Out (C4) + } +}; + +function general_clock(speed, s2) { + return function(pixel){ + for (var i = 0; i < adjacentCoords.length; i++) { + var coord = adjacentCoords[i]; + var x = pixel.x+coord[0]; + var y = pixel.y+coord[1]; + if (!isEmpty(x,y,true)) { + if (pixelMap[x][y].element == "logic_wire"){ + if (pixelTicks % speed < s2){ + pixelMap[x][y].lstate = 2 + pixelMap[x][y].color = pixelColorPick(pixelMap[x][y], "#ffe49c") + } else { + pixelMap[x][y].lstate = -2 + pixelMap[x][y].color = pixelColorPick(pixelMap[x][y], "#3d4d2c") + } + } + } + } + }; +} + +elements.slow_clock = { + color: "#BB66BB", + tick: general_clock(64, 32), +} + +elements.medium_clock = { + color: "#DD88DD", + tick: general_clock(32, 16), +} + +elements.fast_clock = { + color: "#FFAAFF", + tick: general_clock(16, 8), +} + +elements.very_fast_clock = { + color: "#FFCCFF", + tick: general_clock(8, 4), +} + +var addDisplayCallback = function(pixel, pins, w, h) { + for (var y = 1; y < h - 1; y++) { + for (var x = 1; x < w - 1; x++) { + var px = pixel.x + x; + var py = pixel.y + y; + if (!(0 <= px && px < width && 0 <= py && py < height)) {continue;} + + deletePixel(px, py); + createPixel("art", px, py); + pixelMap[px][py].color = "rgb(16, 24, 32)"; + } + } +} + +elements.simple_seven_segment_display = { + tick: function(pixel) { + var pins = [ + // Data inputs (D0-D3) + [-1, 1, true], // D0 + [-1, 3, true], // D1 + [-1, 5, true], // D2 + [-1, 7, true], // D3 + ]; + + initializeCircuit(pixel, pins, 5, 9, false, pixel.circuitRotation, addDisplayCallback); + + // Read inputs + var D = [ + checkPin(pixel, pins, 0), + checkPin(pixel, pins, 1), + checkPin(pixel, pins, 2), + checkPin(pixel, pins, 3) + ]; + + var hexNumber = (D[3] * 8) + (D[2] * 4) + (D[1] * 2) + (D[0] * 1); + if (isNaN(hexNumber)) {return;} + + // Draw the number + var hexGrid = hexToPixelGrid(hexNumber); + for (var y = 2; y <= 6; y++) { + for (var x = 1; x <= 3; x++) { + var px = pixel.x + x; + var py = pixel.y + y; + + if (pixelMap[px][py] && pixelMap[px][py].element == "art") { + if (hexGrid[y - 2][x - 1]) { + pixelMap[px][py].color = "rgb(16, 230, 120)"; + } else { + pixelMap[px][py].color = "rgb(16, 24, 32)"; + } + } + } + } + } +}; + +elements.simple_double_seven_segment_display = { + tick: function(pixel) { + var pins = [ + // Data inputs (D0-D3) + [-1, 1, true], // D0 + [-1, 3, true], // D1 + [-1, 5, true], // D2 + [-1, 7, true], // D3 + + [1, -1, true], // D2-0 + [3, -1, true], // D2-1 + [5, -1, true], // D2-2 + [7, -1, true], // D2-3 + ]; + + initializeCircuit(pixel, pins, 9, 9, false, pixel.circuitRotation, addDisplayCallback); + + // Read inputs + var D = [ + checkPin(pixel, pins, 0), + checkPin(pixel, pins, 1), + checkPin(pixel, pins, 2), + checkPin(pixel, pins, 3), + checkPin(pixel, pins, 4), + checkPin(pixel, pins, 5), + checkPin(pixel, pins, 6), + checkPin(pixel, pins, 7) + ]; + + var hexNumber = (D[3] * 8) + (D[2] * 4) + (D[1] * 2) + (D[0] * 1); + var hexNumber2 = (D[7] * 8) + (D[6] * 4) + (D[5] * 2) + (D[4] * 1); + if (isNaN(hexNumber) || isNaN(hexNumber2)) {return;} + + // Draw the number + var hexGrid = hexToPixelGrid(hexNumber); + for (var y = 2; y <= 6; y++) { + for (var x = 1; x <= 3; x++) { + var px = pixel.x + x; + var py = pixel.y + y; + + if (pixelMap[px][py] && pixelMap[px][py].element == "art") { + if (hexGrid[y - 2][x - 1]) { + pixelMap[px][py].color = "rgb(16, 230, 120)"; + } else { + pixelMap[px][py].color = "rgb(16, 24, 32)"; + } + } + } + } + + var hexGrid2 = hexToPixelGrid(hexNumber2); + for (var y = 2; y <= 6; y++) { + for (var x = 5; x <= 7; x++) { + var px = pixel.x + x; + var py = pixel.y + y; + + if (pixelMap[px][py] && pixelMap[px][py].element == "art") { + if (hexGrid2[y - 2][x - 5]) { + pixelMap[px][py].color = "rgb(16, 230, 120)"; + } else { + pixelMap[px][py].color = "rgb(16, 24, 32)"; + } + } + } + } + } +}; + +elements.circuit_material = { + color: "#444444", + category: "logic" +}; + +elements.input_pin = { + color: "#DDAA33", + category: "logic", + active: false, + tick: function(pixel) { + pixel.active = false; + var neighbors = getNeighbors(pixel); + for (var i = 0;i < neighbors.length;i++) { + var neighbor = neighbors[i]; + + if (neighbor.charge > 0 || neighbor.lstate == 2 || neighbor.active) { + pixel.active = true; + } + } + } +}; + +elements.output_pin = { + color: "#AAAAAA", + category: "logic", + active: false, + tick: function(pixel) { + var neighbors = getNeighbors(pixel); + for (var i = 0;i < neighbors.length;i++) { + var neighbor = neighbors[i]; + + // Check if it's a wire + if (elements[neighbor.element].conduct > 0 && pixel.active) { + neighbor.charge = 1; + } + + // Check if it's a logic wire (logicgates.js) + if (neighbor.lstate != undefined) { + if (pixel.active) { + neighbor.lstate = 2; + neighbor.color = pixelColorPick(neighbor, "#ffe49c"); + } else { + neighbor.lstate = -2; + neighbor.color = pixelColorPick(neighbor, "#3d4d2c"); + } + } + } + } +}; + +elements.logic_corrupt = { + color: "#DD33DD", + category: "logic", + tool: function(pixel) { + if (pixel.element == "logic_wire") { + if (Math.random() < 0.01) { + if (Math.random() < 0.5) { + pixel.lstate = 2; + pixel.color = pixelColorPick(pixel, "#ffe49c"); + } else { + pixel.lstate = -2; + pixel.color = pixelColorPick(pixel, "#3d4d2c"); + } + } + } + }, + excludeRandom: true, +} + +elements.logic_corrupter_machine = { + color: "#DD33DD", + category: "logic", + tick: function(pixel) { + var radius = 10 + for (var y = pixel.y - radius; y < pixel.y + radius; y++) { + for (var x = pixel.x - radius; x < pixel.x + radius; x++) { + if (!isEmpty(x, y, true)) { + if (pixelMap[x][y].element == "logic_wire" && Math.random() < Math.min(Math.max((pixel.temp + 273) / 473, 0), 1) * 0.005) { + if (Math.random() < 0.5) { + pixelMap[x][y].lstate = 2 + pixelMap[x][y].color = pixelColorPick(pixelMap[x][y], "#ffe49c") + } else { + pixelMap[x][y].lstate = -2 + pixelMap[x][y].color = pixelColorPick(pixelMap[x][y], "#3d4d2c") + } + } + } + } + } + }, +} + +elements.circuitCoreTutorial = { + color: "#33FF66", + category: "logic", + onSelect: function() { + window.open("https://redbirdly.github.io/circuitcore_tutorial.html"); + }, +} + +// cc_ is circuit core prefix +const cc_BROWN = "#773317"; +const cc_RED = "#DD3322"; +const cc_ORANGE = "#DD8833"; +const cc_YELLOW = "#DDCC44"; +const cc_LIME = "#77DD44"; +const cc_GREEN = "#33BB44"; +const cc_BLUE = "#224499"; +const cc_LIGHT_BLUE = "#77CCFF"; +const cc_LAVENDER = "#AA88EE"; +//const cc_PINK = "#DD88DD"; +const cc_WHITE = "#DDDDDD"; + +var circuits = [ + // Misc and I/O: brown + { circuit: elements.four_bit_enabler_circuit, color: cc_BROWN, size: [9, 3, true] }, + { circuit: elements.randomizer, color: cc_BROWN }, + { circuit: elements.four_bit_randomizer_circuit, color: cc_BROWN, size: [9, 3, true] }, + // ROM/RAM: red +// { circuit: elements.ROM_circuit, color: cc_RED, size: [18, 18, false] }, + // Encoders and de-multiplexers: orange + { circuit: elements.two_to_one_encoder_circuit, color: cc_ORANGE, size: [5, 3, true] }, + { circuit: elements.four_to_two_encoder_circuit, color: cc_ORANGE, size: [9, 5, true] }, + { circuit: elements.eight_to_three_encoder_circuit, color: cc_ORANGE, size: [17, 7, true] }, + { circuit: elements.sixteen_to_four_encoder_circuit, color: cc_ORANGE, size: [33, 9, true] }, + + { circuit: elements.one_to_two_demultiplexer_circuit, color: cc_ORANGE, size: [3, 5, true] }, + { circuit: elements.one_to_four_demultiplexer_circuit, color: cc_ORANGE, size: [3, 9, true] }, + { circuit: elements.one_to_eight_demultiplexer_circuit, color: cc_ORANGE, size: [3, 17, true] }, + { circuit: elements.one_to_sixteen_demultiplexer_circuit, color: cc_ORANGE, size: [3, 33, true] }, + // Decoders and multiplexers: yellow + { circuit: elements.one_to_two_decoder_circuit, color: cc_YELLOW, size: [3, 5, true] }, + { circuit: elements.two_to_four_decoder_circuit, color: cc_YELLOW, size: [5, 9, true] }, + { circuit: elements.three_to_eight_decoder_circuit, color: cc_YELLOW, size: [7, 17, true] }, + { circuit: elements.four_to_sixteen_decoder_circuit, color: cc_YELLOW, size: [9, 33, true] }, + + { circuit: elements.two_to_one_multiplexer_circuit, color: cc_YELLOW, size: [3, 5, true] }, + { circuit: elements.four_to_one_multiplexer_circuit, color: cc_YELLOW, size: [5, 9, true] }, + { circuit: elements.eight_to_one_multiplexer_circuit, color: cc_YELLOW, size: [7, 17, true] }, + { circuit: elements.sixteen_to_one_multiplexer_circuit, color: cc_YELLOW, size: [9, 33, true] }, + // Program counter and shift registers: lime + { circuit: elements.four_bit_PISO_shift_register_circuit, color: cc_LIME, size: [9, 5, true] }, + { circuit: elements.four_bit_SIPO_shift_register_circuit, color: cc_LIME, size: [3, 9, true] }, + { circuit: elements.four_bit_program_counter_circuit, color: cc_LIME, size: [9, 5, true] }, + // Registers: green + { circuit: elements.four_bit_register_circuit, color: cc_GREEN, size: [9, 5, true] }, + // Latches and flip flops: light blue + { circuit: elements.SR_latch_circuit, color: cc_LIGHT_BLUE, size: [3, 3, true] }, + { circuit: elements.T_flip_flop_circuit, color: cc_LIGHT_BLUE, size: [3, 3, true] }, + { circuit: elements.D_latch_circuit, color: cc_LIGHT_BLUE, size: [3, 3, true] }, + { circuit: elements.D_flip_flop_circuit, color: cc_LIGHT_BLUE, size: [3, 3, true] }, + { circuit: elements.four_bit_D_latch_circuit, color: cc_LIGHT_BLUE, size: [9, 3, true] }, + { circuit: elements.four_bit_D_flip_flop_circuit, color: cc_LIGHT_BLUE, size: [9, 3, true] }, + // Addition/subtraction arithmetic: blue + { circuit: elements.four_bit_adder_circuit, color: cc_BLUE, size: [17, 3, true] }, + { circuit: elements.four_bit_incrementer_circuit, color: cc_BLUE, size: [9, 3, true] }, + // Complex circuits: lavender + // Clocks: pink + { circuit: elements.slow_clock }, + { circuit: elements.medium_clock }, + { circuit: elements.fast_clock }, + { circuit: elements.very_fast_clock }, + // Displays/visual circuits: white + { circuit: elements.simple_seven_segment_display, color: cc_WHITE, size: [5, 9, false] }, + { circuit: elements.simple_double_seven_segment_display, color: cc_WHITE, size: [9, 9, false] }, +]; + +circuits.forEach(circuitInfo => { + if (circuitInfo.color) {circuitInfo.circuit.color = circuitInfo.color;} + circuitInfo.circuit.category = "logic"; + circuitInfo.circuit.maxSize = 1; + circuitInfo.circuit.isCircuitCore = true; + circuitInfo.circuit.previewSize = circuitInfo.size; +}); + +var circuitRotation = 0; +document.addEventListener('keydown', function(event) { + if (event.key === 'ArrowUp') { + circuitRotation = (circuitRotation + 90) % 360; + logMessage('CircuitRotation changed to ' + circuitRotation); + } +}); + +function drawCircuitExtras() { + if (elements[currentElement].isCircuitCore && elements[currentElement].previewSize) { + var circuitWidth = elements[currentElement].previewSize[0]; + var circuitHeight = elements[currentElement].previewSize[1]; + var centered = elements[currentElement].previewSize[2]; + var rotation = circuitRotation; + + var startX = 0; + var startY = 0; + var endX = circuitWidth - 1; + var endY = circuitHeight - 1; + + if (centered) { + startX = -Math.floor(circuitWidth / 2); + startY = -Math.floor(circuitHeight / 2); + endX = Math.floor(circuitWidth / 2); + endY = Math.floor(circuitHeight / 2); + } + + for (var y = startY; y <= endY; y++) { + for (var x = startX; x <= endX; x++) { +// if (!(0 <= x && x < width && 0 <= y && y < height)) {continue;} + + var [rx, ry] = rotateCoordinate(x, y, rotation); + var px = mousePos.x + rx; + var py = mousePos.y + ry; + + ctx.fillStyle = "rgba(255, 255, 255, 0.1)"; + if ((rotation != 0 && !centered) || (0 <= px && px < width && 0 <= py && py < height) && pixelMap[px][py]) { + ctx.fillStyle = "rgba(255, 0, 0, 0.3)"; + } + + ctx.fillRect(px * pixelSize, py * pixelSize, pixelSize, pixelSize); + } + } + } +} + +runAfterLoad(() => { + var originalDrawPixels3 = drawPixels; + drawPixels = function(forceTick=false) { + originalDrawPixels3(forceTick); + drawCircuitExtras(); + }; +}); +resetInterval(tps); diff --git a/mods/fire_extinguisher.js b/mods/fire_extinguisher.js new file mode 100644 index 00000000..3ad77e03 --- /dev/null +++ b/mods/fire_extinguisher.js @@ -0,0 +1,128 @@ +elements.firefighting_foam = { + color: ["#E6F3FF", "#F0F8FF", "#F8FBFF"], + behavior: [ + "XX|CR:foam%2.5|XX", + "M2 AND CR:foam%2.5|CH:foam%0.2|M2 AND CR:foam%2.5", + "M1|M1|M1", + ], + category: "liquids", + state: "liquid", + density: 0.9, + temp: 10, + conduct: 0.01, + stain: 0.01, + viscosity: 3000, + reactions: { + "greek_fire": { elem2: null, chance: 0.2 }, + "water": { elem1: "foam", elem2: "salt_water", chance: 0.1 }, + "acid": { elem1: "foam", elem2: "neutral_acid", chance: 0.05 }, + "acid_gas": { elem1: "foam", elem2: "neutral_acid", chance: 0.05 }, + }, + tick: function (pixel) { + // Extinguish fire and smoke, remove burning state, and cool down pixels in a 6-pixel radius + let radius = 6 + for (let dx = -radius; dx <= radius; dx++) { + for (let dy = -radius; dy <= radius; dy++) { + let nx = pixel.x + dx; + let ny = pixel.y + dy; + if (nx >= 0 && nx < width && ny >= 0 && ny < height) { + let neighborPixel = pixelMap[nx][ny]; + if (neighborPixel && neighborPixel.element) { + if (neighborPixel.element === "fire" || neighborPixel.element === "smoke") { + deletePixel(nx, ny); + } else { + // Remove burning state + if (neighborPixel.burning) { + neighborPixel.burning = false; + } + + // Cool down the pixel + if (neighborPixel.temp > 10) { + neighborPixel.temp = Math.max(10, neighborPixel.temp - 5); + } + } + } + } + } + } + + // Decay over time + if (Math.random() < 0.005) { + changePixel(pixel, "foam"); + } + if (Math.random() < 0.0002) { + deletePixel(pixel.x, pixel.y); + } + } +}; + +elements.fire_extinguisher = { + color: "#ce2029", + behavior: [ + "XX|XX|XX", + "XX|XX|XX", + "XX|XX|XX" + ], + category: "machines", + state: "solid", + density: 2, + temp: 20, + conduct: 0.1, + tick: function (pixel) { + let shouldExplode = false; + + // Check for fire or smoke within a 10-pixel radius + let checkRadius = 10; + for (let dx = -checkRadius; dx <= checkRadius; dx++) { + for (let dy = -checkRadius; dy <= checkRadius; dy++) { + let nx = pixel.x + dx; + let ny = pixel.y + dy; + if (nx >= 0 && nx < width && ny >= 0 && ny < height) { + let neighborPixel = pixelMap[nx][ny]; + if (neighborPixel && (neighborPixel.element === "fire" || neighborPixel.element === "smoke")) { + shouldExplode = true; + break; + } + } + } + if (shouldExplode) break; + } + + if (shouldExplode) { + // Calculate the size of the fire extinguisher block + let blockSize = 1; + for (let dx = 0; dx < 3; dx++) { + for (let dy = 0; dy < 3; dy++) { + let nx = pixel.x + dx - 1; + let ny = pixel.y + dy - 1; + if (nx >= 0 && nx < width && ny >= 0 && ny < height) { + let neighborPixel = pixelMap[nx][ny]; + if (neighborPixel && neighborPixel.element === "fire_extinguisher") { + blockSize++; + } + } + } + } + + // Calculate explosion radius based on block size + let explosionRadius = Math.max(blockSize * 5, 10); + + // Create a large explosion of foam + for (let dx = -explosionRadius; dx <= explosionRadius; dx++) { + for (let dy = -explosionRadius; dy <= explosionRadius; dy++) { + // Check if the pixel is within the circular radius + if (dx * dx + dy * dy <= explosionRadius * explosionRadius) { + let fx = pixel.x + dx; + let fy = pixel.y + dy; + if (fx >= 0 && fx < width && fy >= 0 && fy < height) { + let targetPixel = pixelMap[fx][fy]; + if (!targetPixel || targetPixel.element === "air") { + createPixel("firefighting_foam", fx, fy); + } + } + } + } + } + } + } +}; \ No newline at end of file diff --git a/mods/littlejohnny.js b/mods/littlejohnny.js new file mode 100644 index 00000000..abdd452e --- /dev/null +++ b/mods/littlejohnny.js @@ -0,0 +1,49 @@ +//made by livvo +//first time coding so bear with me + +elements.ecofriendly_wood_veneer = { + color: "#e3bb86", + behavior: behaviors.WALL, + category: "solids", + state: "solid", + tempHigh: 400, + stateHigh: ["ember","charcoal","fire","fire","fire"], + burn: 5, + burnTime: 300, + burnInto: ["ember","charcoal","fire"], + hardness: 0.15, + breakInto: "sawdust", +}; +elements.screws_borrowed_from_aunt = { + color: "#687281", + behavior: behaviors.POWDER, + category: "powders", + state: "solid", + tempHigh: 1538, + stateHigh: "molten_metal_scrap", + breakInto: "metal_scrap", + hardness: 0.8, + conduct: 0.9, +}; +elements.galvanized_square_steel = { + color: "#4a535c", + behavior: behaviors.WALL, + category: "solids", + state: "solid", + tempHigh: 1200, + stateHigh: "molten_galvanized_square_steel", + conduct: 0.5, +}; +elements.molten_galvanized_square_steel = { + color: ["#a14612", "#b0572a", "b35a12", "#b06310"], + behavior: behaviors.MOLTEN, + category: "states", + state: "molten", + density: 7065, + viscosity: 500, + temp: 1200, + tempLow: 419, + stateLow: "galvanized_square_steel", + conduct: 0.5, + hidden: true +}; \ No newline at end of file diff --git a/mods/logicgates.js b/mods/logicgates.js index c8e01d18..a88458bc 100644 --- a/mods/logicgates.js +++ b/mods/logicgates.js @@ -461,4 +461,40 @@ elements.logic_unshock = { if (pixel.element == "logic_wire"){pixel.lstate = -2; pixel.color = pixelColorPick(pixel, "#3d4d2c")} }, excludeRandom: true, +} +elements.list_all_wifi = { + color: elements.lookup.color, + category: "tools", + tool: function(){}, + excludeRandom: true, + onSelect: function(){ + let results = {} + for (let i in currentPixels){ + var otherPixel = currentPixels[i] + if (["logic_receiver", "logic_transmitter"].includes(otherPixel.element)){ + if (otherPixel.channel){ + if (results[otherPixel.channel]){ + results[otherPixel.channel].push([otherPixel.x, otherPixel.y]) + } else { + results[otherPixel.channel] = [[otherPixel.x, otherPixel.y]] + } + } + } + } + console.log(results) + let keys = Object.keys(results) + let ans1 = prompt(keys.length + " unique channels have been found. Type 1 to list them all.", 1) + if(ans1 == "1"){ + ans2 = prompt("["+keys +"]"+ " These are all the channels found. Type the name of one of them to see the positions of all pixels with that channel.", keys[0]) + if (keys.includes(ans2)){ + let finalString = "" + for (i in results[ans2]){ + finalString += ", [" + finalString += results[ans2][i] + finalString += "]" + } + alert(finalString) + } + } + } } \ No newline at end of file diff --git a/mods/minecraft.js b/mods/minecraft.js index ec64d42e..82c4f9c0 100644 --- a/mods/minecraft.js +++ b/mods/minecraft.js @@ -1,116 +1,118 @@ elements.endstone = { -color: ["#e5edc2", "#bec797"], -behavior: behaviors.WALL, -category: "minecraft", -state: "solid", -stateHigh: "molten_endstone", -tempHigh: 1265 + color: ["#e5edc2", "#bec797"], + behavior: behaviors.WALL, + category: "minecraft", + state: "solid", + stateHigh: "molten_endstone", + tempHigh: 1265 }; elements.molten_endstone = { -color: ["#6615d6", "#9651f5", "#d3c3eb"], -behavior: behaviors.LIQUID, -category: "minecraft", -state: "liquid", -stateLow: "endstone", -tempLow: 1265, -temp: 1500, -viscosity: 1000, -density: 1025 + color: ["#6615d6", "#9651f5", "#d3c3eb"], + behavior: behaviors.LIQUID, + category: "minecraft", + state: "liquid", + stateLow: "endstone", + tempLow: 1265, + temp: 1500, + viscosity: 1000, + density: 1025 }; elements.netherrack = { -color: ["#8c2a0a", "#783722"], -behavior: behaviors.WALL, -category: "minecraft", -state: "solid", -tempHigh: 2750, -stateHigh: "molten_netherrack", + color: ["#8c2a0a", "#783722"], + behavior: behaviors.WALL, + category: "minecraft", + state: "solid", + tempHigh: 2750, + stateHigh: "molten_netherrack", }; elements.glowstone_dust = { -color: ["#d9d636", "#fffc63", "#a3a12f", "#e0dd3f"], -behavior: behaviors.POWDER, -category: "minecraft", -state: "solid", -tempHigh: 2500, -stateHigh: "molten_glowstone", -conduct: 0.975, -density: 1075 + color: ["#d9d636", "#fffc63", "#a3a12f", "#e0dd3f"], + behavior: behaviors.POWDER, + category: "minecraft", + state: "solid", + tempHigh: 2500, + stateHigh: "molten_glowstone", + conduct: 0.975, + density: 1075 }; elements.molten_netherrack = { -name: "Nether Magma", -color: ["#f7f09c", "#faf9eb", "#ffffff", "#dcf7f7", "#86dbdb", "#1268a6"], -behavior: behaviors.LIQUID, -category: "minecraft", -state: "liquid", -tempLow: 2750, -stateLow: "netherrack", -temp: 3000, -viscosity: 1000, -density: 2305 + name: "Nether Magma", + color: ["#f7f09c", "#faf9eb", "#ffffff", "#dcf7f7", "#86dbdb", "#1268a6"], + behavior: behaviors.LIQUID, + category: "minecraft", + state: "liquid", + tempLow: 2750, + stateLow: "netherrack", + temp: 3000, + viscosity: 1000, + density: 2305 }; elements.redstone_dust = { -color: ["#bf2424", "#f22424", "#a12020"], -behavior: behaviors.POWDER, -category: "minecraft", -state: "solid", -tempHigh: 1275, -stateHigh: "liquid_redstone", -density: 1250 + color: ["#bf2424", "#f22424", "#a12020"], + behavior: behaviors.POWDER, + category: "minecraft", + state: "solid", + tempHigh: 1275, + stateHigh: "liquid_redstone", + density: 1250 }; +/* behaviors.SOUL_SAND = [ -"SA|SA|SA", -"XX|XX|XX", -"SA AND M2|SA AND M1|SA AND M2", -]; + "SA|SA|SA", + "XX|XX|XX", + "SA AND M2|SA AND M1|SA AND M2", + ]; + */ elements.soul_sand = { -color: ["#91704d", "#704e2b", "#523517"], -behavior: behaviors.SOUL_SAND, -category: "minecraft", -state: "solid", -tempHigh: 2575, -stateHigh: "soul_glass", -density: 1375, -reactions: { -"water": { "elem1":"wet_soul_sand", "elem2":null }, -} + color: ["#91704d", "#704e2b", "#523517"], + behavior: behaviors.SUPPORTPOWDER, + category: "minecraft", + state: "solid", + tempHigh: 2575, + stateHigh: "soul_glass", + density: 1375, + reactions: { + "water": { "elem1":"wet_soul_sand", "elem2":null }, + } }; elements.soul_soil = { -color: ["#80664b", "#6e553b", "#5c452e"], -behavior: behaviors.SOUL_SAND, -category: "minecraft", -state: "solid", -tempHigh: 2565, -stateHigh: "soul_glass", -density: 1450, -reactions: { -"water": { "elem1":"soul_mud", "elem2":null }, -} + color: ["#80664b", "#6e553b", "#5c452e"], + behavior: behaviors.SUPPORTPOWDER, + category: "minecraft", + state: "solid", + tempHigh: 2565, + stateHigh: "soul_glass", + density: 1450, + reactions: { + "water": { "elem1":"soul_mud", "elem2":null }, + } }; elements.soul_mud = { -color: "#6e5437", -behavior: behaviors.POWDER, -category: "minecraft", -state: "solid", -tempHigh: 1985, -stateHigh: "soul_adobe", -density: 1585 + color: "#6e5437", + behavior: behaviors.POWDER, + category: "minecraft", + state: "solid", + tempHigh: 1985, + stateHigh: "soul_adobe", + density: 1585 }; elements.soul_glass = { -color: "#7a90c2", -behavior: behaviors.WALL, -category: "minecraft", -state: "solid", -tempHigh: 2585, -stateHigh: "molten_soul_glass", -density: 1685 + color: "#7a90c2", + behavior: behaviors.WALL, + category: "minecraft", + state: "solid", + tempHigh: 2585, + stateHigh: "molten_soul_glass", + density: 1685 }; elements.wet_soul_sand = { -color: ["#5c452e", "#856d56"], -behavior: behaviors.POWDER, -category: "minecraft", -state: "solid", -tempHigh: 150, -stateHigh: "soul_sand", -density: 1660, + color: ["#5c452e", "#856d56"], + behavior: behaviors.POWDER, + category: "minecraft", + state: "solid", + tempHigh: 150, + stateHigh: "soul_sand", + density: 1660, }; /* unfinished but near: Soul Adobe, @@ -124,3 +126,521 @@ Liquid Redstone, Soul Flame, Soul */ +let wardenMurderList = ["head", "body", "frog", "worm", "fly", "flea", "fish", "bird", "bee"] +function playSculk() { + var audio = new Audio("https://JustAGenericUsername.github.io/sculk" + Math.floor((Math.random()*6) +1) + ".ogg"); + audio.play(); +} +function playShriek() { + var audio = new Audio("https://JustAGenericUsername.github.io/shriek" + Math.floor((Math.random()*4) +1) + ".ogg"); + audio.play(); +} +function turnIntoSpecific(pixel){ + newPixel = pixel.storedPixel + newPixel.x = pixel.x; + newPixel.y = pixel.y; + delete newPixel.del; + delete newPixel.sculkSpread; + delete newPixel.rSeed; + let tempPixel = newPixel; + pixelMap[pixel.x][pixel.y] = tempPixel + currentPixels = currentPixels.filter(a => a.x != pixel.x || a.y != pixel.y); currentPixels.push((tempPixel)) +} +function findAboveNot(pixel, element){ + for (let i = pixel.y; i <= (0-height); i--){ + if (isEmpty(pixelMap[pixel.x][i], true) && element != "air"){ + return {distance: i, element: "air"} + } else if (!(pixelMap[pixel.x][i].element != element)){ + return {distance: i, element: pixelMap[pixel.x][i].element} + } + } + return {distance: height, element: "edge"} +} +function findAboveIs(pixel, element){ + for (let i = pixel.y; i <= (0-height); i--){ + if (isEmpty(pixelMap[pixel.x][i], true) && element == "air"){ + return {distance: i, element: "air"} + } else if (!(pixelMap[pixel.x][i].element == element)){ + return {distance: i, element: element} + } + } + return false +} +let canPlaySculk = 0 +let wardenCooldown = 0 +let totalSculk = { + sculk: 0, + wardens: 0, + sensors: 0, + shriekers: 0 +} +elements.sculk = { + color: ["#0c1a22", "#0a171d", "#071319", "#030f14", "#000a0f"], + behavior: behaviors.SUPPORT, + density: 1500, + category: "minecraft", + tempHigh: 250, + stateHigh: "experience", + tick: function(pixel){ + if (!pixel.type){pixel.type = "sculk"} + if (!pixel.rSeed && pixel.type == "sculk"){pixel.rSeed = [Math.random(), Math.random()]} + if (!pixel.rSeed && pixel.type == "sensor"){ + pixel.rSeed = true + pixel.color = pixelColorPick(pixel, "#112b4d") + } + if (!pixel.rSeed && pixel.type == "shrieker"){ + pixel.rSeed = true + pixel.color = pixelColorPick(pixel, "#dad6b8") + } + if (pixel.type == "sculk" && pixel.rSeed[0] < 0.05){ + pixel.color = "rgb(40, " + (12*Math.sin((0.05*pixelTicks)+(pixel.rSeed[1]*100)) + 201) + ", " + (46*Math.sin((0.1*pixelTicks)+(pixel.rSeed[1]*100))+201) + ")"; + } + if (pixel.type == "sculk"){ + if (!pixel.storedPixel){pixel.storedPixel = {element: "dirt", color: pixelColorPick({element: "dirt"}), start: pixel.start, temp: pixel.temp, x: pixel.x, y: pixel.y}} + if (!pixel.sculkSpread){pixel.sculkSpread = 0} + if (Math.random()<0.0001){ pixel.sculkSpread ++} + for (var i = 0; i < squareCoords.length; i++) { + var x = pixel.x+squareCoords[i][0]; + var y = pixel.y+squareCoords[i][1]; + // higher penalty for top/bottom coords, lower for side coords + let penalty = 0.9994 + for (var j = 0; j < squareCoords.length; j++) { + var jx = x+squareCoords[j][0]; + var jy = y+squareCoords[j][1]; + if (isEmpty(jx,jy,true)) {penalty = 0.8} + } + if (!isEmpty(x,y,true)) { + var newPixel = pixelMap[x][y]; + if (newPixel.element == "sculk" && newPixel.type == "sculk"){ + if (pixel.sculkSpread > newPixel.sculkSpread){ + newPixel.sculkSpread ++ + pixel.sculkSpread -- + } + } else if (!(["fire", "radiation", "uranium", "plasma", "bless", "god_ray", + "experience", "meat", "ash", "cooked_meat", "rotten_meat", "frozen_meat", + "cured_meat", "dead_plant", "frozen_plant", "warden", "warden_head", "warden_body", + "sculk"].includes(newPixel.element) || wardenMurderList.includes(newPixel.element))){ + if (Math.random() > (elements[newPixel.element].hardness || 0.2) && Math.random() > penalty && pixel.sculkSpread > 0 && pixel.temp < 80){ + pixel.sculkSpread -- + let pixelStorage = newPixel + deletePixel(x, y) + createPixel("sculk", x, y) + pixelMap[x][y].storedPixel = pixelStorage + } + } else if (["meat", "ash", "cooked_meat", "rotten_meat", "frozen_meat", "cured_meat", + "dead_plant", "frozen_plant", "blood", "cell", "cancer"].includes(newPixel.element) && pixel.temp < 80){ + deletePixel(x, y) + pixel.sculkSpread += 3 + } + } + } + } + if (pixel.type == "sensor"){ + let cCoords = circleCoords(pixel.x, pixel.y, 8) + for (var i = 0; i < cCoords.length; i++) { + var x = cCoords[i].x + var y = cCoords[i].y + if (!isEmpty(x, y, true)){ + if (wardenMurderList.includes(pixelMap[x][y].element)){ + if (canPlaySculk == 0){playSculk(); canPlaySculk = 4*30} + } + } + } + + } + if (pixel.type == "shrieker"){ + if (canPlaySculk >= 3.7*30 && wardenCooldown == 0){ + if (isEmpty(pixel.x-1, pixel.y-2, true)){ + createPixel("warden", pixel.x-1, pixel.y-2) + playShriek() + wardenCooldown = 15*30 + } + } + } + let nAirDist = findAboveNot(pixel, "air") + if (nAirDist){ + if (Math.random() < 0.001 && pixel.sculkSpread >= 2 && nAirDist.distance >= 6 && (nAirDist.element == "air"||nAirDist.element == "edge") && totalSculk.sensors < 10 && pixel.type == "sculk"){ + if (isEmpty(pixel.x, pixel.y-1, true)){ + createPixel("sculk", pixel.x, pixel.y-1) + pixelMap[pixel.x][pixel.y-1].type = "sensor" + pixel.sculkSpread -= 2 + } + } + else if (Math.random() < 0.0005 && pixel.sculkSpread >= 4 && nAirDist.distance >= 6 && (nAirDist.element == "air"||nAirDist.element == "edge") && totalSculk.shriekers < 4 && pixel.type == "sculk"){ + if (isEmpty(pixel.x, pixel.y-1, true)){ + createPixel("sculk", pixel.x, pixel.y-1) + pixelMap[pixel.x][pixel.y-1].type = "shrieker" + pixel.sculkSpread -= 4 + } + } + } + }, + breakInto: "experience", +} +const randgrid1 = Array.from({ length: Math.pow(12, 2) }, () => Math.random() * 2 * Math.PI); +const randgrid2 = Array.from({ length: Math.pow(12, 2) }, () => Math.random() * 2 * Math.PI); +function xpNoise(weight){ + const length = randgrid1.length; + const weightedGrid = new Array(length); + + for (let i = 0; i < length; i++) { + weightedGrid[i] = weightedAverage(randgrid1[i], randgrid2[i], weight); + } + + return weightedGrid; +} +function weightedAverage(num1, num2, weight){ + return ((weight * num1)+((1-weight)*num2)) +} +function getScalingFactor(d1, d2){ + return Math.min(d1, d2)/6 +} +function badNoise(C, x, y) { + // Helper function to calculate dot product + function dotProduct(a, b) { + return a[0] * b[0] + a[1] * b[1]; + } + + // Function d + function d(index) { + return [Math.cos(C[index]), Math.sin(C[index])]; + } + + // Function f + function f(x, y, t) { + return x + (y - x) * (6 * t ** 5 - 15 * t ** 4 + 10 * t ** 3); + } + + // Function g + function g(x, y, z, w) { + const dx = x - z; + const dy = y - w; + return dotProduct([dx, dy], d(z + w * 6 + 1)); + } + + // Main function h + const x0 = Math.floor(x); + const x1 = Math.ceil(x); + const y0 = Math.floor(y); + const y1 = Math.ceil(y); + + const g00 = g(x, y, x0, y0); + const g10 = g(x, y, x1, y0); + const g01 = g(x, y, x0, y1); + const g11 = g(x, y, x1, y1); + + const fx = f(g00, g10, x - x0); + const fy = f(g01, g11, x - x0); + + return Math.sqrt(2) * f(fx, fy, y - y0); +} +elements.experience = { + color: ["#e0fc2c", "#c2f62b", "#a3f02f", "#83e935", "#5ee13d"], + behavior: behaviors.SUPERFLUID, + density: 1600, + category: "minecraft", + tick: function(pixel){ + /* + if (typeof pixel.rSeed != "number"){delete pixel.rSeed} + if (!pixel.rSeed){pixel.rSeed = Math.random()} + */ + let scaling = getScalingFactor(width, height) + pixel.rSeed = badNoise(xpNoise(Math.sin(pixelTicks/30)), pixel.x/scaling, pixel.y/scaling) + pixel.color = "rgb(" + 70*(Math.sin((4*Math.PI)*pixelTicks/70 + 3*pixel.rSeed)+2.5) + ", 235, 80)" + if (typeof pixel.storedPixel != "undefined" && Math.random() < 0.8){ + deletePixel(pixel.x, pixel.y) + turnIntoSpecific(pixel) + return; + } else if (pixel.storedPixel){delete pixel.storedPixel} + /* // average rseed of neighbors + for (var i = 0; i < adjacentCoords.length; i++) { + var x = pixel.x + adjacentCoords[i][0] + var y = pixel.y + adjacentCoords[i][1] + if (!isEmpty(x,y,true)){ + let otherPixel = pixelMap[x][y] + if (otherPixel.element == "experience"){ + let sum1 = weightedAverage((pixel.rSeed || Math.random()), (otherPixel.rSeed || Math.random()), 0.95) + let sum2 = weightedAverage((pixel.rSeed || Math.random()), (otherPixel.rSeed || Math.random()), 0.05) + pixel.rSeed = sum1 + otherPixel.rSeed = sum2 + pixel.rSeed += (Math.random()/100)-0.005 + otherPixel.rSeed += (Math.random()/100)-0.005 + } + } + } + */ + }, + glow: true, +} + if (!elements.bless.reactions){elements.bless.reactions = {}} + elements.bless.reactions.sculk = {func: turnIntoSpecific, chance: 0.01} +elements.warden = { + color: "#09273f", + category: "minecraft", + properties: { + dead: false, + dir: 1, + }, + tick: function(pixel) { + if (isEmpty(pixel.x, pixel.y+1)) { + createPixel("warden_body", pixel.x, pixel.y+1); + pixel.element = "warden_head"; + } + else if (isEmpty(pixel.x, pixel.y-1)) { + createPixel("warden_head", pixel.x, pixel.y-1); + pixelMap[pixel.x][pixel.y-1].color = pixel.color; + pixel.element = "warden_body"; + pixel.color = pixelColorPick(pixel) + } + else { + deletePixel(pixel.x, pixel.y); + } + }, + related: ["warden_body","warden_head"], + cooldown: defaultCooldown, +} +elements.warden_body = { + color: "#030b16", + category: "minecraft", + hidden: true, + density: 1500, + state: "solid", + conduct: .05, + properties: { + dead: false, + dir: 1, + panic: 0 + }, + tick: function(pixel) { + age = pixelTicks - pixel.start; + if (age > 15*30){ + deletePixel(pixel.x, pixel.y); + } + if (tryMove(pixel, pixel.x, pixel.y+1)) { // Fall + if (!isEmpty(pixel.x, pixel.y-2, true)) { // Drag head down + var headpixel = pixelMap[pixel.x][pixel.y-2]; + if (headpixel.element == "head") { + if (isEmpty(pixel.x, pixel.y-1)) { + movePixel(pixelMap[pixel.x][pixel.y-2], pixel.x, pixel.y-1); + } + else { + swapPixels(pixelMap[pixel.x][pixel.y-2], pixelMap[pixel.x][pixel.y-1]); + } + } + } + } + doHeat(pixel); + doBurning(pixel); + doElectricity(pixel); + if (pixel.dead) { + // // Turn into rotten_meat if pixelTicks-dead > 500 + if (pixelTicks-pixel.dead > 200 && Math.random() < 0.1) { + // changePixel(pixel,"rotten_meat"); + // } + deletePixel(pixel.x, pixel.y); + return + } + } + + // Find the head + if (!isEmpty(pixel.x, pixel.y-1, true) && pixelMap[pixel.x][pixel.y-1].element == "warden_head") { + var head = pixelMap[pixel.x][pixel.y-1]; + if (head.dead) { // If head is dead, kill body + pixel.dead = head.dead; + } + } + else { var head = null } + if (pixel.burning) { + pixel.panic += 0.1; + if (head && pixelTicks-pixel.burnStart > 240) { + pixel.color = head.color; + } + } + else if (pixel.panic > 0) { + pixel.panic -= 0.1; + } + + if (isEmpty(pixel.x, pixel.y-1)) { + // create blood if decapitated 10% chance + /// if (Math.random() < 0.1 && !pixel.charge) { + // createPixel("blood", pixel.x, pixel.y-1); + // set dead to true 15% chance + // if (Math.random() < 0.15) { + pixel.dead = pixelTicks; + // } + // } + } + else if (head == null) { return } + else if (Math.random() < 0.1*(isEmpty(pixel.x, pixel.y+1) ? 1 : pixel.panic+1)) { // Move 10% chance + var movesToTry = [ + [1*pixel.dir,0], + [1*pixel.dir,-1], + ]; + // While movesToTry is not empty, tryMove(pixel, x, y) with a random move, then remove it. if tryMove returns true, break. + while (movesToTry.length > 0) { + var move = movesToTry.splice(Math.floor(Math.random() * movesToTry.length), 1)[0]; + if (isEmpty(pixel.x+move[0], pixel.y+move[1]-1)) { + var origx = pixel.x+move[0]; + var origy = pixel.y+move[1]; + if (tryMove(pixel, pixel.x+move[0], pixel.y+move[1]) && pixel.x===origx && pixel.y===origy) { + movePixel(head, head.x+move[0], head.y+move[1]); + break; + } + } + } + // 15% chance to change direction + if (Math.random() < 0.15) { + pixel.dir *= -1; + } + // homeostasis + if (pixel.temp > 37) { pixel.temp -= 1; } + else if (pixel.temp < 37) { pixel.temp += 1; } + } + + }, + reactions: {} +}, +elements.warden_head = { + color: "#145377", + category: "minecraft", + hidden: true, + density: 1080, + state: "solid", + conduct: .05, + properties: { + dead: false + }, + tick: function(pixel) { + age = pixelTicks - pixel.start; + if (age > 15*30){ + deletePixel(pixel.x, pixel.y); + } + doHeat(pixel); + doBurning(pixel); + doElectricity(pixel); + if (pixel.dead) { + // Turn into rotten_meat if pixelTicks-dead > 500 + if (pixelTicks-pixel.dead > 200 && Math.random() < 0.1) { + // changePixel(pixel,"rotten_meat"); + deletePixel(pixel.x, pixel.y); + return + } + } + + // Find the body + if (!isEmpty(pixel.x, pixel.y+1, true) && pixelMap[pixel.x][pixel.y+1].element == "warden_body") { + var body = pixelMap[pixel.x][pixel.y+1]; + if (body.dead) { // If body is dead, kill head + pixel.dead = body.dead; + } + } + else { var body = null; pixel.dead = pixelTicks } + + if (tryMove(pixel, pixel.x, pixel.y+1)) { + // create blood if severed 10% chance + if (isEmpty(pixel.x, pixel.y+1) && !pixel.dead && Math.random() < 0.1 && !pixel.charge) { + // createPixel("blood", pixel.x, pixel.y+1); + // set dead to true 15% chance + // if (Math.random() < 0.15) { + pixel.dead = pixelTicks; + } + // } + } + // homeostasis + if (pixel.temp > 37) { pixel.temp -= 1; } + else if (pixel.temp < 37) { pixel.temp += 1; } + }, + reactions: {} +} +for (i = 0; i < wardenMurderList.length; i++){ + let e = wardenMurderList[i] + elements.warden_head.reactions[e] = {elem2: "meat", chance: 0.5} + elements.warden_body.reactions[e] = {elem2: "meat", chance: 0.5} +} +setInterval(function(){ + if (canPlaySculk > 0){ + canPlaySculk -- + } + if (typeof currentPixels != "undefined"){ + totalSculk = { + sculk: 0, + wardens: 0, + sensors: 0, + shriekers: 0 + } + for (let i in currentPixels){ + let pixel = currentPixels[i] + if (pixel.element == "warden_body"){ + totalSculk.wardens ++ + } else if (pixel.element == "sculk"){ + if (pixel.type == "sculk"){ + totalSculk.sculk ++ + } else if (pixel.type == "sensor"){ + totalSculk.sensors ++ + } else if (pixel.type == "shrieker"){ + totalSculk.shriekers ++ + } + } + } + } + if (wardenCooldown > 0){ + wardenCooldown -- + } +}, 1000/30) +let channelVar = "0" +elements.sculk_wifi_transmitter = { + color: "#142c47", + category: "minecraft", + behaviors: behaviors.WALL, + tempHigh: 250, + stateHigh: "dirt", + hoverStat: function(pixel){ + return pixel.channel || "unset" + }, + onSelect: function(){ + let ans1 = prompt("What channel should this transmitter be? Wont work if you do multiple while paused. (This is meant to be used in machinery!)", channelVar||0) + channelVar = ans1 + }, + tick: function(pixel){ + if (!pixel.channel){pixel.channel = channelVar} + for (let i in currentPixels){ + let otherPixel = currentPixels[i] + if (otherPixel.element == "sculk_wifi_receiver" && otherPixel.channel == pixel.channel){ + for (var j = 0; j < adjacentCoords.length; j++){ + let coord = adjacentCoords[j] + let x = otherPixel.x + coord[0] + let y = otherPixel.y + coord[1] + if (!isEmpty(x, y, true)){ + let neighborPixel = pixelMap[x][y] + if (elements[neighborPixel.element].conduct){ + neighborPixel.charge = pixel.charge + } + } + } + } + } + if (pixel.charge && canPlaySculk <= 0){ + canPlaySculk = 25 + playSculk() + } + doDefaults(pixel) + }, + conduct: 1 +} +elements.sculk_wifi_receiver = { + color: "#142c47", + category: "minecraft", + behaviors: behaviors.WALL, + tempHigh: 250, + stateHigh: "dirt", + hoverStat: function(pixel){ + return pixel.channel || "unset" + }, + onSelect: function(){ + let ans1 = prompt("What channel should this receiver be? Wont work if you do multiple while paused. (This is meant to be used in machinery!)", channelVar||0) + channelVar = ans1 + }, + tick: function(pixel){ + if (!pixel.channel){pixel.channel = channelVar} + } +} \ No newline at end of file diff --git a/mods/nicer_flame.js b/mods/nicer_flame.js index a988a719..d69c8c50 100644 --- a/mods/nicer_flame.js +++ b/mods/nicer_flame.js @@ -1,7 +1,10 @@ -// Mod that makes fire look better with dark red at the top of the flame +// RedBirdly's mod that makes fire look better with dark red at the top of the flame -let topColor = 'rgb(130, 0, 0)'; -let blending = 0.7; +var topColor = 'rgb(130, 0, 10)'; +var blending = 0.9; + +var topColdFireColor = 'rgb(30, 10, 110)'; +var coldFireBlending = 0.9; function cssColorToRGB(color) { let rgbMatch = color.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/); @@ -24,8 +27,22 @@ elements.fire.tick = function(pixel) { // Call the original tick function originalFireTick(pixel); - if (Math.random()<0.1) { + if (Math.random()<0.4) { let originalColor = pixel.color; pixel.color = blendColors(originalColor, topColor, blending); } }; + +let originalColdFireTick = elements.cold_fire.tick; +elements.cold_fire.tick = function(pixel) { + // Call the original tick function + originalColdFireTick(pixel); + + if (Math.random()<0.4) { + let originalColor = pixel.color; + pixel.color = blendColors(originalColor, topColdFireColor, coldFireBlending); + } +}; + +elements.fire.color = ["#ffcb31","#ffab21","#ff9600"]; +elements.cold_fire.color = ["#11ddff","#2288dd"]; diff --git a/mods/periodic_and_more.js b/mods/periodic_and_more.js new file mode 100644 index 00000000..d032bcee --- /dev/null +++ b/mods/periodic_and_more.js @@ -0,0 +1,64 @@ + +elements.beryllium = { + color: "#b3b3b3", + behavior: behaviors.WALL, + category: "solids", + viscosity: 100000, + state: "solid", + density: 720, + reactions: { + water: {elem1: "beryllium", elem2: "dirty_water"} + } +}; + + elements.galinstan = { + color: "#a9a9a9", + behavior: behaviors.LIQUID, + category: "liquids", + viscosity: 26, + state: "liquid", + density: 0.026 + }; + + +// Adding behavior presets: +behaviors.SELFDELETE = [ + "XX|XX|XX", + "XX|DL|XX", + "XX|XX|XX", +]; + +// Raw JavaScript behaviors: +behaviors.mud.tick = function(pixel) { + if (tryMove(pixel, pixel.x, pixel.y+1)) { + console.log("Moved!"); + } + else { + console.log("Couldn't move!") + } +}; + +elements.sand_exploder = { + color: "#ff0000", + tool: function(pixel) { + if (pixel.element == "sand") { + pixel.element = "explosion" + } + }, + category: "tools", +}; + +if (!elements.water.reactions) { // Include this block once + elements.water.reactions = {} // This creates the property if it doesn't exist +} +elements.water.reactions.mayo = { "elem1":null, "elem2":"mayo_water" } +elements.water.reactions.soap = { "elem1":null, "elem2":"soapy_water" } + +// Run after all mods are loaded, for cross-mod compatibility +runAfterLoad(function() { + + console.log("Hello World!"); +}); + +// Creating eLists: +eLists.PERIODIC = ["beryllium", "scandium", "vanadium", "manganese", "cobalt"] diff --git a/mods/stormlib.js b/mods/stormlib.js new file mode 100644 index 00000000..83bd2731 --- /dev/null +++ b/mods/stormlib.js @@ -0,0 +1,99 @@ +class stormlibRandom { + int(min, max) { + return Math.floor(Math.random() * (max - min + 1)) + min + } + + max(max) { + let treturn = Math.floor(Math.random() * max) + if (treturn !== 0) { + return treturn +1 + } else { + return treturn + } + } + + option(array) { + if (array.length < 1) return null; + return array[this.int(0, array.length -1)] + } + + string(length = 8, disallowed = ['☭']) { + let chars = [ + 'a', 'A', 'b', 'B', 'c', 'C', 'd', 'D', 'e', 'E', 'f', 'F', 'g', 'G', + 'h', 'H', 'i', 'I', 'j', 'J', 'k', 'K', 'l', 'L', 'm', 'M', 'n', 'N', + 'o', 'O', 'p', 'P', 'q', 'Q', 'r', 'R', 's', 'S', 't', 'T', 'u', 'U', + 'v', 'V', 'w', 'W', 'x', 'X', 'y', 'Y', 'z', 'Z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '-', '_', '=', '+', + '[', ']', '{', '}', '\\', '|', ';', ':', '\'', '"', ',', '.', '/', '<', + '>', '?', '`', '~' + ] + + let str = '' + + while (str.length < length) { + let char = this.option(chars) + if (!disallowed.includes(char)) { + str += char + } + } + + return str + } +} + +class stormlibElement { + exists(name) { + return elements.hasOwnProperty(name) + } +} + +class stormlibColor { + lighten(hex, percent = 10) { + let rgb = hexToRGB(hex) + if (rgb == null) return '#ffffff'; + let r = rgb.r + let g = rgb.g + let b = rgb.b + + r = Math.min(255, Math.floor(r + (255 - r) * (percent / 100))); + g = Math.min(255, Math.floor(g + (255 - g) * (percent / 100))); + b = Math.min(255, Math.floor(b + (255 - b) * (percent / 100))); + + return RGBToHex({r: r, g: g, b: b}) + } + + darken(hex, percent = 10) { + let rgb = hexToRGB(hex) + if (rgb == null) return '#000000'; + let r = rgb.r + let g = rgb.g + let b = rgb.b + + r = Math.max(0, Math.floor(r * (1 - percent / 100))); + g = Math.max(0, Math.floor(g * (1 - percent / 100))); + b = Math.max(0, Math.floor(b * (1 - percent / 100))); + + return RGBToHex({r: r, g: g, b: b}) + } +} + +class stormlibPixels { + create(x, y, color) { + let canvas = document.getElementById('game') + if (x < canvas.width && y < canvas.height) { + let old = ctx.fillStyle + ctx.fillStyle = color + ctx.fillRect(x, y, pixelSize, pixelSize) + } + } +} + +let Elements = new stormlibElement +let Random = new stormlibRandom +let Color = new stormlibColor +let Pixels = new stormlibPixels + +setInterval(() => { + Pixels.create(85, 45, '#ff0000') +}, 1000/tps) \ No newline at end of file diff --git a/mods/wifi_draw.js b/mods/wifi_draw.js index 4590ccaa..bd20c247 100644 --- a/mods/wifi_draw.js +++ b/mods/wifi_draw.js @@ -75,3 +75,4 @@ drawPixels = function(forceTick=false) { updateLogicLists(); drawLinks(); }; +resetInterval(tps);