๐ŸŽฎArcadeLab

Little Alchemy - Element Lab

by AtomicBolt46
439 lines8.6 KB
โ–ถ Play

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Little Alchemy - Element Lab</title>

<style>
* { box-sizing:border-box; }

body {
  margin:0;
  font-family:Arial,sans-serif;
  background:#101827;
  color:white;
}

header {
  padding:18px;
  text-align:center;
  background:#18243a;
}

h1 { margin:0; color:#70e5ff; }
header p { color:#bdc9dc; }

.main {
  display:grid;
  grid-template-columns:230px 1fr;
  gap:12px;
  padding:12px;
}

.panel {
  background:#1b2940;
  border-radius:12px;
  padding:12px;
}

h3 { margin-top:5px; }

#elements {
  display:flex;
  flex-wrap:wrap;
  gap:8px;
}

.element {
  background:#344761;
  border:1px solid #607794;
  border-radius:9px;
  padding:10px;
  cursor:grab;
  user-select:none;
  text-align:center;
  min-width:85px;
  touch-action:none;
}

.element:hover { background:#486484; }

#workspace {
  height:570px;
  position:relative;
  overflow:hidden;
  background-color:#172438;
  background-image:radial-gradient(#34445d 1px,transparent 1px);
  background-size:22px 22px;
  border:2px solid #435a79;
  border-radius:12px;
}

.item {
  position:absolute;
  padding:10px 14px;
  background:#344761;
  border:2px solid #83cfff;
  border-radius:12px;
  cursor:grab;
  user-select:none;
  white-space:nowrap;
  touch-action:none;
  box-shadow:0 4px 10px #0005;
}

.item.selected {
  border-color:#ffe66d;
  background:#52678b;
}

button {
  padding:10px 15px;
  border:0;
  border-radius:8px;
  background:#5ac8fa;
  color:#102030;
  font-weight:bold;
  cursor:pointer;
  margin:3px;
}

button:hover { background:#9be4ff; }

#message {
  color:#ffe66d;
  min-height:22px;
  text-align:center;
}

.stats {
  color:#b9cce4;
  margin:10px 0;
}

@media(max-width:650px) {
  .main { grid-template-columns:1fr; }
  #workspace { height:450px; }
  #elements { max-height:200px; overflow:auto; }
}
</style>
</head>

<body>

<header>
<h1>๐Ÿงช LITTLE ALCHEMY</h1>
<p>Combine elements and discover a whole new world!</p>
</header>

<div class="main">

<div class="panel">
<h3>๐Ÿ“š Discoveries</h3>
<div class="stats">
Discovered: <span id="count">4</span>
</div>
<div id="elements"></div>
<br>
<button onclick="clearBoard()">Clear Board</button>
<button onclick="resetGame()">Reset Game</button>
</div>

<div class="panel">
<h3>๐ŸŒŽ Experiment Table</h3>
<div id="workspace"></div>
<p id="message">Drag two elements together to combine them!</p>
</div>

</div>

<script>
const recipes = {
"water+fire":"Steam",
"earth+fire":"Lava",
"air+fire":"Energy",
"earth+water":"Mud",
"air+water":"Rain",
"air+earth":"Dust",
"fire+mud":"Brick",
"water+mud":"Plant",
"rain+earth":"Plant",
"rain+plant":"Flower",
"plant+earth":"Tree",
"plant+water":"Algae",
"fire+tree":"Ash",
"fire+wood":"Ash",
"tree+tree":"Forest",
"air+energy":"Storm",
"water+energy":"Life",
"earth+life":"Human",
"water+life":"Fish",
"air+life":"Bird",
"fire+life":"Phoenix",
"earth+lava":"Volcano",
"water+lava":"Stone",
"air+stone":"Sand",
"fire+sand":"Glass",
"water+sand":"Beach",
"stone+fire":"Metal",
"metal+energy":"Electricity",
"water+electricity":"Battery",
"electricity+metal":"Robot",
"human+stone":"Tool",
"human+tree":"Wood",
"human+fire":"Cook",
"human+water":"Swimmer",
"bird+water":"Duck",
"bird+fire":"Phoenix",
"fish+human":"Mermaid",
"plant+sun":"Flower",
"sun+water":"Rainbow",
"sun+fire":"Solar Energy",
"air+steam":"Cloud",
"cloud+water":"Rain",
"cloud+energy":"Lightning",
"lightning+metal":"Electricity",
"earth+plant":"Grass",
"grass+water":"Swamp",
"swamp+energy":"Life",
"mud+fire":"Brick",
"stone+human":"Statue",
"metal+human":"Knight",
"fire+metal":"Sword",
"water+ice":"Ocean",
"ice+sun":"Water",
"water+water":"Ocean",
"fire+fire":"Energy",
"air+air":"Wind",
"earth+earth":"Land",
"wind+water":"Wave",
"wave+earth":"Beach",
"ocean+life":"Whale",
"human+electricity":"Scientist",
"scientist+robot":"Cyborg",
"tree+fire":"Charcoal"
};

function key(a,b) {
  return [a,b].sort().join("+").toLowerCase();
}

const database = {
  "earth":"๐ŸŒ",
  "water":"๐Ÿ’ง",
  "fire":"๐Ÿ”ฅ",
  "air":"๐Ÿ’จ",
  "steam":"โ™จ๏ธ",
  "lava":"๐ŸŒ‹",
  "energy":"โšก",
  "mud":"๐ŸŸค",
  "rain":"๐ŸŒง๏ธ",
  "dust":"๐ŸŒซ๏ธ",
  "brick":"๐Ÿงฑ",
  "plant":"๐ŸŒฑ",
  "flower":"๐ŸŒธ",
  "tree":"๐ŸŒณ",
  "algae":"๐Ÿชธ",
  "ash":"๐ŸŒ‘",
  "wood":"๐Ÿชต",
  "forest":"๐ŸŒฒ",
  "storm":"โ›ˆ๏ธ",
  "life":"๐Ÿงฌ",
  "human":"๐Ÿง‘",
  "fish":"๐ŸŸ",
  "bird":"๐Ÿฆ",
  "phoenix":"๐Ÿฆ…",
  "volcano":"๐ŸŒ‹",
  "stone":"๐Ÿชจ",
  "sand":"๐Ÿ–๏ธ",
  "glass":"๐ŸชŸ",
  "beach":"๐Ÿ๏ธ",
  "metal":"๐Ÿ”ฉ",
  "electricity":"โšก",
  "battery":"๐Ÿ”‹",
  "robot":"๐Ÿค–",
  "tool":"๐Ÿ”จ",
  "cook":"๐Ÿ‘จโ€๐Ÿณ",
  "swimmer":"๐ŸŠ",
  "duck":"๐Ÿฆ†",
  "mermaid":"๐Ÿงœ",
  "sun":"โ˜€๏ธ",
  "rainbow":"๐ŸŒˆ",
  "solar energy":"๐Ÿ”†",
  "cloud":"โ˜๏ธ",
  "lightning":"๐ŸŒฉ๏ธ",
  "grass":"๐ŸŒฟ",
  "swamp":"๐ŸŠ",
  "statue":"๐Ÿ—ฟ",
  "knight":"โš”๏ธ",
  "sword":"๐Ÿ—ก๏ธ",
  "ice":"๐ŸงŠ",
  "ocean":"๐ŸŒŠ",
  "wind":"๐ŸŒฌ๏ธ",
  "land":"๐Ÿž๏ธ",
  "wave":"๐ŸŒŠ",
  "whale":"๐Ÿ‹",
  "scientist":"๐Ÿง‘โ€๐Ÿ”ฌ",
  "cyborg":"๐Ÿฆพ",
  "charcoal":"โฌ›"
};

let discovered = ["Earth","Water","Fire","Air"];
let selected = null;
let dragging = null;
let offsetX = 0;
let offsetY = 0;

const workspace = document.getElementById("workspace");

function emoji(name) {
  return database[name.toLowerCase()] || "โœจ";
}

function updateList() {
  const list = document.getElementById("elements");
  list.innerHTML = "";

  discovered.forEach(name => {
    const el = document.createElement("div");
    el.className = "element";
    el.textContent = emoji(name)+" "+name;
    el.onpointerdown = e => {
      if(e.target.closest(".element")) {
        createItem(name, 100+Math.random()*250,
          100+Math.random()*250);
      }
    };
    list.appendChild(el);
  });

  document.getElementById("count").textContent =
    discovered.length;
}

function createItem(name,x,y) {
  const item = document.createElement("div");
  item.className = "item";
  item.textContent = emoji(name)+" "+name;
  item.dataset.name = name;

  item.style.left = Math.max(0,Math.min(
    workspace.clientWidth-100,x))+"px";
  item.style.top = Math.max(0,Math.min(
    workspace.clientHeight-50,y))+"px";

  workspace.appendChild(item);

  item.addEventListener("pointerdown", e => {
    e.preventDefault();
    dragging = item;

    const rect = item.getBoundingClientRect();
    const board = workspace.getBoundingClientRect();

    offsetX = e.clientX-rect.left;
    offsetY = e.clientY-rect.top;

    item.setPointerCapture(e.pointerId);
    item.style.zIndex = 1000;
  });

  item.addEventListener("pointermove", e => {
    if(dragging !== item) return;

    const rect = workspace.getBoundingClientRect();

    item.style.left = Math.max(0,Math.min(
      workspace.clientWidth-item.offsetWidth,
      e.clientX-rect.left-offsetX))+"px";

    item.style.top = Math.max(0,Math.min(
      workspace.clientHeight-item.offsetHeight,
      e.clientY-rect.top-offsetY))+"px";
  });

  item.addEventListener("pointerup", () => {
    if(dragging !== item) return;

    dragging = null;
    item.style.zIndex = 1;

    const others = [...workspace.querySelectorAll(".item")]
      .filter(el => el !== item);

    for(const other of others) {
      if(overlap(item,other)) {
        combine(item,other);
        break;
      }
    }
  });
}

function overlap(a,b) {
  const r1 = a.getBoundingClientRect();
  const r2 = b.getBoundingClientRect();

  return !(r1.right<r2.left ||
           r1.left>r2.right ||
           r1.bottom<r2.top ||
           r1.top>r2.bottom);
}

function combine(a,b) {
  const first = a.dataset.name;
  const second = b.dataset.name;

  const result = recipes[key(first,second)];

  if(!result) {
    document.getElementById("message").textContent =
      "โŒ No combination found! Try something else.";
    return;
  }

  if(discovered.includes(result)) {
    document.getElementById("message").textContent =
      "You already discovered "+result+"!";
    return;
  }

  const rect = a.getBoundingClientRect();
  const board = workspace.getBoundingClientRect();

  const x = rect.left-board.left;
  const y = rect.top-board.top;

  a.remove();
  b.remove();

  discovered.push(result);
  updateList();

  createItem(result,x,y);

  document.getElementById("message").textContent =
    "๐ŸŽ‰ NEW DISCOVERY: "+emoji(result)+" "+result+"!";
}

function clearBoard() {
  workspace.innerHTML = "";
  document.getElementById("message").textContent =
    "Board cleared! Drag in some elements.";
}

function resetGame() {
  discovered = ["Earth","Water","Fire","Air"];
  clearBoard();
  updateList();
  document.getElementById("message").textContent =
    "Fresh start! Discover new elements!";
}

updateList();
</script>

</body>
</html>

Game Source: Little Alchemy - Element Lab

Creator: AtomicBolt46

Libraries: none

Complexity: complex (439 lines, 8.6 KB)

The full source code is displayed above on this page.

Remix Instructions

To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: little-alchemy-element-lab-atomicbolt46" to link back to the original. Then publish at arcadelab.ai/publish.