Mas cosas
This commit is contained in:
parent
e2cf465862
commit
569e165690
7
client/package-lock.json
generated
7
client/package-lock.json
generated
@ -11,6 +11,7 @@
|
||||
"@kangc/v-md-editor": "^2.3.17",
|
||||
"axios": "^1.5.1",
|
||||
"babel-plugin-prismjs": "^2.1.0",
|
||||
"dice-notation-js": "^1.0.3",
|
||||
"ef-infinite-canvas": "^0.6.6",
|
||||
"form-data": "^4.0.0",
|
||||
"jquery": "^3.7.1",
|
||||
@ -5102,6 +5103,12 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/dice-notation-js": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/dice-notation-js/-/dice-notation-js-1.0.3.tgz",
|
||||
"integrity": "sha512-K02a/w3kjRp4QPYE3+qlXnQRVw9n00IIJHYtrxAryIIC112SP36YKo2Z9CR/f+ZKzj+YKJmCeTKnMIc4+crG5g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/dir-glob": {
|
||||
"version": "2.2.2",
|
||||
"resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.2.2.tgz",
|
||||
|
@ -12,6 +12,7 @@
|
||||
"@kangc/v-md-editor": "^2.3.17",
|
||||
"axios": "^1.5.1",
|
||||
"babel-plugin-prismjs": "^2.1.0",
|
||||
"dice-notation-js": "^1.0.3",
|
||||
"ef-infinite-canvas": "^0.6.6",
|
||||
"form-data": "^4.0.0",
|
||||
"jquery": "^3.7.1",
|
||||
|
@ -0,0 +1,7 @@
|
||||
<svg version="1.2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="512" height="512">
|
||||
<title>perspective-dice-six-svg</title>
|
||||
<style>
|
||||
.s0 { fill: #000000 }
|
||||
</style>
|
||||
<path id="Layer" fill-rule="evenodd" class="s0" d="m272.8 46.9l152.8 88.4c9.6 5.5 9.6 14.3 0 19.8l-152.8 88.4c-9.5 5.5-24.7 5.5-34.2 0l-152.8-88.4c-9.6-5.5-9.6-14.3 0-19.8l152.8-88.4c4.7-2.8 10.9-4.1 17.1-4.1 6.2 0 12.4 1.3 17.1 4.1zm-44.8 58.3q-4.8 1.9-9.1 4.5-13.4 8-10.3 19.4 3 11.2 21.7 22.9 18.3 11.5 34.9 13.8 16.5 2.2 28.5-5 10.9-6.5 10-14.9-0.8-8.5-13-16.1-11-6.9-23.6-7.6-12.5-0.8-22 4.8-3.7 2.3-5.7 5-1.9 2.7-2.1 5.8-8.9-6.3-9.8-11.7-0.9-5.4 6.2-9.7 3.3-1.9 7.9-3.3 4.5-1.4 10.7-2.4l-14-8.7q-5.5 1.3-10.3 3.2zm34.2 29.1q0.4 0 0.7 0 0.3 0.1 0.6 0.1 0.4 0.1 0.7 0.1 5.4 0.8 12 4.9 6.7 4.2 7.9 7.5 1.2 3.3-3 5.9-4.1 2.5-9.5 1.7-5.4-0.9-12.1-5-6.6-4.2-7.8-7.5-1.3-3.3 2.9-5.8 3.1-1.9 7-1.9 0.3 0 0.6 0zm183.9 49.8v157.7c0 11.1-7.6 24.2-17.2 29.7l-146.9 84.9c-9.6 5.5-17.2 1.1-17.2-9.9v-157.7c0-11.1 7.6-24.2 17.2-29.7l146.9-84.9c2.7-1.5 5-2.3 7-2.4 4.9-0.2 7.7 4.3 10.2 12.3zm-363-9.9l146.9 84.9c9.6 5.5 17.2 18.6 17.2 29.7v157.7c0 11-7.6 15.4-17.2 9.9l-146.9-84.9c-9.6-5.5-17.2-18.6-17.2-29.7v-157.7c0.2-6.5 4-11.8 10.2-11.8 2.1 0 4.4 0.6 7 1.9zm45.4 136.4q4.6 0.4 8.5 1.6 1 0.3 2 0.7 0.9 0.3 1.9 0.8 0.9 0.4 1.8 0.9 0.9 0.4 1.8 1 5.9 3.5 9.3 9.1 3.5 5.7 3.5 12 0 6.2-3.5 7.8-3.4 1.6-9.3-2-4.1-2.4-8.7-7.1-4.6-4.6-9.8-11.4v16.1q5.2 5.7 10.4 10.2 5.3 4.5 10.7 7.7 12.4 7.5 19.4 4.4 7-3.1 7-15.9 0-13-6.4-24.1-6.4-11.1-18-18-2-1.3-3.9-2-0.4-0.2-0.9-0.4-0.5-0.2-0.9-0.3-0.5-0.2-1-0.3-0.4-0.2-0.9-0.3v-12.3l27.5 16.5v-15l-40.5-24.3zm236.2-32.8l-22.7 61.4v17.7l25.2-15.1v14.6l13.8-8.3v-14.6l8.1-4.8v-15l-8.1 4.8v-50.5zm2.5 49.1l-16.1 9.6 16.1-43.2z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.7 KiB |
BIN
client/public/img/d20.png
Normal file
BIN
client/public/img/d20.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 18 KiB |
BIN
client/public/img/dd-dice-512.webp
Normal file
BIN
client/public/img/dd-dice-512.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 17 KiB |
@ -22,7 +22,7 @@
|
||||
--c-black-muter: #585858;
|
||||
|
||||
|
||||
--c-black-blurred: #222222cc;
|
||||
--c-black-blurred: #222222;
|
||||
|
||||
--c-indigo: #2c3e50;
|
||||
|
||||
@ -96,6 +96,9 @@
|
||||
--chat-background: var(--c-blacker);
|
||||
--text-disabled: #7e7e7e;
|
||||
|
||||
--color-chat-other: #1d1d1d;
|
||||
--color-roll-dice-chat: #665750;
|
||||
|
||||
--color-hover: var()
|
||||
}
|
||||
}
|
||||
|
@ -67,6 +67,13 @@ a {
|
||||
background-color: var(--c-button-green-hover);
|
||||
}
|
||||
|
||||
.btn-red {
|
||||
background-color: var(--c-button-red);
|
||||
}
|
||||
.btn-red:hover {
|
||||
background-color: var(--c-button-red-hover);
|
||||
}
|
||||
|
||||
hr {
|
||||
border: 0;
|
||||
height: 1px;
|
||||
|
@ -4,6 +4,7 @@ import { io } from "socket.io-client";
|
||||
import Api from '@/services/Api'
|
||||
import { backendUrl } from './BackendURL';
|
||||
import { GetUser } from './User';
|
||||
import { ExitGame } from './Game';
|
||||
|
||||
let emitter;
|
||||
|
||||
@ -25,7 +26,24 @@ let GetPlayerList = () => { return players; };
|
||||
let GetCampaign = () => { return currentCampaign; };
|
||||
let GetClient = () => { return currentPlayer; };
|
||||
|
||||
let chatMessageId = 0;
|
||||
const chat = ref([
|
||||
/* {
|
||||
id: 1,
|
||||
author: "66ae8aea3e78bb669e25010d",
|
||||
chunks: [
|
||||
{
|
||||
id: 1,
|
||||
type: "text",
|
||||
content: "Hola test"
|
||||
}
|
||||
]
|
||||
} */
|
||||
]);
|
||||
let GetChatRef = () => chat;
|
||||
|
||||
socket.on('update-players', data => {
|
||||
console.log(data);
|
||||
players.value = [];
|
||||
Object.keys(data).forEach((key) => {
|
||||
players.value.push(data[key]);
|
||||
@ -33,22 +51,60 @@ socket.on('update-players', data => {
|
||||
});
|
||||
})
|
||||
|
||||
function DisplayCampaign(data){
|
||||
socket.on('message', (data) => {
|
||||
// Add new chat message, ?
|
||||
if(chat.value.length > 0) if(chat.value[chat.value.length - 1].author == data.author){
|
||||
chat.value[chat.value.length - 1].chunkSize += 1;
|
||||
chat.value[chat.value.length - 1].chunks.push({
|
||||
id: chat.value[chat.value.length - 1].chunkSize,
|
||||
type: data.type ? data.type : 'text',
|
||||
content: data.content
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
chatMessageId += 1;
|
||||
chat.value.push({
|
||||
id: chatMessageId,
|
||||
author: data.author,
|
||||
chunkSize: 1,
|
||||
chunks: [
|
||||
{
|
||||
id: 1,
|
||||
type: data.type ? data.type : 'text',
|
||||
content: data.content
|
||||
}
|
||||
]
|
||||
});
|
||||
});
|
||||
|
||||
function SendMessage(data){
|
||||
socket.emit('message', data);
|
||||
}
|
||||
|
||||
function DisplayCampaign(data = currentCampaign){
|
||||
ClearAll();
|
||||
CreateWindow('campaign_preview', {campaign: data});
|
||||
}
|
||||
|
||||
function ConnectToCampaign(campaign){
|
||||
currentCampaign = campaign;
|
||||
chat.value = [];
|
||||
|
||||
socket.emit('enter', GetUser(), currentCampaign._id);
|
||||
}
|
||||
|
||||
function Disconnect(){
|
||||
socket.emit('exit');
|
||||
ExitGame();
|
||||
|
||||
currentCampaign = null;
|
||||
currentPlayer = null;
|
||||
chat.value = [];
|
||||
}
|
||||
|
||||
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];
|
||||
}
|
||||
@ -66,4 +122,7 @@ export {
|
||||
GetClient,
|
||||
GetPlayerList,
|
||||
GetPlayer,
|
||||
|
||||
GetChatRef,
|
||||
SendMessage
|
||||
};
|
@ -12,7 +12,9 @@ const windows = {
|
||||
campaign_list: ref([]),
|
||||
new_campaign: ref([]),
|
||||
join_campaign: ref([]),
|
||||
campaign_preview: ref([])
|
||||
campaign_preview: ref([]),
|
||||
chat: ref([]),
|
||||
dice_menu: ref([]),
|
||||
};
|
||||
|
||||
const defValues = {
|
||||
@ -66,6 +68,16 @@ const defValues = {
|
||||
ClearWindow('campaign_preview');
|
||||
CreateWindow('campaign_list');
|
||||
}
|
||||
},
|
||||
'chat': {
|
||||
id: 'chat',
|
||||
title: 'Chat',
|
||||
close: true
|
||||
},
|
||||
'dice_menu': {
|
||||
id: 'dice_menu',
|
||||
title: 'Dice roll',
|
||||
close: true
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,12 +4,26 @@ 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 { ClearAll, CreateWindow } from '../../services/Windows';
|
||||
|
||||
const game = ref(null);
|
||||
const in_game = InGameRef();
|
||||
|
||||
function OpenCampaignPreview(){
|
||||
CreateWindow('campaign_preview', {campaign: GetCampaign(), style: 'compact', hide_start: true, back: undefined, close: true});
|
||||
}
|
||||
|
||||
function OpenChat(){
|
||||
CreateWindow('chat');
|
||||
}
|
||||
|
||||
function OpenDiceMenu(){
|
||||
CreateWindow('dice_menu');
|
||||
}
|
||||
|
||||
watch(game, () => {
|
||||
if(game){
|
||||
if(game.value && in_game.value){
|
||||
AddSound(game.value);
|
||||
}
|
||||
});
|
||||
@ -22,12 +36,14 @@ watch(game, () => {
|
||||
<!-- Aquests dos son absolute -->
|
||||
|
||||
<div class="vertical-button">
|
||||
<IconButton icon="icons/iconoir/regular/menu.svg"></IconButton>
|
||||
<IconButton icon="icons/iconoir/regular/menu.svg" :action="OpenCampaignPreview"></IconButton>
|
||||
<IconButton icon="icons/iconoir/regular/cursor-pointer.svg"></IconButton>
|
||||
<IconButton icon="icons/game-icons/000000/delapouite/rolling-dice-cup.svg"></IconButton>
|
||||
<IconButton icon="icons/game-icons/000000/delapouite/rolling-dice-cup.svg" :action="OpenDiceMenu"></IconButton>
|
||||
</div>
|
||||
<div class="horizontal-button">
|
||||
|
||||
<IconButton icon="icons/iconoir/regular/group.svg"></IconButton>
|
||||
<IconButton icon="icons/iconoir/regular/bookmark-book.svg"></IconButton>
|
||||
<IconButton icon="icons/iconoir/regular/chat-bubble.svg" :action="OpenChat"></IconButton>
|
||||
</div>
|
||||
|
||||
<!-- Tilemap -->
|
||||
@ -44,5 +60,18 @@ watch(game, () => {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
z-index: 1;
|
||||
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.horizontal-button {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
z-index: 1;
|
||||
|
||||
user-select: none;
|
||||
}
|
||||
</style>
|
@ -115,12 +115,16 @@ onMounted(() => {
|
||||
offsetY = oldOffsetY + ((event.clientY - startY) * (1 / scale));
|
||||
|
||||
draw();
|
||||
console.log("x: " + offsetX + ", y: " + offsetY);
|
||||
// console.log("x: " + offsetX + ", y: " + offsetY);
|
||||
});
|
||||
|
||||
tilemap.addEventListener("mouseup", () => mouseDown = false);
|
||||
|
||||
addEventListener("resize", draw)
|
||||
|
||||
offsetX = window.innerWidth / 2;
|
||||
offsetY = window.innerHeight / 2;
|
||||
|
||||
draw();
|
||||
});
|
||||
|
||||
|
@ -15,6 +15,8 @@ import CampaignListWindow from '../windows/campaigns/CampaignListWindow.vue'
|
||||
import NewCampaignWindow from '../windows/campaigns/NewCampaignWindow.vue'
|
||||
import JoinCampaignWindow from '../windows/campaigns/JoinCampaignWindow.vue'
|
||||
import CampaignPreviewWindow from '@/views/windows/campaigns/CampaignPreviewWindow.vue'
|
||||
import ChatWindow from '../windows/game/ChatWindow.vue'
|
||||
import DiceWindow from '../windows/game/DiceWindow.vue'
|
||||
|
||||
// Gestionem ventanas
|
||||
const reload = ReloadRef();
|
||||
@ -32,6 +34,8 @@ const campaign_list = windows.campaign_list;
|
||||
const new_campaign = windows.new_campaign;
|
||||
const join_campaign = windows.join_campaign;
|
||||
const campaign_preview = windows.campaign_preview;
|
||||
const chat = windows.chat;
|
||||
const dice_menu = windows.dice_menu;
|
||||
|
||||
</script>
|
||||
|
||||
@ -48,6 +52,8 @@ const campaign_preview = windows.campaign_preview;
|
||||
<NewCampaignWindow v-for="win in new_campaign" :key="win.id" :data="win"></NewCampaignWindow>
|
||||
<JoinCampaignWindow v-for="win in join_campaign" :key="win.id" :data="win"></JoinCampaignWindow>
|
||||
<CampaignPreviewWindow v-for="win in campaign_preview" :key="win.id" :data="win"></CampaignPreviewWindow>
|
||||
<ChatWindow v-for="win in chat" :key="win.id" :data="win"></ChatWindow>
|
||||
<DiceWindow v-for="win in dice_menu" :key="win.id" :data="win"></DiceWindow>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -57,7 +63,7 @@ const campaign_preview = windows.campaign_preview;
|
||||
.window-wrapper {
|
||||
background-color: var(--window-background);
|
||||
|
||||
backdrop-filter: blur(10px);
|
||||
/* backdrop-filter: blur(10px); */
|
||||
position: fixed;
|
||||
|
||||
|
||||
|
146
client/src/views/partials/ChatComponent.vue
Normal file
146
client/src/views/partials/ChatComponent.vue
Normal file
@ -0,0 +1,146 @@
|
||||
<script setup>
|
||||
import IconButton from '@/views/partials/game/IconButton.vue';
|
||||
import { onMounted, ref, watch } from 'vue';
|
||||
import MessageComponent from './MessageComponent.vue';
|
||||
import { GetChatRef, GetClient, SendMessage } from '../../services/Dragonroll';
|
||||
|
||||
const textInput = ref(null);
|
||||
const chat = GetChatRef();
|
||||
const messageContainer = ref(null);
|
||||
|
||||
function Send(message){
|
||||
SendMessage({
|
||||
content: {
|
||||
text: message
|
||||
},
|
||||
author: GetClient()._id
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
textInput.value.addEventListener('input', (event) => {
|
||||
// textInput.value.style.height = "1px";
|
||||
textInput.value.style.height = (textInput.value.scrollHeight)+"px";
|
||||
})
|
||||
textInput.value.addEventListener("keypress", (event) => {
|
||||
if(event.shiftKey) return;
|
||||
if (event.key === "Enter") {
|
||||
event.preventDefault();
|
||||
|
||||
Send(textInput.value.value);
|
||||
textInput.value.value = "";
|
||||
|
||||
|
||||
textInput.value.style.height = "1px";
|
||||
textInput.value.style.height = (textInput.value.scrollHeight)+"px";
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
watch(chat, () => {
|
||||
if(chat.value.length > 0) if(chat.value[chat.value.length - 1].author == GetClient()._id){
|
||||
setTimeout(() => {
|
||||
messageContainer.value.scrollTop = messageContainer.value.scrollHeight;
|
||||
console.log(messageContainer.value.scrollHeight)
|
||||
}, 0);
|
||||
}
|
||||
}, {deep: true})
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
<div class="chat-container">
|
||||
<div class="message-container" ref="messageContainer">
|
||||
<MessageComponent v-for="message in chat" :key="message._id" :message="message"></MessageComponent>
|
||||
</div>
|
||||
<div class="chat-input-container">
|
||||
<textarea ref="textInput" class="chat-input"></textarea>
|
||||
<div class="chat-input-actions">
|
||||
<div class="chat-input-actions-left">
|
||||
|
||||
</div>
|
||||
<div class="chat-input-actions-right">
|
||||
<IconButton icon="icons/iconoir/regular/send.svg"></IconButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
.chat-container {
|
||||
display: flex;
|
||||
height: 720px;
|
||||
width: 100%;
|
||||
flex-direction: column;
|
||||
|
||||
h2 {
|
||||
font-family: MrEavesRemake;
|
||||
margin-left: 10px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.message-container {
|
||||
flex-grow: 1;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
overflow-y: auto;
|
||||
max-height: 580px;
|
||||
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.chat-input-container {
|
||||
position: sticky;
|
||||
bottom: 0px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.chat-input {
|
||||
background-color: var(--color-background-softer);
|
||||
border: none;
|
||||
padding: 12px;
|
||||
border-radius: 6px;
|
||||
color: var(--color-text);
|
||||
transition: 300ms background-color;
|
||||
width: calc(100% - 20px);
|
||||
margin-left: 10px;
|
||||
margin-right: 10px;
|
||||
min-height: 30px;
|
||||
height: 41px;
|
||||
max-height: 100px;
|
||||
resize: none;
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
background-color: var(--color-background-softest);
|
||||
}
|
||||
}
|
||||
|
||||
.chat-input-actions {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-bottom: 8px;
|
||||
margin-left: 10px;
|
||||
margin-right: 10px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.chat-input-actions-left {
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
|
||||
.chat-input-actions-right {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
|
||||
</style>
|
61
client/src/views/partials/MessageChunk.vue
Normal file
61
client/src/views/partials/MessageChunk.vue
Normal file
@ -0,0 +1,61 @@
|
||||
<script setup>
|
||||
import { onMounted, ref } from 'vue';
|
||||
|
||||
const text = ref("");
|
||||
const roll = ref("");
|
||||
|
||||
const props = defineProps(['data']);
|
||||
const data = props.data;
|
||||
|
||||
onMounted(() => {
|
||||
if(data.type == "dice-roll"){
|
||||
text.value = data.content.throw;
|
||||
roll.value = data.content.roll;
|
||||
} else {
|
||||
text.value = data.content.text;
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="msg-chunk">
|
||||
<span v-if="data.type == 'text'">{{ text }}</span>
|
||||
<div v-if="data.type == 'dice-roll'" class="dice-roll-container">
|
||||
<span class="roll-title">Rolled {{ text }}</span>
|
||||
<span class="roll-result">{{ roll }}</span>
|
||||
<img class="roll-bg" src="/img/d20.png">
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.dice-roll-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
width: 300px;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.roll-title {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.roll-result {
|
||||
font-size: 48px;
|
||||
font-weight: bold;
|
||||
font-family: MrEavesRemake;
|
||||
text-align: center;
|
||||
margin-bottom: -75px;
|
||||
}
|
||||
|
||||
.roll-bg {
|
||||
filter: invert(0.9);
|
||||
opacity: 0.1;
|
||||
width: 75px;
|
||||
position: relative;
|
||||
left: 112px;
|
||||
top: 0px;
|
||||
}
|
||||
</style>
|
65
client/src/views/partials/MessageComponent.vue
Normal file
65
client/src/views/partials/MessageComponent.vue
Normal file
@ -0,0 +1,65 @@
|
||||
<script setup>
|
||||
import { onMounted, ref } from 'vue';
|
||||
|
||||
import MessageChunk from './MessageChunk.vue';
|
||||
import { GetPlayer } from '../../services/Dragonroll';
|
||||
import { backendUrl } from '../../services/BackendURL';
|
||||
|
||||
const props = defineProps(['message']);
|
||||
const message = props.message;
|
||||
|
||||
const avatar = ref(null);
|
||||
const title = ref("");
|
||||
const chunks = ref([]);
|
||||
|
||||
onMounted(() => {
|
||||
let sender = GetPlayer(message.author);
|
||||
if(sender.user.image) avatar.value.src = backendUrl + "public/" + sender.user.image;
|
||||
title.value = sender.user.username;
|
||||
chunks.value = message.chunks;
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="msg-parent-container rcv">
|
||||
<img class="user-icon" src="img/def-avatar.jpg" ref="avatar" draggable="false">
|
||||
<div class="content-container">
|
||||
<div class="msg-title">
|
||||
{{ title }}
|
||||
</div>
|
||||
|
||||
<MessageChunk v-for="chunk in chunks" :id="chunk.id" :data="chunk"></MessageChunk>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.msg-title {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.msg-parent-container {
|
||||
width: 100%;
|
||||
background-color: var(--color-chat-other);
|
||||
display: flex;
|
||||
margin-top: 2px;
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
.content-container {
|
||||
max-width: 75%;
|
||||
padding: 7px 15px;
|
||||
margin-bottom: 7px;
|
||||
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.user-icon {
|
||||
margin-top: 9px;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
</style>
|
@ -1,13 +1,16 @@
|
||||
<script setup>
|
||||
const props = defineProps(['icon']);
|
||||
const props = defineProps(['icon', 'action','size']);
|
||||
let icon = props.icon;
|
||||
let action = props.action;
|
||||
|
||||
let size = props.size;
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
<template>
|
||||
<div class="icon-button sound-click">
|
||||
<img class="icon" :src="icon">
|
||||
<div class="icon-button sound-click" :class="size" v-on:click.prevent="action">
|
||||
<img class="icon" draggable="false" :src="icon" :class="size">
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -16,6 +19,12 @@ let icon = props.icon;
|
||||
.icon-button {
|
||||
height: 32px;
|
||||
width: 32px;
|
||||
|
||||
&.big {
|
||||
height: 42px;
|
||||
width: 42px;
|
||||
}
|
||||
|
||||
background-color: var(--color-background-soft);
|
||||
border-radius: 6px;
|
||||
display: flex;
|
||||
@ -34,4 +43,9 @@ let icon = props.icon;
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
}
|
||||
|
||||
.big {
|
||||
height: 38px;
|
||||
width: 38px;
|
||||
}
|
||||
</style>
|
@ -4,23 +4,39 @@ import { SetupHandle, SetSize, SetPosition, ResetPosition } from '@/services/Win
|
||||
|
||||
import WindowHandle from '@/views/partials/WindowHandle.vue';
|
||||
import PlayerList from '../../partials/PlayerList.vue';
|
||||
import { DisplayToast, GetCampaign, GetClient } from '../../../services/Dragonroll';
|
||||
import { Disconnect, DisplayToast, GetCampaign, GetClient } from '../../../services/Dragonroll';
|
||||
import CampaignBookList from '../../partials/books/CampaignBookList.vue';
|
||||
import { ClearWindow } from '../../../services/Windows';
|
||||
import { ClearAll, ClearWindow, CreateWindow } from '../../../services/Windows';
|
||||
import { LaunchGame } from '../../../services/Game';
|
||||
import { AddSound } from '../../../services/Sound';
|
||||
import ChatComponent from '../../partials/ChatComponent.vue';
|
||||
|
||||
const handle = ref(null);
|
||||
|
||||
const props = defineProps(['data']);
|
||||
const data = props.data;
|
||||
|
||||
const hide_start = ref(false);
|
||||
const hide_chat = ref(false);
|
||||
|
||||
const container = ref(null);
|
||||
|
||||
let id = data.id;
|
||||
onMounted(() => {
|
||||
SetupHandle(id, handle);
|
||||
SetSize(id, {x: 1200, y: 750});
|
||||
|
||||
if(data.style == 'compact') {
|
||||
SetSize(id, {x: 800, y: 750});
|
||||
hide_chat.value = true;
|
||||
} else {
|
||||
SetSize(id, {x: 1200, y: 750});
|
||||
}
|
||||
|
||||
ResetPosition(id, "center");
|
||||
|
||||
console.log(data);
|
||||
hide_start.value = data.hide_start;
|
||||
|
||||
AddSound(container.value)
|
||||
});
|
||||
|
||||
function CopyCode(){
|
||||
@ -33,6 +49,12 @@ function Launch(){
|
||||
LaunchGame();
|
||||
}
|
||||
|
||||
function Exit(){
|
||||
Disconnect();
|
||||
ClearAll();
|
||||
CreateWindow('campaign_list');
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
@ -43,7 +65,7 @@ function Launch(){
|
||||
<!-- Body
|
||||
<h1>{{ data.campaign.name }}</h1> -->
|
||||
|
||||
<div class="campaign-preview-container">
|
||||
<div class="campaign-preview-container" :class="hide_chat ? 'campaign-preview-compact' : ''" ref="container">
|
||||
<div class="campaign-preview-column left">
|
||||
<h2>Players</h2>
|
||||
<PlayerList :campaign="data.campaign"></PlayerList>
|
||||
@ -61,11 +83,12 @@ function Launch(){
|
||||
</div>
|
||||
</div>
|
||||
<div class="buttons-row">
|
||||
<button class="btn-primary button-row sound-click btn-green" v-on:click.prevent="Launch">Launch game</button>
|
||||
<button class="btn-primary button-row sound-click btn-green" v-if="!hide_start" v-on:click.prevent="Launch">Launch game</button>
|
||||
<button class="btn-primary button-row sound-click btn-red" v-if="hide_start" v-on:click.prevent="Exit">Exit game</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="campaign-preview-column right">
|
||||
<h2>Chat</h2>
|
||||
<div v-if="!hide_chat" class="campaign-preview-column right">
|
||||
<ChatComponent></ChatComponent>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -115,6 +138,10 @@ function Launch(){
|
||||
|
||||
display: grid;
|
||||
grid-template-columns: 3fr 5fr 4fr;
|
||||
|
||||
&.campaign-preview-compact {
|
||||
grid-template-columns: 2fr 3fr;
|
||||
}
|
||||
}
|
||||
|
||||
.campaign-preview-column {
|
||||
|
38
client/src/views/windows/game/ChatWindow.vue
Normal file
38
client/src/views/windows/game/ChatWindow.vue
Normal file
@ -0,0 +1,38 @@
|
||||
<script setup>
|
||||
import WindowHandle from '@/views/partials/WindowHandle.vue';
|
||||
|
||||
import { onMounted, onUpdated, ref } from 'vue';
|
||||
import { SetupHandle, SetSize, SetPosition, ResetPosition } from '@/services/Windows';
|
||||
import ChatComponent from '../../partials/ChatComponent.vue';
|
||||
|
||||
const props = defineProps(['data']);
|
||||
const data = props.data;
|
||||
|
||||
const handle = ref(null);
|
||||
|
||||
let id = data.id;
|
||||
|
||||
onMounted(() => {
|
||||
SetupHandle(id, handle);
|
||||
SetSize(id, {x: 400, y: 750});
|
||||
ResetPosition(id, "center");
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
<template>
|
||||
<div class="window-wrapper" :id="'window-wrapper-' + id">
|
||||
<WindowHandle :window="id" ref="handle"></WindowHandle>
|
||||
|
||||
<ChatComponent></ChatComponent>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
<style scoped>
|
||||
.window-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
</style>
|
140
client/src/views/windows/game/DiceWindow.vue
Normal file
140
client/src/views/windows/game/DiceWindow.vue
Normal file
@ -0,0 +1,140 @@
|
||||
<script setup>
|
||||
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 Dice from 'dice-notation-js'
|
||||
import { GetClient, SendMessage } from '../../../services/Dragonroll';
|
||||
|
||||
const props = defineProps(['data']);
|
||||
const data = props.data;
|
||||
|
||||
const handle = ref(null);
|
||||
|
||||
const diceResult = ref("");
|
||||
const diceField = ref(null);
|
||||
|
||||
let id = data.id;
|
||||
|
||||
let ThrowD4 = () => { ThrowDice("d4") };
|
||||
let ThrowD6 = () => { ThrowDice("d6") };
|
||||
let ThrowD8 = () => { ThrowDice("d8") };
|
||||
let ThrowD10 = () => { ThrowDice("d10") };
|
||||
let ThrowD12 = () => { ThrowDice("d12") };
|
||||
let ThrowD20 = () => { ThrowDice("d20") };
|
||||
|
||||
function comp (s, m, n, f, a) {
|
||||
m = parseInt( m );
|
||||
if( isNaN( m ) ) m = 1;
|
||||
n = parseInt( n );
|
||||
if( isNaN( n ) ) n = 1;
|
||||
f = parseInt( f );
|
||||
a = typeof(a) == 'string' ? parseInt( a.replace(/\s/g, '') ) : 0;
|
||||
if( isNaN( a ) ) a = 0;
|
||||
var r = 0;
|
||||
for( var i=0; i<n; i++ )
|
||||
r += Math.floor( Math.random() * f ) + 1;
|
||||
return r * m + a;
|
||||
};
|
||||
function parse( de ) {
|
||||
return comp.apply( this, de.match(/(?:(\d+)\s*\*\s*)?(\d*)d(\d+)(?:\s*([\+\-]\s*\d+))?/i) );
|
||||
}
|
||||
|
||||
function ThrowDice(expr){
|
||||
// let result = Dice.detailed(expr);
|
||||
diceField.value.value = expr;
|
||||
let result = parse(expr);
|
||||
|
||||
let audios = ['/sounds/roll1.wav', '/sounds/roll2.wav']
|
||||
const audio = new Audio(audios[Math.floor(Math.random() * audios.length)]);
|
||||
audio.type = "audio/wav"
|
||||
audio.play();
|
||||
|
||||
console.log(result)
|
||||
diceResult.value = result;
|
||||
|
||||
SendMessage({
|
||||
content: {
|
||||
roll: result,
|
||||
throw: expr
|
||||
},
|
||||
author: GetClient()._id,
|
||||
type: 'dice-roll'
|
||||
})
|
||||
}
|
||||
|
||||
function ThrowCustomDice(){
|
||||
ThrowDice(diceField.value.value);
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
SetupHandle(id, handle);
|
||||
SetSize(id, {x: 300, y: 210});
|
||||
ResetPosition(id, "center");
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
<template>
|
||||
<div class="window-wrapper" :id="'window-wrapper-' + id">
|
||||
<WindowHandle :window="id" ref="handle"></WindowHandle>
|
||||
|
||||
<div class="horizontal-dice">
|
||||
<IconButton icon="icons/game-icons/000000/skoll/d4.svg" :action="ThrowD4" size="big"></IconButton>
|
||||
<IconButton icon="icons/game-icons/000000/delapouite/perspective-dice-six-svg.svg" :action="ThrowD6" size="big"></IconButton>
|
||||
<IconButton icon="icons/game-icons/000000/delapouite/dice-eight-faces-eight.svg" :action="ThrowD8" size="big"></IconButton>
|
||||
<IconButton icon="icons/game-icons/000000/skoll/d10.svg" :action="ThrowD10" size="big"></IconButton>
|
||||
<IconButton icon="icons/game-icons/000000/skoll/d12.svg" :action="ThrowD12" size="big"></IconButton>
|
||||
<IconButton icon="icons/game-icons/000000/delapouite/dice-twenty-faces-twenty.svg" :action="ThrowD20" size="big"></IconButton>
|
||||
</div>
|
||||
|
||||
<div class="custom-dice">
|
||||
<input type="text" ref="diceField">
|
||||
<IconButton icon="icons/game-icons/000000/delapouite/rolling-dices.svg" size="big" :action="ThrowCustomDice"></IconButton>
|
||||
</div>
|
||||
|
||||
<div class="roll-result">{{ diceResult }}</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
<style scoped>
|
||||
.window-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.horizontal-dice {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
z-index: 1;
|
||||
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.custom-dice {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
input[type=text] {
|
||||
height: 32px;
|
||||
padding: 6px;
|
||||
border-radius: 6px;
|
||||
margin-right: 6px;
|
||||
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.roll-result {
|
||||
font-size: 48px;
|
||||
font-weight: bold;
|
||||
font-family: MrEavesRemake;
|
||||
}
|
||||
</style>
|
@ -33,7 +33,8 @@ module.exports = io => {
|
||||
socket.campaign = campaignId;
|
||||
|
||||
if(!sessions[campaignId]) sessions[campaignId] = {
|
||||
players: await GetOfflinePlayers(campaignId)
|
||||
players: await GetOfflinePlayers(campaignId),
|
||||
chat: []
|
||||
};
|
||||
|
||||
|
||||
@ -55,5 +56,9 @@ module.exports = io => {
|
||||
|
||||
console.log(socket.user.username + " ha salido!")
|
||||
});
|
||||
|
||||
socket.on('message', (data) => {
|
||||
io.to(socket.campaign).emit('message', data);
|
||||
})
|
||||
});
|
||||
}
|
Loading…
Reference in New Issue
Block a user