class Dice { constructor(sides, addition = 0, count = 1, color = "#000") { this.sides = sides; this.addition = addition; this.count = count; this.color = color; this.selected = false; } } class DiceHistoryEntry { constructor(dice = undefined, formula = undefined, result = undefined) { this.dice = dice; this.formula = formula; this.result = result; } } const default_sides = [4, 6, 8, 10, 12, 20, 100]; const default_colors = ["#de324c", "#f4895f", "#f8e16f", "#95cf92", "#369acc", "#9656a2", "#000000"]; let darkMode = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches; let dices = []; let history = []; let dicesContainer = document.getElementById("dices"); let historyContainer = document.getElementById("history"); function getDiceLabel(dice) { return (dice.count > 1 ? dice.count : "") + "D" + dice.sides + (dice.addition ? (dice.addition < 0 ? "-" : "+") + Math.abs(dice.addition) : ""); } function renderDice(dice, index) { const diceElement = document.createElement("div"); diceElement.classList.add("dice"); const diceImage = document.createElement("div"); diceImage.classList.add("dice-image"); if (default_sides.indexOf(dice.sides) != -1) { diceImage.classList.add("dice-image-" + dice.sides); } else { diceImage.classList.add("dice-image-custom"); } if (dice.color) { diceImage.style.backgroundColor = dice.color; // revert b/w on dark-mode if (darkMode) { if (["#000", "#000000", "black", "rgb(0,0,0)", "rgb(0, 0, 0)"].indexOf(dice.color) != -1) { diceImage.style.backgroundColor = "#fff"; } else if (["#fff", "#ffffff", "white", "rgb(255,255,255)", "rgb(255, 255, 255)"].indexOf(dice.color) != -1) { diceImage.style.backgroundColor = "#000"; } } } const diceImageContainer = document.createElement("div"); diceImageContainer.classList.add("dice-image-container"); diceImageContainer.onclick = function () { if (dices[index].selected) { dices[index].selected = false; diceElement.classList.remove("selected"); } else { dices[index].selected = true; diceElement.classList.add("selected"); } if (dices.filter((dice) => dice.selected).length) { document.getElementById('roll-button').classList.remove("disabled"); } else { document.getElementById('roll-button').classList.add("disabled"); } localStorage.setItem('dices', JSON.stringify(dices)); }; diceImageContainer.appendChild(diceImage); diceElement.appendChild(diceImageContainer); const diceLabelContainer = document.createElement("div"); diceLabelContainer.classList.add("label-container"); const diceLabel = document.createElement("label"); diceLabel.innerText = getDiceLabel(dice); diceLabelContainer.appendChild(diceLabel); diceElement.appendChild(diceLabelContainer); const diceRemove = document.createElement("div"); diceRemove.classList.add("remove"); diceRemove.onclick = function () { removeDice(index); }; diceElement.appendChild(diceRemove); if (dice.selected) { diceElement.classList.add("selected"); } else { diceElement.classList.remove("selected"); } dicesContainer.appendChild(diceElement); } function renderDices() { dicesContainer.innerHTML = ""; dices.forEach((dice, index) => { renderDice(dice, index); }) if (dices.filter((dice) => dice.selected).length) { document.getElementById('roll-button').classList.remove("disabled"); } else { document.getElementById('roll-button').classList.add("disabled"); } } function addDice(sides, addition = 0, count = 1, color = "#000") { dices.push(new Dice(sides, addition, count, color)); localStorage.setItem('dices', JSON.stringify(dices)); renderDices(); } function removeDice(index) { dices.splice(index, 1); localStorage.setItem('dices', JSON.stringify(dices)); renderDices(); } function resetDices() { dices = []; default_sides.forEach((side, i) => { dices.push(new Dice(side, 0, 1, default_colors[i])); }); localStorage.setItem('dices', JSON.stringify(dices)); renderDices(); } function addDiceForm() { let inputSides = document.getElementById("inputSides"); if (!inputSides.value) { inputSides = document.getElementById("inputCustom") } const inputAddition = document.getElementById("inputAddition"); const inputCount = document.getElementById("inputCount"); const inputColor = document.getElementById("inputColor"); addDice(+inputSides.value, +inputAddition.value, +inputCount.value, inputColor.value); } function setDicesContainer(container) { dicesContainer = container; } function renderHistory() { historyContainer.innerHTML = ""; if (history.length) { document.getElementById('history-button').classList.remove("disabled"); } else { document.getElementById('history-button').classList.add("disabled"); } history.forEach((entry) => { if (entry.dice && entry.result) { const entryElement = document.createElement("div"); entryElement.classList.add("entry"); const diceLabel = document.createElement("label"); diceLabel.innerHTML = getDiceLabel(entry.dice) + ": "; diceLabel.style.color = entry.dice.color; // revert b/w on dark-mode if (darkMode) { if (["#000", "#000000", "black", "rgb(0,0,0)", "rgb(0, 0, 0)"].indexOf(entry.dice.color) != -1) { diceLabel.style.color = "#fff"; } else if (["#fff", "#ffffff", "white", "rgb(255,255,255)", "rgb(255, 255, 255)"].indexOf(entry.dice.color) != -1) { diceLabel.style.color = "#000"; } } entryElement.appendChild(diceLabel); const diceResult = document.createElement("span"); diceResult.innerText = entry.result; entryElement.appendChild(diceResult); const diceFormula = document.createElement("span"); const diceEqual = document.createElement("span"); entryElement.appendChild(diceEqual); entryElement.appendChild(diceFormula); if (entry.formula) { diceFormula.innerText = entry.formula; diceEqual.innerText = " = " } historyContainer.appendChild(entryElement); } else { historyContainer.appendChild(document.createElement("hr")); } }) } function roll(dice) { let formula = ""; for (let index = 0; index < dice.count; index++) { formula += Math.floor(Math.random() * dice.sides + 1); if (index < dice.count - 1) { formula += " + "; } } if (dice.addition) { formula += " + " + dice.addition; } const result = eval(formula); if (formula.indexOf("+") == -1) { formula = ""; } history.unshift(new DiceHistoryEntry(dice, formula, result)); localStorage.setItem('history', JSON.stringify(history)); renderHistory(); } function rollSelected() { if (dices.filter((dice) => dice.selected).length) { if (history.length) { history.unshift(new DiceHistoryEntry()); } dices.forEach((dice) => { if (dice.selected) { roll(dice); } }) } } function clearHistory() { history = []; localStorage.removeItem('history'); renderHistory(); } function exportData() { const downloadButton = document.createElement('a'); downloadButton.setAttribute('href', 'data:application/json;charset=utf-8,' + encodeURIComponent(JSON.stringify({ dices: dices, history: history }))); downloadButton.setAttribute('download', 'rgp-dices-' + new Date().toISOString() + '.json'); document.body.appendChild(downloadButton); downloadButton.click(); document.body.removeChild(downloadButton); } function importData(event) { event.target.parentElement.classList.remove("error"); try { const reader = new FileReader(); reader.addEventListener('load', async (event) => { const data = JSON.parse(event.target.result); if (data.dices) { dices = data.dices; renderDices(); } if (data.history) { history = data.history; renderHistory(); } }); reader.readAsText(event.target.files[0]); } catch (e) { console.warn(e); event.target.parentElement.classList.add("error"); } } function updateCustom() { if (!document.getElementById("inputSides").value) { document.getElementById("inputCustom").classList.remove("hidden"); } else { document.getElementById("inputCustom").classList.add("hidden"); } } function toggleDarkMode() { if (darkMode) { darkMode = false; document.body.classList.remove("dark"); document.getElementById("dark-mode-icon").src = "./assets/dark.svg"; document.getElementById("dark-mode-text").innerText = "Dark Mode"; } else { darkMode = true; document.body.classList.add("dark"); document.getElementById("dark-mode-icon").src = "./assets/light.svg"; document.getElementById("dark-mode-text").innerText = "Light Mode"; } renderDices(); renderHistory(); } if (localStorage.getItem('dices')) { dices = JSON.parse(localStorage.getItem('dices')); } else { default_sides.forEach((side, i) => { dices.push(new Dice(side, 0, 1, default_colors[i])); }) } if (localStorage.getItem('history')) { history = JSON.parse(localStorage.getItem('history')); } document.getElementById("importFile").addEventListener("change", importData); renderDices(); renderHistory(); updateCustom(); if (darkMode) { document.body.classList.add("dark"); document.getElementById("dark-mode-icon").src = "./assets/light.svg"; document.getElementById("dark-mode-text").innerText = "Light Mode"; }