diff --git a/client/src/assets/base.css b/client/src/assets/base.css index 388ad274..32ddb187 100644 --- a/client/src/assets/base.css +++ b/client/src/assets/base.css @@ -71,13 +71,14 @@ --chat-background: var(--c-white); --section-gap: 160px; + --separator-color: #2e2e2e; } @media (prefers-color-scheme: dark) { :root { --window-background: var(--c-black-blurred); - --color-handler: var(--c-black-semisoft); + --color-handler: #181818; --color-background: var(--c-black); --color-background-semisoft: var(--c-black-semisoft); --color-background-soft: var(--c-black-soft); @@ -98,8 +99,9 @@ --color-chat-other: #1d1d1d; --color-roll-dice-chat: #665750; + --separator-color: #363636; - --color-hover: var() + /* --color-hover: var() */ } } diff --git a/client/src/assets/main.css b/client/src/assets/main.css index 29c747f3..bb234972 100644 --- a/client/src/assets/main.css +++ b/client/src/assets/main.css @@ -143,4 +143,25 @@ button:active { margin-left: auto; margin-right: auto; display: block; +} + +.parameters { + display: flex; + flex-direction: column; + width: 100%; + padding: 10px; +} + +.param-element { + width: 100%; + display: flex; + flex-direction: row; +} + +.param-text { + margin-right: auto; +} + +.param-value { + margin-left: auto; } \ No newline at end of file diff --git a/client/src/services/Map.js b/client/src/services/Map.js new file mode 100644 index 00000000..1c9ade4a --- /dev/null +++ b/client/src/services/Map.js @@ -0,0 +1,191 @@ +import { initCustomFormatter, ref } from 'vue'; + +let offsetX = 0; +let offsetY = 0; +let scale = 1; + +let mouseX = 0; +let mouseY = 0; + +function SetupTilemap(){ + let tilemap = document.getElementById("tilemap"); + tilemap.addEventListener("wheel", (event) => { + let direction = 0; + if(event.deltaY > 0) direction = 0.95; + else if(event.deltaY < 0) direction = 1.05; + + zoom(direction); + }) + + let mouseDown = false; + let startX = 0, startY = 0; + let oldOffsetX = offsetX, oldOffsetY = offsetY; + + tilemap.addEventListener("mousedown", (event) => { + mouseDown = true + startX = event.clientX; + startY = event.clientY; + oldOffsetX = offsetX; + oldOffsetY = offsetY; + }); + + tilemap.addEventListener("mousemove", (event) => { + mouseX = event.clientX; + mouseY = event.clientY; + + if(!mouseDown) return; + offsetX = oldOffsetX + ((event.clientX - startX) * (1 / scale)); + offsetY = oldOffsetY + ((event.clientY - startY) * (1 / scale)); + + Draw(); + }); + + tilemap.addEventListener("mouseup", () => mouseDown = false); + + addEventListener("resize", Draw) + + offsetX = window.innerWidth / 2; + offsetY = window.innerHeight / 2; + + Draw(); +} + +// Map coords -> Real coords +function toMapX(xReal) { + return (xReal + offsetX) * scale; +} + +function toMapY(yReal){ + return (yReal + offsetY) * scale; +} + +// Real coords -> Map coords +function toRealX(xVirt){ + return xVirt / scale - offsetX; +} + +function toRealY(yVirt){ + return yVirt / scale - offsetY; +} + +function zoom(amount){ + let dx = ((mouseX) / scale - offsetX) - ((mouseX) / (scale * amount) - offsetX) + let dy = ((mouseY) / scale - offsetY) - ((mouseY) / (scale * amount) - offsetY) + scale *= amount; + offsetX -= dx; + offsetY -= dy; + Draw(); +} + +let xUpperLeft = -Infinity; +let yUpperLeft = -Infinity; +let xDownRight = Infinity; +let yDownRight = Infinity; + +function drawGrid(cellSize){ + let canvas = document.getElementById('tilemap'); + + const width = canvas.clientWidth; + const height = canvas.clientHeight; + + let ctx = canvas.getContext("2d"); + + ctx.strokeStyle = "#434343"; + ctx.lineWidth = 1; + + ctx.beginPath(); + + for (let x = (offsetX % cellSize) * scale; x <= width; x += cellSize * scale) { + if(toRealX(x) < xUpperLeft) continue; + if(toRealX(x) > xDownRight) continue; + + ctx.moveTo(x, Math.max(0, toMapY(yUpperLeft))); + ctx.lineTo(x, Math.min(height, toMapY(yDownRight))); + } + + for (let y = (offsetY % cellSize) * scale; y <= height; y += cellSize * scale) { + if(toRealY(y) < yUpperLeft) continue; + if(toRealY(y) > yDownRight) continue; + ctx.moveTo(Math.max(0, toMapX(xUpperLeft)), y); + ctx.lineTo(Math.min(width, toMapX(xDownRight)), y); + } + + + ctx.stroke(); + + ctx.strokeStyle = "red"; + ctx.lineWidth = 1; + + ctx.beginPath(); + ctx.rect(toMapX(-10), toMapY(-10), 10 * scale, 10 * scale); + ctx.stroke(); +} + +// Ok aqui coses del mapa en si +let gridSize = 150; +let images = []; +let lines_of_sight = []; +let backgroundColor = ref('#0f0f0f'); + +function Draw(){ + let canvas = document.getElementById('tilemap'); + let ctx = canvas.getContext("2d"); + canvas.width = window.innerWidth; + canvas.height = window.innerHeight; + + ctx.clearRect(0, 0, canvas.width, canvas.height); + + images.forEach((image) => { + ctx.drawImage(image, toMapX(0), toMapY(0), image.naturalWidth * scale, image.naturalHeight * scale); + }); + + + ctx.beginPath(); + ctx.strokeStyle = "white"; + ctx.lineWidth = 3; + lines_of_sight.forEach((line) => { + ctx.moveTo(toMapX(line[0].x * gridSize), toMapY(line[0].y * gridSize)); + ctx.lineTo(toMapX(line[1].x * gridSize), toMapY(line[1].y * gridSize)); + }); + ctx.stroke(); + + drawGrid(gridSize); +} + +function ImportDD2VTT(data){ + console.log(data); + + var image = new Image(); + image.onload = function() { + images.push(image); + Draw(); + }; + + gridSize = data.resolution.pixels_per_grid; + lines_of_sight = data.line_of_sight; + backgroundColor.value = '#' + data.environment.ambient_light; + + xUpperLeft = data.resolution.map_origin.x * data.resolution.pixels_per_grid; + yUpperLeft = data.resolution.map_origin.y * data.resolution.pixels_per_grid; + xDownRight = xUpperLeft + data.resolution.map_size.x * data.resolution.pixels_per_grid; + yDownRight = yUpperLeft + data.resolution.map_size.y * data.resolution.pixels_per_grid; + + + image.src = "data:image/png;base64," + data.image; + +} + +let GetBackgroundColor = () => backgroundColor; + +export { + toMapX, + toMapY, + toRealX, + toRealY, + zoom, + + SetupTilemap, + // Draw, + ImportDD2VTT, + GetBackgroundColor, +}; \ No newline at end of file diff --git a/client/src/services/Windows.js b/client/src/services/Windows.js index ab70af77..2c486649 100644 --- a/client/src/services/Windows.js +++ b/client/src/services/Windows.js @@ -1,30 +1,16 @@ import { reactive, ref } from 'vue' import { Disconnect } from './Dragonroll'; -const windows = { - login: ref([]), - register: ref([]), - test: ref([]), - main_menu: ref([]), - edit_profile: ref([]), - account_settings: ref([]), - db_window: ref([]), - campaign_list: ref([]), - new_campaign: ref([]), - join_campaign: ref([]), - campaign_preview: ref([]), - chat: ref([]), - dice_menu: ref([]), -}; +const windows = ref([]) const defValues = { 'login': { id: 'login', - title: 'Login' + title: 'Login', }, 'register': { id: 'register', - title: 'Register' + title: 'Register', }, 'main_menu': { id: 'main_menu', @@ -78,6 +64,16 @@ const defValues = { id: 'dice_menu', title: 'Dice roll', close: true + }, + 'map_buttons': { + id: 'map_buttons', + title: '', + close: true + }, + 'environment': { + id: 'environment', + title: 'Edit environment', + close: true } } @@ -166,6 +162,11 @@ function SetPosition(id, pos){ SaveWindowPos({id, x: pos.x, y: pos.y}) } +function GetPosition(id){ + let win = GetWindowWithId(id); + return {x: win.x, y: win.y}; +} + function ResetPosition(id, pos){ let win = GetWindowWithId(id); let data = {x: win.x, y: win.y}; @@ -181,22 +182,18 @@ function ResetPosition(id, pos){ function CreateWindow(type, data = {}){ let finalData = {...{type}, ...defValues[type], ...data} - if(windows[finalData.type] === undefined){ - console.error("Window type " + finalData.type + " is not defined!"); - return; - } - let contains = false; - for (let i = 0; i < windows[finalData.type].value.length; i++) { - if(windows[finalData.type].value[i].id == finalData.id){ + for (let i = 0; i < windows.value.length; i++) { + if(windows.value[i].id == finalData.id){ contains = true; break; } } if(!contains) { - windows[finalData.type].value.push(finalData); + windows.value.push(finalData); // reload.value += 1; + console.log(windows.value); setTimeout(() => SetOnTop(finalData.id), 0); } } @@ -212,7 +209,7 @@ function CreateChildWindow(parentId, type, data = {}){ function ClearAll(){ Object.keys(windows).forEach((key) => { - windows[key].value = []; + windows.value = []; }); } @@ -227,16 +224,14 @@ function ClearWindow(id){ let win = GetWindowWithId(id); if(!win) return; if(win.children) for(let i = 0; i < win.children.length; i++) ClearWindow(win.children[i]); - windows[win.type].value = windows[win.type].value.filter((e) => {return e.id !== id}); + windows.value = windows.value.filter((e) => {return e.id !== id}); // reload.value += 1; } function GetWindowWithId(id){ - for(let key of Object.keys(windows)){ - for(let i = 0; i < windows[key].value.length; i++){ - if(windows[key].value[i].id == id){ - return windows[key].value[i]; - } + for(let i = 0; i < windows.value.length; i++){ + if(windows.value[i].id == id){ + return windows.value[i]; } } } @@ -268,6 +263,7 @@ export { CreateChildWindow, GetWindowWithId, SaveWindowPos, + GetPosition, ClearWindow, ClearAll } \ No newline at end of file diff --git a/client/src/views/managers/GameManager.vue b/client/src/views/managers/GameManager.vue index 98c86356..a3865b38 100644 --- a/client/src/views/managers/GameManager.vue +++ b/client/src/views/managers/GameManager.vue @@ -4,11 +4,12 @@ import { InGameRef } from '../../services/Game'; import IconButton from '../partials/game/IconButton.vue'; import { AddSound } from '../../services/Sound'; import TileMap from './TileMap.vue'; -import { DisplayCampaign, GetCampaign } from '../../services/Dragonroll'; +import { DisplayCampaign, GetCampaign, GetClient } from '../../services/Dragonroll'; import { ClearAll, CreateWindow } from '../../services/Windows'; const game = ref(null); const in_game = InGameRef(); +const is_dm = ref(false); function OpenCampaignPreview(){ CreateWindow('campaign_preview', {campaign: GetCampaign(), style: 'compact', hide_start: true, back: undefined, close: true}); @@ -22,9 +23,16 @@ function OpenDiceMenu(){ CreateWindow('dice_menu'); } +function OpenMapButtons(){ + CreateWindow('map_buttons'); +} + watch(game, () => { if(game.value && in_game.value){ AddSound(game.value); + + // Check if we are dm + is_dm.value = GetClient().is_dm; } }); @@ -40,6 +48,11 @@ watch(game, () => { + +
+ +
+
@@ -62,6 +75,10 @@ watch(game, () => { z-index: 1; user-select: none; + + &.gm { + left: 48px; + } } .horizontal-button { diff --git a/client/src/views/managers/TileMap.vue b/client/src/views/managers/TileMap.vue index 22604236..52903b9e 100644 --- a/client/src/views/managers/TileMap.vue +++ b/client/src/views/managers/TileMap.vue @@ -1,133 +1,18 @@