Compare commits
1 Commits
main
...
d1f8e03673
| Author | SHA1 | Date | |
|---|---|---|---|
| d1f8e03673 |
@@ -16,7 +16,6 @@
|
|||||||
<p align="center">
|
<p align="center">
|
||||||
<img src=".github/media/preview.gif" />
|
<img src=".github/media/preview.gif" />
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Key Features
|
## Key Features
|
||||||
@@ -28,7 +27,6 @@
|
|||||||
* Watch roll results (also available as Browser Source in OBS)
|
* Watch roll results (also available as Browser Source in OBS)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Start Container
|
## Start Container
|
||||||
|
|
||||||
You can start dice-tower with docker compose
|
You can start dice-tower with docker compose
|
||||||
@@ -42,8 +40,6 @@ services:
|
|||||||
restart: always
|
restart: always
|
||||||
ports:
|
ports:
|
||||||
- "8080:8080"
|
- "8080:8080"
|
||||||
environment:
|
|
||||||
DICE_LIMIT: 30 # OPTIONAL: amount of dice allowed to roll (default: 30)
|
|
||||||
```
|
```
|
||||||
Run the container with:
|
Run the container with:
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
2
pom.xml
2
pom.xml
@@ -4,7 +4,7 @@
|
|||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<groupId>de.arindy</groupId>
|
<groupId>de.arindy</groupId>
|
||||||
<artifactId>dice-tower</artifactId>
|
<artifactId>dice-tower</artifactId>
|
||||||
<version>1.2.8-SNAPSHOT</version>
|
<version>1.2.0-SNAPSHOT</version>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<compiler-plugin.version>3.13.0</compiler-plugin.version>
|
<compiler-plugin.version>3.13.0</compiler-plugin.version>
|
||||||
|
|||||||
@@ -21,16 +21,13 @@ class ChatOverlayResource {
|
|||||||
@QueryParam("modsAllowed") modsAllowed: Boolean = false,
|
@QueryParam("modsAllowed") modsAllowed: Boolean = false,
|
||||||
@QueryParam("vipAllowed") vipAllowed: Boolean = false,
|
@QueryParam("vipAllowed") vipAllowed: Boolean = false,
|
||||||
@QueryParam("subsAllowed") subsAllowed: Boolean = false,
|
@QueryParam("subsAllowed") subsAllowed: Boolean = false,
|
||||||
@QueryParam("allAllowed") allAllowed: Boolean = false,
|
|
||||||
@QueryParam("cmd") cmd: String? = "roll",
|
@QueryParam("cmd") cmd: String? = "roll",
|
||||||
@QueryParam("theme") theme: String? = "default",
|
@QueryParam("theme") theme: String? = "default",
|
||||||
@QueryParam("faceColor") faceColor: String? = "#ff0202",
|
@QueryParam("themeColor") themeColor: String? = "default",
|
||||||
@QueryParam("numberColor") numberColor: String? = "#ffffff",
|
|
||||||
@QueryParam("clearAfter") clearAfter: Long? = -1,
|
@QueryParam("clearAfter") clearAfter: Long? = -1,
|
||||||
@QueryParam("timeout") timeout: Long? = -1,
|
@QueryParam("timeout") timeout: Long? = -1
|
||||||
@QueryParam("showResults") showResults: Boolean = true
|
|
||||||
): TemplateInstance {
|
): TemplateInstance {
|
||||||
return Templates.chatoverlay(channel, scale ?: 7, maxDice ?: 20, modsAllowed, vipAllowed, subsAllowed, allAllowed, cmd ?: "roll", theme ?: "default", faceColor ?: "#ff0202", numberColor ?: "#ffffff", clearAfter ?: 10, timeout ?: 60, showResults)
|
return Templates.chatoverlay(channel, scale ?: 7, maxDice ?: 20, modsAllowed, vipAllowed, subsAllowed, cmd ?: "roll", theme ?: "default", themeColor ?: "ff0202", clearAfter ?: 10, timeout ?: 60)
|
||||||
}
|
}
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
|
|||||||
@@ -14,19 +14,12 @@ import jakarta.ws.rs.sse.OutboundSseEvent
|
|||||||
import jakarta.ws.rs.sse.Sse
|
import jakarta.ws.rs.sse.Sse
|
||||||
import jakarta.ws.rs.sse.SseBroadcaster
|
import jakarta.ws.rs.sse.SseBroadcaster
|
||||||
import jakarta.ws.rs.sse.SseEventSink
|
import jakarta.ws.rs.sse.SseEventSink
|
||||||
import org.eclipse.microprofile.config.inject.ConfigProperty
|
|
||||||
import java.util.Optional
|
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
private const val LIMIT = 30
|
|
||||||
|
|
||||||
@Path("/dice/{id}")
|
@Path("/dice/{id}")
|
||||||
@ApplicationScoped
|
@ApplicationScoped
|
||||||
final class DiceResource(@Context val sse: Sse) {
|
final class DiceResource(@Context val sse: Sse) {
|
||||||
|
|
||||||
@ConfigProperty(name = "dice.limit")
|
|
||||||
private lateinit var diceLimit: Optional<Int>
|
|
||||||
|
|
||||||
private var eventBuilder: OutboundSseEvent.Builder = sse.newEventBuilder()
|
private var eventBuilder: OutboundSseEvent.Builder = sse.newEventBuilder()
|
||||||
private var sseBroadcasters: MutableMap<String, SseBroadcaster> = HashMap()
|
private var sseBroadcasters: MutableMap<String, SseBroadcaster> = HashMap()
|
||||||
|
|
||||||
@@ -35,31 +28,18 @@ final class DiceResource(@Context val sse: Sse) {
|
|||||||
fun parseCommand(@PathParam("id") id: String, data: RollPayload) {
|
fun parseCommand(@PathParam("id") id: String, data: RollPayload) {
|
||||||
data.room = id.split(":")[0]
|
data.room = id.split(":")[0]
|
||||||
data.user = id.split(":")[1]
|
data.user = id.split(":")[1]
|
||||||
|
|
||||||
val results = ArrayList<Results>()
|
val results = ArrayList<Results>()
|
||||||
if (data.results == null) {
|
|
||||||
var numberOfDice = 0
|
|
||||||
data.command.split(" ", "&", "and").filter { it.isNotEmpty() }.map { it.trim() }.toTypedArray<String>().forEach { command ->
|
data.command.split(" ", "&", "and").filter { it.isNotEmpty() }.map { it.trim() }.toTypedArray<String>().forEach { command ->
|
||||||
val dice = command.split("d")
|
val dice = command.split("d")
|
||||||
var amount = dice[0].toInt()
|
val result = IntArray(dice[0].toInt())
|
||||||
val limit = diceLimit.orElse(LIMIT)
|
|
||||||
if (limit < numberOfDice + amount) {
|
|
||||||
amount = limit - numberOfDice
|
|
||||||
}
|
|
||||||
numberOfDice += amount
|
|
||||||
if (amount > 0) {
|
|
||||||
val result = IntArray(amount)
|
|
||||||
val sides = dice[1].split("+", "-")
|
val sides = dice[1].split("+", "-")
|
||||||
val modifier = if (dice[1].contains("+")) sides[1].toInt() else if (dice[1].contains("+")) -1 * sides[1].toInt() else 0
|
val modifier = if (dice[1].contains("+")) sides[1].toInt() else if (dice[1].contains("+")) -1 * sides[1].toInt() else 0
|
||||||
repeat(amount) { index ->
|
repeat(dice[0].toInt()) { index ->
|
||||||
result[index] = (Math.random() * sides[0].toInt() + 1).toInt()
|
result[index] = (Math.random() * sides[0].toInt() + 1).toInt()
|
||||||
}
|
}
|
||||||
results.add(Results(sides[0].toInt(), modifier, result.sum() + modifier, result.map { Roll(it) }.toTypedArray()))
|
results.add(Results(sides[0].toInt(), modifier, result.sum() + modifier, result.map { Roll(it) }.toTypedArray()))
|
||||||
}
|
}
|
||||||
}
|
|
||||||
} else {
|
|
||||||
results.addAll(data.results)
|
|
||||||
}
|
|
||||||
|
|
||||||
val map = results.map { r ->
|
val map = results.map { r ->
|
||||||
"${r.rolls.size}d${r.sides}@${
|
"${r.rolls.size}d${r.sides}@${
|
||||||
if (r.sides == 100) r.rolls.map { roll -> Roll(roll.value / 10 * 10) }
|
if (r.sides == 100) r.rolls.map { roll -> Roll(roll.value / 10 * 10) }
|
||||||
@@ -68,15 +48,15 @@ final class DiceResource(@Context val sse: Sse) {
|
|||||||
}.toTypedArray()
|
}.toTypedArray()
|
||||||
|
|
||||||
data.roll = map + results.filter { it.sides == 100 }.map { r -> "${r.rolls.size}d10@${r.rolls.map { roll -> Roll(roll.value % 10) }.joinToString(",")}"}.toTypedArray()
|
data.roll = map + results.filter { it.sides == 100 }.map { r -> "${r.rolls.size}d10@${r.rolls.map { roll -> Roll(roll.value % 10) }.joinToString(",")}"}.toTypedArray()
|
||||||
if (data.roll.all { it.trim().isNotEmpty() }) {
|
|
||||||
results(data.room!!, Result(data.name, data.user!!, data.faceColor, null))
|
|
||||||
}
|
|
||||||
sseBroadcasters[id]?.broadcast(
|
sseBroadcasters[id]?.broadcast(
|
||||||
eventBuilder.id((UUID.randomUUID()).toString())
|
eventBuilder.id((UUID.randomUUID()).toString())
|
||||||
.mediaType(MediaType.APPLICATION_JSON_TYPE).data(data).build()
|
.mediaType(MediaType.APPLICATION_JSON_TYPE).data(data).build()
|
||||||
)
|
)
|
||||||
|
if (data.roll.all { it.trim().isNotEmpty() }) {
|
||||||
|
results(data.room!!, Result(data.name, data.user!!, data.themeColor, null))
|
||||||
|
}
|
||||||
Thread.sleep(1000)
|
Thread.sleep(1000)
|
||||||
results(data.room!!, Result(data.name, data.user!!, data.faceColor, results.toTypedArray()))
|
results(data.room!!, Result(data.name, data.user!!, data.themeColor, results.toTypedArray()))
|
||||||
}
|
}
|
||||||
|
|
||||||
@POST
|
@POST
|
||||||
@@ -130,7 +110,7 @@ final class DiceResource(@Context val sse: Sse) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@RegisterForReflection
|
@RegisterForReflection
|
||||||
data class Result(val name: String, val user: String, val faceColor: String, val results: Array<Results>?) {
|
data class Result(val name: String, val user: String, val themeColor: String, val results: Array<Results>?) {
|
||||||
override fun equals(other: Any?): Boolean {
|
override fun equals(other: Any?): Boolean {
|
||||||
if (this === other) return true
|
if (this === other) return true
|
||||||
if (javaClass != other?.javaClass) return false
|
if (javaClass != other?.javaClass) return false
|
||||||
@@ -139,7 +119,7 @@ final class DiceResource(@Context val sse: Sse) {
|
|||||||
|
|
||||||
if (name != other.name) return false
|
if (name != other.name) return false
|
||||||
if (user != other.user) return false
|
if (user != other.user) return false
|
||||||
if (faceColor != other.faceColor) return false
|
if (themeColor != other.themeColor) return false
|
||||||
if (results != null) {
|
if (results != null) {
|
||||||
if (other.results == null) return false
|
if (other.results == null) return false
|
||||||
if (!results.contentEquals(other.results)) return false
|
if (!results.contentEquals(other.results)) return false
|
||||||
@@ -151,7 +131,7 @@ final class DiceResource(@Context val sse: Sse) {
|
|||||||
override fun hashCode(): Int {
|
override fun hashCode(): Int {
|
||||||
var result = name.hashCode()
|
var result = name.hashCode()
|
||||||
result = 31 * result + user.hashCode()
|
result = 31 * result + user.hashCode()
|
||||||
result = 31 * result + faceColor.hashCode()
|
result = 31 * result + themeColor.hashCode()
|
||||||
result = 31 * result + (results?.contentHashCode() ?: 0)
|
result = 31 * result + (results?.contentHashCode() ?: 0)
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ class OverlayResource {
|
|||||||
@GET
|
@GET
|
||||||
@Path("/results")
|
@Path("/results")
|
||||||
@Produces(MediaType.TEXT_HTML)
|
@Produces(MediaType.TEXT_HTML)
|
||||||
fun results(@PathParam("diceid") room: String, @QueryParam("name") name: String?): TemplateInstance {
|
fun results(@PathParam("diceid") room: String, @QueryParam("name") name: String?, @QueryParam("user") user: String?): TemplateInstance {
|
||||||
return Templates.results(room, name ?: "all")
|
return Templates.results(room, name ?: "all", user ?: "all")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,11 @@
|
|||||||
package de.arindy.dicetower
|
package de.arindy.dicetower
|
||||||
|
|
||||||
import io.quarkus.runtime.annotations.RegisterForReflection
|
|
||||||
|
|
||||||
@RegisterForReflection
|
|
||||||
data class RollPayload(
|
data class RollPayload(
|
||||||
val name: String,
|
val name: String,
|
||||||
var command: String,
|
var command: String,
|
||||||
val results: Array<DiceResource.Results>?,
|
var predetermined: Boolean? = false,
|
||||||
val faceColor: String = "white",
|
val themeColor: String = "white",
|
||||||
val numberColor: String = "white",
|
val themeForegroundColor: String = "white",
|
||||||
val theme: String = "default",
|
val theme: String = "default",
|
||||||
var room: String?,
|
var room: String?,
|
||||||
var user: String?,
|
var user: String?,
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ object Templates {
|
|||||||
external fun overlay(diceid: String, scale: Int?, clearAfter: Long?): TemplateInstance
|
external fun overlay(diceid: String, scale: Int?, clearAfter: Long?): TemplateInstance
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
external fun results(room: String, name: String?): TemplateInstance
|
external fun results(room: String, name: String?, user: String?): TemplateInstance
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
external fun index(version: String): TemplateInstance
|
external fun index(version: String): TemplateInstance
|
||||||
@@ -25,13 +25,10 @@ object Templates {
|
|||||||
modsAllowed: Boolean,
|
modsAllowed: Boolean,
|
||||||
vipAllowed: Boolean,
|
vipAllowed: Boolean,
|
||||||
subsAllowed: Boolean,
|
subsAllowed: Boolean,
|
||||||
allAllowed: Boolean,
|
|
||||||
cmd: String?,
|
cmd: String?,
|
||||||
theme: String?,
|
theme: String?,
|
||||||
faceColor: String?,
|
themeColor: String?,
|
||||||
numberColor: String?,
|
|
||||||
clearAfter: Long?,
|
clearAfter: Long?,
|
||||||
timeout: Long?,
|
timeout: Long?
|
||||||
showResults: Boolean?
|
|
||||||
): TemplateInstance
|
): TemplateInstance
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 26 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 76 KiB |
@@ -14,17 +14,6 @@ function url() {
|
|||||||
return window.location.protocol + '//' + window.location.hostname + (window.location.port?.length > 0 ? ':' + window.location.port : '');
|
return window.location.protocol + '//' + window.location.hostname + (window.location.port?.length > 0 ? ':' + window.location.port : '');
|
||||||
}
|
}
|
||||||
|
|
||||||
function register() {
|
|
||||||
let httpRequest = new XMLHttpRequest();
|
|
||||||
httpRequest.open('POST', url() + '/dice/' + document.getElementById('room').value + '/register')
|
|
||||||
httpRequest.setRequestHeader('Content-Type', 'application/json')
|
|
||||||
httpRequest.send(JSON.stringify({
|
|
||||||
name: document.getElementById('name').value,
|
|
||||||
overlay: document.getElementById('overlayId').value,
|
|
||||||
id: document.getElementById('room').value + ':' + localStorage.getItem('userId')
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
function start(event = undefined) {
|
function start(event = undefined) {
|
||||||
if ((!event || event.keyCode === 13) && document.getElementById('name').value.length > 0 && document.getElementById('room').value.length > 0) {
|
if ((!event || event.keyCode === 13) && document.getElementById('name').value.length > 0 && document.getElementById('room').value.length > 0) {
|
||||||
document.getElementById('overlayId').value = url() + '/overlay/' + document.getElementById('room').value + ':' + localStorage.getItem('userId') + '?scale=10&clearAfter=30';
|
document.getElementById('overlayId').value = url() + '/overlay/' + document.getElementById('room').value + ':' + localStorage.getItem('userId') + '?scale=10&clearAfter=30';
|
||||||
@@ -59,11 +48,11 @@ function start(event = undefined) {
|
|||||||
if (localStorage.getItem(document.getElementById('name').value + "-theme")) {
|
if (localStorage.getItem(document.getElementById('name').value + "-theme")) {
|
||||||
document.getElementById('theme').value = localStorage.getItem(document.getElementById('name').value + "-theme")
|
document.getElementById('theme').value = localStorage.getItem(document.getElementById('name').value + "-theme")
|
||||||
}
|
}
|
||||||
if (localStorage.getItem(document.getElementById('name').value + "-faceColor")) {
|
if (localStorage.getItem(document.getElementById('name').value + "-themeColor")) {
|
||||||
document.getElementById('faceColor').setColor(localStorage.getItem(document.getElementById('name').value + "-faceColor"));
|
document.getElementById('themeColor').setColor(localStorage.getItem(document.getElementById('name').value + "-themeColor"));
|
||||||
}
|
}
|
||||||
if (localStorage.getItem(document.getElementById('name').value + "-numberColor")) {
|
if (localStorage.getItem(document.getElementById('name').value + "-themeForegroundColor")) {
|
||||||
document.getElementById('numberColor').setColor(localStorage.getItem(document.getElementById('name').value + "-numberColor"));
|
document.getElementById('themeForegroundColor').setColor(localStorage.getItem(document.getElementById('name').value + "-themeForegroundColor"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!localStorage.getItem(document.getElementById('name').value + '-started')) {
|
if (!localStorage.getItem(document.getElementById('name').value + '-started')) {
|
||||||
@@ -72,7 +61,14 @@ function start(event = undefined) {
|
|||||||
|
|
||||||
localStorage.setItem(document.getElementById('name').value + '-started', "true")
|
localStorage.setItem(document.getElementById('name').value + '-started', "true")
|
||||||
|
|
||||||
register();
|
let httpRequest = new XMLHttpRequest();
|
||||||
|
httpRequest.open('POST', url() + '/dice/' + document.getElementById('room').value + '/register')
|
||||||
|
httpRequest.setRequestHeader('Content-Type', 'application/json')
|
||||||
|
httpRequest.send(JSON.stringify({
|
||||||
|
name: document.getElementById('name').value,
|
||||||
|
overlay: document.getElementById('overlayId').value,
|
||||||
|
id: document.getElementById('room').value + ':' + localStorage.getItem('userId')
|
||||||
|
}))
|
||||||
if (document.getElementById('gm').checked) {
|
if (document.getElementById('gm').checked) {
|
||||||
document.getElementById('resultSwitch').checked = true;
|
document.getElementById('resultSwitch').checked = true;
|
||||||
document.getElementById('resultFrame').src = document.getElementById('resultsId').value;
|
document.getElementById('resultFrame').src = document.getElementById('resultsId').value;
|
||||||
@@ -102,7 +98,6 @@ function start(event = undefined) {
|
|||||||
newOverlay.appendChild(newInput);
|
newOverlay.appendChild(newInput);
|
||||||
overlays.appendChild(newOverlay);
|
overlays.appendChild(newOverlay);
|
||||||
|
|
||||||
if (!document.getElementById(data.id + '-diceFrame')) {
|
|
||||||
let dice = document.createElement('iframe');
|
let dice = document.createElement('iframe');
|
||||||
dice.id = data.id + '-diceFrame'
|
dice.id = data.id + '-diceFrame'
|
||||||
dice.style.width = "50%";
|
dice.style.width = "50%";
|
||||||
@@ -115,8 +110,6 @@ function start(event = undefined) {
|
|||||||
dice.style.left = "50%";
|
dice.style.left = "50%";
|
||||||
dice.src = data.overlay;
|
dice.src = data.overlay;
|
||||||
document.getElementById('results-dice').appendChild(dice)
|
document.getElementById('results-dice').appendChild(dice)
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
configurePopover();
|
configurePopover();
|
||||||
@@ -132,15 +125,14 @@ function rollEasy(dice) {
|
|||||||
|
|
||||||
function roll(event) {
|
function roll(event) {
|
||||||
if ((!event || event.keyCode === 13) && document.getElementById('command').value?.length > 0) {
|
if ((!event || event.keyCode === 13) && document.getElementById('command').value?.length > 0) {
|
||||||
register()
|
|
||||||
let httpRequest = new XMLHttpRequest();
|
let httpRequest = new XMLHttpRequest();
|
||||||
httpRequest.open('POST', url() + '/dice/' + document.getElementById('room').value + ':' + localStorage.getItem(`userId`))
|
httpRequest.open('POST', url() + '/dice/' + document.getElementById('room').value + ':' + localStorage.getItem(`userId`))
|
||||||
httpRequest.setRequestHeader('Content-Type', 'application/json')
|
httpRequest.setRequestHeader('Content-Type', 'application/json')
|
||||||
httpRequest.send(JSON.stringify({
|
httpRequest.send(JSON.stringify({
|
||||||
name: document.getElementById('name').value,
|
name: document.getElementById('name').value,
|
||||||
command: document.getElementById('command').value,
|
command: document.getElementById('command').value,
|
||||||
faceColor: document.getElementById('faceColor').value,
|
themeColor: document.getElementById('themeColor').value,
|
||||||
numberColor: document.getElementById('numberColor').value,
|
themeForegroundColor: document.getElementById('themeForegroundColor').value,
|
||||||
theme: document.getElementById('theme').value
|
theme: document.getElementById('theme').value
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
@@ -148,8 +140,8 @@ function roll(event) {
|
|||||||
|
|
||||||
function saveDice() {
|
function saveDice() {
|
||||||
localStorage.setItem(document.getElementById('name').value + "-theme", document.getElementById('theme').value)
|
localStorage.setItem(document.getElementById('name').value + "-theme", document.getElementById('theme').value)
|
||||||
localStorage.setItem(document.getElementById('name').value + "-faceColor", document.getElementById('faceColor').value)
|
localStorage.setItem(document.getElementById('name').value + "-themeColor", document.getElementById('themeColor').value)
|
||||||
localStorage.setItem(document.getElementById('name').value + "-numberColor", document.getElementById('numberColor').value)
|
localStorage.setItem(document.getElementById('name').value + "-themeForegroundColor", document.getElementById('themeForegroundColor').value)
|
||||||
}
|
}
|
||||||
|
|
||||||
function configurePopover() {
|
function configurePopover() {
|
||||||
@@ -198,7 +190,6 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|||||||
document.querySelector('meta[property="twitter:url"]').setAttribute("content", url());
|
document.querySelector('meta[property="twitter:url"]').setAttribute("content", url());
|
||||||
document.querySelector('meta[property="og:image"]').setAttribute("content", url() + '/rich.png');
|
document.querySelector('meta[property="og:image"]').setAttribute("content", url() + '/rich.png');
|
||||||
document.querySelector('meta[name="twitter:image"]').setAttribute("content", url() + '/rich.png');
|
document.querySelector('meta[name="twitter:image"]').setAttribute("content", url() + '/rich.png');
|
||||||
document.querySelector('meta[property="twitter:domain"]').setAttribute("content", window.location.hostname);
|
|
||||||
|
|
||||||
if (localStorage.getItem('last-name') && localStorage.getItem('last-room')) {
|
if (localStorage.getItem('last-name') && localStorage.getItem('last-room')) {
|
||||||
document.getElementById('name').value = localStorage.getItem('last-name');
|
document.getElementById('name').value = localStorage.getItem('last-name');
|
||||||
@@ -215,7 +206,11 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
document.getElementById('resultDiceSwitch').addEventListener('change', function () {
|
document.getElementById('resultDiceSwitch').addEventListener('change', function () {
|
||||||
document.getElementById('results-dice').hidden = !this.checked;
|
if (!this.checked) {
|
||||||
|
document.getElementById('results-dice').hidden = true
|
||||||
|
} else {
|
||||||
|
document.getElementById('results-dice').hidden = false
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
document.getElementById('chatOverlayLink').href = url() + '/chatoverlay'
|
document.getElementById('chatOverlayLink').href = url() + '/chatoverlay'
|
||||||
@@ -223,3 +218,6 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|||||||
configurePopover();
|
configurePopover();
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if (!localStorage.getItem("userId")) {
|
||||||
|
localStorage.setItem("userId", self.crypto.randomUUID());
|
||||||
|
}
|
||||||
|
|||||||
@@ -115,8 +115,8 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|||||||
diceBox.clearDice();
|
diceBox.clearDice();
|
||||||
await diceBox.updateConfig({
|
await diceBox.updateConfig({
|
||||||
theme_customColorset: {
|
theme_customColorset: {
|
||||||
background: document.getElementById('faceColor').value,
|
background: document.getElementById('themeColor').value,
|
||||||
foreground: document.getElementById('numberColor').value,
|
foreground: document.getElementById('themeForegroundColor').value,
|
||||||
texture: document.getElementById('theme').value
|
texture: document.getElementById('theme').value
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "Dice-Tower",
|
|
||||||
"icons": [
|
|
||||||
{
|
|
||||||
"src": "192.png",
|
|
||||||
"type": "image/png",
|
|
||||||
"sizes": "192x192"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"src": "512.png",
|
|
||||||
"type": "image/png",
|
|
||||||
"sizes": "512x512"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"start_url": "/",
|
|
||||||
"display_override": ["window-controls-overlay", "minimal-ui"],
|
|
||||||
"display": "standalone"
|
|
||||||
}
|
|
||||||
40
src/main/resources/META-INF/resources/rich-preview.js
Normal file
40
src/main/resources/META-INF/resources/rich-preview.js
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
const title = `${document.querySelector('title').textContent}`
|
||||||
|
const dexcription = ` Easy to use online dice rolling with customizable overlays.`
|
||||||
|
function url() {
|
||||||
|
return window.location.protocol + '//' + window.location.hostname + (window.location.port?.length > 0 ? ':' + window.location.port : '');
|
||||||
|
}
|
||||||
|
|
||||||
|
const image = url() + '/rich.png'
|
||||||
|
|
||||||
|
function createMetaTag(name, property, content) {
|
||||||
|
let tag = document.createElement('meta');
|
||||||
|
if (name) {
|
||||||
|
tag.setAttribute('name', name)
|
||||||
|
}
|
||||||
|
if (property) {
|
||||||
|
tag.setAttribute('property', property)
|
||||||
|
}
|
||||||
|
tag.setAttribute('content', content)
|
||||||
|
return tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createTags() {
|
||||||
|
return [
|
||||||
|
createMetaTag('description', undefined, dexcription),
|
||||||
|
createMetaTag(undefined, 'og:url', url()),
|
||||||
|
createMetaTag(undefined, 'og:image', image),
|
||||||
|
createMetaTag(undefined, 'og:description', dexcription),
|
||||||
|
createMetaTag(undefined, 'og:title', title),
|
||||||
|
createMetaTag(undefined, 'og:site_name', title),
|
||||||
|
createMetaTag(undefined, 'og:type', 'website'),
|
||||||
|
createMetaTag(undefined, 'twitter:url', url()),
|
||||||
|
createMetaTag(undefined, 'twitter:domain', document.location.hostname),
|
||||||
|
createMetaTag('twitter:title', undefined, title),
|
||||||
|
createMetaTag('twitter:image', undefined, image),
|
||||||
|
createMetaTag('twitter:card', undefined, 'summary_large_image'),
|
||||||
|
createMetaTag('twitter:description', undefined, dexcription),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
document.addEventListener("DOMContentLoaded", async () => {
|
||||||
|
createTags().forEach(tag => document.head.appendChild(tag))
|
||||||
|
})
|
||||||
@@ -32,22 +32,6 @@ button:active {
|
|||||||
background: #222222;
|
background: #222222;
|
||||||
}
|
}
|
||||||
|
|
||||||
#install {
|
|
||||||
padding: 10px;
|
|
||||||
border: #666666 3px solid;
|
|
||||||
border-radius: 10px;
|
|
||||||
background: #666666;
|
|
||||||
color: #fff
|
|
||||||
}
|
|
||||||
|
|
||||||
#install:hover {
|
|
||||||
background: #444444;
|
|
||||||
}
|
|
||||||
|
|
||||||
#install:active {
|
|
||||||
background: #222222;
|
|
||||||
}
|
|
||||||
|
|
||||||
input {
|
input {
|
||||||
margin: 10px;
|
margin: 10px;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -123,15 +123,15 @@ class ColorPicker extends HTMLElement {
|
|||||||
<div class="bg-cover" id="controls">
|
<div class="bg-cover" id="controls">
|
||||||
<label for="hue">
|
<label for="hue">
|
||||||
<abbr title="Hue: The color tint.">H</abbr>
|
<abbr title="Hue: The color tint.">H</abbr>
|
||||||
<input type="range" id="hue" min="0" max="360" value="0" step="0.01">
|
<input type="range" id="hue" min="0" max="360" value="0">
|
||||||
</label>
|
</label>
|
||||||
<label for="saturation">
|
<label for="saturation">
|
||||||
<abbr title="Saturation: The amount of gray in the color.">S</abbr>
|
<abbr title="Saturation: The amount of gray in the color.">S</abbr>
|
||||||
<input type="range" id="saturation" min="0" max="100" value="0" step="0.01">
|
<input type="range" id="saturation" min="0" max="100" value="0">
|
||||||
</label>
|
</label>
|
||||||
<label for="lightness">
|
<label for="lightness">
|
||||||
<abbr title="Lightness: How light or dark the color is.">L</abbr>
|
<abbr title="Lightness: How light or dark the color is.">L</abbr>
|
||||||
<input type="range" id="lightness" min="0" max="100" value="0" step="0.01">
|
<input type="range" id="lightness" min="0" max="100" value="0">
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>`;
|
</div>`;
|
||||||
@@ -251,9 +251,9 @@ class ColorPicker extends HTMLElement {
|
|||||||
*/
|
*/
|
||||||
updateColor() {
|
updateColor() {
|
||||||
|
|
||||||
const hue = parseFloat( this.hueInput.value );
|
const hue = Math.round( this.hueInput.value );
|
||||||
const saturation = parseFloat( this.saturationInput.value );
|
const saturation = Math.round( this.saturationInput.value );
|
||||||
const lightness = parseFloat( this.lightnessInput.value );
|
const lightness = Math.round( this.lightnessInput.value );
|
||||||
const hsla = [ hue, saturation, lightness ];
|
const hsla = [ hue, saturation, lightness ];
|
||||||
|
|
||||||
const rgba = this.HSLAToRGBA( hsla );
|
const rgba = this.HSLAToRGBA( hsla );
|
||||||
@@ -324,10 +324,6 @@ class ColorPicker extends HTMLElement {
|
|||||||
*/
|
*/
|
||||||
setColor( color = 'hsla(60, 100%, 50%, 0.5)' ) {
|
setColor( color = 'hsla(60, 100%, 50%, 0.5)' ) {
|
||||||
|
|
||||||
if ( /^[0-9A-F]{3,6}$/i.test( color ) ) {
|
|
||||||
color = '#' + color;
|
|
||||||
}
|
|
||||||
|
|
||||||
const rgba = this.colorToRGBA( color );
|
const rgba = this.colorToRGBA( color );
|
||||||
const hsla = this.rgbToHSLA( rgba );
|
const hsla = this.rgbToHSLA( rgba );
|
||||||
|
|
||||||
@@ -488,8 +484,7 @@ class ColorPicker extends HTMLElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Calculate hue
|
// Calculate hue
|
||||||
let hue = 0;
|
let hue;
|
||||||
if ( max !== min ) {
|
|
||||||
if ( max === normalizedR ) {
|
if ( max === normalizedR ) {
|
||||||
hue = ( ( normalizedG - normalizedB ) / ( max - min ) ) % 6;
|
hue = ( ( normalizedG - normalizedB ) / ( max - min ) ) % 6;
|
||||||
} else if ( max === normalizedG ) {
|
} else if ( max === normalizedG ) {
|
||||||
@@ -497,7 +492,6 @@ class ColorPicker extends HTMLElement {
|
|||||||
} else {
|
} else {
|
||||||
hue = ( ( normalizedR - normalizedG ) / ( max - min ) + 4 );
|
hue = ( ( normalizedR - normalizedG ) / ( max - min ) + 4 );
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Convert hue to degrees
|
// Convert hue to degrees
|
||||||
hue *= 60;
|
hue *= 60;
|
||||||
@@ -506,7 +500,7 @@ class ColorPicker extends HTMLElement {
|
|||||||
hue += 360;
|
hue += 360;
|
||||||
}
|
}
|
||||||
|
|
||||||
return [ parseFloat(hue.toFixed(2)), parseFloat((saturation * 100).toFixed(2)), parseFloat((lightness * 100).toFixed(2)), a ];
|
return [ Math.round( hue ), Math.round( saturation * 100 ), Math.round( lightness * 100 ), a ];
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,42 +12,37 @@
|
|||||||
<div popover id="results" class="tooltip">
|
<div popover id="results" class="tooltip">
|
||||||
</div>
|
</div>
|
||||||
<script type="module">
|
<script type="module">
|
||||||
import DiceBox from "/vendor/dice-box/dice-box-threejs.es.js";
|
import DiceBox from "/vendor/dice-box/dice-box.es.js";
|
||||||
|
|
||||||
const diceBox = new DiceBox("#dice-box", {
|
const diceBox = new DiceBox("#dice-box", {
|
||||||
assetPath: "/vendor/dice-box/",
|
assetPath: "/vendor/assets/",
|
||||||
light_intensity: 2,
|
theme: '{theme}',
|
||||||
gravity_multiplier: 600,
|
themeColor: '{themeColor}',
|
||||||
baseScale: {scale} * 10,
|
scale: {scale}
|
||||||
strength: Math.floor(Math.random() * 4),
|
|
||||||
theme_customColorset: {
|
|
||||||
texture: '{theme}',
|
|
||||||
background: '{faceColor}',
|
|
||||||
foreground: '{numberColor}'
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
diceBox.initialize();
|
diceBox.init()
|
||||||
ComfyJS.Init('{channel}');
|
ComfyJS.Init('{channel}');
|
||||||
|
|
||||||
//maxDice
|
//maxDice
|
||||||
|
|
||||||
ComfyJS.onCommand = async (user, command, message, flags) => {
|
ComfyJS.onCommand = async (user, command, message, flags) => {
|
||||||
if ((flags.broadcaster || {allAllowed} || ({modsAllowed} && flags.mod) || ({vipAllowed} && flags.vip) || ({subsAllowed} && flags.subscriber)) && '{cmd}' === command && !shouldWait() && message.match(/^\d+d(4|6|8|10|12|20|100)$/) && (+message.split('d')[0] <= {maxDice})
|
if ((
|
||||||
|
flags.broadcaster || ({modsAllowed} && flags.mod) || ({vipAllowed} && flags.vip) || ({subsAllowed} && flags.subscriber
|
||||||
|
)) && '{cmd}' === command && !shouldWait() && message.match(/^\d+d(4|6|8|10|12|20|100)$/) && (+message.split('d')[0] <= {maxDice})
|
||||||
) {
|
) {
|
||||||
toggleWait(true);
|
toggleWait(true);
|
||||||
|
|
||||||
diceBox.onRollComplete = (rollResult) => {
|
diceBox.onRollComplete = (rollResult) => {
|
||||||
rollResult.sets.forEach(result => {
|
rollResult.forEach(result => {
|
||||||
let values = []
|
let values = []
|
||||||
result.rolls.forEach(roll => {
|
result.rolls.forEach(roll => {
|
||||||
values.push(roll.value);
|
values.push(roll.value);
|
||||||
})
|
})
|
||||||
document.getElementById('results').innerHTML = '<strong>' + user + '</strong> rolls <strong>' + message + '</strong>:<br/> [' + values.map(value => value === 1 ? '<strong style="text-shadow: 2px 2px 10px red">' + value + '</strong>' : value === result.sides ? '<strong style="text-shadow: 2px 2px 10px green">' + value + '</strong>' : value).join(' + ') + '] = <strong>' + result.total + '</strong> '
|
document.getElementById('results').innerHTML = '<strong>' + user + '</strong> rolls <strong>' + message + '</strong>:<br/> [' + values.map(value => value === 1 ? '<strong style="text-shadow: 2px 2px 10px red">' + value + '</strong>' : value === result.sides ? '<strong style="text-shadow: 2px 2px 10px green">' + value + '</strong>' : value).join(' + ') + (result.modifier > 0 ? ' <a style="text-decoration: underline">+' + result.modifier + '</a>' : result.modifier < 0 ? ' <a style="text-decoration: underline">' + result.modifier + '</a>' : '') + '] = <strong>' + result.value + '</strong> '
|
||||||
})
|
})
|
||||||
if({showResults}) {
|
|
||||||
document.getElementById('results').showPopover()
|
document.getElementById('results').showPopover()
|
||||||
}
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
diceBox.clearDice();
|
diceBox.clear();
|
||||||
document.getElementById('results').hidePopover()
|
document.getElementById('results').hidePopover()
|
||||||
}, {clearAfter} * 1000)
|
}, {clearAfter} * 1000)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,16 +23,18 @@
|
|||||||
<div class="w3-panel w3-theme-l4 w3-card w3-display-container"
|
<div class="w3-panel w3-theme-l4 w3-card w3-display-container"
|
||||||
style="padding: 25px; text-align: center; margin-bottom: auto;">
|
style="padding: 25px; text-align: center; margin-bottom: auto;">
|
||||||
<label for="theme">Theme </label>
|
<label for="theme">Theme </label>
|
||||||
<select name="theme" id="theme" style="margin: 0 25px"></select>
|
<select name="theme" id="theme" style="margin: 25px">
|
||||||
|
<option value="default">Default</option>
|
||||||
<div style="display: flex; flex-direction: row; justify-content: space-between; align-items: baseline">
|
<option value="blueGreenMetal">Blue-Green Metal</option>
|
||||||
<div style="flex-grow: 1; padding: 0 10px">
|
<option value="diceOfRolling">Dice of Rolling</option>
|
||||||
<color-picker id="faceColor" name="Face" value="black"></color-picker>
|
<option value="gemstone">Gemstone</option>
|
||||||
</div>
|
<option value="gemstoneMarble">Marble Gemstone</option>
|
||||||
<div style="flex-grow: 1; padding: 10px 0">
|
<option value="rock">Rock</option>
|
||||||
<color-picker id="numberColor" name="Numbers" value="white"></color-picker>
|
<option value="rust">Rust</option>
|
||||||
</div>
|
<option value="smooth">Smooth</option>
|
||||||
</div>
|
<option value="wooden">Wooden</option>
|
||||||
|
</select>
|
||||||
|
<color-picker id="themeColor"></color-picker>
|
||||||
<div>
|
<div>
|
||||||
<label for="channel">Channel </label>
|
<label for="channel">Channel </label>
|
||||||
<input type="text" id="channel" style="width: 400px; margin-top: 20px" value="arindy"/>
|
<input type="text" id="channel" style="width: 400px; margin-top: 20px" value="arindy"/>
|
||||||
@@ -52,14 +54,10 @@
|
|||||||
<input type="checkbox" id="subsAllowed">
|
<input type="checkbox" id="subsAllowed">
|
||||||
<span class="checkmark"></span>
|
<span class="checkmark"></span>
|
||||||
</label>
|
</label>
|
||||||
<label class="checkbox" id="allAllowed-container">Allow everyone to roll (yes, everyone!!)
|
|
||||||
<input type="checkbox" id="allAllowed">
|
|
||||||
<span class="checkmark"></span>
|
|
||||||
</label>
|
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label for="scale">Dice-Scale </label>
|
<label for="scale">Dice-Scale </label>
|
||||||
<input type="number" id="scale" style="width: 50px; margin-top: 20px" value="15"/>
|
<input type="number" id="scale" style="width: 50px; margin-top: 20px" value="9"/>
|
||||||
<label for="maxDice">Max number of dice </label>
|
<label for="maxDice">Max number of dice </label>
|
||||||
<input type="number" id="maxDice" style="width: 50px; margin-top: 20px" value="20"/>
|
<input type="number" id="maxDice" style="width: 50px; margin-top: 20px" value="20"/>
|
||||||
</div>
|
</div>
|
||||||
@@ -68,15 +66,8 @@
|
|||||||
<input type="number" id="clearAfter" style="width: 50px; margin-top: 20px" value="10"/>
|
<input type="number" id="clearAfter" style="width: 50px; margin-top: 20px" value="10"/>
|
||||||
<label for="timeout">Command-timeout (in seconds)</label>
|
<label for="timeout">Command-timeout (in seconds)</label>
|
||||||
<input type="number" id="timeout" style="width: 50px; margin-top: 20px" value="60"/>
|
<input type="number" id="timeout" style="width: 50px; margin-top: 20px" value="60"/>
|
||||||
<label class="checkbox" id="showResults-container">Show Results Overlay
|
|
||||||
<input type="checkbox" id="showResults" checked>
|
|
||||||
<span class="checkmark"></span>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="dice-box" style="width: 850px; height: 400px">
|
|
||||||
<div id="app"></div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div id="dice-box" style="height: 400px"></div>
|
||||||
<button style="margin: 10px" id="preview">Preview <i class="fa-solid fa-magnifying-glass"></i></button>
|
<button style="margin: 10px" id="preview">Preview <i class="fa-solid fa-magnifying-glass"></i></button>
|
||||||
<button style="margin: 10px" id="generate">Generate overlay-link <i class="fa-solid fa-link"></i></button>
|
<button style="margin: 10px" id="generate">Generate overlay-link <i class="fa-solid fa-link"></i></button>
|
||||||
<div>
|
<div>
|
||||||
@@ -113,148 +104,37 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<script type="module">
|
<script type="module">
|
||||||
import DiceBox from "/vendor/dice-box/dice-box-threejs.es.js";
|
import DiceBox from "/vendor/dice-box/dice-box.es.js";
|
||||||
let diceBox
|
|
||||||
|
|
||||||
const Themes = {
|
|
||||||
cloudy: {
|
|
||||||
name: "Clouds (Transparent)"
|
|
||||||
},
|
|
||||||
cloudy_2: {
|
|
||||||
name: "Clouds"
|
|
||||||
},
|
|
||||||
fire: {
|
|
||||||
name: "Fire"
|
|
||||||
},
|
|
||||||
marble: {
|
|
||||||
name: "Marble"
|
|
||||||
},
|
|
||||||
water: {
|
|
||||||
name: "Water"
|
|
||||||
},
|
|
||||||
ice: {
|
|
||||||
name: "Ice"
|
|
||||||
},
|
|
||||||
paper: {
|
|
||||||
name: "Paper"
|
|
||||||
},
|
|
||||||
speckles: {
|
|
||||||
name: "Speckles"
|
|
||||||
},
|
|
||||||
glitter: {
|
|
||||||
name: "Glitter"
|
|
||||||
},
|
|
||||||
glitter_2: {
|
|
||||||
name: "Glitter (Transparent)"
|
|
||||||
},
|
|
||||||
stars: {
|
|
||||||
name: "Stars"
|
|
||||||
},
|
|
||||||
stainedglass: {
|
|
||||||
name: "Stained Glass"
|
|
||||||
},
|
|
||||||
wood: {
|
|
||||||
name: "Wood"
|
|
||||||
},
|
|
||||||
metal: {
|
|
||||||
name: "Stainless Steel"
|
|
||||||
},
|
|
||||||
skulls: {
|
|
||||||
name: "Skulls"
|
|
||||||
},
|
|
||||||
leopard: {
|
|
||||||
name: "Leopard"
|
|
||||||
},
|
|
||||||
tiger: {
|
|
||||||
name: "Tiger"
|
|
||||||
},
|
|
||||||
cheetah: {
|
|
||||||
name: "Cheetah"
|
|
||||||
},
|
|
||||||
dragon: {
|
|
||||||
name: "Dragon"
|
|
||||||
},
|
|
||||||
lizard: {
|
|
||||||
name: "Lizard"
|
|
||||||
},
|
|
||||||
bird: {
|
|
||||||
name: "Bird"
|
|
||||||
},
|
|
||||||
astral: {
|
|
||||||
name: "Astral Sea"
|
|
||||||
},
|
|
||||||
bronze01: {
|
|
||||||
name: "Bronze 1"
|
|
||||||
},
|
|
||||||
bronze02: {
|
|
||||||
name: "Bronze 2"
|
|
||||||
},
|
|
||||||
bronze03: {
|
|
||||||
name: "Bronze 3"
|
|
||||||
},
|
|
||||||
bronze03a: {
|
|
||||||
name: "Bronze 3a"
|
|
||||||
},
|
|
||||||
bronze03b: {
|
|
||||||
name: "Bronze 3b"
|
|
||||||
},
|
|
||||||
bronze04: {
|
|
||||||
name: "Bronze 4"
|
|
||||||
},
|
|
||||||
none: {
|
|
||||||
name: "none"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function url() {
|
function url() {
|
||||||
return window.location.protocol + '//' + window.location.hostname + (window.location.port?.length > 0 ? ':' + window.location.port : '');
|
return window.location.protocol + '//' + window.location.hostname + (window.location.port?.length > 0 ? ':' + window.location.port : '');
|
||||||
}
|
}
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", async () => {
|
document.addEventListener("DOMContentLoaded", async () => {
|
||||||
const themeSelector = document.getElementById('theme');
|
|
||||||
for (const theme in Themes) {
|
|
||||||
let option = document.createElement('option');
|
|
||||||
option.value = theme;
|
|
||||||
option.innerText = Themes[theme].name;
|
|
||||||
themeSelector.appendChild(option);
|
|
||||||
}
|
|
||||||
themeSelector.value = 'cloudy';
|
|
||||||
document.getElementById('preview').onclick = async () => {
|
document.getElementById('preview').onclick = async () => {
|
||||||
document.getElementById('app').replaceChildren(...[])
|
document.getElementById('dice-box').replaceChildren(...[])
|
||||||
diceBox = new DiceBox("#app", {
|
const diceBox = new DiceBox("#dice-box", {
|
||||||
assetPath: "/vendor/dice-box/",
|
assetPath: "/vendor/assets/",
|
||||||
light_intensity: 2,
|
theme: document.getElementById('theme').value,
|
||||||
gravity_multiplier: 600,
|
themeColor: document.getElementById('themeColor').value,
|
||||||
baseScale: 120,
|
scale: +document.getElementById('scale').value
|
||||||
strength: Math.floor(Math.random() * 4),
|
|
||||||
});
|
});
|
||||||
await diceBox.initialize();
|
await diceBox.init()
|
||||||
diceBox.clearDice();
|
diceBox.roll(['1d2', '1d4', '1d6', '1d8', '1d10', '1d12', '1d20', '1d100']);
|
||||||
await diceBox.updateConfig({
|
|
||||||
theme_customColorset: {
|
|
||||||
background: document.getElementById('faceColor').value,
|
|
||||||
foreground: document.getElementById('numberColor').value,
|
|
||||||
texture: document.getElementById('theme').value
|
|
||||||
}
|
|
||||||
});
|
|
||||||
await diceBox.roll('1d2 & 1d4 & 1d6 & 1d8 & 1d10 & 1d12 & 1d20 & 1d100');
|
|
||||||
}
|
}
|
||||||
document.getElementById('generate').onclick = async () => {
|
document.getElementById('generate').onclick = async () => {
|
||||||
document.getElementById('link').value = url() +
|
document.getElementById('link').value = url() +
|
||||||
"/chatoverlay/" + document.getElementById('channel').value +
|
"/chatoverlay/" + document.getElementById('channel').value +
|
||||||
"?cmd=" + document.getElementById('cmd').value +
|
"?cmd=" + document.getElementById('cmd').value +
|
||||||
"&theme=" + document.getElementById('theme').value +
|
"&theme=" + document.getElementById('theme').value +
|
||||||
"&faceColor=" + encodeURIComponent(document.getElementById('faceColor').value) +
|
"&themeColor=" + encodeURIComponent(document.getElementById('themeColor').value) +
|
||||||
"&numberColor=" + encodeURIComponent(document.getElementById('numberColor').value) +
|
|
||||||
"&scale=" + document.getElementById('scale').value +
|
"&scale=" + document.getElementById('scale').value +
|
||||||
"&maxDice=" + document.getElementById('maxDice').value +
|
"&maxDice=" + document.getElementById('maxDice').value +
|
||||||
"&clearAfter=" + document.getElementById('clearAfter').value +
|
"&clearAfter=" + document.getElementById('clearAfter').value +
|
||||||
"&timeout=" + document.getElementById('timeout').value +
|
"&timeout=" + document.getElementById('timeout').value +
|
||||||
"&modsAllowed=" + document.getElementById('modsAllowed').checked +
|
"&modsAllowed=" + document.getElementById('modsAllowed').checked +
|
||||||
"&vipAllowed=" + document.getElementById('vipAllowed').checked +
|
"&vipAllowed=" + document.getElementById('vipAllowed').checked +
|
||||||
"&subsAllowed=" + document.getElementById('subsAllowed').checked +
|
"&subsAllowed=" + document.getElementById('subsAllowed').checked
|
||||||
"&allAllowed=" + document.getElementById('allAllowed').checked +
|
|
||||||
"&showResults=" + document.getElementById('showResults').checked
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>Dice-Tower</title>
|
<title>Dice-Tower</title>
|
||||||
<meta name="version" content="{version}">
|
<meta name="version" content="{version}">
|
||||||
<link rel="manifest" href="manifest.json" />
|
|
||||||
<link rel="stylesheet" href="/vendor/w3css/4/w3.css">
|
<link rel="stylesheet" href="/vendor/w3css/4/w3.css">
|
||||||
<link rel="stylesheet" href="/vendor/font-awesome/css/fontawesome.css">
|
<link rel="stylesheet" href="/vendor/font-awesome/css/fontawesome.css">
|
||||||
<link rel="stylesheet" href="/vendor/font-awesome/css/all.css">
|
<link rel="stylesheet" href="/vendor/font-awesome/css/all.css">
|
||||||
@@ -12,39 +12,10 @@
|
|||||||
<link rel="icon" type="image/png" href="/favicon.png">
|
<link rel="icon" type="image/png" href="/favicon.png">
|
||||||
<script src="/vendor/color-picker.js"></script>
|
<script src="/vendor/color-picker.js"></script>
|
||||||
<script type="module" src="/dice-preview.js"></script>
|
<script type="module" src="/dice-preview.js"></script>
|
||||||
|
<script type="text/javascript" src="/rich-preview.js"></script>
|
||||||
<script type="text/javascript" src="/app.js"></script>
|
<script type="text/javascript" src="/app.js"></script>
|
||||||
|
|
||||||
<meta name="twitter:card" content="summary_large_image">
|
|
||||||
<meta name="twitter:title" content="Dice-Tower">
|
|
||||||
<meta name="twitter:image" content="{http:request.scheme}://{http:request.authority}/rich.png">
|
|
||||||
<meta name="description" content="Easy to use online dice rolling with customizable overlays.">
|
|
||||||
<meta name="twitter:description" content="Easy to use online dice rolling with customizable overlays.">
|
|
||||||
<meta property="og:url" content="{http:request.absoluteURI}">
|
|
||||||
<meta property="og:image" content="{http:request.scheme}://{http:request.authority}/rich.png">
|
|
||||||
<meta property="og:description" content="Easy to use online dice rolling with customizable overlays.">
|
|
||||||
<meta property="og:title" content="Dice-Tower">
|
|
||||||
<meta property="og:site_name" content="Dice-Tower">
|
|
||||||
<meta property="og:type" content="website">
|
|
||||||
<meta property="twitter:url" content="{http:request.absoluteURI}">
|
|
||||||
<meta property="twitter:domain" content="{http:request.authority}">
|
|
||||||
</head>
|
</head>
|
||||||
<body class="w3-theme-l1">
|
<body class="w3-theme-l1">
|
||||||
<script>
|
|
||||||
if (!localStorage.getItem("userId")) {
|
|
||||||
localStorage.setItem("userId", self.crypto.randomUUID());
|
|
||||||
}
|
|
||||||
addEventListener("beforeinstallprompt", (event) => {
|
|
||||||
event.preventDefault()
|
|
||||||
let install = document.createElement('button');
|
|
||||||
install.id = 'install'
|
|
||||||
install.style.position = 'absolute'
|
|
||||||
install.style.top = '25px'
|
|
||||||
install.style.right = '25px'
|
|
||||||
install.innerHTML = 'Install Dice-Tower'
|
|
||||||
install.onclick = () => event.prompt()
|
|
||||||
document.body.appendChild(install)
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
<div class="w3-container w3-content"
|
<div class="w3-container w3-content"
|
||||||
style="height: 95vh; display: flex; flex-direction: column; justify-content: space-between; padding: 25px">
|
style="height: 95vh; display: flex; flex-direction: column; justify-content: space-between; padding: 25px">
|
||||||
<h1 style="text-align: center"><i class="fa-solid fa-dice-d20"></i> Dice-Tower <i class="fa-solid fa-dice-d20"></i>
|
<h1 style="text-align: center"><i class="fa-solid fa-dice-d20"></i> Dice-Tower <i class="fa-solid fa-dice-d20"></i>
|
||||||
@@ -89,7 +60,6 @@
|
|||||||
<button style="border: transparent; border-radius: 100%; font-size: large; font-weight: bold; height: 50px; width: 50px"
|
<button style="border: transparent; border-radius: 100%; font-size: large; font-weight: bold; height: 50px; width: 50px"
|
||||||
onclick="addDice()">+
|
onclick="addDice()">+
|
||||||
</button>
|
</button>
|
||||||
<!--<button style="font-size: large; font-weight: bold;" onclick="rollEasy('d2')">D2</button><-->
|
|
||||||
<button style="font-size: large; font-weight: bold;" onclick="rollEasy('d4')">D4</button>
|
<button style="font-size: large; font-weight: bold;" onclick="rollEasy('d4')">D4</button>
|
||||||
<button style="font-size: large; font-weight: bold;" onclick="rollEasy('d6')">D6</button>
|
<button style="font-size: large; font-weight: bold;" onclick="rollEasy('d6')">D6</button>
|
||||||
<button style="font-size: large; font-weight: bold;" onclick="rollEasy('d8')">D8</button>
|
<button style="font-size: large; font-weight: bold;" onclick="rollEasy('d8')">D8</button>
|
||||||
@@ -150,10 +120,10 @@
|
|||||||
</div>
|
</div>
|
||||||
<div style="display: flex; flex-direction: row; justify-content: space-between; align-items: baseline">
|
<div style="display: flex; flex-direction: row; justify-content: space-between; align-items: baseline">
|
||||||
<div style="flex-grow: 1; padding: 0 10px">
|
<div style="flex-grow: 1; padding: 0 10px">
|
||||||
<color-picker id="faceColor" name="Face" value="#8d8981"></color-picker>
|
<color-picker id="themeColor" name="Face"></color-picker>
|
||||||
</div>
|
</div>
|
||||||
<div style="flex-grow: 1; padding: 10px 0">
|
<div style="flex-grow: 1; padding: 10px 0">
|
||||||
<color-picker id="numberColor" name="Numbers" value="black"></color-picker>
|
<color-picker id="themeForegroundColor" name="Numbers"></color-picker>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="dice-box">
|
<div id="dice-box">
|
||||||
@@ -202,8 +172,11 @@
|
|||||||
<h2 style="text-align: center">How-To</h2>
|
<h2 style="text-align: center">How-To</h2>
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
Join a room by entering your character name and the name of the room
|
Join a room by entering your character name and the name of the room.<br/>
|
||||||
|
<strong>If you are a GM, make sure to join the room first or let all other players rejoin to get all
|
||||||
|
Overlay-URLs.</strong>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li>Open your Dice-Overlay either in a new Tab or as a browser source in OBS</li>
|
<li>Open your Dice-Overlay either in a new Tab or as a browser source in OBS</li>
|
||||||
<ul>
|
<ul>
|
||||||
<li>You can configure your Overlay with query parameters (for more information hover over the link)
|
<li>You can configure your Overlay with query parameters (for more information hover over the link)
|
||||||
|
|||||||
@@ -36,11 +36,11 @@
|
|||||||
diceBox.updateConfig({
|
diceBox.updateConfig({
|
||||||
theme_customColorset: {
|
theme_customColorset: {
|
||||||
texture: data.theme,
|
texture: data.theme,
|
||||||
background: data.faceColor,
|
background: data.themeColor,
|
||||||
foreground: data.numberColor
|
foreground: data.themeForegroundColor
|
||||||
}
|
}
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
diceBox.roll(data.roll.filter(it => it.split('@')[0].split('d')[1] !== "2").join('&'));
|
diceBox.roll(data.roll.join('&'));
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -17,9 +17,9 @@
|
|||||||
const evtSource = new EventSource(url() + '/dice/{room}/results');
|
const evtSource = new EventSource(url() + '/dice/{room}/results');
|
||||||
evtSource.addEventListener('message', function (event) {
|
evtSource.addEventListener('message', function (event) {
|
||||||
let data = JSON.parse(event.data);
|
let data = JSON.parse(event.data);
|
||||||
if ("{name}" === "all" || "{name}" === data.name) {
|
if ("{name}" === "all" && "{user}" === "all" || "{name}" === data.name && "{user}" === data.user || "{name}" === "all" && "{user}" === data.user || "{name}" === data.name && "{user}" === "all") {
|
||||||
let name = document.getElementById(data.name) ?? document.createElement('div');
|
let name = document.getElementById(data.user + '-' + data.name) ?? document.createElement('div');
|
||||||
name.id = data.name;
|
name.id = data.user + '-' + data.name;
|
||||||
name.replaceChildren(...[]);
|
name.replaceChildren(...[]);
|
||||||
let node = document.createElement('p');
|
let node = document.createElement('p');
|
||||||
let resultText = ''
|
let resultText = ''
|
||||||
@@ -34,7 +34,7 @@
|
|||||||
resultText += '<br/><strong>  D' + result.sides + '</strong>: [' + values.map(value => value === 1 ? '<strong style="text-shadow: 2px 2px 10px red">' + value + '</strong>' : value === result.sides ? '<strong style="text-shadow: 2px 2px 10px green">' + value + '</strong>' : value).join(' + ') + (result.modifier > 0 ? ' <a style="text-decoration: underline">+' + result.modifier + '</a>' : result.modifier < 0 ? ' <a style="text-decoration: underline">' + result.modifier + '</a>' : '') + '] = <strong>' + result.value + '</strong> '
|
resultText += '<br/><strong>  D' + result.sides + '</strong>: [' + values.map(value => value === 1 ? '<strong style="text-shadow: 2px 2px 10px red">' + value + '</strong>' : value === result.sides ? '<strong style="text-shadow: 2px 2px 10px green">' + value + '</strong>' : value).join(' + ') + (result.modifier > 0 ? ' <a style="text-decoration: underline">+' + result.modifier + '</a>' : result.modifier < 0 ? ' <a style="text-decoration: underline">' + result.modifier + '</a>' : '') + '] = <strong>' + result.value + '</strong> '
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
node.innerHTML = '<strong style="text-shadow: 2px 2px 10px ' + data.faceColor + ';">' + data.name + ':</strong> ' + resultText
|
node.innerHTML = '<strong style="text-shadow: 2px 2px 10px ' + data.themeColor + ';">' + data.name + ':</strong> ' + resultText
|
||||||
name.appendChild(node)
|
name.appendChild(node)
|
||||||
document.getElementById('results').insertBefore(name, document.getElementById('results').firstChild);
|
document.getElementById('results').insertBefore(name, document.getElementById('results').firstChild);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user