rpg-dices/script.js

502 lines
17 KiB
JavaScript
Executable File

class Dice {
constructor(sides, addition = 0, count = 1, color = undefined) {
this.sides = sides;
this.addition = addition;
this.count = count;
this.color = color;
this.selected = false;
}
toText(color = false) {
let result = "";
if (this.count > 1) {
result += this.count;
}
result += "D" + this.sides;
if (this.addition) {
result += (this.addition < 0 ? "-" : "+") + Math.abs(this.addition);
}
if (color && this.color) {
result += "[" + this.color + "]";
}
return result;
}
fromText(value) {
const result = value.match(dice_regex);
if (result) {
if (result[1]) {
this.count = +result[1];
}
this.sides = +result[2];
if (result[3]) {
this.addition = +result[3];
}
if (result[5]) {
this.color = result[5];
}
}
}
}
class DiceHistoryEntry {
constructor(dices = undefined, formula = undefined, result = undefined, time = undefined) {
this.dices = dices;
this.formula = formula;
this.result = result;
this.time = time;
}
}
const default_sides = [4, 6, 8, 10, 12, 20, 100];
const default_colors = ["#de324c", "#f4895f", "#f8e16f", "#95cf92", "#369acc", "#9656a2", "#6c584c"];
const dice_regex = /(\d+)?[D|d](\d+)([\+|\-]\d+)?(\[(.+)\])?/;
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 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");
document.getElementById('roll-add-button').classList.add("disabled");
if (dices.filter((dice) => dice.selected).length > 1) {
document.getElementById('roll-add-button').classList.remove("disabled");
}
} else {
document.getElementById('roll-button').classList.add("disabled");
document.getElementById('roll-add-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 = dice.toText();
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");
document.getElementById('roll-add-button').classList.add("disabled");
if (dices.filter((dice) => dice.selected).length > 1) {
document.getElementById('roll-add-button').classList.remove("disabled");
}
} else {
document.getElementById('roll-button').classList.add("disabled");
document.getElementById('roll-add-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.result) {
const diceLabelContainer = document.createElement("div");
diceLabelContainer.classList.add("label");
if (entry.dices) {
entry.dices.forEach((diceData, i) => {
const dice = new Dice(diceData.sides, diceData.addition, diceData.count, diceData.color);
const diceLabel = document.createElement("label");
diceLabel.innerHTML = dice.toText() + (i == entry.dices.length - 1 ? ":&nbsp;" : "");
diceLabel.style.color = 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) {
diceLabel.style.color = "#fff";
} else if (["#fff", "#ffffff", "white", "rgb(255,255,255)", "rgb(255, 255, 255)"].indexOf(dice.color) != -1) {
diceLabel.style.color = "#000";
}
}
diceLabelContainer.appendChild(diceLabel);
if (i < entry.dices.length - 1) {
const diceLabelAdd = document.createElement("span");
diceLabelAdd.innerHTML = "&nbsp;+&nbsp;";
diceLabelContainer.appendChild(diceLabelAdd);
}
})
}
historyContainer.appendChild(diceLabelContainer);
const diceResult = document.createElement("span");
diceResult.classList.add("result");
diceResult.innerText = entry.result;
historyContainer.appendChild(diceResult);
const diceFormulaContainer = document.createElement("span");
diceFormulaContainer.classList.add("formula-container");
historyContainer.appendChild(diceFormulaContainer);
if (entry.formula) {
const diceFormulaEqual = document.createElement("span");
diceFormulaEqual.innerText = " = ";
diceFormulaContainer.appendChild(diceFormulaEqual);
const diceFormula = document.createElement("span");
diceFormula.classList.add("formula");
diceFormula.innerText = entry.formula;
diceFormulaContainer.appendChild(diceFormula);
}
const diceTime = document.createElement("span");
diceTime.classList.add("time");
historyContainer.appendChild(diceTime);
if (entry.time) {
diceTime.innerText = entry.time.fromNow();
diceTime.title = entry.time.format("LLLL");
}
} else {
historyContainer.appendChild(document.createElement("hr"));
}
})
}
function formula(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;
}
return formula;
}
function roll(dice, time = true) {
let f = formula(dice);
const result = eval(f);
if (f.indexOf("+") == -1) {
f = "";
}
history.unshift(new DiceHistoryEntry([dice], f, result, time ? moment() : undefined));
localStorage.setItem('history', JSON.stringify(history));
renderHistory();
}
function rollSelected() {
const selected = dices.filter((dice) => dice.selected);
if (selected.length) {
if (history.length) {
history.unshift(new DiceHistoryEntry());
}
selected.forEach((dice, i) => {
roll(dice, i == selected.length - 1);
})
}
}
function rollForm() {
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");
if (history.length) {
history.unshift(new DiceHistoryEntry());
}
roll(new Dice(+inputSides.value, +inputAddition.value, +inputCount.value, inputColor.value));
}
function rollText() {
let inputText = document.getElementById("inputText");
const dicesTexts = inputText.value.split(" + ");
let textDices = [];
dicesTexts.forEach((diceText, i) => {
let dice = new Dice(0);
dice.fromText(diceText);
if (!dice.color) {
dice.color = default_colors[(i + dices.length) % 7];
}
if (dice.sides > 1) {
textDices.push(dice);
}
})
if (textDices.length) {
if (history.length) {
history.unshift(new DiceHistoryEntry());
}
let f = "";
textDices.forEach((dice, i) => {
f += formula(dice);
if (i < textDices.length - 1) {
f += " + ";
}
})
const result = eval(f);
if (f.indexOf("+") == -1) {
f = "";
}
history.unshift(new DiceHistoryEntry(textDices, f, result, moment()));
localStorage.setItem('history', JSON.stringify(history));
renderHistory();
}
}
function addSelected() {
const selected = dices.filter((dice) => dice.selected);
if (selected.length) {
if (history.length) {
history.unshift(new DiceHistoryEntry());
}
let f = "";
selected.forEach((dice, i) => {
f += formula(dice);
if (i < selected.length - 1) {
f += " + ";
}
})
const result = eval(f);
if (f.indexOf("+") == -1) {
f = "";
}
history.unshift(new DiceHistoryEntry(selected, f, result, moment()));
localStorage.setItem('history', JSON.stringify(history));
renderHistory();
}
}
function addDicesText() {
let inputText = document.getElementById("inputText");
const dicesTexts = inputText.value.split(" + ");
let textDices = [];
dicesTexts.forEach((diceText, i) => {
let dice = new Dice(0);
dice.fromText(diceText);
if (!dice.color) {
dice.color = default_colors[(i + dices.length) % 7];
}
if (dice.sides > 1) {
textDices.push(dice);
}
})
if (textDices.length) {
textDices.forEach((dice) => {
dices.push(dice);
})
localStorage.setItem('dices', JSON.stringify(dices));
renderDices();
}
}
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.map((dice) => new Dice(dice.sides, dice.addition, dice.count, dice.color));
localStorage.setItem('dices', JSON.stringify(dices));
renderDices();
}
if (data.history) {
history = data.history.map((entry) => new DiceHistoryEntry(entry.dices && entry.dices.map((dice) => new Dice(dice.sides, dice.addition, dice.count, dice.color)) || undefined, entry.formula, entry.result, entry.time && moment(entry.time) || undefined));
localStorage.setItem('history', JSON.stringify(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')).map((dice) => new Dice(dice.sides, dice.addition, dice.count, dice.color));
} 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')).map((entry) => new DiceHistoryEntry(entry.dices && entry.dices.map((dice) => new Dice(dice.sides, dice.addition, dice.count, dice.color)) || undefined, entry.formula, entry.result, entry.time && moment(entry.time) || undefined));
}
document.getElementById("importFile").addEventListener("change", importData);
document.getElementById("inputText").addEventListener("keyup", (event) => {
if (event.key.toUpperCase() === 'ENTER') {
this.rollText();
}
});
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";
}
if ("serviceWorker" in navigator) {
window.addEventListener("load", function () {
navigator.serviceWorker
.register("/sw.js")
.then(res => console.trace("service worker registered"))
.catch(err => console.error("service worker not registered", err))
})
}