diff --git a/mod-list.html b/mod-list.html
index 78200bde..1c26c2cf 100644
--- a/mod-list.html
+++ b/mod-list.html
@@ -194,6 +194,7 @@
| Science & Chemistry |
| alcohol.js | Methanol, (iso-)propanol, and butanol | Alice |
| alkahest.js | The alkahest, a liquid which dissolves anything | Alice |
+| alkali_metal.js | The missing alkali metals. (Inspired by nobegas.js and halogen.js) | Cube14yt |
| aScientistsWish.js | Several things related to science and physics | Carbon Monoxide, salmonfishy |
| bettermetalscrap.js | Metal scrap can be melted back into its original material | nousernamefound |
| bigger_star_spawners.js | Spawners for larger stars | Alice |
diff --git a/mods/alkali_metal.js b/mods/alkali_metal.js
new file mode 100644
index 00000000..88fa3524
--- /dev/null
+++ b/mods/alkali_metal.js
@@ -0,0 +1,308 @@
+// Inspiration from halogen.js
+// --- Halogens and water-containing liquids ---
+var halogens = [
+ "fluorine", "liquid_fluorine", "fluorine_ice",
+ "chlorine", "liquid_chlorine", "chlorine_ice",
+ "bromine_ice", "bromine", "bromine_gas",
+ "iodine", "liquid_iodine", "iodine_gas",
+ "astatine", "molten_astatine", "astatine_gas",
+ "tennessine"
+];
+
+var water_containing = [
+ "water", "dirty_water", "salt_water", "sugar_water",
+ "seltzer", "pool_water", "primordial_soup", "nut_milk"
+];
+
+// --- Alkali metal hydroxide densities (kg/m³) ---
+const hydroxideDensities = { li: 1460, na: 2130, k: 2120, rb: 2350, cs: 2200, fr: 2400 };
+
+// --- Normal alkali reaction with halogens ---
+function normalAlkali(pixel, reactto = "pop") {
+ for (let i = 0; i < adjacentCoords.length; i++) {
+ const x = pixel.x + adjacentCoords[i][0];
+ const y = pixel.y + adjacentCoords[i][1];
+ const otherPixel = getPixel(x, y);
+ if (!isEmpty(x, y, true) && halogens.includes(otherPixel.element)) {
+ if (Math.random() <= 0.1) {
+ changePixel(pixel, "salt");
+ changePixel(otherPixel, reactto);
+ }
+ }
+ }
+}
+
+
+function waterReaction(pixel, options = {}) {
+ const { tempIncrease = 100, reactionChance = 0.05, effectType = "pop" } = options;
+
+ if (!pixel) return;
+
+ for (let i = 0; i < adjacentCoords.length; i++) {
+ const x = pixel.x + adjacentCoords[i][0];
+ const y = pixel.y + adjacentCoords[i][1];
+ const otherPixel = getPixel(x, y);
+
+ if (!otherPixel) continue;
+ if (!isEmpty(x, y, true) && water_containing.includes(otherPixel.element)) {
+ if (Math.random() <= reactionChance) {
+ const rand = Math.random();
+
+ if (rand <= 0.33 && pixel && (pixel.element !== "explosion" || pixel.element !== "pop")) {
+ if (effectType === "explosion") {
+ deletePixel(pixel.x, pixel.y)
+ explodeAt(pixel.x, pixel.y, 10)
+ } else {
+ changePixel(pixel, effectType);
+ }
+
+ } else if (rand <= 0.66) {
+ // form hydroxide
+ const hydroxide = pixel.symbol + "oh";
+ if (!elements[hydroxide]) {
+ addElement(hydroxide, {
+ color: "#dfefff",
+ category: "compounds",
+ behavior: behaviors.POWDER,
+ state: "solid",
+ density: hydroxideDensities[pixel.symbol] || 1500,
+ tempHigh: 500,
+ reactions: {
+ "acid": { elem1: "water", elem2: "salt" }
+ }
+ });
+ }
+ changePixel(pixel, hydroxide);
+ deletePixel(otherPixel.x, otherPixel.y);
+
+ } else {
+ // produce hydrogen gas
+ createPixel("hydrogen", otherPixel.x, otherPixel.y);
+ }
+
+ pixel.temp += tempIncrease;
+ }
+ }
+ }
+}
+
+
+// Solid lithium
+elements.lithium = {
+ color: "#bfbfbf",
+ state: "solid",
+ behavior: behaviors.POWDER,
+ category: "alkali",
+ tempHigh: 180.5,
+ density: 533,
+ conduct: 0.42,
+ reactions: {
+ "chlorine": { elem1: "salt", elem2: "pop" },
+ "vinegar": { elem1: "sodium_acetate", elem2: [null, null, null, "hydrogen"], attr1: { "foam": 15 } },
+ "water": { elem1: ["pop", "pop", "pop", "hydrogen", "lioh"], chance: 0.01, temp2: 200 },
+ "salt_water": { elem1: ["pop", "pop", "pop", "hydrogen", "lioh"], chance: 0.01, temp2: 200 },
+ "sugar_water": { elem1: ["pop", "pop", "pop", "hydrogen", "lioh"], chance: 0.01, temp2: 200 },
+ "dirty_water": { elem1: ["pop", "pop", "pop", "hydrogen", "lioh"], chance: 0.01, temp2: 200 },
+ "seltzer": { elem1: ["pop", "pop", "pop", "hydrogen", "lioh"], chance: 0.01, temp2: 200 },
+ "pool_water": { elem1: ["pop", "pop", "pop", "hydrogen", "lioh"], chance: 0.01, temp2: 200 },
+ "primordial_soup": { elem1: ["pop", "pop", "pop", "hydrogen", "lioh"], chance: 0.01, temp2: 200 },
+ "nut_milk": { elem1: ["pop", "pop", "pop", "hydrogen", "lioh"], chance: 0.01, temp2: 200 },
+ "acid": { elem1: ["hydrogen", "salt", "salt", "pop"], elem2: ["hydrogen", "salt", "salt", "pop"], temp1: 100, temp2: 100 }
+ },
+ properties: { symbol: "li" }
+};
+
+// Molten lithium
+elements.molten_lithium = {
+ tempLow: 180.5,
+ tempHigh: 1334,
+ stateLow: "lithium",
+ density: 533,
+ conduct: 0.42,
+ category: "states",
+ reactions: {
+ "chlorine": { elem1: "salt", elem2: "pop" },
+ "vinegar": { elem1: "sodium_acetate", elem2: [null, null, null, "hydrogen"], attr1: { "foam": 15 } },
+ "water": { elem1: ["pop", "pop", "pop", "hydrogen", "lye"], chance: 0.01, temp2: 200 },
+ "salt_water": { elem1: ["pop", "pop", "pop", "hydrogen", "lye"], chance: 0.01, temp2: 200 },
+ "sugar_water": { elem1: ["pop", "pop", "pop", "hydrogen", "lye"], chance: 0.01, temp2: 200 },
+ "dirty_water": { elem1: ["pop", "pop", "pop", "hydrogen", "lye"], chance: 0.01, temp2: 200 },
+ "seltzer": { elem1: ["pop", "pop", "pop", "hydrogen", "lye"], chance: 0.01, temp2: 200 },
+ "pool_water": { elem1: ["pop", "pop", "pop", "hydrogen", "lye"], chance: 0.01, temp2: 200 },
+ "primordial_soup": { elem1: ["pop", "pop", "pop", "hydrogen", "lye"], chance: 0.01, temp2: 200 },
+ "nut_milk": { elem1: ["pop", "pop", "pop", "hydrogen", "lye"], chance: 0.01, temp2: 200 },
+ "acid": { elem1: ["hydrogen", "salt", "salt", "pop"], elem2: ["hydrogen", "salt", "salt", "pop"], temp1: 100, temp2: 100 }
+ },
+ properties: { symbol: "li" }
+};
+
+// Gas
+elements.lithium_gas = {
+ color: "#bfbfbf",
+ state: "gas",
+ tempLow: 1334,
+ stateLow: "molten_lithium"
+};
+
+elements.lioh = {
+ color: "#dfefff",
+ category: "compounds",
+ behavior: behaviors.POWDER,
+ state: "solid",
+ density: 1460,
+ tempHigh: 500,
+ reactions: {
+ "acid": { elem1: "water", elem2: "salt" }
+ }
+}
+
+
+// --- Alkali metals configuration ---
+const alkaliMetals = [
+ { name: "rubidium", symbol: "rb", density: 1534, conduct: 0.6, melt: 39.3, boil: 688, color: ["#ffffff", "#e1e1e1", "#c0c0c0", "#bbbbbb"] },
+ { name: "caesium", symbol: "cs", density: 1850, conduct: 0.59, melt: 28.44, boil: 671, color: ["#ffffff", "#e5e5e5", "#cfcfcf", "#bbbbbb"] }
+];
+
+// --- Generate elements dynamically ---
+alkaliMetals.forEach(metal => {
+ // Solid
+ elements[metal.name] = {
+ color: metal.color,
+ state: "solid",
+ behavior: behaviors.POWDER,
+ category: "alkali",
+ tempHigh: metal.melt,
+ stateHigh: `liquid_${metal.name}`,
+ density: metal.density,
+ conduct: metal.conduct,
+ tick(pixel) {
+ normalAlkali(pixel);
+ waterReaction(pixel, { effectType: "explosion", reactionChance: 0.05 });
+ },
+ properties: { symbol: metal.symbol }
+ };
+
+ // Liquid
+ elements[`liquid_${metal.name}`] = {
+ color: metal.color,
+ state: "liquid",
+ behavior: behaviors.LIQUID,
+ tempLow: metal.melt,
+ temp: metal.melt,
+ tempHigh: metal.boil,
+ stateLow: metal.name,
+ stateHigh: `${metal.name}_gas`,
+ density: metal.density,
+ conduct: metal.conduct,
+ category: "states",
+ hidden: true,
+ tick(pixel) {
+ normalAlkali(pixel);
+ waterReaction(pixel, { effectType: "explosion", reactionChance: 0.05 });
+ },
+ properties: { symbol: metal.symbol }
+ };
+
+ // Gas
+ elements[`${metal.name}_gas`] = {
+ color: metal.color,
+ state: "gas",
+ tempLow: metal.boil,
+ stateLow: `liquid_${metal.name}`,
+ category: "states",
+ hidden: true,
+ behavior: behaviors.GAS,
+ temp: metal.boil
+ };
+});
+
+elements.sodium.category = "alkali"
+elements.potassium.category = "alkali"
+
+elements.francium = {
+ color: ["#ddddff", "#ccccff", "#aaaaff"],
+ category: "alkali",
+ behavior: behaviors.POWDER,
+ state: "solid",
+ density: 1870,
+ tempHigh: 27,
+ stateHigh: "molten_francium",
+ conduct: 0.2,
+ tick(pixel) {
+ normalAlkali(pixel);
+ waterReaction(pixel, { effectType: "explosion", reactionChance: 0.2, tempIncrease: 300 });
+ if (Math.random() < 0.001) {
+ changePixel(pixel, "lead");
+ }
+ 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) && Math.random() <= 0.02) {
+ createPixel("radiation", x, y)
+ getPixel(x, y).temp = pixel.temp
+ }
+ if (!isEmpty(x, y)) {
+ let p = getPixel(x, y)
+ if (p && p.element === "neutron" && Math.random() <= 0.5) {
+ var rand = Math.random()
+ if (rand <= 0.01) {
+ changePixel(pixel, "lead")
+ }
+ }
+ }
+ }
+ },
+ properties: {
+ symbol: "fr"
+ }
+}
+
+elements.molten_francium = {
+ color: ["#ddddff", "#ccccff", "#aaaaff"],
+ state: "liquid",
+ behavior: behaviors.LIQUID,
+ density: 1870,
+ tempLow: 27,
+ temp: 27,
+ tempHigh: 600,
+ stateLow: "francium",
+ stateHigh: "francium_gas",
+ category: "states",
+ hidden: true,
+ tick(pixel) {
+ normalAlkali(pixel);
+ waterReaction(pixel, { effectType: "explosion", reactionChance: 0.2, tempIncrease: 300 });
+ if (Math.random() < 0.001) {
+ changePixel(pixel, "lead");
+ }
+ 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) && Math.random() <= 0.02) {
+ createPixel("radiation", x, y)
+ getPixel(x, y).temp = pixel.temp
+ }
+ if (!isEmpty(x, y)) {
+ let p = getPixel(x, y)
+ if (p && p.element === "neutron" && Math.random() <= 0.5) {
+ var rand = Math.random()
+ if (rand <= 0.01) {
+ changePixel(pixel, "lead")
+ }
+ }
+ }
+ }
+ },
+ properties: { symbol: "fr" }
+}
+
+elements.francium_gas = {
+ color: "#ccccff",
+ behavior: behaviors.GAS,
+ state: "gas",
+ tempLow: 600,
+ temp: 600,
+ stateLow: "molten_francium",
+ category: "states",
+ hidden: true
+}