This commit is contained in:
Lurkars 2024-05-04 22:06:19 +02:00
parent ff7f24f02e
commit 027e223e7e
5 changed files with 336 additions and 80 deletions

1
assets/plus.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><path d="M0 0h24v24H0z" fill="none"/><path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/></svg>

After

Width:  |  Height:  |  Size: 192 B

View File

@ -4,6 +4,7 @@
<title>RPG Dices</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<link rel="stylesheet" href="style.css">
<script src="js/libs/moment.js"></script>
<script src="js/libs/sidebar/sidebar.js"></script>
<link href="js/libs/sidebar/sidebar.css" type="text/css" rel="stylesheet" />
<style>
@ -25,7 +26,8 @@
</li>
</ul>
<ul class="menu">
<li onclick="toggleDarkMode()"><img id="dark-mode-icon" src="./assets/dark.svg"><span id="dark-mode-text">Dark Mode</span></li>
<li onclick="toggleDarkMode()"><img id="dark-mode-icon" src="./assets/dark.svg"><span
id="dark-mode-text">Dark Mode</span></li>
</ul>
<script>
Sidebar();
@ -35,31 +37,47 @@
<div class="dices-container">
<div class="dices" id="dices"></div>
<div class="form" id="form">
<input min="1" value="1" type="number" id="inputCount">
D
<select value="6" id="inputSides" onchange="updateCustom()">
<option>4</option>
<option>6</option>
<option>8</option>
<option>10</option>
<option>12</option>
<option>20</option>
<option>100</option>
<option value="">custom</option>
</select>
<input class="hidden" min="2" value="2" type="number" id="inputCustom">
+
<input value="0" type="number" id="inputAddition">
<input value="" type="color" id="inputColor">
<button onclick="addDiceForm()"><img src="./assets/roll.svg"> Add Dice</button>
<div class="form-container">
<div class="form">
<input min="1" value="1" type="number" id="inputCount">
D
<select value="6" id="inputSides" onchange="updateCustom()">
<option>4</option>
<option>6</option>
<option>8</option>
<option>10</option>
<option>12</option>
<option>20</option>
<option>100</option>
<option value="">custom</option>
</select>
<input class="hidden" min="2" value="2" type="number" id="inputCustom">
+
<input value="0" type="number" id="inputAddition">
&nbsp;
<input type="color" id="inputColor">
</div>
<div class="actions">
<button onclick="addDiceForm()"><img src="./assets/plus.svg"> Add Dice</button>
<button onclick="rollForm()"><img src="./assets/roll.svg"> Roll</button>
</div>
<div class="form">
<input type="text" id="inputText">
</div>
<div class="actions">
<button onclick="addDicesText()"><img src="./assets/plus.svg"> Add Dices</button>
<button onclick="rollText()"><img src="./assets/roll.svg"> Roll</button>
</div>
</div>
</div>
<div class="history-container">
<div class="menu">
<button id="roll-button" onclick="rollSelected()"><img src="./assets/roll.svg"> Roll</button>
<button id="history-button" onclick="clearHistory()"><img src="./assets/history.svg"> Clear History</button>
<div class="actions">
<button id="history-button" onclick="clearHistory()"><img src="./assets/history.svg"> Clear
History</button>
<button id="roll-add-button" onclick="addSelected()"><img src="./assets/add.svg"> Add Selected</button>
<button id="roll-button" onclick="rollSelected()"><img src="./assets/roll.svg"> Roll Selected</button>
</div>
<div class="history" id="history"></div>

2
js/libs/moment.js Normal file

File diff suppressed because one or more lines are too long

240
script.js
View File

@ -1,24 +1,58 @@
class Dice {
constructor(sides, addition = 0, count = 1, color = "#000") {
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(dice = undefined, formula = undefined, result = undefined) {
this.dice = dice;
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", "#000000"];
const dice_regex = /(\d+)?[D|d](\d+)([\+|\-]\d+)?(\[(.+)\])?/;
let darkMode = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
let dices = [];
@ -27,10 +61,6 @@ 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");
@ -67,8 +97,10 @@ function renderDice(dice, index) {
if (dices.filter((dice) => dice.selected).length) {
document.getElementById('roll-button').classList.remove("disabled");
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));
@ -81,7 +113,7 @@ function renderDice(dice, index) {
diceLabelContainer.classList.add("label-container");
const diceLabel = document.createElement("label");
diceLabel.innerText = getDiceLabel(dice);
diceLabel.innerText = dice.toText();
diceLabelContainer.appendChild(diceLabel);
diceElement.appendChild(diceLabelContainer);
@ -111,8 +143,10 @@ function renderDices() {
if (dices.filter((dice) => dice.selected).length) {
document.getElementById('roll-button').classList.remove("disabled");
document.getElementById('roll-add-button').classList.remove("disabled");
} else {
document.getElementById('roll-button').classList.add("disabled");
document.getElementById('roll-add-button').classList.add("disabled");
}
}
@ -161,44 +195,63 @@ function renderHistory() {
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) + ":&nbsp;";
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";
}
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);
}
})
}
entryElement.appendChild(diceLabel);
historyContainer.appendChild(diceLabelContainer);
const diceResult = document.createElement("span");
diceResult.classList.add("result");
diceResult.innerText = entry.result;
entryElement.appendChild(diceResult);
historyContainer.appendChild(diceResult);
const diceFormula = document.createElement("span");
const diceEqual = document.createElement("span");
entryElement.appendChild(diceEqual);
entryElement.appendChild(diceFormula);
diceFormula.classList.add("formula");
historyContainer.appendChild(diceFormula);
if (entry.formula) {
diceFormula.innerText = entry.formula;
diceEqual.innerText = " = "
diceFormula.innerText = " = " + entry.formula;
}
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");
}
historyContainer.appendChild(entryElement);
} else {
historyContainer.appendChild(document.createElement("hr"));
}
})
}
function roll(dice) {
function formula(dice) {
let formula = "";
for (let index = 0; index < dice.count; index++) {
formula += Math.floor(Math.random() * dice.sides + 1);
@ -209,25 +262,125 @@ function roll(dice) {
if (dice.addition) {
formula += " + " + dice.addition;
}
const result = eval(formula);
if (formula.indexOf("+") == -1) {
formula = "";
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, formula, result));
history.unshift(new DiceHistoryEntry([dice], f, result, time ? moment() : undefined));
localStorage.setItem('history', JSON.stringify(history));
renderHistory();
}
function rollSelected() {
if (dices.filter((dice) => dice.selected).length) {
const selected = dices.filter((dice) => dice.selected);
if (selected.length) {
if (history.length) {
history.unshift(new DiceHistoryEntry());
}
dices.forEach((dice) => {
if (dice.selected) {
roll(dice);
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();
}
}
@ -253,11 +406,14 @@ function importData(event) {
reader.addEventListener('load', async (event) => {
const data = JSON.parse(event.target.result);
if (data.dices) {
dices = 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;
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();
}
});
@ -294,7 +450,7 @@ function toggleDarkMode() {
}
if (localStorage.getItem('dices')) {
dices = JSON.parse(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]));
@ -302,7 +458,7 @@ if (localStorage.getItem('dices')) {
}
if (localStorage.getItem('history')) {
history = JSON.parse(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);

111
style.css
View File

@ -6,6 +6,7 @@
body {
background-color: #fff;
color: #000;
font-family: 'Courier New', Courier, monospace;
}
.hidden {
@ -68,6 +69,11 @@ body {
max-width: 100%;
}
.dices-container {
margin-top: 1em;
font-size: 0.8em;
}
.dices {
display: flex;
justify-content: center;
@ -95,6 +101,7 @@ body {
align-items: center;
margin-top: 0.2em;
z-index: 2;
font-size: 1.3em;
height: 2em;
}
@ -121,6 +128,7 @@ body {
position: relative;
width: 100%;
height: 100%;
background-color: #000;
mask-size: contain;
mask-repeat: no-repeat;
mask-position: center;
@ -191,48 +199,98 @@ body {
mask-image: url(./assets/dices/custom.svg);
}
.form {
.form-container {
margin-top: 1em;
display: grid;
align-items: center;
row-gap: 1em;
grid-template-columns: auto auto;
}
.form-container .actions {
display: flex;
flex-wrap: wrap;
justify-content: center;
justify-content: space-between;
align-items: center;
}
.form {
font-size: 1.5em;
display: flex;
flex-wrap: wrap;
justify-content: start;
align-items: center;
margin-top: 1em;
}
.form input,
.form select {
font-size: 0.9em;
width: 5em;
height: 2.2em;
background-color: #fff;
font-family: 'Courier New', Courier, monospace;
border: 0.15em solid #000;
outline: none;
padding: 0.4em;
box-sizing: border-box;
}
.form #inputText {
width: 22em;
max-width: 99vw;
}
.history-container {
margin-top: 2em;
width: 400px;
margin: 2em 0;
max-width: 99vw;
}
.history-container .menu {
.history-container .actions {
display: flex;
justify-content: space-between;
flex-wrap: wrap;
justify-content: space-around;
width: 100%;
}
.history-container .actions button {
font-size: 1em;
margin: 0 0.5em;
}
.history {
display: flex;
flex-direction: column;
display: grid;
grid-template-columns: repeat(4, auto);
width: 100%;
margin-top: 2em;
}
.history .entry {
margin: 0.3em 0;
display: grid;
grid-template-columns: 1fr 1fr 0.5fr 5fr;
row-gap: 0.3em;
column-gap: 0.3em;
}
.history hr {
width: 100%;
grid-column: span 4;
}
.history .label {
font-size: 1.2em;
}
.history .result {
text-align: center;
font-size: 1.2em;
font-weight: bold;
}
.history .formula {
margin: 0 0.5em;
}
.history .time {
text-align: right;
}
button {
font-family: 'Courier New', Courier, monospace;
cursor: pointer;
display: flex;
justify-content: center;
@ -279,12 +337,33 @@ body.dark button {
color: #fff;
}
body.dark .dice .dice-image {
background-color: #fff;
}
body.dark .dice.selected .dice-image-container {
filter: drop-shadow(0.1em 0.1em 0.1em #efefef) drop-shadow(-0.1em -0.1em 0.1em #efefef);
}
@media screen and (max-width: 720px) {
body.dark .form input,
body.dark .form select {
color: #fff;
background-color: #000;
border-color: #fff;
}
@media screen and (min-width: 768px) {
.container {
padding: 0 250px;
}
}
@media screen and (max-width: 767px) {
.dices {
font-size: 0.65em;
}
.form-container {
grid-template-columns: auto;
}
}