292 lines
12 KiB
HTML
292 lines
12 KiB
HTML
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<script src="./socket.js"></script>
|
|
<script>
|
|
// init - game view (map)
|
|
const GRID_SIZE = 200; // Grid size in pixels
|
|
var mapScale = 1.0;
|
|
var mapOffsetX = 0.0;
|
|
var mapOffsetY = 0.0;
|
|
var draggedToken = { token: null, offX: 0, offY: 0 };
|
|
|
|
function init() {
|
|
let view = document.getElementById('game-view');
|
|
view.onwheel = onGameViewScroll;
|
|
view.onclick = (e) => console.log('click', e);
|
|
view.onauxclick = (e) => console.log(e);
|
|
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) {
|
|
sendChatMessage();
|
|
return false;
|
|
}
|
|
}
|
|
document.getElementById('login-username').onkeypress = (e) => {
|
|
if(e.key == 'Enter') {
|
|
document.getElementById('login-pass').focus();
|
|
return false;
|
|
}
|
|
}
|
|
document.getElementById('login-pass').onkeypress = (e) => {
|
|
if(e.key == 'Enter') {
|
|
onLoginClick();
|
|
return false;
|
|
}
|
|
}
|
|
// focus on the username field for the sake of just pressing enter multiple times
|
|
document.getElementById('login-username').focus();
|
|
document.body.onclick = (e) => {
|
|
document.getElementById('msg-context-menu').style.display = 'none';
|
|
}
|
|
// TODO: Remove when done dev-ing
|
|
tavern.onmessage({ text: 'test', id: 1, source: 'rusty', character: 'bart' });
|
|
}
|
|
|
|
tavern.onlogin = (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();
|
|
}
|
|
else {
|
|
alert("Invalid username or password!");
|
|
}
|
|
}
|
|
tavern.onmessage = (m) => {
|
|
console.log(m);
|
|
let msg = document.createElement('div');
|
|
msg.className = 'chat-message';
|
|
// #abusing_style_order_as_both_id_variable_and_forcing_chronological_order
|
|
msg.style.order = m.id;
|
|
msg.innerHTML = `
|
|
<p style='align-self: center; margin: 4px 0;'>
|
|
<b>${m.character ?? ''}</b>
|
|
(${m.source})
|
|
</p>
|
|
<hr style='width: 75%;' />
|
|
<p style='margin: 4px 2px;'>
|
|
${m.text}
|
|
</p>
|
|
`
|
|
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;
|
|
}
|
|
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) {
|
|
history.removeChild(exists);
|
|
}
|
|
history.appendChild(msg);
|
|
msg.scrollIntoView();
|
|
}
|
|
tavern.onspawntoken = (t) => {
|
|
console.log(t);
|
|
let map = document.getElementById('map');
|
|
let token = document.createElement('div');
|
|
token.className = 'token token-transition';
|
|
token.style.top = `${t.y * GRID_SIZE}px`;
|
|
token.style.left = `${t.x * GRID_SIZE}px`;
|
|
token.token_id = t.token_id;
|
|
token.innerHTML = `
|
|
<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);
|
|
}
|
|
tavern.onmovetoken = (m) => {
|
|
let token = Array.from(document.getElementsByClassName('token')).filter(t => t.token_id == m.token_id)[0]
|
|
if(token) {
|
|
token.style.top = `${m.y * GRID_SIZE}px`;
|
|
token.style.left = `${m.x * GRID_SIZE}px`;
|
|
}
|
|
}
|
|
function onLoginClick() {
|
|
let username = document.getElementById('login-username').value;
|
|
let pass = document.getElementById('login-pass').value;
|
|
if(username == 'test') {
|
|
// TODO: Remove this for when im done dev-ing with this file
|
|
tavern.onlogin(true);
|
|
}
|
|
tavern.login(username, pass);
|
|
}
|
|
function onGameViewScroll(event) {
|
|
let map = document.getElementById('map');
|
|
mapScale += (event.wheelDelta / 1800.0);
|
|
if(mapScale < 0.1) { mapScale = 0.1; }
|
|
map.style.transform = `scale(${mapScale})`;
|
|
}
|
|
function onGameMouseMove(event) {
|
|
if(event.buttons == 2) {
|
|
// middle click
|
|
let map = document.getElementById('map');
|
|
let mult = event.ctrlKey ? 2.0 : 1.0;
|
|
mapOffsetX += event.movementX * mult;
|
|
mapOffsetY += event.movementY * mult;
|
|
map.style.left = `${mapOffsetX}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() {
|
|
let tb = document.getElementById('newmsg-content');
|
|
// get the msg and reset the textarea
|
|
let text = tb.value;
|
|
tb.value = '';
|
|
tavern.simple_msg(text);
|
|
}
|
|
</script>
|
|
<style>
|
|
html, body {
|
|
margin: 0;
|
|
height: 100%;
|
|
}
|
|
body{
|
|
background-color: rgb(32, 35, 35);
|
|
color: white;
|
|
}
|
|
#side-panel {
|
|
display: flex;
|
|
flex-direction: row;
|
|
justify-content: center;
|
|
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;
|
|
}
|
|
.chat-message {
|
|
background-color: #ffffd6;
|
|
color: #000000;
|
|
border-width: 2px;
|
|
border-color: rgb(73, 49, 49);
|
|
border-style: solid;
|
|
border-radius: 8px;
|
|
padding: 4px 16px;
|
|
margin: 2px;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
#msg-context-menu {
|
|
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;
|
|
}
|
|
.token img {
|
|
cursor: grab;
|
|
width: 200px;
|
|
height: 200px;
|
|
}
|
|
</style>
|
|
|
|
</head>
|
|
<body onload="init()">
|
|
<div id="login-screen" style="display: flex; justify-content: center; width: 100%; height: 100%;">
|
|
<div style="display: flex; justify-content: center; flex-direction: column;" >
|
|
<input type="text" id="login-username" placeholder="Username..." >
|
|
<input type="password" id="login-pass" placeholder="Password..." >
|
|
<button onclick="onLoginClick()">
|
|
Login
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div id="game" style="display: none; width: 100%; height: 100%;" >
|
|
<div id="side-panel">
|
|
<div style="display: flex; flex-direction: column; width: calc(100% - 15px); height: 100%;" >
|
|
<div id="chat-history">
|
|
</div>
|
|
<div id="chat-input" style="width: 100%; flex-grow: 1; margin: 10px 0; display:flex;">
|
|
<textarea id="newmsg-content" placeholder="Message..." spellcheck="true" style="resize:none; height: auto; flex-grow: 1; min-width:0px;"></textarea>
|
|
<!-- it looks better without the button :( -->
|
|
<!-- <button style="height: fit-content; align-self: center; margin: 0 4px;" onclick="sendChatMessage()">Send</button> -->
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
<div id="game-view" style="display: flex; overflow: hidden; position: relative; top: 0px; left: 0px; flex-grow: 1;" >
|
|
<div style="position:absolute; top: 10px; left: 5px; background-color: rgb(255, 166, 0); z-index: 1;" >
|
|
floating<br>stuff
|
|
</div>
|
|
<div id="map" style="position:absolute; transform-origin: top left;">
|
|
<img src="https://rustystriker.dev/molly.jpg" height="200%" >
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div id="msg-context-menu" class="chat-message">
|
|
<ul>
|
|
<li>Delete (TODO)</li>
|
|
</ul>
|
|
</div>
|
|
</body>
|
|
</html>
|