More map upgrades.
This commit is contained in:
parent
168d683b13
commit
55304c569d
@ -2,3 +2,12 @@
|
|||||||
|
|
||||||
An open-source virtual table top for role-playing games
|
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)
|
@ -46,7 +46,6 @@ const chat = ref([
|
|||||||
let GetChatRef = () => chat;
|
let GetChatRef = () => chat;
|
||||||
|
|
||||||
socket.on('update-players', data => {
|
socket.on('update-players', data => {
|
||||||
console.log(data);
|
|
||||||
players.value = [];
|
players.value = [];
|
||||||
Object.keys(data).forEach((key) => {
|
Object.keys(data).forEach((key) => {
|
||||||
players.value.push(data[key]);
|
players.value.push(data[key]);
|
||||||
@ -107,7 +106,6 @@ function Disconnect(){
|
|||||||
}
|
}
|
||||||
|
|
||||||
function GetPlayer(player_campaign){
|
function GetPlayer(player_campaign){
|
||||||
console.log(players.value);
|
|
||||||
let index = players.value.findIndex((p) => {return p._id == player_campaign});
|
let index = players.value.findIndex((p) => {return p._id == player_campaign});
|
||||||
if(index != -1) return players.value[index];
|
if(index != -1) return players.value[index];
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,6 @@ let InGameRef = () => inGameRef;
|
|||||||
|
|
||||||
function LaunchGame(){
|
function LaunchGame(){
|
||||||
inGameRef.value = true;
|
inGameRef.value = true;
|
||||||
console.log("jdksadjlo")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function ExitGame(){
|
function ExitGame(){
|
||||||
|
@ -1,5 +1,21 @@
|
|||||||
import { initCustomFormatter, ref } from 'vue';
|
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 offsetX = 0;
|
||||||
let offsetY = 0;
|
let offsetY = 0;
|
||||||
let scale = 1;
|
let scale = 1;
|
||||||
@ -77,6 +93,7 @@ function zoom(amount){
|
|||||||
Draw();
|
Draw();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
let xUpperLeft = -Infinity;
|
let xUpperLeft = -Infinity;
|
||||||
let yUpperLeft = -Infinity;
|
let yUpperLeft = -Infinity;
|
||||||
let xDownRight = Infinity;
|
let xDownRight = Infinity;
|
||||||
@ -112,19 +129,21 @@ function drawGrid(cellSize){
|
|||||||
|
|
||||||
|
|
||||||
ctx.stroke();
|
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
|
// Ok aqui coses del mapa en si
|
||||||
let gridSize = 150;
|
let currentMap = {
|
||||||
let images = [];
|
gridSize: undefined,
|
||||||
let lines_of_sight = [];
|
images: [],
|
||||||
|
lines_of_sight: [],
|
||||||
|
backgroundColor: '#0f0f0f',
|
||||||
|
title: "Untitled map"
|
||||||
|
};
|
||||||
|
let imageData = [];
|
||||||
|
|
||||||
|
const currentMapId = ref('');
|
||||||
|
let GetMapId = () => currentMapId;
|
||||||
|
|
||||||
let backgroundColor = ref('#0f0f0f');
|
let backgroundColor = ref('#0f0f0f');
|
||||||
|
|
||||||
function Draw(){
|
function Draw(){
|
||||||
@ -135,7 +154,7 @@ function Draw(){
|
|||||||
|
|
||||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
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);
|
ctx.drawImage(image, toMapX(0), toMapY(0), image.naturalWidth * scale, image.naturalHeight * scale);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -143,39 +162,117 @@ function Draw(){
|
|||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.strokeStyle = "white";
|
ctx.strokeStyle = "white";
|
||||||
ctx.lineWidth = 3;
|
ctx.lineWidth = 3;
|
||||||
lines_of_sight.forEach((line) => {
|
currentMap.lines_of_sight.forEach((line) => {
|
||||||
ctx.moveTo(toMapX(line[0].x * gridSize), toMapY(line[0].y * gridSize));
|
ctx.moveTo(toMapX(line[0].x * currentMap.gridSize), toMapY(line[0].y * currentMap.gridSize));
|
||||||
ctx.lineTo(toMapX(line[1].x * gridSize), toMapY(line[1].y * gridSize));
|
ctx.lineTo(toMapX(line[1].x * currentMap.gridSize), toMapY(line[1].y * currentMap.gridSize));
|
||||||
});
|
});
|
||||||
ctx.stroke();
|
ctx.stroke();
|
||||||
|
|
||||||
drawGrid(gridSize);
|
if(currentMap.gridSize) drawGrid(currentMap.gridSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
function ImportDD2VTT(data){
|
function ImportDD2VTT(data){
|
||||||
console.log(data);
|
|
||||||
|
|
||||||
var image = new Image();
|
var image = new Image();
|
||||||
image.onload = function() {
|
image.onload = function() {
|
||||||
images.push(image);
|
UploadResource(image).then((imagePath) => {
|
||||||
Draw();
|
currentMap.images.push(imagePath);
|
||||||
|
CreateMap(currentMap);
|
||||||
|
ReloadImages();
|
||||||
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
gridSize = data.resolution.pixels_per_grid;
|
currentMap.gridSize = data.resolution.pixels_per_grid;
|
||||||
lines_of_sight = data.line_of_sight;
|
currentMap.lines_of_sight = data.line_of_sight;
|
||||||
|
|
||||||
backgroundColor.value = '#' + data.environment.ambient_light;
|
backgroundColor.value = '#' + data.environment.ambient_light;
|
||||||
|
currentMap.backgroundColor = '#' + data.environment.ambient_light;
|
||||||
|
|
||||||
xUpperLeft = data.resolution.map_origin.x * data.resolution.pixels_per_grid;
|
xUpperLeft = data.resolution.map_origin.x * data.resolution.pixels_per_grid;
|
||||||
yUpperLeft = data.resolution.map_origin.y * 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;
|
xDownRight = xUpperLeft + data.resolution.map_size.x * data.resolution.pixels_per_grid;
|
||||||
yDownRight = yUpperLeft + data.resolution.map_size.y * 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;
|
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;
|
let GetBackgroundColor = () => backgroundColor;
|
||||||
|
function ChangeBackgroundColor(color){
|
||||||
|
backgroundColor.value = color; // XD
|
||||||
|
currentMap.backgroundColor = color;
|
||||||
|
SaveMap(currentMapId.value);
|
||||||
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
toMapX,
|
toMapX,
|
||||||
@ -188,4 +285,11 @@ export {
|
|||||||
// Draw,
|
// Draw,
|
||||||
ImportDD2VTT,
|
ImportDD2VTT,
|
||||||
GetBackgroundColor,
|
GetBackgroundColor,
|
||||||
|
ChangeBackgroundColor,
|
||||||
|
GetMapId,
|
||||||
|
|
||||||
|
UpdateMapList,
|
||||||
|
GetMapList,
|
||||||
|
LoadMap,
|
||||||
|
RenameMap,
|
||||||
};
|
};
|
@ -84,6 +84,16 @@ const defValues = {
|
|||||||
id: 'system-selector',
|
id: 'system-selector',
|
||||||
title: "Select a game system",
|
title: "Select a game system",
|
||||||
close: true
|
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);
|
windows.value.push(finalData);
|
||||||
// reload.value += 1;
|
// reload.value += 1;
|
||||||
|
|
||||||
console.log(windows.value);
|
|
||||||
setTimeout(() => SetOnTop(finalData.id), 0);
|
setTimeout(() => SetOnTop(finalData.id), 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,18 @@ function OpenMapButtons(){
|
|||||||
CreateWindow('map_buttons');
|
CreateWindow('map_buttons');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function OpenMapWindows(){
|
||||||
|
CreateWindow('map_window');
|
||||||
|
}
|
||||||
|
|
||||||
|
function OpenCombatMenu(){
|
||||||
|
CreateWindow('combat_window');
|
||||||
|
}
|
||||||
|
|
||||||
|
function ToggleGrid(){
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
watch(game, () => {
|
watch(game, () => {
|
||||||
if(game.value && in_game.value){
|
if(game.value && in_game.value){
|
||||||
AddSound(game.value);
|
AddSound(game.value);
|
||||||
@ -46,11 +58,14 @@ watch(game, () => {
|
|||||||
<div class="vertical-button">
|
<div class="vertical-button">
|
||||||
<IconButton icon="icons/iconoir/regular/menu.svg" :action="OpenCampaignPreview"></IconButton>
|
<IconButton icon="icons/iconoir/regular/menu.svg" :action="OpenCampaignPreview"></IconButton>
|
||||||
<IconButton icon="icons/iconoir/regular/cursor-pointer.svg"></IconButton>
|
<IconButton icon="icons/iconoir/regular/cursor-pointer.svg"></IconButton>
|
||||||
|
<IconButton icon="icons/iconoir/regular/orthogonal-view.svg" :action="ToggleGrid"></IconButton>
|
||||||
<IconButton icon="icons/game-icons/000000/delapouite/rolling-dice-cup.svg" :action="OpenDiceMenu"></IconButton>
|
<IconButton icon="icons/game-icons/000000/delapouite/rolling-dice-cup.svg" :action="OpenDiceMenu"></IconButton>
|
||||||
|
<IconButton icon="icons/game-icons/000000/lorc/crossed-sabres.svg" :action="OpenCombatMenu"></IconButton>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="vertical-button gm" v-if="is_dm">
|
<div class="vertical-button gm" v-if="is_dm">
|
||||||
<IconButton icon="icons/iconoir/regular/map.svg" :action="OpenMapButtons"></IconButton>
|
<IconButton icon="icons/iconoir/regular/map.svg" :action="OpenMapWindows"></IconButton>
|
||||||
|
<IconButton icon="icons/iconoir/regular/hammer.svg" :action="OpenMapButtons"></IconButton>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="horizontal-button">
|
<div class="horizontal-button">
|
||||||
|
@ -20,6 +20,8 @@ import DiceWindow from '../windows/game/DiceWindow.vue'
|
|||||||
import MapButtons from '../windows/dm/MapButtons.vue'
|
import MapButtons from '../windows/dm/MapButtons.vue'
|
||||||
import EnvironmentWindow from '../windows/dm/EnvironmentWindow.vue'
|
import EnvironmentWindow from '../windows/dm/EnvironmentWindow.vue'
|
||||||
import SystemSelectorWindow from '../windows/campaigns/SystemSelectorWindow.vue'
|
import SystemSelectorWindow from '../windows/campaigns/SystemSelectorWindow.vue'
|
||||||
|
import MapWindow from '../windows/dm/MapWindow.vue'
|
||||||
|
import CombatWindow from '../windows/game/CombatWindow.vue'
|
||||||
|
|
||||||
// Gestionem ventanas
|
// Gestionem ventanas
|
||||||
const reload = ReloadRef();
|
const reload = ReloadRef();
|
||||||
@ -40,7 +42,9 @@ const WindowMap = {
|
|||||||
dice_menu: DiceWindow,
|
dice_menu: DiceWindow,
|
||||||
map_buttons: MapButtons,
|
map_buttons: MapButtons,
|
||||||
environment: EnvironmentWindow,
|
environment: EnvironmentWindow,
|
||||||
system_selector: SystemSelectorWindow
|
system_selector: SystemSelectorWindow,
|
||||||
|
map_window: MapWindow,
|
||||||
|
combat_window: CombatWindow
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
80
client/src/views/partials/MapEntry.vue
Normal file
80
client/src/views/partials/MapEntry.vue
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
<script setup>
|
||||||
|
import IconButton from '@/views/partials/game/IconButton.vue';
|
||||||
|
import { onMounted, ref, toRaw, watch } from 'vue';
|
||||||
|
import { GetMapId, LoadMap, RenameMap } from '../../services/Map';
|
||||||
|
|
||||||
|
const toggled = ref("");
|
||||||
|
const title = ref(null);
|
||||||
|
const mapId = GetMapId();
|
||||||
|
|
||||||
|
const props = defineProps(['data']);
|
||||||
|
let data = props.data;
|
||||||
|
|
||||||
|
function ViewMap(){
|
||||||
|
LoadMap(toRaw(data))
|
||||||
|
}
|
||||||
|
|
||||||
|
function DeleteMap(){
|
||||||
|
console.log("Delete map");
|
||||||
|
}
|
||||||
|
|
||||||
|
function Rename(){
|
||||||
|
RenameMap(data._id, title.value.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
title.value.value = data.data.title;
|
||||||
|
|
||||||
|
watch(mapId, () => {
|
||||||
|
if(mapId.value == data._id){
|
||||||
|
toggled.value = "toggled-yes";
|
||||||
|
} else {
|
||||||
|
toggled.value = "toggled-no";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="map-entry-container">
|
||||||
|
<input type="text" ref="title" v-on:change.prevent="Rename">
|
||||||
|
<div class="horizontal-button">
|
||||||
|
<div class="toggler" :class="toggled"><IconButton icon="icons/iconoir/regular/eye.svg" :action="ViewMap" size="small"></IconButton></div>
|
||||||
|
<IconButton icon="icons/iconoir/regular/trash.svg" :action="DeleteMap" size="small"></IconButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.toggler {
|
||||||
|
transition: filter 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggled-yes {
|
||||||
|
filter: invert(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.map-entry-container {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
background-color: var(--color-background-softer);
|
||||||
|
padding: 10px;
|
||||||
|
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
&.selected {
|
||||||
|
background-color: var(--color-background-softest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.horizontal-button {
|
||||||
|
margin-left: auto;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
46
client/src/views/partials/MapList.vue
Normal file
46
client/src/views/partials/MapList.vue
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
<script setup>
|
||||||
|
import { onMounted } from 'vue';
|
||||||
|
import { GetMapList, UpdateMapList } from '../../services/Map';
|
||||||
|
import MapEntry from './MapEntry.vue';
|
||||||
|
|
||||||
|
const maps = GetMapList();
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
UpdateMapList();
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="map-list-container">
|
||||||
|
<MapEntry v-for="map in maps" :data="map" :id="map._id"></MapEntry>
|
||||||
|
<div class="no-maps-message" v-show="maps.length == 0">
|
||||||
|
<span class="create-map">You haven't created any map!</span>
|
||||||
|
<span class="create-map">Upload or create a new one</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.map-list-container {
|
||||||
|
display: flex;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.create-map {
|
||||||
|
font-size: 14px;
|
||||||
|
color: var(--text-disabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-maps-message {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 10px;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
@ -74,6 +74,8 @@ emitter.on('toast', data => {
|
|||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|
||||||
|
z-index: 9999999;
|
||||||
|
|
||||||
|
|
||||||
animation: slide-in 0.4s ease-in-out;
|
animation: slide-in 0.4s ease-in-out;
|
||||||
@keyframes slide-in {
|
@keyframes slide-in {
|
||||||
|
@ -1,15 +1,16 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
const props = defineProps(['icon', 'action','size']);
|
const props = defineProps(['icon', 'action', 'size', 'toggled']);
|
||||||
let icon = props.icon;
|
let icon = props.icon;
|
||||||
let action = props.action;
|
let action = props.action;
|
||||||
|
|
||||||
let size = props.size;
|
let size = props.size;
|
||||||
|
let toggled = props.toggled;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="icon-button sound-click" :class="size" v-on:click.prevent="action">
|
<div class="icon-button sound-click" :class="size + ' ' + toggled" v-on:click.prevent="action">
|
||||||
<img class="icon" draggable="false" :src="icon" :class="size">
|
<img class="icon" draggable="false" :src="icon" :class="size">
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -25,6 +26,15 @@ let size = props.size;
|
|||||||
width: 42px;
|
width: 42px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.small {
|
||||||
|
height: 24px;
|
||||||
|
width: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.toggled {
|
||||||
|
filter: invert(0.9);
|
||||||
|
}
|
||||||
|
|
||||||
background-color: var(--color-background-soft);
|
background-color: var(--color-background-soft);
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -42,10 +52,19 @@ let size = props.size;
|
|||||||
.icon {
|
.icon {
|
||||||
height: 24px;
|
height: 24px;
|
||||||
width: 24px;
|
width: 24px;
|
||||||
|
|
||||||
|
&.big {
|
||||||
|
height: 38px;
|
||||||
|
width: 38px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.small {
|
||||||
|
height: 18px;
|
||||||
|
width: 18px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.big {
|
|
||||||
height: 38px;
|
|
||||||
width: 38px;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
@ -5,7 +5,7 @@ import { onMounted, onUpdated, ref, watch } from 'vue';
|
|||||||
import { SetupHandle, SetSize, SetPosition, ResetPosition } from '@/services/Windows';
|
import { SetupHandle, SetSize, SetPosition, ResetPosition } from '@/services/Windows';
|
||||||
import IconButton from '@/views/partials/game/IconButton.vue'
|
import IconButton from '@/views/partials/game/IconButton.vue'
|
||||||
import ColorValue from '../../partials/parameters/ColorValue.vue';
|
import ColorValue from '../../partials/parameters/ColorValue.vue';
|
||||||
import { GetBackgroundColor } from '../../../services/Map';
|
import { GetBackgroundColor, ChangeBackgroundColor } from '../../../services/Map';
|
||||||
|
|
||||||
const props = defineProps(['data']);
|
const props = defineProps(['data']);
|
||||||
const data = props.data;
|
const data = props.data;
|
||||||
@ -24,7 +24,7 @@ onMounted(() => {
|
|||||||
|
|
||||||
console.log(env_background.value.GetColor());
|
console.log(env_background.value.GetColor());
|
||||||
watch(env_background.value.GetColor(), () => {
|
watch(env_background.value.GetColor(), () => {
|
||||||
GetBackgroundColor().value = env_background.value.GetColor().value; // XD
|
ChangeBackgroundColor(env_background.value.GetColor().value);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -4,36 +4,22 @@ import WindowHandle from '@/views/partials/WindowHandle.vue';
|
|||||||
import { onMounted, onUpdated, ref } from 'vue';
|
import { onMounted, onUpdated, ref } from 'vue';
|
||||||
import { SetupHandle, SetSize, SetPosition, ResetPosition } from '@/services/Windows';
|
import { SetupHandle, SetSize, SetPosition, ResetPosition } from '@/services/Windows';
|
||||||
import IconButton from '@/views/partials/game/IconButton.vue'
|
import IconButton from '@/views/partials/game/IconButton.vue'
|
||||||
import { ImportDD2VTT } from '../../../services/Map';
|
|
||||||
import { CreateChildWindow, GetPosition } from '../../../services/Windows';
|
import { CreateChildWindow, GetPosition } from '../../../services/Windows';
|
||||||
|
|
||||||
const props = defineProps(['data']);
|
const props = defineProps(['data']);
|
||||||
const data = props.data;
|
const data = props.data;
|
||||||
|
|
||||||
const handle = ref(null);
|
const handle = ref(null);
|
||||||
const mapUploader = ref(null);
|
|
||||||
|
|
||||||
let id = data.id;
|
let id = data.id;
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
SetupHandle(id, handle);
|
SetupHandle(id, handle);
|
||||||
SetSize(id, {x: 40, y: 500});
|
SetSize(id, {x: 40, y: 200});
|
||||||
ResetPosition(id, {x: 10, 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(){
|
function EditEnvironment(){
|
||||||
let winPos = GetPosition(id);
|
let winPos = GetPosition(id);
|
||||||
@ -44,19 +30,13 @@ function EditEnvironment(){
|
|||||||
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<form id="send-map-form" enctype="multipart/form-data">
|
|
||||||
<input name="file" type="file" accept=".dd2vtt" ref="mapUploader">
|
|
||||||
</form>
|
|
||||||
|
|
||||||
|
|
||||||
<div class="window-wrapper" :id="'window-wrapper-' + id">
|
<div class="window-wrapper" :id="'window-wrapper-' + id">
|
||||||
<WindowHandle :window="id" ref="handle"></WindowHandle>
|
<WindowHandle :window="id" ref="handle"></WindowHandle>
|
||||||
|
|
||||||
<div class="vertical-button">
|
<div class="vertical-button">
|
||||||
<IconButton icon="icons/iconoir/regular/upload.svg" :action="UploadButton"></IconButton>
|
<IconButton icon="icons/iconoir/regular/square-3d-three-points.svg" :action="EditEnvironment"></IconButton>
|
||||||
<hr>
|
|
||||||
<IconButton icon="icons/iconoir/regular/square-3d-three-points.svg" :action="UploadButton"></IconButton>
|
|
||||||
<IconButton icon="icons/iconoir/regular/orthogonal-view.svg" :action="UploadButton"></IconButton>
|
|
||||||
<hr>
|
<hr>
|
||||||
<IconButton icon="icons/iconoir/regular/sun-light.svg" :action="EditEnvironment"></IconButton>
|
<IconButton icon="icons/iconoir/regular/sun-light.svg" :action="EditEnvironment"></IconButton>
|
||||||
</div>
|
</div>
|
||||||
@ -65,9 +45,7 @@ function EditEnvironment(){
|
|||||||
|
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
#send-map-form {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.window-wrapper {
|
.window-wrapper {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
83
client/src/views/windows/dm/MapWindow.vue
Normal file
83
client/src/views/windows/dm/MapWindow.vue
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
<script setup>
|
||||||
|
import WindowHandle from '@/views/partials/WindowHandle.vue';
|
||||||
|
|
||||||
|
import { onMounted, onUpdated, ref, watch } from 'vue';
|
||||||
|
import { SetupHandle, SetSize, SetPosition, ResetPosition } from '@/services/Windows';
|
||||||
|
import { ImportDD2VTT } from '../../../services/Map';
|
||||||
|
|
||||||
|
import MapList from '../../partials/MapList.vue';
|
||||||
|
import IconButton from '@/views/partials/game/IconButton.vue';
|
||||||
|
|
||||||
|
const props = defineProps(['data']);
|
||||||
|
const data = props.data;
|
||||||
|
|
||||||
|
const handle = ref(null);
|
||||||
|
const mapUploader = ref(null);
|
||||||
|
|
||||||
|
let id = data.id;
|
||||||
|
|
||||||
|
function UploadButton(){
|
||||||
|
mapUploader.value.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
function NewMapButton(){
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
SetupHandle(id, handle);
|
||||||
|
SetSize(id, {x: 300, y: 600});
|
||||||
|
ResetPosition(id, {x: 100, y: 10});
|
||||||
|
|
||||||
|
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);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<template>
|
||||||
|
|
||||||
|
<form id="send-map-form" enctype="multipart/form-data">
|
||||||
|
<input name="file" type="file" accept=".dd2vtt" ref="mapUploader">
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div class="window-wrapper" :id="'window-wrapper-' + id">
|
||||||
|
<WindowHandle :window="id" ref="handle"></WindowHandle>
|
||||||
|
|
||||||
|
<div class="horizontal-button">
|
||||||
|
<IconButton icon="icons/iconoir/regular/upload.svg" :action="UploadButton"></IconButton>
|
||||||
|
<IconButton icon="icons/iconoir/regular/empty-page.svg" :action="NewMapButton"></IconButton>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<MapList></MapList>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
#send-map-form {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.window-wrapper {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.horizontal-button {
|
||||||
|
margin-top: 5px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
</style>
|
38
client/src/views/windows/game/CombatWindow.vue
Normal file
38
client/src/views/windows/game/CombatWindow.vue
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
<script setup>
|
||||||
|
import WindowHandle from '@/views/partials/WindowHandle.vue';
|
||||||
|
|
||||||
|
import { onMounted, onUpdated, ref, watch } from 'vue';
|
||||||
|
import { SetupHandle, SetSize, SetPosition, ResetPosition } from '@/services/Windows';
|
||||||
|
|
||||||
|
const props = defineProps(['data']);
|
||||||
|
const data = props.data;
|
||||||
|
|
||||||
|
const handle = ref(null);
|
||||||
|
|
||||||
|
let id = data.id;
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
SetupHandle(id, handle);
|
||||||
|
SetSize(id, {x: 200, y: 300});
|
||||||
|
ResetPosition(id, {x: 30, y: 300});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="window-wrapper" :id="'window-wrapper-' + id">
|
||||||
|
<WindowHandle :window="id" ref="handle"></WindowHandle>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
.window-wrapper {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
</style>
|
@ -2,11 +2,10 @@ const mongoose = require("mongoose");
|
|||||||
const Schema = mongoose.Schema;
|
const Schema = mongoose.Schema;
|
||||||
|
|
||||||
const MapSchema = new Schema({
|
const MapSchema = new Schema({
|
||||||
name: {type: String, required: true},
|
|
||||||
data: { type: Object }, // Data del format dd2vtt
|
data: { type: Object }, // Data del format dd2vtt
|
||||||
campaign: {type: mongoose.Types.ObjectId, ref: "Campaign"},
|
campaign: {type: mongoose.Types.ObjectId, ref: "Campaign"},
|
||||||
image: { type: String },
|
image: { type: String },
|
||||||
entities: { type: Object }
|
entities: { type: Object }
|
||||||
});
|
});
|
||||||
|
|
||||||
module.exports = mongoose.model('Entity', EntitySchema);
|
module.exports = mongoose.model('Map', MapSchema);
|
121
server/routes/map.js
Normal file
121
server/routes/map.js
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
const express = require('express');
|
||||||
|
const router = express.Router();
|
||||||
|
|
||||||
|
const passport = require('passport');
|
||||||
|
const rateLimitMiddleware = require("../config/rate-limiter");
|
||||||
|
|
||||||
|
const Campaign = require("../models/Campaign");
|
||||||
|
const CampaignUser = require("../models/CampaignUser");
|
||||||
|
const Map = require("../models/Map");
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
const upload = require("../config/storage");
|
||||||
|
|
||||||
|
router.post('/create-resource', upload.single("image"), passport.authenticate('jwt', {session: false}), (req, res) => {
|
||||||
|
const imageName = req.file.filename;
|
||||||
|
|
||||||
|
Campaign.findById(req.query.campaign).then((campaign) => {
|
||||||
|
CampaignUser.findOne({campaign, user: req.user}).then((data) => {
|
||||||
|
if(!data) {
|
||||||
|
res.json({status: "error", msg: "not-found"})
|
||||||
|
fs.unlink(imageName);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(data.is_dm){
|
||||||
|
res.json({
|
||||||
|
status: "ok",
|
||||||
|
data: imageName
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
res.json({status: "error", msg: "not-dm"})
|
||||||
|
fs.unlink(imageName);
|
||||||
|
return;
|
||||||
|
}).catch((err) => res.json({status: "error", msg: "not-found"}));
|
||||||
|
}).catch((err) => res.json({status: "error", err}));
|
||||||
|
});
|
||||||
|
|
||||||
|
// rateLimitMiddleware?
|
||||||
|
router.post('/create', passport.authenticate('jwt', {session: false}), (req, res) => {
|
||||||
|
Campaign.findById(req.body.campaign).then((campaign) => {
|
||||||
|
CampaignUser.findOne({campaign, user: req.user}).then((data) => {
|
||||||
|
if(!data) {
|
||||||
|
res.json({status: "error", msg: "not-found"})
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(data.is_dm){
|
||||||
|
let mapData = req.body.data;
|
||||||
|
if(mapData){
|
||||||
|
let map = new Map({
|
||||||
|
data: mapData,
|
||||||
|
campaign
|
||||||
|
});
|
||||||
|
map.save().then(map => {
|
||||||
|
res.json({
|
||||||
|
status: "ok",
|
||||||
|
data: map
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
});
|
||||||
|
} else res.json({status: "error", msg: "args"})
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}).catch((err) => res.json({status: "error", msg: "not-found"}));
|
||||||
|
}).catch((err) => res.json({status: "error", err}));
|
||||||
|
});
|
||||||
|
|
||||||
|
router.post('/update', passport.authenticate('jwt', {session: false}), (req, res) => {
|
||||||
|
Campaign.findById(req.query.campaign).then((campaign) => {
|
||||||
|
CampaignUser.findOne({campaign, user: req.user}).then((data) => {
|
||||||
|
if(!data) {
|
||||||
|
res.json({status: "error", msg: "not-found"})
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(data.is_dm){
|
||||||
|
console.log("Ab");
|
||||||
|
let mapData = req.body.data;
|
||||||
|
if(mapData){
|
||||||
|
console.log("Map data:");
|
||||||
|
console.log(mapData)
|
||||||
|
Map.updateOne({_id: req.query.map, campaign}, {data: mapData}).then(map => {
|
||||||
|
res.json({
|
||||||
|
status: "ok",
|
||||||
|
data: map
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else res.json({status: "error", msg: "args"})
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}).catch((err) => res.json({status: "error", msg: "not-found"}));
|
||||||
|
}).catch((err) => res.json({status: "error", err}));
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get('/list', passport.authenticate('jwt', {session: false}), (req, res) => {
|
||||||
|
Campaign.findById(req.query.campaign).then((campaign) => {
|
||||||
|
CampaignUser.findOne({campaign, user: req.user}).then((data) => {
|
||||||
|
if(!data) {
|
||||||
|
res.json({status: "error", msg: "not-found"})
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map.find({campaign}).then(data => {
|
||||||
|
res.json({status: "ok", data});
|
||||||
|
return;
|
||||||
|
}).catch(err => res.json({status: "error", msg: "internal"}));
|
||||||
|
}).catch((err) => res.json({status: "error", msg: "not-found"}));
|
||||||
|
}).catch((err) => res.json({status: "error", err}));
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get('/get', passport.authenticate('jwt', {session: false}), (req, res) => {
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
router.delete('/delete', passport.authenticate('jwt', {session: false}), (req, res) => {
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = router;
|
@ -64,6 +64,7 @@ app.use(cors());
|
|||||||
// Routes (/ només)
|
// Routes (/ només)
|
||||||
app.use('/user', require('./routes/user'));
|
app.use('/user', require('./routes/user'));
|
||||||
app.use('/campaign', require('./routes/campaign'));
|
app.use('/campaign', require('./routes/campaign'));
|
||||||
|
app.use('/maps', require('./routes/map'))
|
||||||
|
|
||||||
|
|
||||||
app.use('/public', express.static('uploads'));
|
app.use('/public', express.static('uploads'));
|
||||||
|
Loading…
Reference in New Issue
Block a user