diff --git a/assets/web/app.js b/assets/web/app.js
index 1d341b5..fd1755a 100644
--- a/assets/web/app.js
+++ b/assets/web/app.js
@@ -7,26 +7,26 @@ var draggedToken = { token: null, offX: 0, offY: 0 };
var draggedDiv = { div: null, offX: 0, offY: 0 };
function init() {
- let view = document.getElementById('game-view');
+ let view = document.getElementById('game-view');
view.onwheel = onGameViewScroll;
view.onmousemove = onGameMouseMove;
view.onmouseup = onGameMouseUp;
view.oncontextmenu = () => false;
// allow sending chat message using enter (and shift-enter for new line)
document.getElementById('newmsg-content').onkeypress = (e) => {
- if(e.key == "Enter" && !e.shiftKey) {
+ if (e.key == "Enter" && !e.shiftKey) {
sendChatMessage();
return false;
}
}
document.getElementById('login-username').onkeypress = (e) => {
- if(e.key == 'Enter') {
+ if (e.key == 'Enter') {
document.getElementById('login-pass').focus();
return false;
}
}
document.getElementById('login-pass').onkeypress = (e) => {
- if(e.key == 'Enter') {
+ if (e.key == 'Enter') {
onLoginClick();
return false;
}
@@ -38,19 +38,17 @@ function init() {
}
document.body.onmousemove = onMoveableDivDrag;
document.body.onmouseup = onMoveableDivMouseUp;
- // TODO: Remove when done dev-ing
- tavern.onmessage({ text: 'test', id: 1, source: 'rusty', character: 'bart' });
}
tavern.onlogin = (s) => {
- if(s) {
+ if (s) {
let login = document.getElementById('login-screen');
let game = document.getElementById('game');
login.style.display = 'none';
game.style.display = 'flex';
// get last 50 msgs (i think that is enough for now) when we get in
- tavern.get_last_msgs(50);
- tavern.get_tokens();
+ tavern.get_last_msgs(50);
+ tavern.get_current_scene();
}
else {
alert("Invalid username or password!");
@@ -72,15 +70,15 @@ tavern.onmessage = (m) => {
${m.text.replace('\n', '\n
\n')}
`
- msg.oncontextmenu = (e) => {
- if(e.shiftKey) { return true; }
+ msg.oncontextmenu = (e) => {
+ if (e.shiftKey) { return true; }
let cm = document.getElementById('msg-context-menu');
cm.style.display = 'flex';
cm.style.top = `${e.pageY}px`;
cm.style.left = `${e.pageX}px`;
return false;
}
- if(m.actions) {
+ if (m.actions) {
let holder = document.createElement('div');
holder.style.display = 'flex';
holder.style.flexWrap = 'wrap';
@@ -98,7 +96,7 @@ tavern.onmessage = (m) => {
let history = document.getElementById('chat-history');
// this is to force update everytime we get a duplicate msg to allow msg editing (yay)
let exists = Array.from(history.children).filter(e => e.style.order == m.id)[0];
- if(exists) {
+ if (exists) {
history.removeChild(exists);
}
history.appendChild(msg);
@@ -126,15 +124,24 @@ tavern.onspawntoken = (t) => {
}
tavern.onmovetoken = (m) => {
let token = Array.from(document.getElementsByClassName('token')).filter(t => t.token_id == m.token_id)[0]
- if(token) {
+ if (token) {
token.style.top = `${m.y * GRID_SIZE}px`;
token.style.left = `${m.x * GRID_SIZE}px`;
}
}
+tavern.onshowscene = (show) => {
+ let map = document.getElementById('map');
+ // Remove existing tokens
+ Array.from(map.children).filter(c => c.classList.contains('token')).forEach(c => map.removeChild(c));
+
+ for (let token of show.tokens) {
+ tavern.onspawntoken(token);
+ }
+}
function onLoginClick() {
let username = document.getElementById('login-username').value;
let pass = document.getElementById('login-pass').value;
- if(username == 'test') {
+ if (username == 'test') {
// TODO: Remove this for when im done dev-ing with this file
tavern.onlogin(true);
}
@@ -143,11 +150,11 @@ function onLoginClick() {
function onGameViewScroll(event) {
let map = document.getElementById('map');
mapScale += (event.wheelDelta / 1800.0);
- if(mapScale < 0.1) { mapScale = 0.1; }
+ if (mapScale < 0.1) { mapScale = 0.1; }
map.style.transform = `scale(${mapScale})`;
}
function onGameMouseMove(event) {
- if(event.buttons == 2) {
+ if (event.buttons == 2) {
// right click
let map = document.getElementById('map');
let mult = event.ctrlKey ? 2.0 : 1.0;
@@ -156,7 +163,7 @@ function onGameMouseMove(event) {
map.style.left = `${mapOffsetX}px`;
map.style.top = `${mapOffsetY}px`;
}
- else if(draggedToken.token != null && event.buttons == 1) {
+ else if (draggedToken.token != null && event.buttons == 1) {
let top = (event.clientY - mapOffsetY) / mapScale - draggedToken.offY;
let left = (event.clientX - mapOffsetX) / mapScale - draggedToken.offX;
draggedToken.token.style.top = `${top}px`;
@@ -164,7 +171,7 @@ function onGameMouseMove(event) {
}
}
function onGameMouseUp() {
- if(draggedToken.token != null) {
+ if (draggedToken.token != null) {
let t = draggedToken.token;
let x = Math.floor(0.5 + parseInt(t.style.left) / GRID_SIZE);
let y = Math.floor(0.5 + parseInt(t.style.top) / GRID_SIZE);
@@ -186,7 +193,7 @@ function openRollsPopup(action) {
let holder = document.getElementById('dice-roll-holder');
holder.innerHTML = ''; // remove all holder children
holder.action = action;
- if(action.rolls != undefined && Array.isArray(action.rolls)) {
+ if (action.rolls != undefined && Array.isArray(action.rolls)) {
for (const r of action.rolls) {
// name (extra) (dice_amount)d(dice_type) + constant | enabled
console.log(r);
@@ -211,8 +218,8 @@ function rollPopup() {
// get the holder and start rolling dice
let rolls = [];
let holder = document.getElementById('dice-roll-holder');
- for(const h of holder.children) {
- if(h.roll && h.children[2].checked) {
+ for (const h of holder.children) {
+ if (h.roll && h.children[2].checked) {
let roll = { name: h.roll.name, extra: h.roll.extra };
let msg = '';
let sum = 0;
@@ -221,12 +228,12 @@ function rollPopup() {
let roll = Math.floor(Math.random() * h.roll.dice_type) + 1;
sum += roll;
msg += `${roll}`;
- if(i != h.roll.dice_amount - 1 || h.roll.constant != 0) {
+ if (i != h.roll.dice_amount - 1 || h.roll.constant != 0) {
msg += ' + ';
}
}
- if(h.roll.constant != 0) {
- sum += h.roll.constant;
+ if (h.roll.constant != 0) {
+ sum += h.roll.constant;
msg += `${h.roll.constant}`;
}
roll.result = sum;
@@ -238,7 +245,7 @@ function rollPopup() {
tavern.action_result(holder.action.name, 'Louise', [], rolls);
}
function onMoveableDivMouseDown(e, id) {
- if(e.buttons == 1) {
+ if (e.buttons == 1) {
let div = document.getElementById(id);
let rect = div.getBoundingClientRect();
draggedDiv.div = div;
@@ -248,7 +255,7 @@ function onMoveableDivMouseDown(e, id) {
}
}
function onMoveableDivDrag(e) {
- if(draggedDiv.div) {
+ if (draggedDiv.div) {
draggedDiv.div.style.right = '';
draggedDiv.div.style.top = `${e.clientY - draggedDiv.offY}px`;
draggedDiv.div.style.left = `${e.clientX - draggedDiv.offX}px`;
diff --git a/assets/web/style.css b/assets/web/style.css
index fbba9dc..8d391a1 100644
--- a/assets/web/style.css
+++ b/assets/web/style.css
@@ -1,33 +1,38 @@
-html, body {
+html,
+body {
margin: 0;
height: 100%;
}
-body{
+
+body {
background-color: rgb(32, 35, 35);
color: white;
}
+
#side-panel {
- display: flex;
+ display: flex;
flex-direction: row;
justify-content: center;
- resize: horizontal;
- overflow: auto;
- border: 0px solid black;
- width: 20%;
+ resize: horizontal;
+ overflow: auto;
+ border: 0px solid black;
+ width: 20%;
min-width: 10%;
max-width: 50%;
background-image: linear-gradient(135deg, #0f0f2f 0px, #0f0f2f 99%, rgb(188, 255, 185) 100%);
}
+
#chat-history {
- display: flex;
- flex-direction: column;
- width: 100%;
- height: 90%;
- resize: none;
- overflow: auto;
- margin-top: 10px;
- background-color:#0f0f2f;
+ display: flex;
+ flex-direction: column;
+ width: 100%;
+ height: 90%;
+ resize: none;
+ overflow: auto;
+ margin-top: 10px;
+ background-color: #0f0f2f;
}
+
.chat-message {
background-color: #ffffd6;
color: #000000;
@@ -40,33 +45,40 @@ body{
display: flex;
flex-direction: column;
}
+
#msg-context-menu {
- display: none;
- position: absolute;
- z-index: 1000;
+ display: none;
+ position: absolute;
+ z-index: 1000;
padding: 4px;
}
+
#msg-context-menu ul {
padding: 0px;
margin: 0px;
list-style: none;
}
+
#msg-context-menu ul li {
padding: 4px;
-
+
}
+
#msg-context-menu ul li:hover {
background: darkgray;
cursor: pointer;
}
+
.token {
position: absolute;
}
+
.token-transition {
- transition:
- top 0.5s ease-in,
- left 0.5s ease-in;
+ transition:
+ top 0.1s ease-in,
+ left 0.1s ease-in;
}
+
.token img {
cursor: grab;
width: 200px;
diff --git a/assets/web/tavern.js b/assets/web/tavern.js
index 8bf1beb..0b52947 100644
--- a/assets/web/tavern.js
+++ b/assets/web/tavern.js
@@ -1,24 +1,25 @@
-const tavern = {
+const tavern = {
socket: socket = new WebSocket('ws:/' + window.location.host + '/ws'),
msgs: [],
connected: false,
loggedIn: false,
+ currentScene: 0,
call: (f, ...args) => {
- if(typeof(f) == "function") {
+ if (typeof (f) == "function") {
f(...args);
}
}
};
tavern.add_msg_to_history = (m) => {
let id = m.id - 1;
- if(id >= 0) {
- if(id < tavern.msgs.length) {
- if(tavern.msgs[id].id == id + 1) {
+ if (id >= 0) {
+ if (id < tavern.msgs.length) {
+ if (tavern.msgs[id].id == id + 1) {
tavern.msgs[id] = m;
- }
+ }
else {
- for(let i = 0; i < tavern.msgs.length; i += 1) {
- if(tavern.msgs[i].id > id) {
+ for (let i = 0; i < tavern.msgs.length; i += 1) {
+ if (tavern.msgs[i].id > id) {
tavern.msgs.splice(i, 0, m);
break;
}
@@ -35,59 +36,67 @@ tavern.socket.onopen = () => tavern.connected = true;
tavern.socket.onmessage = (m) => {
m = JSON.parse(m.data);
console.log(m);
- if(m.login) {
+ if (m.login) {
tavern.socket.loggedIn = m.login.success;
tavern.call(tavern.onlogin, tavern.socket.loggedIn);
}
- if(m.message) {
+ if (m.message) {
tavern.add_msg_to_history(m.message);
tavern.call(tavern.onmessage, m.message);
}
- if(m.get_chat_history) {
+ if (m.get_chat_history) {
m.get_chat_history.forEach(msg => {
tavern.add_msg_to_history(msg);
tavern.call(tavern.onmessage, msg);
});
}
- if(m.spawn_token) {
+ if (m.spawn_token) {
tavern.call(tavern.onspawntoken, m.spawn_token);
}
- if(m.move_token) {
+ if (m.move_token) {
tavern.call(tavern.onmovetoken, m.move_token);
}
+ if (m.show_scene) {
+ tavern.currentScene = m.show_scene.scene;
+ tavern.call(tavern.onshowscene, m.show_scene);
+ }
}
tavern.login = (username, password) => {
- if(!tavern.connected || tavern.loggedIn) { return false; }
- tavern.socket.send(JSON.stringify({ login: { username, password }}));
+ if (!tavern.connected || tavern.loggedIn) { return false; }
+ tavern.socket.send(JSON.stringify({ login: { username, password } }));
}
tavern.simple_msg = (msg, token) => {
- if(!tavern.connected || tavern.loggedIn) { return false; }
+ if (!tavern.connected || tavern.loggedIn) { return false; }
tavern.socket.send(JSON.stringify({ message: { text: msg, character: token ?? "" } }));
}
tavern.edit_msg = (new_text, id) => {
- if(id <= tavern.msgs.length && id > 0) {
+ if (id <= tavern.msgs.length && id > 0) {
let msg = tavern.msgs[id - 1];
msg.text = new_text;
tavern.socket.send(JSON.stringify({ message: msg }));
}
}
tavern.get_chat_history = (from, amount) => {
- if(!tavern.connected || tavern.loggedIn) { return false; }
+ if (!tavern.connected || tavern.loggedIn) { return false; }
tavern.socket.send(JSON.stringify({ get_chat_history: { from: from, amount: amount } }))
}
tavern.get_last_msgs = (amount) => {
- if(!tavern.connected || tavern.loggedIn) { return false; }
+ if (!tavern.connected || tavern.loggedIn) { return false; }
tavern.socket.send(JSON.stringify({ get_last_messages: { amount: amount } }))
}
-tavern.get_tokens = () => {
- if(!tavern.connected || tavern.loggedIn) { return false; }
- tavern.socket.send(JSON.stringify('get_tokens'));
+tavern.get_tokens = (mapId) => {
+ if (!tavern.connected || tavern.loggedIn) { return false; }
+ tavern.socket.send(JSON.stringify({ get_tokens: { scene: mapId } }));
}
tavern.move_token = (id, x, y) => {
- if(!tavern.connected || tavern.loggedIn) { return false; }
- tavern.socket.send(JSON.stringify({ move_token: { token_id: id, x: x, y: y } }));
+ if (!tavern.connected || tavern.loggedIn) { return false; }
+ tavern.socket.send(JSON.stringify({ move_token: { token_id: id, x: x, y: y } }));
}
tavern.action_result = (name, source, targets, results) => {
- if(!tavern.connected || tavern.loggedIn) { return false; }
- tavern.socket.send(JSON.stringify({ action_result: { name: name, source: source ?? '', targets: targets ?? [], results: results } }));
+ if (!tavern.connected || tavern.loggedIn) { return false; }
+ tavern.socket.send(JSON.stringify({ action_result: { name: name, source: source ?? '', targets: targets ?? [], results: results } }));
+}
+tavern.get_current_scene = () => {
+ if (!tavern.connected || tavern.loggedIn) { return; }
+ tavern.socket.send(JSON.stringify('get_current_scene'))
}
\ No newline at end of file
diff --git a/src/api/game_actions.rs b/src/api/game_actions.rs
index 1ae98a2..1fc2bf8 100644
--- a/src/api/game_actions.rs
+++ b/src/api/game_actions.rs
@@ -1,7 +1,5 @@
//! General game actions
-use serde::{Serialize, Deserialize};
-
-
+use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
pub struct Ping {
@@ -14,23 +12,23 @@ pub struct Ping {
#[derive(Serialize, Deserialize)]
pub struct ShowImage {
/// Which texture to show
- pub texture: String
+ pub texture: String,
}
#[derive(Serialize, Deserialize, Clone)]
pub struct SpawnToken {
- pub token_id: usize,
- pub x: f32,
- pub y: f32,
- pub img: String
+ pub token_id: usize,
+ pub x: f32,
+ pub y: f32,
+ pub img: String,
}
impl std::fmt::Debug for SpawnToken {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- // I dont want it to print the `img` field because it VERY long :)
+ // I dont want it to print the `img` field because it is VERY long :)
f.debug_struct("SpawnToken")
.field("token_id", &self.token_id)
.field("x", &self.x)
.field("y", &self.y)
.finish()
}
-}
\ No newline at end of file
+}
diff --git a/src/api/mod.rs b/src/api/mod.rs
index 583c974..e0e6a97 100644
--- a/src/api/mod.rs
+++ b/src/api/mod.rs
@@ -1,5 +1,5 @@
-pub mod login;
pub mod game_actions;
+pub mod login;
pub mod map_actions;
use game_actions::SpawnToken;
@@ -12,28 +12,60 @@ use crate::game::{character_sheet::EntryType, chat_message::ChatMessage, entry::
pub enum Request {
#[default]
Error,
- // Connection requests
+ // Connection requests
Login(login::LoginRequest),
Quit,
Kick(String),
Shutdown,
// Character stuff
CreateCharacter,
- CharacterDisplay { id: usize },
- CharacterInputs { id: usize },
- CharacterGetField { id: usize, field: String },
- CharacterSetField { id: usize, field: String, val: EntryType },
- CharacterAssign { id: usize, user: String },
+ CharacterDisplay {
+ id: usize,
+ },
+ CharacterInputs {
+ id: usize,
+ },
+ CharacterGetField {
+ id: usize,
+ field: String,
+ },
+ CharacterSetField {
+ id: usize,
+ field: String,
+ val: EntryType,
+ },
+ CharacterAssign {
+ id: usize,
+ user: String,
+ },
// Chat requests
Message(ChatMessage),
- GetChatHistory { amount: usize, from: usize },
- GetLastMessages { amount: usize, },
+ GetChatHistory {
+ amount: usize,
+ from: usize,
+ },
+ GetLastMessages {
+ amount: usize,
+ },
// Map requests
- GetTokens { scene: usize },
- SpawnToken { map_id: usize, character: String, x: f32, y: f32, img_path: String },
- MoveToken { token_id: usize, x: f32, y: f32 },
+ GetCurrentScene,
+ GetTokens {
+ scene: usize,
+ },
+ SpawnToken {
+ map_id: usize,
+ character: String,
+ x: f32,
+ y: f32,
+ img_path: String,
+ },
+ MoveToken {
+ token_id: usize,
+ x: f32,
+ y: f32,
+ },
// Actions requests
- ActionResult(ActionResult)
+ ActionResult(ActionResult),
}
#[derive(Serialize, Clone, Debug)]
@@ -43,12 +75,12 @@ pub enum Response {
Login(login::LoginResult),
Message(ChatMessage),
GetChatHistory(Vec),
+ ShowScene { scene: usize, tokens: Vec },
MoveToken { token_id: usize, x: f32, y: f32 },
SpawnToken(SpawnToken),
CharacterCreated(usize),
Quit { id: String },
Shutdown,
-
}
#[derive(Serialize, Debug, Clone)]
@@ -56,4 +88,4 @@ pub enum Response {
pub enum RequestError {
InvalidRequest,
AlreadyLoggedIn,
-}
\ No newline at end of file
+}
diff --git a/src/lib.rs b/src/lib.rs
index 885d758..c5ddc1a 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -42,7 +42,7 @@ impl GameServer {
ActionDefinition::new("weapon/attack".to_string())
.display_name(Some("Attack +7".to_string()))
.with_roll(DiceRoll::new("Pierce".to_string(), 12, 1).constant(1))
- .with_roll(DiceRoll::new("Fire".to_string(), 4, 2)),
+ .with_roll(DiceRoll::new("Fire".to_string(), 4, 2).enabled(false)),
)
.with_action(ActionDefinition::new("Attack +3".to_string()).with_roll(DiceRoll::new(
"Base".to_string(),
@@ -158,6 +158,33 @@ impl GameServer {
}
}
}
+ api::Request::GetCurrentScene => {
+ let scene = self.game.current_scene();
+ let mut scene_tokens = self
+ .game
+ .available_tokens(scene)
+ .iter()
+ .map(|id| self.game.token_info(0, *id).map(|info| (id, info)))
+ .flatten()
+ .map(|(id, info)| SpawnToken {
+ token_id: *id,
+ x: info.x,
+ y: info.y,
+ img: info.img_source.clone(),
+ })
+ .collect::>();
+ for spawn in scene_tokens.iter_mut() {
+ let bits = std::fs::read(&spawn.img).expect("FAILED READING TOKEN IMAGE");
+ spawn.img = base64::Engine::encode(&base64::prelude::BASE64_STANDARD, &bits);
+ }
+ _ = broadcast.send((
+ Some(id.clone()),
+ api::Response::ShowScene {
+ scene: scene,
+ tokens: scene_tokens,
+ },
+ ));
+ }
api::Request::SpawnToken {
map_id,
character,
diff --git a/src/main.rs b/src/main.rs
index ef01f2d..18d5794 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -111,9 +111,9 @@ async fn handle_socket(
b = brecv.recv() => {
println!("{} trying to log in: {:?}", &temp_id, &b);
if let Ok((to_id, msg)) = b {
- if let Response::Login(open_tavern::api::login::LoginResult { success, username }) = msg.clone() {
+ if let Response::Login(open_tavern::api::login::LoginResult { success, username }) = &msg {
let to_id = to_id.map(|ti| ti == temp_id).unwrap_or(false);
- if to_id && success && id.as_ref().map(|id| id == &username).unwrap_or(false) {
+ if to_id && *success && id.as_ref().map(|id| id == username).unwrap_or(false) {
_ = socket.send(Message::Text(serde_json::to_string(&msg).unwrap_or_default().into())).await;
break;
}