diff --git a/gametable/server.go b/gametable/server.go
index 2ad47bf..75c118f 100644
--- a/gametable/server.go
+++ b/gametable/server.go
@@ -231,8 +231,6 @@ func (self *GameTableServer) writeToDB(tableMsg *models.TableMessage) error {
if err != nil {
return err
}
- tableMsg.Token.X = nil
- tableMsg.Token.Y = nil
} else if t.X != nil && t.Y != nil {
err := self.dbAdapter.MoveToken(key, t)
if err != nil {
@@ -273,11 +271,6 @@ func (self *GameTableServer) writeToDB(tableMsg *models.TableMessage) error {
}
if tableMsg.Token != nil {
t := tableMsg.Token
- strId := ""
- if t.Id != nil {
- strId = *t.Id
- }
- fmt.Println(strId +"::" + t.Name + "::" + t.Sprite)
if t.Id == nil {
id, err := self.dbAdapter.CreateToken(key, *t)
t.Id = &id
diff --git a/models/models.go b/models/models.go
index 7f6c37f..8e2d60d 100644
--- a/models/models.go
+++ b/models/models.go
@@ -18,16 +18,16 @@ type DiceRoll struct {
}
type Token struct {
- Id *string `json:"id" bson:"_id"`
- Name string `json:"name"`
- Sprite string `json:"sprite"`
- W int `json:"w"`
- H int `json:"h"`
- OX int `json:"oX"`
- OY int `json:"oY"`
- X *int `json:"x"`
- Y *int `json:"y"`
- Active bool `json:"active"`
+ Id *string `json:"id" bson:"_id"`
+ Name string `json:"name"`
+ Sprite string `json:"sprite"`
+ W int `json:"w"`
+ H int `json:"h"`
+ OX int `json:"oX"`
+ OY int `json:"oY"`
+ X *float64 `json:"x"`
+ Y *float64 `json:"y"`
+ Active bool `json:"active"`
}
type Table struct {
@@ -42,8 +42,8 @@ type Table struct {
type TableMessage struct {
Auth *string `json:"auth,omitempty"`
Key *TableKey `json:"key"`
- DiceRoll *DiceRoll `json:"diceRoll"`
- Token *Token `json:"token"`
- MapImg *string `json:"mapImg"`
- AuxMsg *string `json:"auxMsg"`
+ DiceRoll *DiceRoll `json:"diceRoll,omitempty"`
+ Token *Token `json:"token,omitempty"`
+ MapImg *string `json:"mapImg,omitempty"`
+ AuxMsg *string `json:"auxMsg,omitempty"`
}
diff --git a/mongodb/adapter.go b/mongodb/adapter.go
index 381b87a..eee3abf 100644
--- a/mongodb/adapter.go
+++ b/mongodb/adapter.go
@@ -290,24 +290,21 @@ func (self *DbEngine) GetAuxMessage(table models.TableKey) (string, error) {
}
func (self *DbEngine) CheckToken(table models.TableKey, tokenId string) (bool, bool) {
- mongoId, err := primitive.ObjectIDFromHex(tokenId)
- if err != nil {
- return false, false
- }
tables := self.db.Collection("tables")
if tables != nil {
result := models.Table{}
err := tables.FindOne(self.mkCtx(10), bson.D{
{"name", table.Name},
{"passcode", table.Passcode},
- {"tokens", bson.E{"_id", mongoId}},
+ {"tokens._id", tokenId},
}).Decode(&result)
if err != nil {
+ fmt.Printf("%v", err)
return false, false
} else {
active := false
for _, t := range result.Tokens {
- if *t.Id == tokenId && t.Active {
+ if t.Id != nil && *t.Id == tokenId && t.Active {
active = true
}
}
@@ -344,10 +341,6 @@ func (self *DbEngine) CreateToken(table models.TableKey, token models.Token) (st
}
func (self *DbEngine) ActivateToken(table models.TableKey, tokenId string, active bool) error {
- mongoId, err := primitive.ObjectIDFromHex(tokenId)
- if err != nil {
- return err
- }
tables := self.db.Collection("tables")
if tables != nil {
var result models.Table
@@ -356,12 +349,10 @@ func (self *DbEngine) ActivateToken(table models.TableKey, tokenId string, activ
bson.D{
{"name", table.Name},
{"passcode", table.Passcode},
- {"tokens", bson.E{"_id", mongoId}},
+ {"tokens._id", tokenId},
},
bson.D{
- {"$set", bson.D{{"tokens.$", bson.D{
- {"active", active},
- }}}},
+ {"$set", bson.D{{"tokens.$.active", active}}},
},
).Decode(&result)
return err
@@ -370,10 +361,6 @@ func (self *DbEngine) ActivateToken(table models.TableKey, tokenId string, activ
}
func (self *DbEngine) MoveToken(table models.TableKey, token models.Token) error {
- mongoId, err := primitive.ObjectIDFromHex(*token.Id)
- if err != nil {
- return err
- }
tables := self.db.Collection("tables")
if tables != nil {
var result models.Table
@@ -382,13 +369,10 @@ func (self *DbEngine) MoveToken(table models.TableKey, token models.Token) error
bson.D{
{"name", table.Name},
{"passcode", table.Passcode},
- {"tokens", bson.E{"_id", mongoId}},
+ {"tokens._id", token.Id},
},
bson.D{
- {"$set", bson.D{{"tokens.$", bson.D{
- {"x", token.X},
- {"y", token.Y},
- }}}},
+ {"$set", bson.D{{"tokens.$.x", token.X}, {"tokens.$.y", token.Y}}},
},
).Decode(&result)
return err
@@ -397,10 +381,7 @@ func (self *DbEngine) MoveToken(table models.TableKey, token models.Token) error
}
func (self *DbEngine) DestroyToken(table models.TableKey, tokenId string) error {
- mongoId, err := primitive.ObjectIDFromHex(tokenId)
- if err != nil {
- return err
- }
+
tables := self.db.Collection("tables")
if tables != nil {
var result models.Table
@@ -411,7 +392,7 @@ func (self *DbEngine) DestroyToken(table models.TableKey, tokenId string) error
{"passcode", table.Passcode},
},
bson.D{
- {"$pull", bson.D{{"tokens", bson.D{{"_id", mongoId}}}}},
+ {"$pull", bson.D{{"tokens", bson.D{{"_id", tokenId}}}}},
},
).Decode(&result)
return err
diff --git a/static/admin.js b/static/admin.js
index eb0130c..017adaf 100644
--- a/static/admin.js
+++ b/static/admin.js
@@ -43,17 +43,17 @@ async function rebindUi(name, pass) {
document.getElementById("input_table_pass").value = pass;
dial();
const table = await res.json()
- infoHtml = "← table list
";
+ infoHtml = "← table list
";
infoHtml += `
`
infoHtml += "
";
infoHtml += "
"
if (mapImgs.ok) {
infoHtml += "";
const imgs = await mapImgs.json();
- infoHtml += "
";
+ infoHtml += "";
for (const i of imgs) {
const parts = i.split("/");
- infoHtml += `- ${parts[parts.length - 1]} view
\n`;
+ infoHtml += `- ${parts[parts.length - 1]}
\n`;
}
infoHtml += "
";
} else {
@@ -65,10 +65,10 @@ async function rebindUi(name, pass) {
if (tokenImgs.ok) {
tokenListHTML += "";
const tokens = await tokenImgs.json();
- tokenListHTML += "";
+ tokenListHTML += "";
for (const t of tokens) {
const parts = t.split("/");
- tokenListHTML += `- ${parts[parts.length - 1]} view
\n`
+ tokenListHTML += `- ${parts[parts.length - 1]}
\n`
}
tokenListHTML += "
";
fillSpriteDropdown(tokens);
@@ -156,6 +156,7 @@ function scaleSpritePreview(source) {
}
function drawTokenOrigin() {
+ if (tokenSpriteDropdown.selectedIndex >= 0) {
const img = previewZone.children[0];
const x = Number(tokenWidth.value) / Number(tokenCX.value);
const y = Number(tokenHeight.value) / Number(tokenCY.value);
@@ -173,6 +174,7 @@ function drawTokenOrigin() {
} else {
previewZone.appendChild(originImg);
}
+ }
}
function reinitializeSpritePreview() {
@@ -189,11 +191,8 @@ function reinitializeSpritePreview() {
tokenHeight.value = h;
scaleSpritePreview();
}
- if (previewZone.children.length) {
- previewZone.replaceChild(img, previewZone.children[0]);
- } else {
- previewZone.appendChild(img);
- }
+ previewZone.innerHTML = "";
+ previewZone.appendChild(img);
}
function createToken() {
@@ -233,6 +232,46 @@ function createToken() {
setErr("All token fields are required");
}
+function destroyToken(id) {
+ const existing = tokens.find(t=>t.t.id == id);
+ if (existing) {
+ const self = Object.assign({}, existing.t);
+ self.active = false;
+ self.x = null;
+ self.y = null;
+ sendToken(self);
+ }
+}
+
+function previewExistingToken(id) {
+ const existing = tokens.find(t=>t.t.id == id);
+ if (existing) {
+ tokenWidth.value = existing.t.w;
+ tokenHeight.value = existing.t.h;
+ tokenCX.value = existing.t.oX;
+ tokenCY.value = existing.t.oY;
+ tokenName.value = existing.t.name;
+ for (let i = 0; i < tokenSpriteDropdown.options.length; i++) {
+ if (tokenSpriteDropdown.options[i].value == existing.t.sprite) {
+ tokenSpriteDropdown.selectedIndex = i;
+ break;
+ }
+ }
+ reinitializeSpritePreview();
+ }
+}
+
+function renderTokenMasterList() {
+ if (tokenZone) {
+ let tokenMasterListHTML = "
";
+ for (const t of tokens) {
+ tokenMasterListHTML += `- ${t.t.name}
\n`;
+ }
+ tokenMasterListHTML += "
";
+ tokenZone.innerHTML = tokenMasterListHTML;
+ }
+}
+
function publishAuxMsg() {
const txtArea = document.getElementById("auxMsgZone");
if (txtArea != null) {
@@ -248,12 +287,6 @@ function sendToken(t) {
publish({token: t, auth: adminToken.access_token});
}
-function revokeToken(t) {
- t.x = null;
- t.y = null;
- sendToken(t);
-}
-
async function uploadMapImg() {
try {
var input = document.getElementById("map_img_upload");
@@ -391,6 +424,7 @@ async function doLogin() {
getTables();
adminWrapper.style.display="inline";
adminZone.style.display = "block";
+ closeErr();
} else {
setErr("Incorrect credentials");
}
@@ -430,11 +464,19 @@ function setTableCreateFormVisible(v) {
}
function setTokenCreateFormVisible(v) {
- if (createTokenForm) {
+ if (createTokenForm && tokenZone) {
+ if (v) {
+ // clear the form when displaying because we may have values from a preview of an existing token
+ tokenWidth.value = "";
+ tokenHeight.value = "";
+ tokenCX.value = "";
+ tokenCY.value = "";
+ tokenName.value = "";
+ tokenSpriteDropdown.selectedIndex = 0;
+ }
createTokenForm.style.display = v ? "block" : "none";
- }
- if (!v) {
- // clear the form
+ tokenZone.style.display = v ? "none" : "inline";
+ reinitializeSpritePreview();
}
}
diff --git a/static/index.html b/static/index.html
index e47b645..5d1fc95 100644
--- a/static/index.html
+++ b/static/index.html
@@ -106,8 +106,8 @@
-
+
sprites
diff --git a/static/map.js b/static/map.js
index a797daa..2bb4212 100644
--- a/static/map.js
+++ b/static/map.js
@@ -19,10 +19,8 @@ function initializeMap(mapImgUrl) {
if (init) {
map.setView([0,0], 2);
}
- while (tokens.some(t=>t)) {
- tokens[0].m.removeFrom(map);
- tokens.shift();
- }
+ /*
+*/
}
// this works but assumes the map is square (reasonable limitation I think)
@@ -30,11 +28,58 @@ function resizeMarkers() {
tokens.forEach(t=>{
const icon = t.m.options.icon;
const scaleFactor = mapImg._image.clientWidth / mapImg._image.naturalWidth;
- icon.options.iconSize = [scaleFactor * t.sz[0], scaleFactor * t.sz[1]];
+ icon.options.iconSize = [scaleFactor * t.t.w, scaleFactor * t.t.h];
+ icon.options.iconAnchor = [scaleFactor * t.t.oX, scaleFactor * t.t.oY];
t.m.setIcon(icon);
});
}
+function processTokens(tokenChanges) {
+ for (const t of tokenChanges) {
+ const i = tokens.findIndex(tk=>tk.t.id == t.id);
+ if (i >= 0) {
+ const self = tokens[i];
+ // token was made active
+ if (t.x != null && t.y != null && !self.t.active && t.active) {
+ self.t.active = true;
+ self.m.addTo(map);
+ // token was made inactive
+ } else if (t.x != null && t.y != null && self.t.active && !t.active) {
+ self.t.active = false;
+ self.m.removeFrom(map);
+ // token was destroyed
+ } else if (t.x == null && t.y == null) {
+ self.m.removeFrom(map);
+ tokens.splice(i, 1);
+ // token was moved
+ } else {
+ self.t.x = t.x;
+ self.t.y = t.y;
+ self.m.setLatLng([t.y, t.x]);
+ }
+ } else {
+ if (t.x != null && t.y != null) {
+ const self = NewToken(t);
+ tokens.push(self);
+ if (t.active) {
+ self.m.addTo(map);
+ }
+ }
+ }
+ }
+ resizeMarkers();
+}
+
+function toggleActive(tokenId) {
+ const existing = tokens.find(t=>t.t.id == tokenId);
+ if (existing) {
+ const self = Object.assign({}, existing.t);
+ self.active = !self.active;
+ console.log(self);
+ publish({token: self});
+ }
+}
+
function getCascadingPos() {
const topLeft = [0,0];
const n = tokens.length;
@@ -43,19 +88,36 @@ function getCascadingPos() {
return topLeft;
}
-function NewToken(w, h, oX, oY, img, name, x, y) {
+function moveToken(id) {
+ const existing = tokens.find(t=>t.t.id == id);
+ if (existing) {
+ const self = Object.assign({}, existing.t);
+ const realPos = existing.m.getLatLng();
+ self.x = realPos.lng;
+ self.y = realPos.lat;
+ console.log(self);
+ publish({token: self});
+ }
+}
+
+function NewToken(token) {
+ const marker = L.marker([token.y,token.x], {
+ icon: L.icon({
+ iconUrl: token.sprite,
+ iconSize: [token.w,token.h],
+ iconAnchor: [token.oX, token.oY]
+ }),
+ title: token.name,
+ draggable: true,
+ autoPan: true
+ });
+
+ marker.on("moveend", ()=>{moveToken(token.id)});
+
return {
- sz: [w, h],
- m: L.marker((x && y) ? [y,x] : getCascadingPos(), {
- icon: L.icon({
- iconUrl: img,
- iconSize: [w,h],
- }),
- title: name,
- draggable: true,
- autoPan: true
- }),
- };
+ t: token,
+ m: marker,
+ };
}
function addToken(token) {
diff --git a/static/socket.js b/static/socket.js
index 1ab52d4..19ba7c5 100644
--- a/static/socket.js
+++ b/static/socket.js
@@ -33,9 +33,9 @@ function formatDice(r) {
return p;
}
-function logDice(dice, many) {
+function logDice(dice) {
const diceLog = document.getElementById("dice_log");
- if (!many) {
+ if (!Array.isArray(dice)) {
dice = [ dice ];
} else {
if (diceLog) {
@@ -58,19 +58,45 @@ function setAuxMsg(msg) {
}
}
+function updateTokens(tokens) {
+ // update internal token array and map
+ processTokens(tokens);
+ // update token select window
+ renderTokenSelect();
+ // if admin, update token master list
+ renderTokenMasterList();
+}
+
+function renderTokenSelect() {
+ const tokenSelect = document.getElementById("token_select");
+ let tokenSelectHTML = "";
+ for (const t of tokens) {
+ tokenSelectHTML += `- ${t.t.name}
\n`;
+ }
+ tokenSelectHTML += "
";
+ tokenSelect.innerHTML = tokenSelectHTML;
+}
+
function makeUpToDate(table) {
if (table) {
- if (table.diceRolls) {
- logDice(table.diceRolls, true);
- } else if (table.diceRoll) {
- logDice(table.diceRoll, false);
- }
+ // map image has to be set before tokens can be handled!
if (table.mapImg) {
setMapImg(table.mapImg);
}
if (table.auxMsg) {
setAuxMsg(table.auxMsg);
}
+ if (table.diceRolls) {
+ logDice(table.diceRolls);
+ } else if (table.diceRoll) {
+ logDice([table.diceRoll]);
+ }
+ if (table.tokens) {
+ updateTokens(table.tokens);
+ } else if (table.token) {
+ updateTokens([table.token]);
+ }
+
}
}
@@ -109,6 +135,10 @@ function dial() {
tabletop.style.display = "none";
}
table = null;
+ while (tokens.some(t=>t)) {
+ tokens[0].m.removeFrom(map);
+ tokens.shift();
+ }
}
});
conn.addEventListener("open", e => {
@@ -126,7 +156,7 @@ function dial() {
conn.addEventListener("message", e => {
const data = JSON.parse(e.data);
if (table == null) {
- // first fetch comes from mongo, so the rolls array in each diceRoll is a byte array and needs to be decoded
+ // dicerolls are treated as a byte array when marshalling to json, so we have to decode them
data.diceRolls.forEach(r=>{
r.roll = Uint8Array.from(atob(r.roll), c => c.charCodeAt(0))
})
diff --git a/static/style.css b/static/style.css
index 17c6194..a350d36 100644
--- a/static/style.css
+++ b/static/style.css
@@ -24,6 +24,7 @@ body {
background: url('./bg.png');
background-repeat: repeat;
background-attachment: fixed;
+ font-family: sans-serif;
}
label {
@@ -150,6 +151,7 @@ pre {
.ui_win ul {
max-height: 10em;
+ overflow: auto;
}
#admin_section {
@@ -159,6 +161,9 @@ pre {
.admin_win {
}
+.admin_win summary {
+ text-align: right;
+}
#map {
position:fixed;
@@ -182,4 +187,14 @@ nav {
#tokenPreview_zone {
position: relative;
+}
+
+.single_btn_list li {
+ display: grid;
+ grid-template-columns: 1fr auto;
+}
+
+.two_btn_list li {
+ display: grid;
+ grid-template-columns: 1fr auto auto;
}
\ No newline at end of file
diff --git a/static/util.js b/static/util.js
index 3448c98..7dba3f2 100644
--- a/static/util.js
+++ b/static/util.js
@@ -20,7 +20,6 @@ function saveName() {
console.log("saving username");
const username = document.getElementById("name_entry");
if (username) {
- console.log(username.value + "input found");
document.cookie = "username=" + username.value;
}
}