fix dice decoding, token editor can create clientside tokens!
This commit is contained in:
parent
8dfa99c8bc
commit
f4513a28f7
7 changed files with 185 additions and 15 deletions
155
static/admin.js
155
static/admin.js
|
@ -7,6 +7,15 @@ const createTokenForm = document.getElementById("createTokenForm");
|
|||
const tokenWrapper = document.getElementById("adminWrapper_tokens");
|
||||
const newTableName = document.getElementById("newTableName");
|
||||
const newTablePass = document.getElementById("newTablePass");
|
||||
const tokenSpriteDropdown = document.getElementById("token_combobox");
|
||||
const tokenName = document.getElementById("token_name");
|
||||
const tokenWidth = document.getElementById("token_width");
|
||||
const tokenHeight = document.getElementById("token_height");
|
||||
const tokenCX = document.getElementById("token_cx");
|
||||
const tokenCY = document.getElementById("token_cy");
|
||||
const previewZone = document.getElementById("tokenPreview_zone");
|
||||
const tokenAspect = document.getElementById("tokenKeepAspect");
|
||||
const aspectLockLabel = document.getElementById("aspectLockLabel");
|
||||
|
||||
async function getTable(name, pass) {
|
||||
try {
|
||||
|
@ -50,7 +59,7 @@ async function getTable(name, pass) {
|
|||
}
|
||||
adminZone.innerHTML = infoHtml;
|
||||
|
||||
let tokenListHTML = "<input id='token_img_upload' type='file'/><button onclick='uploadTokenImg()'>Upload Token</button><br/>";
|
||||
let tokenListHTML = "<input id='token_img_upload' type='file'/><button onclick='uploadTokenImg()'>Upload Sprite</button><br/>";
|
||||
if (tokenImgs.ok) {
|
||||
tokenListHTML += "<label>Available Sprites</label>";
|
||||
const tokens = await tokenImgs.json();
|
||||
|
@ -60,15 +69,19 @@ async function getTable(name, pass) {
|
|||
tokenListHTML += `<li>${parts[parts.length - 1]} <a href="${t}" target="_blank">view</a> <button onclick="deleteImg('${t}')">Delete</button></li>\n`
|
||||
}
|
||||
tokenListHTML += "</ul>";
|
||||
fillSpriteDropdown(tokens);
|
||||
} else {
|
||||
tokenListHTML += "<label>Sprites couldn't be retrieved</label>"
|
||||
}
|
||||
|
||||
spriteZone.innerHTML = tokenListHTML;
|
||||
|
||||
|
||||
|
||||
tokenWrapper.style.display = "inline";
|
||||
|
||||
// also, we have to fill and toggle the tokens window
|
||||
|
||||
} else {
|
||||
console.log(res.status);
|
||||
}
|
||||
|
@ -77,6 +90,144 @@ async function getTable(name, pass) {
|
|||
}
|
||||
}
|
||||
|
||||
function fillSpriteDropdown(tokens) {
|
||||
let options = "<option value=''>select</option>";
|
||||
for (const t of tokens) {
|
||||
const parts = t.split("/");
|
||||
const o = `<option value="${t}">${parts[parts.length - 1]}</option>\n`;
|
||||
options += o;
|
||||
}
|
||||
tokenSpriteDropdown.innerHTML = options;
|
||||
}
|
||||
|
||||
function previewSprite(source) {
|
||||
if (source) {
|
||||
switch (source.id) {
|
||||
case "token_combobox":
|
||||
reinitializeSpritePreview();
|
||||
break;
|
||||
case "token_cx":
|
||||
case "token_cy":
|
||||
drawTokenOrigin();
|
||||
break;
|
||||
default:
|
||||
scaleSpritePreview(source);
|
||||
console.log("default case");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function toggleAspectLock() {
|
||||
if (tokenKeepAspect.checked) {
|
||||
aspectLockLabel.innerHTML = "🔒"
|
||||
} else {
|
||||
aspectLockLabel.innerHTML = "🔓"
|
||||
}
|
||||
}
|
||||
|
||||
function scaleSpritePreview(source) {
|
||||
if (mapImg && mapImg._image) {
|
||||
console.log(mapImg);
|
||||
const scaleFactor = mapImg._image.clientWidth / mapImg._image.naturalWidth;
|
||||
const keepAspect = tokenAspect.checked;
|
||||
const img = previewZone.children[0];
|
||||
if (img) {
|
||||
if (!keepAspect || !source) {
|
||||
img.width = Number(tokenWidth.value) * scaleFactor;
|
||||
img.height = Number(tokenHeight.value) * scaleFactor;
|
||||
} else {
|
||||
|
||||
const currentAspect = img.width/img.height;
|
||||
switch (source.id) {
|
||||
case "token_width":
|
||||
img.width = Number(tokenWidth.value) * scaleFactor;
|
||||
img.height = (img.clientWidth / img.naturalWidth) * img.naturalHeight;
|
||||
tokenHeight.value = Number(tokenWidth.value)/currentAspect;
|
||||
break;
|
||||
case "token_height":
|
||||
img.height = Number(tokenHeight.value) * scaleFactor;
|
||||
img.width = (img.clientHeight / img.naturalHeight) * img.naturalWidth;
|
||||
tokenWidth.value = currentAspect * Number(tokenHeight.value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
tokenCX.value = Number(tokenWidth.value)/2;
|
||||
tokenCY.value = Number(tokenHeight.value)/2;
|
||||
drawTokenOrigin();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function drawTokenOrigin() {
|
||||
const img = previewZone.children[0];
|
||||
const x = Number(tokenWidth.value) / Number(tokenCX.value);
|
||||
const y = Number(tokenHeight.value) / Number(tokenCY.value);
|
||||
const origin = {x: img.width/x, y: img.height/y};
|
||||
const originImg = document.createElement("img");
|
||||
originImg.src="/table/origin.png";
|
||||
originImg.style.position = "absolute";
|
||||
originImg.style.left = (origin.x - 2) + "px";
|
||||
originImg.style.top = (origin.y - 2) + "px";
|
||||
if (previewZone.children.length > 1) {
|
||||
previewZone.replaceChild(originImg, previewZone.children[1]);
|
||||
} else {
|
||||
previewZone.appendChild(originImg);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
function reinitializeSpritePreview() {
|
||||
const img = document.createElement("img");
|
||||
img.src = tokenSpriteDropdown[tokenSpriteDropdown.selectedIndex].value;
|
||||
const tokenNameParts = tokenSpriteDropdown[tokenSpriteDropdown.selectedIndex].text.split(".");
|
||||
tokenNameParts.pop();
|
||||
tokenName.value = tokenNameParts.join(".");
|
||||
img.onload = () => {
|
||||
const w = img.naturalWidth;
|
||||
const h = img.naturalHeight;
|
||||
|
||||
tokenWidth.value = w;
|
||||
tokenHeight.value = h;
|
||||
scaleSpritePreview();
|
||||
}
|
||||
if (previewZone.children.length) {
|
||||
previewZone.replaceChild(img, previewZone.children[0]);
|
||||
} else {
|
||||
previewZone.appendChild(img);
|
||||
}
|
||||
}
|
||||
|
||||
function createToken() {
|
||||
const w = Number(tokenWidth.value);
|
||||
const h = Number(tokenHeight.value);
|
||||
const oX = Number(tokenCX.value);
|
||||
const oY = Number(tokenCY.value);
|
||||
const img = tokenSpriteDropdown[tokenSpriteDropdown.selectedIndex].value;
|
||||
const name = tokenName.value;
|
||||
|
||||
console.log("creating token");
|
||||
if (!isNaN(w) && !isNaN(h) && !isNaN(oX) && !isNaN(oY) && img && name) {
|
||||
console.log("all green");
|
||||
const self = {
|
||||
sz: [w, h],
|
||||
m: L.marker(getCascadingPos(), {
|
||||
icon: L.icon({
|
||||
iconUrl: img,
|
||||
iconSize: [w,h],
|
||||
}),
|
||||
title: name,
|
||||
draggable: true,
|
||||
autoPan: true
|
||||
}),
|
||||
};
|
||||
tokens.push(self);
|
||||
self.m.addTo(map);
|
||||
resizeMarkers();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
function publishAuxMsg() {
|
||||
const txtArea = document.getElementById("auxMsgZone");
|
||||
if (txtArea != null) {
|
||||
|
@ -223,8 +374,8 @@ async function doLogin() {
|
|||
if (adminUsrInput && adminPassInput) {
|
||||
adminToken = await getAdminToken(adminUsrInput.value, adminPassInput.value);
|
||||
if (adminToken) {
|
||||
adminWrapper.style.display="inline";
|
||||
getTables();
|
||||
adminWrapper.style.display="inline";
|
||||
adminZone.style.display = "block";
|
||||
} else {
|
||||
setErr("Incorrect credentials");
|
||||
|
|
|
@ -89,14 +89,15 @@
|
|||
<details id="admin_token_win" class="ui_win admin_win"><summary>tokens</summary>
|
||||
<button onclick="setTokenCreateFormVisible(true)">New Token</button>
|
||||
<form onsubmit="return false" id="createTokenForm">
|
||||
<label>Sprite<select id="token_combobox"></select></label><br/>
|
||||
<label>Name<input id="newToken_name"/></label><br/>
|
||||
<label>Width<input type="number" id="newToken_width" min="1" max="9999"/></label><br/>
|
||||
<label>Height<input type="number" id="newToken_height" min="1" max="9999"/></label><br/>
|
||||
<label>cX<input type="number" id="newToken_cx" min="0" max="9999"/></label><br/>
|
||||
<label>cY<input type="number" id="newToken_cy" min="0" max="9999"/></label><br/>
|
||||
<label>Sprite<select id="token_combobox" onchange="previewSprite(this)"></select></label><br/>
|
||||
<label>Name<input id="token_name"/></label><br/>
|
||||
|
||||
<label>Width<input type="number" id="token_width" min="1" max="9999" onchange="previewSprite(this)"/></label><label id="aspectLockLabel" for="tokenKeepAspect">🔒</label><input type="checkbox" checked id="tokenKeepAspect" onchange="toggleAspectLock()"/><br/>
|
||||
<label>Height<input type="number" id="token_height" min="1" max="9999" onchange="previewSprite(this)"/></label><br/>
|
||||
<label>cX<input type="number" id="token_cx" min="0" max="9999" onchange="previewSprite(this)"/></label><br/>
|
||||
<label>cY<input type="number" id="token_cy" min="0" max="9999" onchange="previewSprite(this)"/></label><br/>
|
||||
<div id="tokenPreview_zone"></div>
|
||||
<button type="submit" onlcick="createToken()">Create</button>
|
||||
<button type="submit" onclick="createToken()">Create</button>
|
||||
<button onclick="setTokenCreateFormVisible(false)">Cancel</button>
|
||||
</form>
|
||||
<div id="tokenZone"></div>
|
||||
|
|
|
@ -1,18 +1,19 @@
|
|||
let map = null;
|
||||
let mapImg = null;
|
||||
let tokens = [];
|
||||
const worldBounds = [[180, -180],[-180, 180]];
|
||||
|
||||
function initializeMap(mapImgUrl) {
|
||||
if (!map) {
|
||||
map = L.map('map', { minZoom: 0, maxZoom: 4, crs: L.CRS.Simple });
|
||||
map.on("zoomend", resizeMarkers);
|
||||
map.on("zoomend", ()=>{resizeMarkers();scaleSpritePreview();});
|
||||
}
|
||||
if (mapImg) {
|
||||
mapImg.removeFrom(map);
|
||||
}
|
||||
mapImg = L.imageOverlay(mapImgUrl, [[-180, 180],[180, -180]]);
|
||||
mapImg = L.imageOverlay(mapImgUrl, worldBounds);
|
||||
mapImg.addTo(map);
|
||||
map.setMaxBounds([[-180,180],[180,-180]]);
|
||||
map.setMaxBounds(worldBounds);
|
||||
map.setView([0,0], 2);
|
||||
while (tokens.some(t=>t)) {
|
||||
tokens[0].m.removeFrom(map);
|
||||
|
@ -30,6 +31,14 @@ function resizeMarkers() {
|
|||
});
|
||||
}
|
||||
|
||||
function getCascadingPos() {
|
||||
const topLeft = worldBounds[0];
|
||||
const n = tokens.length;
|
||||
topLeft[1] += (n+1)*5;
|
||||
topLeft[0] -= (n+1)*5;
|
||||
return topLeft;
|
||||
}
|
||||
|
||||
function addToken(token) {
|
||||
const self = { sz: token.sz, m: L.marker(token.pos, {
|
||||
icon: L.icon({
|
||||
|
|
BIN
static/origin.png
Normal file
BIN
static/origin.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 580 B |
|
@ -19,6 +19,7 @@ function fmtLeading(n) {
|
|||
}
|
||||
|
||||
function formatDice(r) {
|
||||
console.log(r);
|
||||
const date = new Date(r.timestamp)
|
||||
const p = document.createElement("p");
|
||||
const month = date.getMonth() + 1;
|
||||
|
@ -51,7 +52,6 @@ function logDice(dice, many) {
|
|||
function setAuxMsg(msg) {
|
||||
const auxDiv = document.getElementById("aux");
|
||||
if (auxDiv) {
|
||||
console.log("eeee");
|
||||
auxDiv.innerText = msg;
|
||||
}
|
||||
}
|
||||
|
@ -111,7 +111,6 @@ function dial() {
|
|||
});
|
||||
conn.addEventListener("open", e => {
|
||||
// TODO: add message to let user know they are at the table
|
||||
console.info("socket connected");
|
||||
tabletop = document.getElementById("tabletop");
|
||||
if (tabletop) {
|
||||
tabletop.style.display = "block";
|
||||
|
@ -132,6 +131,9 @@ function dial() {
|
|||
table = data;
|
||||
makeUpToDate(table);
|
||||
} else {
|
||||
if (data.diceRoll) {
|
||||
data.diceRoll.roll = Uint8Array.from(atob(data.diceRoll.roll), c => c.charCodeAt(0));
|
||||
}
|
||||
makeUpToDate(data);
|
||||
}
|
||||
|
||||
|
|
|
@ -170,3 +170,11 @@ nav {
|
|||
.leaflet-container {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
#tokenKeepAspect {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#tokenPreview_zone {
|
||||
position: relative;
|
||||
}
|
|
@ -30,7 +30,6 @@ function loadName() {
|
|||
if (username) {
|
||||
const cookies = document.cookie.split(";")
|
||||
cookies.forEach(c=>{
|
||||
console.log(c);
|
||||
if (c.trim().startsWith("username=")) {
|
||||
username.value = c.trim().split("=")[1];
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue