token movement now works (and syncs between devices)
This commit is contained in:
parent
be6dd7c0e4
commit
95c2595279
6 changed files with 65 additions and 11 deletions
|
@ -8,6 +8,7 @@
|
||||||
var mapScale = 1.0;
|
var mapScale = 1.0;
|
||||||
var mapOffsetX = 0.0;
|
var mapOffsetX = 0.0;
|
||||||
var mapOffsetY = 0.0;
|
var mapOffsetY = 0.0;
|
||||||
|
var draggedToken = { token: null, offX: 0, offY: 0 };
|
||||||
|
|
||||||
function init() {
|
function init() {
|
||||||
let view = document.getElementById('game-view');
|
let view = document.getElementById('game-view');
|
||||||
|
@ -15,6 +16,7 @@
|
||||||
view.onclick = (e) => console.log('click', e);
|
view.onclick = (e) => console.log('click', e);
|
||||||
view.onauxclick = (e) => console.log(e);
|
view.onauxclick = (e) => console.log(e);
|
||||||
view.onmousemove = onGameMouseMove;
|
view.onmousemove = onGameMouseMove;
|
||||||
|
view.onmouseup = onGameMouseUp;
|
||||||
view.oncontextmenu = () => false;
|
view.oncontextmenu = () => false;
|
||||||
// allow sending chat message using enter (and shift-enter for new line)
|
// allow sending chat message using enter (and shift-enter for new line)
|
||||||
document.getElementById('newmsg-content').onkeypress = (e) => {
|
document.getElementById('newmsg-content').onkeypress = (e) => {
|
||||||
|
@ -95,13 +97,20 @@
|
||||||
console.log(t);
|
console.log(t);
|
||||||
let map = document.getElementById('map');
|
let map = document.getElementById('map');
|
||||||
let token = document.createElement('div');
|
let token = document.createElement('div');
|
||||||
token.className = 'token';
|
token.className = 'token token-transition';
|
||||||
token.style.top = `${t.y * GRID_SIZE}px`;
|
token.style.top = `${t.y * GRID_SIZE}px`;
|
||||||
token.style.left = `${t.x * GRID_SIZE}px`;
|
token.style.left = `${t.x * GRID_SIZE}px`;
|
||||||
token.token_id = t.token_id;
|
token.token_id = t.token_id;
|
||||||
token.innerHTML = `
|
token.innerHTML = `
|
||||||
<img src='data:image/jpg;base64,${t.img}'>
|
<img src='data:image/jpg;base64,${t.img}' ondragstart='return false;'>
|
||||||
`
|
`
|
||||||
|
token.onmousedown = (e) => {
|
||||||
|
token.classList.remove('token-transition');
|
||||||
|
draggedToken.token = token;
|
||||||
|
draggedToken.offX = ((e.clientX - mapOffsetX) / mapScale) - parseInt(token.style.left);
|
||||||
|
draggedToken.offY = ((e.clientY - mapOffsetY) / mapScale) - parseInt(token.style.top);
|
||||||
|
token.children[0].style.cursor = 'grabbing';
|
||||||
|
}
|
||||||
map.append(token);
|
map.append(token);
|
||||||
}
|
}
|
||||||
tavern.onmovetoken = (m) => {
|
tavern.onmovetoken = (m) => {
|
||||||
|
@ -136,6 +145,25 @@
|
||||||
map.style.left = `${mapOffsetX}px`;
|
map.style.left = `${mapOffsetX}px`;
|
||||||
map.style.top = `${mapOffsetY}px`;
|
map.style.top = `${mapOffsetY}px`;
|
||||||
}
|
}
|
||||||
|
else if(draggedToken.token != null && event.buttons == 1) {
|
||||||
|
let top = (event.clientY - mapOffsetY) / mapScale - draggedToken.offY;
|
||||||
|
let left = (event.clientX - mapOffsetX) / mapScale - draggedToken.offX;
|
||||||
|
console.log(event.clientX, event.clientY, draggedToken.offX, draggedToken.offY);
|
||||||
|
draggedToken.token.style.top = `${top}px`;
|
||||||
|
draggedToken.token.style.left = `${left}px`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function onGameMouseUp() {
|
||||||
|
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);
|
||||||
|
let id = t.token_id;
|
||||||
|
t.classList.add('token-transition');
|
||||||
|
t.children[0].style.cursor = '';
|
||||||
|
tavern.move_token(id, x, y);
|
||||||
|
draggedToken.token = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
function sendChatMessage() {
|
function sendChatMessage() {
|
||||||
let tb = document.getElementById('newmsg-content');
|
let tb = document.getElementById('newmsg-content');
|
||||||
|
@ -209,6 +237,8 @@
|
||||||
}
|
}
|
||||||
.token {
|
.token {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
}
|
||||||
|
.token-transition {
|
||||||
transition:
|
transition:
|
||||||
top 0.5s ease-in,
|
top 0.5s ease-in,
|
||||||
left 0.5s ease-in;
|
left 0.5s ease-in;
|
||||||
|
@ -248,7 +278,7 @@
|
||||||
<div style="position:absolute; top: 10px; left: 5px; background-color: rgb(255, 166, 0); z-index: 1;" >
|
<div style="position:absolute; top: 10px; left: 5px; background-color: rgb(255, 166, 0); z-index: 1;" >
|
||||||
floating<br>stuff
|
floating<br>stuff
|
||||||
</div>
|
</div>
|
||||||
<div id="map" style="position:absolute;">
|
<div id="map" style="position:absolute; transform-origin: top left;">
|
||||||
<img src="https://rustystriker.dev/molly.jpg" height="200%" >
|
<img src="https://rustystriker.dev/molly.jpg" height="200%" >
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -16,3 +16,20 @@ pub struct ShowImage {
|
||||||
/// Which texture to show
|
/// Which texture to show
|
||||||
pub texture: String
|
pub texture: String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone)]
|
||||||
|
pub struct SpawnToken {
|
||||||
|
pub token_id: usize,
|
||||||
|
pub x: i32,
|
||||||
|
pub y: i32,
|
||||||
|
pub img: String
|
||||||
|
}
|
||||||
|
impl std::fmt::Debug for SpawnToken {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.debug_struct("SpawnToken")
|
||||||
|
.field("token_id", &self.token_id)
|
||||||
|
.field("x", &self.x)
|
||||||
|
.field("y", &self.y)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,7 +6,7 @@ pub struct LoginRequest {
|
||||||
pub password: String,
|
pub password: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Clone)]
|
#[derive(Serialize, Clone, Debug)]
|
||||||
pub struct LoginResult {
|
pub struct LoginResult {
|
||||||
pub success: bool,
|
pub success: bool,
|
||||||
// TODO: Figure out what the user needs on successful login to reduce traffic
|
// TODO: Figure out what the user needs on successful login to reduce traffic
|
||||||
|
|
|
@ -2,6 +2,7 @@ pub mod login;
|
||||||
pub mod game_actions;
|
pub mod game_actions;
|
||||||
pub mod map_actions;
|
pub mod map_actions;
|
||||||
|
|
||||||
|
use game_actions::SpawnToken;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::game::chat_message::ChatMessage;
|
use crate::game::chat_message::ChatMessage;
|
||||||
|
@ -22,7 +23,7 @@ pub enum Request {
|
||||||
Kick(String),
|
Kick(String),
|
||||||
Shutdown
|
Shutdown
|
||||||
}
|
}
|
||||||
#[derive(Serialize, Clone)]
|
#[derive(Serialize, Clone, Debug)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
pub enum Response {
|
pub enum Response {
|
||||||
Error(RequestError),
|
Error(RequestError),
|
||||||
|
@ -30,7 +31,7 @@ pub enum Response {
|
||||||
Message(ChatMessage),
|
Message(ChatMessage),
|
||||||
GetChatHistory(Vec<ChatMessage>),
|
GetChatHistory(Vec<ChatMessage>),
|
||||||
MoveToken { token_id: usize, x: i32, y: i32 },
|
MoveToken { token_id: usize, x: i32, y: i32 },
|
||||||
SpawnToken { token_id: usize, x: i32, y: i32, img: String },
|
SpawnToken(SpawnToken),
|
||||||
Quit { id: String },
|
Quit { id: String },
|
||||||
Shutdown,
|
Shutdown,
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use api::game_actions::SpawnToken;
|
||||||
use game::{chat_message::ChatMessage, Game, GameImpl};
|
use game::{chat_message::ChatMessage, Game, GameImpl};
|
||||||
use tokio::sync::{broadcast, mpsc};
|
use tokio::sync::{broadcast, mpsc};
|
||||||
|
|
||||||
|
@ -87,7 +88,7 @@ impl GameServer {
|
||||||
for (i, (path, x, y)) in self.tokens.iter().enumerate() {
|
for (i, (path, x, y)) in self.tokens.iter().enumerate() {
|
||||||
let bits = std::fs::read(path).expect("FAILED READING TOKEN IMAGE");
|
let bits = std::fs::read(path).expect("FAILED READING TOKEN IMAGE");
|
||||||
let img = base64::Engine::encode(&base64::prelude::BASE64_STANDARD, &bits);
|
let img = base64::Engine::encode(&base64::prelude::BASE64_STANDARD, &bits);
|
||||||
_ = broadcast.send((Some(id.clone()), api::Response::SpawnToken { token_id: i, x: *x, y: *y, img }));
|
_ = broadcast.send((Some(id.clone()), api::Response::SpawnToken(SpawnToken { token_id: i, x: *x, y: *y, img })));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
api::Request::SpawnToken { x, y, img_path } => {
|
api::Request::SpawnToken { x, y, img_path } => {
|
||||||
|
@ -95,11 +96,15 @@ impl GameServer {
|
||||||
self.tokens.push((img_path.clone(), x, y));
|
self.tokens.push((img_path.clone(), x, y));
|
||||||
let bits = std::fs::read(img_path).expect("FAILED READING TOKEN IMAGE");
|
let bits = std::fs::read(img_path).expect("FAILED READING TOKEN IMAGE");
|
||||||
let img = base64::Engine::encode(&base64::prelude::BASE64_STANDARD, &bits);
|
let img = base64::Engine::encode(&base64::prelude::BASE64_STANDARD, &bits);
|
||||||
_ = broadcast.send((Some(id.clone()), api::Response::SpawnToken { token_id, x, y, img }));
|
_ = broadcast.send((Some(id.clone()), api::Response::SpawnToken(SpawnToken { token_id, x, y, img })));
|
||||||
},
|
},
|
||||||
api::Request::MoveToken { token_id, x, y } => {
|
api::Request::MoveToken { token_id, x, y } => {
|
||||||
// TODO: add check to make sure the actor is authorized to move the token
|
// TODO: add check to make sure the actor is authorized to move the token
|
||||||
|
if token_id < self.tokens.len() {
|
||||||
|
self.tokens[token_id].1 = x;
|
||||||
|
self.tokens[token_id].2 = y;
|
||||||
_ = broadcast.send((None, api::Response::MoveToken { token_id, x, y }));
|
_ = broadcast.send((None, api::Response::MoveToken { token_id, x, y }));
|
||||||
|
}
|
||||||
},
|
},
|
||||||
api::Request::Quit => _ = broadcast.send((None, api::Response::Quit { id })),
|
api::Request::Quit => _ = broadcast.send((None, api::Response::Quit { id })),
|
||||||
api::Request::Kick(id) => _ = broadcast.send((Some(id), api::Response::Shutdown)),
|
api::Request::Kick(id) => _ = broadcast.send((Some(id), api::Response::Shutdown)),
|
||||||
|
|
|
@ -54,6 +54,7 @@ async fn socket_receiver(mut recv: SplitStream<ws::WebSocket>, msend: mpsc::Send
|
||||||
async fn socket_sender(id: String, mut send: SplitSink<ws::WebSocket, ws::Message>, mut brecv: broadcast::Receiver<(Option<String>, Response)>) {
|
async fn socket_sender(id: String, mut send: SplitSink<ws::WebSocket, ws::Message>, mut brecv: broadcast::Receiver<(Option<String>, Response)>) {
|
||||||
while let Ok((to_id, msg)) = brecv.recv().await {
|
while let Ok((to_id, msg)) = brecv.recv().await {
|
||||||
if to_id.is_none() || to_id.map(|t| t == id).unwrap_or(false) {
|
if to_id.is_none() || to_id.map(|t| t == id).unwrap_or(false) {
|
||||||
|
println!("Sending a message to {}: {:?}", &id, &msg);
|
||||||
let err = send.send(ws::Message::Text(serde_json::to_string(&msg).unwrap())).await.is_err();
|
let err = send.send(ws::Message::Text(serde_json::to_string(&msg).unwrap())).await.is_err();
|
||||||
if err || matches!(msg, Response::Shutdown) {
|
if err || matches!(msg, Response::Shutdown) {
|
||||||
_ = send.close().await;
|
_ = send.close().await;
|
||||||
|
|
Loading…
Reference in a new issue