Compare commits

..

No commits in common. "main" and "develop" have entirely different histories.

24 changed files with 357 additions and 519 deletions

View File

@ -8,7 +8,7 @@
},
"package": {
"productName": "domino-client",
"version": "0.2.1"
"version": "0.2.0"
},
"tauri": {
"allowlist": {
@ -72,7 +72,7 @@
"fullscreen": false,
"height": 720,
"resizable": true,
"title": "Domino v0.2.1",
"title": "Domino v0.2.0",
"width": 1280,
"minHeight": 720,
"minWidth": 1280,

View File

@ -15,3 +15,9 @@
--bulma-danger-s: 74%;
--bulma-danger-l: 37%;
}
.tabs li.is-disabled {
pointer-events: none;
cursor: default;
opacity: 0.3;
}

View File

@ -1,35 +1,7 @@
@import './base.css';
html,
body,
#app {
html {
font-size: 16px;
overflow-y: auto;
overflow-x: hidden;
height: 100%;
}
#app.game {
overflow-y: hidden;
}
.tabs li.is-disabled {
pointer-events: none;
cursor: default;
opacity: 0.3;
}
.box.is-no-shadow {
box-shadow: none;
border: 1px solid var(--bulma-input-border-color);
}
.landing .unauthenticated-layout {
height: 100%;
}
.is-ellipsis {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}

View File

@ -143,10 +143,3 @@ export function createStringMatrix(
return matrix
}
export function isMobile() {
const userAgent = navigator.userAgent || navigator.vendor || window.opera
return /android|avantgo|blackberry|bada\/|bb|meego|ip(hone|od|ad)|opera m(ob|in)i|phone|tablet|mobi|ipad|playbook|silk|windows (phone|ce)|webos|kindle|mobile|palm|fennec|gobrowser|hiptop|iemobile|iris|w3c|wap|opera mini|iemobile/i.test(
userAgent,
)
}

View File

@ -19,7 +19,7 @@ let options = ref<MatchSessionOptions>({
const winTargetPointsList = [20, 50, 80, 100, 150, 200]
const winTargetRoundsList = [1, 2, 3, 4, 5, 6]
const turnWaitSecondsList = [5, 10, 15, 20, 30, 40, 50, 60]
const turnWaitSecondsList = [15, 20, 30, 40, 50, 60]
const backgroundOptiopnList = [
{
@ -58,7 +58,6 @@ function startSingleMatch() {
<template>
<div>
<div class="fixed-grid has-3-cols has-1-cols-mobile">
<div class="grid">
<div class="cell">
<div class="field">
@ -128,9 +127,7 @@ function startSingleMatch() {
</div>
</div>
</div>
</div>
<div class="flex-grid has-2-cols has-1-cols-mobile" v-if="info?.development">
<div class="grid">
<div class="grid" v-if="info?.development">
<div class="cell">
<div class="field">
<label class="label">{{ $t('session-name') }}</label>
@ -144,7 +141,6 @@ function startSingleMatch() {
</div>
</div>
</div>
</div>
<!---->
<div class="cell">
<div class="field">

View File

@ -1,86 +0,0 @@
<template>
<div class="block">
<h2 class="title is-4">{{ sessionState?.name }}</h2>
<h6 class="title is-size-5">{{ $t('players') }}</h6>
<div v-for="player in sessionState?.players" :key="player.id">
<div class="fixed-grid" style="max-width: 300px">
<div class="grid">
<div class="cell">{{ player.name }}</div>
<div class="cell has-text-centered">
<div class="mb-2">
<span
class="tag"
:class="{ 'is-success': player.ready, 'is-danger': !player.ready }"
>{{ $t(player.ready ? 'ready' : 'unready') }}</span
>
</div>
</div>
</div>
</div>
</div>
<div class="mt-4" v-if="amIHost && gameOptions?.teamed && playersToTeamUpWith.length > 0">
<div class="field">
<label for="background" class="label">Team up with</label>
<div class="control">
<div class="select">
<select v-model="teamedWith" name="teamedWidth">
<option v-for="player in playersToTeamUpWith" :key="player.id" :value="player.id">
{{ player.name }}
</option>
</select>
</div>
</div>
</div>
</div>
<div class="buttons mt-6">
<button class="button is-dark" @click="() => emit('ready')">
<span v-if="!readyForStart">{{ $t('ready') }}</span
><span v-else>{{ $t('unready') }}</span>
</button>
<button
class="button is-success"
:disabled="!canStart"
@click="() => emit('start', teamedWith)"
v-if="amIHost"
>
<span>{{ $t('start') }}</span>
</button>
<button class="button is-danger" @click="() => emit('cancel')">
<span>{{ $t('cancel') }}</span>
</button>
</div>
</div>
</template>
<script setup lang="ts">
import { useGameStore } from '@/stores/game'
import { useGameOptionsStore } from '@/stores/gameOptions'
import { storeToRefs } from 'pinia'
import { computed, ref } from 'vue'
let teamedWith = ref<string | undefined>(undefined)
const emit = defineEmits(['ready', 'start', 'cancel'])
const gameStore = useGameStore()
const gameOptionsStore = useGameOptionsStore()
const { sessionState, amIHost, readyForStart } = storeToRefs(gameStore)
const { gameOptions } = storeToRefs(gameOptionsStore)
const playersToTeamUpWith = computed(() => {
return (
sessionState?.value?.players.filter((player) => player.id !== sessionState?.value?.creator) ||
[]
)
})
const canStart = computed(() => {
const players = sessionState?.value?.players || []
const options = gameOptions.value
const allReady = (players.length || 0) > 0 && players.every((player) => player.ready)
return (!options?.teamed && allReady) || (options?.teamed && !!teamedWith.value && allReady)
})
</script>
<style scoped></style>

View File

@ -1,69 +0,0 @@
<template>
<div class="card">
<div class="card-content">
<p class="title is-6">{{ props.session.name }}</p>
<div class="fixed-grid">
<div class="grid">
<div class="cell has-text-weight-light">{{ $t('id-session-_id') }}</div>
<div class="cell has-text-weight-medium">{{ props.session._id }}</div>
<div class="cell has-text-weight-light">{{ $t('host-session-host') }}</div>
<div class="cell has-text-weight-medium">{{ creatorName }}</div>
<div class="cell has-text-weight-light">{{ $t('players-session-players-length') }}</div>
<div class="cell has-text-weight-medium">
{{ props.session.numPlayers }} / {{ props.session.options.numPlayers }}
</div>
<div class="cell has-text-weight-light" v-if="info.development">
{{ $t('seed-session-seed') }}
</div>
<div class="cell has-text-weight-medium is-family-monospace" v-if="info.development">
<span class="is-ellipsis is-display-inline-block" style="max-width: 100px">{{
props.session.seed
}}</span>
<a class="" @click="() => emit('copy', props.session.seed)">
{{ $t('copy') }}
</a>
</div>
<div class="cell has-text-weight-light">{{ $t('status-session-status') }}</div>
<div class="cell has-text-weight-medium">{{ props.session.status }}</div>
</div>
</div>
<div class="buttons is-centered mt-6">
<button
class="button is-primary"
@click.once.prevent="() => emit('join', props.session._id)"
>
<span>{{ $t('join') }}</span>
</button>
<button
v-if="amIHost || info.development"
class="button is-text"
@click.once.prevent="() => emit('delete', props.session._id)"
>
<span>{{ $t('delete') }}</span>
</button>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import type { MatchSessionDto } from '@/common/interfaces'
import type { InfoService } from '@/services/InfoService'
import { useGameStore } from '@/stores/game'
import { storeToRefs } from 'pinia'
import { computed, inject } from 'vue'
const emit = defineEmits(['join', 'delete', 'copy'])
const gameStore = useGameStore()
const { amIHost } = storeToRefs(gameStore)
const props = defineProps<{
session: MatchSessionDto
}>()
const info: InfoService = inject<InfoService>('info') as InfoService
const creatorName = computed(() => {
return props.session.players.find((player) => player.id === props.session.creator)?.name || ''
})
</script>
<style scoped></style>

View File

@ -102,7 +102,7 @@ if (info.tauri) {
>Check for update</a
>
<hr class="navbar-divider" />
<div class="navbar-item">Version 0.2.1</div>
<div class="navbar-item">Version 0.2.0</div>
</div>
</div>
</div>

View File

@ -1,10 +0,0 @@
export default {
install(app: any) {
app.config.globalProperties.$isMobile = () => {
const userAgent = navigator.userAgent || navigator.vendor || window.opera
return /android|avantgo|blackberry|bada\/|bb|meego|ip(hone|od|ad)|opera m(ob|in)i|phone|tablet|mobi|ipad|playbook|silk|windows (phone|ce)|webos|kindle|mobile|palm|fennec|gobrowser|hiptop|iemobile|iris|w3c|wap|opera mini|iemobile/i.test(
userAgent,
)
}
},
}

View File

@ -16,7 +16,6 @@ import { DIRECTION_INDEXES, DIRECTIONS, ORIENTATION_ANGLES } from '@/common/cons
import type { OtherHand } from './OtherHand'
import { SoundManager } from '@/game/utilities/SoundManager'
import { t } from '@/i18n'
import { gsap } from 'gsap'
export class Board extends EventEmitter {
private _scale: number = 1
@ -226,7 +225,7 @@ export class Board extends EventEmitter {
const availableMoves = this.nextTileValidMoves(tileDto, move.type)
const availablePositions = this.nextTileValidPoints(tileDto, move.type, availableMoves)
let directionIndex = DIRECTION_INDEXES[direction]
let directionIndex = DIRECTIONS.indexOf(direction)
let availablePosition: [number, number] | undefined = availablePositions[directionIndex]
let endlessLoop: number = 0
while (endlessLoop < 4 && availablePosition === undefined) {
@ -235,10 +234,7 @@ export class Board extends EventEmitter {
endlessLoop++
}
if (endlessLoop >= 4) {
console.log('availableMoves :>>', availableMoves)
console.log('availablePositions :>>', availablePositions)
// throw new Error('No available position')
directionIndex = DIRECTION_INDEXES[direction]
throw new Error('No available position')
}
direction = DIRECTIONS[directionIndex]
isLeft ? (this.leftDirection = direction) : (this.rightDirection = direction)
@ -264,37 +260,23 @@ export class Board extends EventEmitter {
async animateTile(tile: Tile, x: number, y: number, orientation: string, move: Movement) {
const targetX = this.scaleX(x)
const targetY = this.scaleY(y)
const animation: AnimationOptions = {
x: targetX,
y: targetY,
rotation: ORIENTATION_ANGLES[orientation],
duration: 20,
}
const tempAlpha = tile.alpha
tile.alpha = 0
const clonedTile = tile.clone()
clonedTile.addTo(this.tilesContainer)
const pos = this.getAnimationInitialPosition(move)
clonedTile.setPosition(this.scaleX(pos.x), this.scaleY(pos.y))
return new Promise((resolve) => {
gsap.timeline({ repeat: 0 }).fromTo(
clonedTile.getSprite(),
{
x: this.scaleX(pos.x),
y: this.scaleY(pos.y),
},
{
x: targetX,
y: targetY,
duration: 1,
rotation: ORIENTATION_ANGLES[orientation],
// rotate: ,
onComplete: () => {
await clonedTile.animateTo(animation)
clonedTile.removeFromParent()
tile.setOrientation(orientation)
tile.setPosition(targetX, targetY)
tile.alpha = tempAlpha
resolve('done')
},
ease: 'power4.out',
},
)
})
}
getAnimationInitialPosition(move: Movement): { x: number; y: number } {
@ -363,7 +345,6 @@ export class Board extends EventEmitter {
try {
// const { tileDto: tileDto } = move
// const tile = this.getTileInHand(tileDto?.id ?? '')
this.interactionContainer.removeChildren()
this.movements.push(move)
if (tile === undefined) {
return

View File

@ -1,5 +1,5 @@
import type { Dimension } from '@/common/interfaces'
import { BlurFilter, Container, Graphics, Point, Text } from 'pixi.js'
import { BlurFilter, Container, Graphics, Point, Text, TextStyle } from 'pixi.js'
import { gsap } from 'gsap'
import { SoundManager } from '@/game/utilities/SoundManager'

View File

@ -20,8 +20,6 @@ import Config from './Config'
import { createText, grayStyle } from './utilities/fonts'
import { t } from '@/i18n'
import { DIRECTION_INDEXES, DIRECTIONS } from '@/common/constants'
import { SoundManager } from './utilities/SoundManager'
export class Game extends EventEmitter {
public board!: Board
public hand!: Hand
@ -32,7 +30,6 @@ export class Game extends EventEmitter {
private backgroundLayer: Container = new Container()
private gameSummaryView!: GameSummayView
private players: PlayerDto[] = []
private soundManager: SoundManager = new SoundManager()
constructor(
private options: MatchSessionOptions,
@ -200,6 +197,7 @@ export class Game extends EventEmitter {
return
}
const freeEnds = this.board.freeEnds
console.log('freeEnds :>> ', freeEnds, this.playerId)
const move: Movement = {
id: '',
tile,
@ -217,6 +215,8 @@ export class Game extends EventEmitter {
dirIndex += 1
safeCount -= 1
}
console.log('validMoves :>> ', validMoves)
console.log('validPoints :>> ', validPoints)
const validPoint = validPoints[dirIndex % 4]
if (validPoint !== undefined) {
move.x = validPoint[0]
@ -227,7 +227,6 @@ export class Game extends EventEmitter {
}
this.currentMove = move
this.board.updateBoard(move, this.hand.tileMoved(tile))
this.hand.afterMove()
}
private async sendPassEvent() {
@ -237,7 +236,7 @@ export class Game extends EventEmitter {
playerId: this.playerId,
}
this.socketService &&
this.socketService.send('client:player-move', {
this.socketService.sendMessage('client:player-move', {
sessionId: this.sessionId,
move: move,
})
@ -288,7 +287,6 @@ export class Game extends EventEmitter {
this.hand.setActive(true)
this.hand.prepareForMove(this.board.count === 0, this.board.freeEnds)
this.board.setPlayerTurn(currentPlayer)
this.soundManager.play('snd-ding-1')
} else {
this.board.setServerPlayerTurn(currentPlayer)
this.highLightPlayer(currentPlayer)
@ -325,20 +323,17 @@ export class Game extends EventEmitter {
this.board.on('game:tile-animation-ended', async (tile) => {
if (tile !== null && tile !== undefined && tile.playerId === this.playerId) {
this.socketService &&
this.socketService.send('client:player-move', {
this.socketService.sendMessage('client:player-move', {
sessionId: this.sessionId,
move: this.currentMove,
})
// this.socketService.sendMessage('client:player-move', {
// sessionId: this.sessionId,
// move: this.currentMove,
// })
}
} else {
this.socketService &&
this.socketService.sendMessage('client:animation-ended', {
sessionId: this.sessionId,
userId: this.playerId,
})
}
})
}

View File

@ -18,7 +18,6 @@ import { createText, playerNameText, whiteStyle } from './utilities/fonts'
import Config from '@/game/Config'
import { TimerText } from './TimerText'
import { Button } from './Button'
import gsap from 'gsap'
export class Hand extends EventEmitter {
tiles: Tile[] = []
@ -113,16 +112,10 @@ export class Hand extends EventEmitter {
this.buttonPass.disabled = false
}
this.availableTiles.forEach((tile) => {
gsap.to(tile.getSprite(), {
duration: 0.3,
tile.animateTo({
x: tile.x,
y: tile.y - 10,
alpha: 1,
ease: 'power4.out',
})
// tile.animateTo({
// x: tile.x,
// y: tile.y - 10,
// })
tile.interactive = true
})
if (this.timer) {
@ -153,21 +146,15 @@ export class Hand extends EventEmitter {
}
afterMove() {
this.timer && this.timer.reset()
this.availableTiles.forEach((tile) => {
if (tile.selected) return
gsap.to(tile.getSprite(), {
duration: 0.3,
tile.animateTo({
x: tile.x,
y: tile.y + 10,
alpha: 0.7,
ease: 'power4.out',
})
// tile.animateTo({
// x: tile.x,
// y: tile.y + 10,
// })
// tile.setPosition(tile.x, tile.y + 10)
tile.setPosition(tile.x, tile.y + 10)
tile.interactive = false
})
this.timer && this.timer.reset()
}
hasMoves(tile: TileDto, freeEnds?: [number, number]): boolean {
@ -228,12 +215,7 @@ export class Hand extends EventEmitter {
if (!tile) return
this.afterMove()
this.tiles = this.tiles.filter((t) => t.id !== tile.id)
this.tiles.forEach((t) => {
t.interactive = false
t.alpha = 0.7
t.clearFilters()
})
this.tiles = this.tiles.filter((t) => t.id !== tileDto.id)
tile.interactive = false
tile.clearFilters()
@ -308,9 +290,9 @@ export class Hand extends EventEmitter {
newTile.setFilters([
new GlowFilter({
distance: 10,
outerStrength: 1,
outerStrength: 2,
innerStrength: 1,
color: 0xffff66,
color: 0xffffff,
quality: 0.5,
}),
])
@ -318,7 +300,7 @@ export class Hand extends EventEmitter {
newTile.on('pointerout', () => {
if (!newTile.selected) {
this.emit('tileHover')
newTile.alpha = 1
newTile.alpha = 0.7
newTile.getSprite().filters = []
}
})
@ -337,18 +319,18 @@ export class Hand extends EventEmitter {
this.scoreLayer.removeChildren()
const name = createText({
text: this.player?.name ?? '-',
x: 65,
y: 25,
x: 100,
y: 50,
style: playerNameText,
})
const text = createText({
text: `${this.score}`,
x: 65,
x: 100,
// x: this.width - 5,
y: 75,
style: whiteStyle(60, 'bold'),
y: 80,
style: whiteStyle(36, 'bold'),
})
text.anchor.set(0.5, 0.5)
text.anchor.set(1, 0.5)
this.scoreLayer.addChild(name)
this.scoreLayer.addChild(text)
}

View File

@ -9,7 +9,7 @@ export abstract class SpriteBase {
constructor(
protected ticker?: Ticker,
protected scale: number = 1,
protected scale: number = 1
) {
this.ticker = ticker
this.scale = scale
@ -84,6 +84,58 @@ export abstract class SpriteBase {
this.sprite.filters = []
}
animateTo(options: AnimationOptions): Promise<void> {
return new Promise((resolve) => {
const {
x: targetX,
y: targetY,
rotation: targetRotation,
duration = 10,
width: targetWidth,
height: targetHeight
} = options
const initialX = this.sprite.x
const initialY = this.sprite.y
const initialRotation = this.sprite.rotation
const initialWidth = this.sprite.width
const initialHeight = this.sprite.height
const deltaX = targetX ? targetX - this.sprite.x : null
const deltaY = targetY ? targetY - this.sprite.y : null
const deltaRotation = targetRotation ? targetRotation - this.sprite.rotation : null
const deltaWidth = targetWidth ? targetWidth - this.sprite.width : null
const deltaHeight = targetHeight ? targetHeight - this.sprite.height : null
let elapsed: number = 0
const tick: any = (delta: any) => {
elapsed += delta.deltaTime
const progress = Math.min(elapsed / duration, 1)
// Linear interpolation
if (deltaX !== null) this.sprite.x = initialX + deltaX * progress
if (deltaY !== null) this.sprite.y = initialY + deltaY * progress
// Rotation interpolation
if (deltaRotation !== null)
this.sprite.rotation = initialRotation + deltaRotation * progress
// Scale interpolation
// this.sprite.width = initialWidth + deltaWidth * progress
// this.sprite.height = initialHeight + deltaHeight * progress
//
if (progress === 1) {
this.ticker?.remove(tick)
resolve()
}
}
this.ticker?.add(tick)
})
}
addTo(container: any) {
this.container = container
container.addChild(this.sprite)

View File

@ -2,10 +2,9 @@ import { Container, Point, Text, TextStyle } from 'pixi.js'
import { createText, timerStyle } from './utilities/fonts'
import { gsap } from 'gsap'
import { Timer } from './utilities/Timer'
export class TimerText extends Container {
private timer: Timer
private intervalHandle?: any
private text?: Text
animation: boolean = false
textStyle: TextStyle = timerStyle()
@ -17,35 +16,41 @@ export class TimerText extends Container {
private point: Point,
) {
super()
this.timer = new Timer(seconds)
this.timer.on('timeout', this.onTImeOut.bind(this))
this.timer.on('tick', this.onTick.bind(this))
this.countdown = seconds
this.render()
}
private onTImeOut() {
this.timer.reset()
this.emit('timeout')
}
private onTick(seconds: number) {
!this.animation && this.text?.destroy()
this.countdown = seconds
this.render()
}
reset() {
this.timer.stop()
this.timer.reset()
this.stop()
this.countdown = this.seconds
this.isInitiated = false
this.render()
}
start() {
this.timer.reset()
this.timer.start()
clearInterval(this.intervalHandle)
this.isInitiated = true
this.removeChildren()
this.render()
this.intervalHandle = setInterval(() => {
this.countdown--
!this.animation && this.text?.destroy()
this.render()
if (this.countdown === 0) {
clearInterval(this.intervalHandle)
this.emit('timeout')
}
}, 1000)
}
destroy() {
this.stop()
this.destroy()
}
stop() {
clearInterval(this.intervalHandle)
this.emit('stop', this.countdown)
}
private render() {

View File

@ -15,7 +15,6 @@ export class Timer extends EventEmitter {
this.countdown--
if (this.countdown === 0) {
clearInterval(this.intervalHandle)
console.log('tic')
this.emit('timeout')
} else {
this.emit('tick', this.countdown)

View File

@ -14,11 +14,11 @@
"scoreboard": "Scoreboard",
"available-sessions": "Available Sessions",
"no-sessions-available": "No sessions available",
"id-session-_id": "ID:",
"players-session-players-length": "Players:",
"id-session-_id": "ID: {0}",
"players-session-players-length": "Players: {0}",
"copy": "Copy",
"seed-session-seed": "Seed:",
"status-session-status": "Status:",
"seed-session-seed": "Seed: {0}",
"status-session-status": "Status: {0}",
"delete": "Delete",
"join": "Join",
"welcome-to-the-user-username-s-home-page": "Welcome to the {0}'s Home Page",
@ -35,7 +35,7 @@
"crossed-game-teamed": "Crossed game",
"create-match-session": "Create Match Session",
"ready": "Ready",
"unready": "Not ready",
"unready": "Unready",
"start": "Start",
"cancel": "Cancel",
"game": {
@ -59,18 +59,16 @@
"n-rounds": "One Round|{count} Rounds",
"n-of-m-rounds": "{0} of {1} Rounds",
"create-session": "Create Session",
"join-a-multiplayer-session": "Multiplayer (0)|Multiplayer ({count})|Multiplayer ({count})",
"join-a-multiplayer-session": "Join a Multiplayer Session (No sessions)|Join a Multiplayer Session ({count})|Join a Multiplayer Session ({count})",
"tournaments": "Tournaments",
"start-game": "Start Game",
"player": "Player",
"final-score": "Final Score",
"round-index": "Round #{0}",
"first-actor-to-win-this-options-wintarget-this-options-wintype": "First {0} to reach {1}",
"first-actor-to-win-this-options-wintarget-this-options-wintype": "First {0} to win {1}",
"team": "team",
"winner-name": "Winner: {0}",
"blocked": "Blocked",
"round-summary": "Round Summary",
"match-finished": "Match Finished",
"host-session-host": "Host:",
"players": "Players"
"match-finished": "Match Finished"
}

View File

@ -12,10 +12,10 @@
"scoreboard": "Marcador",
"winner": "Ganador",
"final-scoreboard": "Puntaje Final",
"id-session-_id": "ID:",
"id-session-_id": "ID: {0}",
"seed-placeholder": "¡Escriba la semilla de la sesión aquí!",
"session-name-placeholder": "Nombre de la sesión",
"status-session-status": "Estado:",
"status-session-status": "Estado: {0}",
"unready": "No preparado",
"welcome-to-the-user-username-s-home-page": "Bienvenido a la página de inicio de {0}",
"available-sessions": "Sesiones disponibles",
@ -38,11 +38,11 @@
"join": "Unirse",
"name": "Nombre",
"no-sessions-available": "No hay sesiones disponibles",
"players-session-players-length": "Jugadores:",
"players-session-players-length": "Jugadores: {0}",
"ready": "Listo",
"red-fabric": "Tela roja",
"seed": "Semilla",
"seed-session-seed": "Semilla:",
"seed-session-seed": "Semilla: {0}",
"start": "Comenzar",
"yellow-fabric": "Tela amarilla",
"back": "Volver",
@ -70,7 +70,5 @@
"blocked": "Cerrado",
"round-summary": "Resumen de la ronda",
"match-finished": "Partida terminado",
"team": "equipo",
"host-session-host": "Creada por:",
"players": "Jugadores"
"team": "equipo"
}

View File

@ -105,16 +105,4 @@ router.beforeEach((to, from, next) => {
}
})
router.beforeResolve((to, from, next) => {
const app = document.querySelector('#app')
if (app) {
if (from.name !== undefined) {
app.classList.replace(<string>from.name, <string>to.name)
} else {
app.classList.add(<string>to.name)
}
}
next()
})
export default router

View File

@ -50,10 +50,9 @@ export class SocketIoClientService extends ServiceBase {
}
addEvents(): void {
this.socket.on('disconnect', (reason) => {
this.socket.on('disconnect', () => {
this.isConnected = false
console.log('SOCKET: Disconnected from server')
console.log(`Reason: ${reason}`)
})
this.socket.on('reconnect', () => {
@ -88,15 +87,6 @@ export class SocketIoClientService extends ServiceBase {
})
}
send(event: string, data: any): void {
if (this.isConnected) {
this.socket?.emit(event, data)
this.logger.trace(`SOCKET: sendEvent :>> ${event}`, data)
} else {
this.logger.trace('Not connected to server')
}
}
sendMessage(event: string, data: any): void {
if (this.isConnected) {
this.socket?.emit('client:event', { event, data })

View File

@ -23,6 +23,7 @@ if (!playerState?.value) {
function makeMove(move: any) {
moveToMake.value = move
canMakeMove.value = false
console.log('makemove :>> ', move)
}
onBeforeUnmount(() => {

View File

@ -13,8 +13,6 @@ import MatchConfiguration from '@/components/MatchConfiguration.vue'
import { useI18n } from 'vue-i18n'
import { SessionExpiredError } from '@/common/errors/SessionExpiredError'
import type { AuthenticationService } from '@/services/AuthenticationService'
import SessionBoxComponent from '@/components/SessionBoxComponent.vue'
import MultiplayerSetupComponent from '@/components/MultiplayerSetupComponent.vue'
let teamedWith = ref<string | undefined>(undefined)
@ -33,7 +31,8 @@ const authService: AuthenticationService = inject<AuthenticationService>(
'auth',
) as AuthenticationService
const { sessionState, isSessionStarted, playerState, amIHost } = storeToRefs(gameStore)
const { sessionState, isSessionStarted, playerState, amIHost, readyForStart } =
storeToRefs(gameStore)
const { gameOptions } = storeToRefs(gameOptionsStore)
const { updateSessionState, updatePlayerState, updateGameState } = gameStore
const { t } = useI18n()
@ -44,13 +43,17 @@ eventBus.subscribe('window-before-unload', () => {
})
async function createMatch(options: MatchSessionOptions) {
logger.debug('Creating match')
await socketService.connect()
gameOptions.value = options
const sessionId = await gameService.createMatchSession(options)
socketService.joinRoom(`room-${sessionId}`)
logger.debug('Match created successfully')
// router.push({ name: 'match', params: { id } })
}
async function setPlayerReady() {
logger.debug('Starting game')
if (!sessionState.value) {
logger.error('No session found')
return
@ -65,15 +68,14 @@ async function setPlayerReady() {
})
}
async function startMatch(teamedWith?: string) {
console.log('teamedWith :>> ', teamedWith)
async function startMatch() {
const sessionId = sessionState?.value?.id
const playerId = playerState?.value?.id
if (sessionId) {
await socketService.sendMessageWithAck('client:start-session', {
sessionId: sessionId,
playerId: playerId,
teamedWith: teamedWith,
teamedWith: teamedWith.value,
})
}
}
@ -115,6 +117,20 @@ async function deleteMatch(id: string) {
}
}
const playersToTeamUpWith = computed(() => {
return (
sessionState?.value?.players.filter((player) => player.id !== sessionState?.value?.creator) ||
[]
)
})
const canStart = computed(() => {
const players = sessionState?.value?.players || []
const options = gameOptions.value
const allReady = (players.length || 0) > 0 && players.every((player) => player.ready)
return (!options?.teamed && allReady) || (options?.teamed && !!teamedWith.value && allReady)
})
const isMultiplayer = computed(
() => (sessionState?.value?.options?.numPlayers || gameOptions.value?.numPlayers || 0) > 1,
)
@ -151,18 +167,18 @@ function copy(sessionSeed: string) {
copyToclipboard(sessionSeed)
}
let selectedTab = ref('join-tab')
let selectedTab = ref('create-tab')
const isCreateTab = computed(() => selectedTab.value === 'create-tab')
const isJoinTab = computed(() => selectedTab.value === 'join-tab')
const isTournamentTab = computed(() => selectedTab.value === 'torunaments-tab')
const tabs = computed<any[]>((): any => [
{
label: t('join-a-multiplayer-session', matchSessions.value?.length ?? 0),
id: 'join-tab',
disabled: false, //matchSessions.value.length <= 0,
},
{ label: t('create-session'), id: 'create-tab', disabled: false },
{
label: t('join-a-multiplayer-session', matchSessions.value.length),
id: 'join-tab',
disabled: matchSessions.value.length <= 0,
},
{ label: t('tournaments'), id: 'torunaments-tab', disabled: true },
])
@ -213,28 +229,38 @@ async function onStartSingleMatch(options: MatchSessionOptions) {
<section class="section available-sessions" v-if="isJoinTab">
<div class="block">
<div v-if="matchSessions.length === 0">
<p class="has-text-centered mt-6 pt-6">{{ $t('no-sessions-available') }}</p>
<div class="buttons is-justify-content-center mt-6">
<button class="button is-primary is-small" @click="() => tabClick(tabs[1])">
{{ $t('create-session') }}
</button>
<p>{{ $t('no-sessions-available') }}</p>
</div>
</div>
<div v-else>
<div class="buttons is-justify-content-center mt-6">
<button class="button is-primary is-small" @click="() => tabClick(tabs[1])">
{{ $t('create-session') }}
</button>
</div>
<div class="fixed-grid has-3-cols">
<div v-else class="fixed-grid has-3-cols">
<div class="grid">
<div class="cell" v-for="session in matchSessions" :key="session.id">
<SessionBoxComponent
:session="session"
@join="joinMatch"
@delete="deleteMatch"
@copy="copy"
/>
<div class="card">
<div class="card-content">
<p class="title is-6">{{ session.name }}</p>
<p>{{ $t('id-session-_id', [session._id]) }}</p>
<p>{{ $t('players-session-players-length', [session.players.length]) }}</p>
<p>
{{ $t('seed-session-seed', [session.seed]) }}
<button class="button is-small is-ghost" @click="() => copy(session.seed)">
{{ $t('copy') }}
</button>
</p>
<p>{{ $t('status-session-status', [session.status]) }}</p>
<div class="buttons is-centered mt-6">
<button
class="button is-primary"
@click.once.prevent="() => joinMatch(session._id)"
>
<span>{{ $t('join') }}</span>
</button>
<button
class="button is-text"
@click.once.prevent="() => deleteMatch(session._id)"
>
<span>{{ $t('delete') }}</span>
</button>
</div>
</div>
</div>
</div>
</div>
@ -246,12 +272,45 @@ async function onStartSingleMatch(options: MatchSessionOptions) {
<section class="section" v-if="isTournamentTab"></section>
<!-- Tournaments End -->
</div>
<MultiplayerSetupComponent
v-if="isSessionStarted && isMultiplayer"
@ready="setPlayerReady"
@start="startMatch"
@cancel="cancelMatch"
/>
<div class="block" v-if="isSessionStarted && isMultiplayer">
<h2 class="title is-4">{{ sessionState?.name }}</h2>
<h6 class="title is-size-5">Players</h6>
<div v-for="player in sessionState?.players" :key="player.id">
<p>{{ player.name }} ({{ player.ready ? 'Ready' : 'Not ready' }})</p>
</div>
<div class="mt-4" v-if="amIHost && gameOptions?.teamed && playersToTeamUpWith.length > 0">
<div class="field">
<label for="background" class="label">Team up with</label>
<div class="control">
<div class="select">
<select v-model="teamedWith" name="teamedWidth">
<option v-for="player in playersToTeamUpWith" :key="player.id" :value="player.id">
{{ player.name }}
</option>
</select>
</div>
</div>
</div>
</div>
<div class="buttons mt-6">
<button class="button is-dark" @click="setPlayerReady">
<span v-if="!readyForStart">{{ $t('ready') }}</span
><span v-else>{{ $t('unready') }}</span>
</button>
<button
class="button is-success"
:disabled="!canStart"
@click="startMatch"
v-if="amIHost"
>
<span>{{ $t('start') }}</span>
</button>
<button class="button is-danger" @click="cancelMatch">
<span>{{ $t('cancel') }}</span>
</button>
</div>
</div>
</section>
</div>
</template>

View File

@ -52,9 +52,8 @@ async function login() {
</script>
<template>
<div class="container login">
<div class="is-flex is-justify-content-center is-align-items-center">
<div class="login-form-container box is-no-shadow" style="width: 400px">
<div class="login">
<h1 class="title">{{ $t('login') }}</h1>
<div class="message is-danger">
<div class="message-body" v-if="errorLogin">
{{ $t('invalid-username-or-password') }}
@ -88,20 +87,10 @@ async function login() {
<div class="control">
<button class="button is-primary" type="submit">{{ $t('login-button') }}</button>
</div>
<!-- <div class="control">
<button class="button">Cancel</button>
</div> -->
</div>
</form>
</div>
</div>
</div>
</template>
<style scoped>
.login,
.login > div {
height: 100%;
}
.login-form-container {
width: 400px;
}
</style>

1
types/type.d.ts vendored
View File

@ -6,5 +6,4 @@ declare module 'socket.io' {
declare interface Window {
__TAURI__: any
opera: any
}