diff --git a/README.md b/README.md index 1f8a9cee..e86a3c11 100644 --- a/README.md +++ b/README.md @@ -2,3 +2,12 @@ An open-source virtual table top for role-playing games +Falta posar coses legals de: + +- WOC icones +- Iconoir +- Game Assets +- Fonts +- Splash art (Josep) +- Sounds (FreeSound) +- Caeora (Tokens) \ No newline at end of file diff --git a/client/src/services/Dragonroll.js b/client/src/services/Dragonroll.js index dbf4d68b..fa5c41f7 100644 --- a/client/src/services/Dragonroll.js +++ b/client/src/services/Dragonroll.js @@ -46,7 +46,6 @@ const chat = ref([ let GetChatRef = () => chat; socket.on('update-players', data => { - console.log(data); players.value = []; Object.keys(data).forEach((key) => { players.value.push(data[key]); @@ -107,7 +106,6 @@ function Disconnect(){ } function GetPlayer(player_campaign){ - console.log(players.value); let index = players.value.findIndex((p) => {return p._id == player_campaign}); if(index != -1) return players.value[index]; } diff --git a/client/src/services/Game.js b/client/src/services/Game.js index 8372ba7f..2965947e 100644 --- a/client/src/services/Game.js +++ b/client/src/services/Game.js @@ -5,7 +5,6 @@ let InGameRef = () => inGameRef; function LaunchGame(){ inGameRef.value = true; - console.log("jdksadjlo") } function ExitGame(){ diff --git a/client/src/services/Map.js b/client/src/services/Map.js index 1c9ade4a..4c027ce8 100644 --- a/client/src/services/Map.js +++ b/client/src/services/Map.js @@ -1,5 +1,21 @@ import { initCustomFormatter, ref } from 'vue'; +import Api from '@/services/Api' +import { GetCampaign } from './Dragonroll'; +import { backendUrl } from './BackendURL'; + +function dataURLtoFile(dataurl, filename) { + var arr = dataurl.split(","), + mime = arr[0].match(/:(.*?);/)[1], + bstr = atob(arr[arr.length - 1]), + n = bstr.length, + u8arr = new Uint8Array(n); + while (n--) { + u8arr[n] = bstr.charCodeAt(n); + } + return new File([u8arr], filename, { type: mime }); +} + let offsetX = 0; let offsetY = 0; let scale = 1; @@ -77,6 +93,7 @@ function zoom(amount){ Draw(); } + let xUpperLeft = -Infinity; let yUpperLeft = -Infinity; let xDownRight = Infinity; @@ -112,19 +129,21 @@ function drawGrid(cellSize){ 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 currentMap = { + gridSize: undefined, + images: [], + lines_of_sight: [], + backgroundColor: '#0f0f0f', + title: "Untitled map" +}; +let imageData = []; + +const currentMapId = ref(''); +let GetMapId = () => currentMapId; + let backgroundColor = ref('#0f0f0f'); function Draw(){ @@ -135,7 +154,7 @@ function Draw(){ ctx.clearRect(0, 0, canvas.width, canvas.height); - images.forEach((image) => { + imageData.forEach((image) => { ctx.drawImage(image, toMapX(0), toMapY(0), image.naturalWidth * scale, image.naturalHeight * scale); }); @@ -143,39 +162,117 @@ function Draw(){ 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)); + currentMap.lines_of_sight.forEach((line) => { + ctx.moveTo(toMapX(line[0].x * currentMap.gridSize), toMapY(line[0].y * currentMap.gridSize)); + ctx.lineTo(toMapX(line[1].x * currentMap.gridSize), toMapY(line[1].y * currentMap.gridSize)); }); ctx.stroke(); - drawGrid(gridSize); + if(currentMap.gridSize) drawGrid(currentMap.gridSize); } function ImportDD2VTT(data){ - console.log(data); - var image = new Image(); image.onload = function() { - images.push(image); - Draw(); + UploadResource(image).then((imagePath) => { + currentMap.images.push(imagePath); + CreateMap(currentMap); + ReloadImages(); + }) }; - gridSize = data.resolution.pixels_per_grid; - lines_of_sight = data.line_of_sight; + currentMap.gridSize = data.resolution.pixels_per_grid; + currentMap.lines_of_sight = data.line_of_sight; + backgroundColor.value = '#' + data.environment.ambient_light; + currentMap.backgroundColor = '#' + 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; +} + + +const mapList = ref([]); +let GetMapList = () => mapList; + +function UpdateMapList(){ + Api().get('/maps/list?campaign=' + GetCampaign()._id).then(response => { + mapList.value = response.data.data; + console.log(mapList.value); + }).catch((err) => console.log(err)); +} + +function ReloadImages(){ + imageData = []; + currentMap.images.forEach(path => { + let image = new Image(); + image.src = backendUrl + "public/" + path; + imageData.push(image); + + image.onload = Draw; + }); +} + +function RenameMap(id, new_title){ + currentMap.title = new_title; + SaveMap(id); +} + +function SaveMap(id){ + Api().post('/maps/update?campaign=' + GetCampaign()._id + "&map=" + id, {data: currentMap}).then(response => { + console.log("Map updated"); + }).catch(err => console.log(err)); +} + +function NewMap(){ } +function DeleteMap(map_id){ + +} + +function LoadMap(map){ + currentMap = map.data; + currentMapId.value = map._id; + backgroundColor.value = currentMap.backgroundColor; + ReloadImages(); + +} + +function UploadResource(image){ + return new Promise((resolve, reject) => { + const formData = new FormData(); + formData.append("image", dataURLtoFile(image.src)); + + Api().post('/maps/create-resource?campaign=' + GetCampaign()._id, formData, { + headers: { "Content-Type": "multipart/form-data"} + }).then(response => { + resolve(response.data.data); + }).catch(err => console.log(err)); + }); +} + +function CreateMap(){ + Api().post('/maps/create', { + campaign: GetCampaign()._id, + data: currentMap, + }).then(response => { + UpdateMapList(); + }).catch(err => console.log(err)); + +} + let GetBackgroundColor = () => backgroundColor; +function ChangeBackgroundColor(color){ + backgroundColor.value = color; // XD + currentMap.backgroundColor = color; + SaveMap(currentMapId.value); +} export { toMapX, @@ -188,4 +285,11 @@ export { // Draw, ImportDD2VTT, GetBackgroundColor, + ChangeBackgroundColor, + GetMapId, + + UpdateMapList, + GetMapList, + LoadMap, + RenameMap, }; \ No newline at end of file diff --git a/client/src/services/Windows.js b/client/src/services/Windows.js index c289cab6..1ff810d6 100644 --- a/client/src/services/Windows.js +++ b/client/src/services/Windows.js @@ -84,6 +84,16 @@ const defValues = { id: 'system-selector', title: "Select a game system", close: true + }, + 'map_window': { + id: 'map_window', + title: 'Maps', + close: true + }, + 'combat_window': { + id: 'combat_window', + title: "Combat", + close: true } } @@ -203,7 +213,6 @@ function CreateWindow(type, data = {}){ windows.value.push(finalData); // reload.value += 1; - console.log(windows.value); setTimeout(() => SetOnTop(finalData.id), 0); } } diff --git a/client/src/views/managers/GameManager.vue b/client/src/views/managers/GameManager.vue index a3865b38..114e1928 100644 --- a/client/src/views/managers/GameManager.vue +++ b/client/src/views/managers/GameManager.vue @@ -27,6 +27,18 @@ function OpenMapButtons(){ CreateWindow('map_buttons'); } +function OpenMapWindows(){ + CreateWindow('map_window'); +} + +function OpenCombatMenu(){ + CreateWindow('combat_window'); +} + +function ToggleGrid(){ + +} + watch(game, () => { if(game.value && in_game.value){ AddSound(game.value); @@ -46,11 +58,14 @@ watch(game, () => {
+ +
- + +
diff --git a/client/src/views/managers/WindowManager.vue b/client/src/views/managers/WindowManager.vue index 683654d5..74758278 100644 --- a/client/src/views/managers/WindowManager.vue +++ b/client/src/views/managers/WindowManager.vue @@ -20,6 +20,8 @@ import DiceWindow from '../windows/game/DiceWindow.vue' import MapButtons from '../windows/dm/MapButtons.vue' import EnvironmentWindow from '../windows/dm/EnvironmentWindow.vue' import SystemSelectorWindow from '../windows/campaigns/SystemSelectorWindow.vue' +import MapWindow from '../windows/dm/MapWindow.vue' +import CombatWindow from '../windows/game/CombatWindow.vue' // Gestionem ventanas const reload = ReloadRef(); @@ -40,7 +42,9 @@ const WindowMap = { dice_menu: DiceWindow, map_buttons: MapButtons, environment: EnvironmentWindow, - system_selector: SystemSelectorWindow + system_selector: SystemSelectorWindow, + map_window: MapWindow, + combat_window: CombatWindow }; diff --git a/client/src/views/partials/MapEntry.vue b/client/src/views/partials/MapEntry.vue new file mode 100644 index 00000000..becabe50 --- /dev/null +++ b/client/src/views/partials/MapEntry.vue @@ -0,0 +1,80 @@ + + + + + + + \ No newline at end of file diff --git a/client/src/views/partials/MapList.vue b/client/src/views/partials/MapList.vue new file mode 100644 index 00000000..2231aa0b --- /dev/null +++ b/client/src/views/partials/MapList.vue @@ -0,0 +1,46 @@ + + + + + + + \ No newline at end of file diff --git a/client/src/views/partials/Toast.vue b/client/src/views/partials/Toast.vue index dfc2a54f..69a9705a 100644 --- a/client/src/views/partials/Toast.vue +++ b/client/src/views/partials/Toast.vue @@ -74,6 +74,8 @@ emitter.on('toast', data => { border-radius: 6px; text-align: center; + z-index: 9999999; + animation: slide-in 0.4s ease-in-out; @keyframes slide-in { diff --git a/client/src/views/partials/game/IconButton.vue b/client/src/views/partials/game/IconButton.vue index 5f88a9a4..64c4ce5e 100644 --- a/client/src/views/partials/game/IconButton.vue +++ b/client/src/views/partials/game/IconButton.vue @@ -1,15 +1,16 @@ @@ -25,6 +26,15 @@ let size = props.size; width: 42px; } + &.small { + height: 24px; + width: 24px; + } + + &.toggled { + filter: invert(0.9); + } + background-color: var(--color-background-soft); border-radius: 6px; display: flex; @@ -42,10 +52,19 @@ let size = props.size; .icon { height: 24px; width: 24px; + + &.big { + height: 38px; + width: 38px; + } + + &.small { + height: 18px; + width: 18px; + } } -.big { - height: 38px; - width: 38px; -} + + + \ No newline at end of file diff --git a/client/src/views/windows/dm/EnvironmentWindow.vue b/client/src/views/windows/dm/EnvironmentWindow.vue index b1b6db26..302bc2a4 100644 --- a/client/src/views/windows/dm/EnvironmentWindow.vue +++ b/client/src/views/windows/dm/EnvironmentWindow.vue @@ -5,7 +5,7 @@ import { onMounted, onUpdated, ref, watch } from 'vue'; import { SetupHandle, SetSize, SetPosition, ResetPosition } from '@/services/Windows'; import IconButton from '@/views/partials/game/IconButton.vue' import ColorValue from '../../partials/parameters/ColorValue.vue'; -import { GetBackgroundColor } from '../../../services/Map'; +import { GetBackgroundColor, ChangeBackgroundColor } from '../../../services/Map'; const props = defineProps(['data']); const data = props.data; @@ -24,7 +24,7 @@ onMounted(() => { console.log(env_background.value.GetColor()); watch(env_background.value.GetColor(), () => { - GetBackgroundColor().value = env_background.value.GetColor().value; // XD + ChangeBackgroundColor(env_background.value.GetColor().value); }); }); diff --git a/client/src/views/windows/dm/MapButtons.vue b/client/src/views/windows/dm/MapButtons.vue index 041c2223..db0e8a37 100644 --- a/client/src/views/windows/dm/MapButtons.vue +++ b/client/src/views/windows/dm/MapButtons.vue @@ -4,36 +4,22 @@ import WindowHandle from '@/views/partials/WindowHandle.vue'; import { onMounted, onUpdated, ref } from 'vue'; import { SetupHandle, SetSize, SetPosition, ResetPosition } from '@/services/Windows'; import IconButton from '@/views/partials/game/IconButton.vue' -import { ImportDD2VTT } from '../../../services/Map'; import { CreateChildWindow, GetPosition } from '../../../services/Windows'; const props = defineProps(['data']); const data = props.data; const handle = ref(null); -const mapUploader = ref(null); let id = data.id; onMounted(() => { SetupHandle(id, handle); - SetSize(id, {x: 40, y: 500}); + SetSize(id, {x: 40, y: 200}); ResetPosition(id, {x: 10, y: 200}); - - mapUploader.value.addEventListener('change', (event) => { - let file = event.target.files[0]; - let reader = new FileReader(); - reader.addEventListener('load', (event) => { - ImportDD2VTT(JSON.parse(event.target.result)); - }); - - reader.readAsText(file); - }) }); -function UploadButton(){ - mapUploader.value.click(); -} + function EditEnvironment(){ let winPos = GetPosition(id); @@ -44,19 +30,13 @@ function EditEnvironment(){