0.1.4
This commit is contained in:
		
							
								
								
									
										31
									
								
								src/App.vue
									
									
									
									
									
								
							
							
						
						
									
										31
									
								
								src/App.vue
									
									
									
									
									
								
							@@ -3,6 +3,7 @@ import { inject, onMounted, onUnmounted } from 'vue'
 | 
			
		||||
import { RouterView } from 'vue-router'
 | 
			
		||||
import type { AuthenticationService } from './services/AuthenticationService'
 | 
			
		||||
import { useEventBusStore } from './stores/eventBus'
 | 
			
		||||
import { sound } from '@pixi/sound'
 | 
			
		||||
 | 
			
		||||
const auth: AuthenticationService = inject<AuthenticationService>('auth') as AuthenticationService
 | 
			
		||||
auth.fromStorage()
 | 
			
		||||
@@ -17,8 +18,38 @@ const handleBeforeUnload = (evt: any) => {
 | 
			
		||||
  // console.log('location.href :>> ', location.pathname)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// document.addEventListener('visibilitychange', () => {
 | 
			
		||||
//   console.log('visibilitychange')
 | 
			
		||||
//   let playingOnHide = false
 | 
			
		||||
//   if (document.hidden) {
 | 
			
		||||
//     playingOnHide = true
 | 
			
		||||
//     sound.pauseAll()
 | 
			
		||||
//   } else {
 | 
			
		||||
//     // Page became visible! Resume playing if audio was "playing on hide"
 | 
			
		||||
//     if (playingOnHide) {
 | 
			
		||||
//       sound.resumeAll()
 | 
			
		||||
//     }
 | 
			
		||||
//   }
 | 
			
		||||
// })
 | 
			
		||||
 | 
			
		||||
// const soundContextResume = () => {
 | 
			
		||||
//   const context = sound.context.audioContext
 | 
			
		||||
//   if (context.state === 'suspended' || context.state === 'interrupted') {
 | 
			
		||||
//     context.resume()
 | 
			
		||||
//   }
 | 
			
		||||
// }
 | 
			
		||||
 | 
			
		||||
// document.addEventListener('click', function (event) {
 | 
			
		||||
//   console.log('click document :>> ', event)
 | 
			
		||||
//   console.log('screen :>> ', screen)
 | 
			
		||||
//   // if (event.target instanceof HTMLButtonElement) {
 | 
			
		||||
//   //   sound.play('click')
 | 
			
		||||
//   // }
 | 
			
		||||
// })
 | 
			
		||||
 | 
			
		||||
onMounted(() => {
 | 
			
		||||
  window.addEventListener('beforeunload', handleBeforeUnload)
 | 
			
		||||
  // window.addEventListener('focus', soundContextResume)
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
onUnmounted(() => {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,17 @@
 | 
			
		||||
:root {
 | 
			
		||||
  /* bulma color variables */
 | 
			
		||||
  --bulma-primary-h: 40deg;
 | 
			
		||||
  --bulma-primary-s: 48%;
 | 
			
		||||
  --bulma-primary-l: 48%;
 | 
			
		||||
  --bulma-info-h: 168deg;
 | 
			
		||||
  --bulma-info-s: 58%;
 | 
			
		||||
  --bulma-info-l: 28%;
 | 
			
		||||
  --bulma-primary-l: 38%;
 | 
			
		||||
  --bulma-link-h: 36deg;
 | 
			
		||||
  --bulma-link-s: 19%;
 | 
			
		||||
  --bulma-link-l: 16%;
 | 
			
		||||
  --bulma-info-h: 192deg;
 | 
			
		||||
  --bulma-info-l: 34%;
 | 
			
		||||
  --bulma-success-s: 52%;
 | 
			
		||||
  --bulma-success-l: 38%;
 | 
			
		||||
  --bulma-warning-h: 58deg;
 | 
			
		||||
  --bulma-warning-s: 61%;
 | 
			
		||||
  --bulma-warning-l: 41%;
 | 
			
		||||
  --bulma-danger-s: 74%;
 | 
			
		||||
  --bulma-danger-l: 37%;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -3,11 +3,20 @@ export const DEFAULT_CONTAINER_OPTIONS = {
 | 
			
		||||
  height: 100,
 | 
			
		||||
  x: 0,
 | 
			
		||||
  y: 0,
 | 
			
		||||
  visible: true
 | 
			
		||||
  visible: true,
 | 
			
		||||
}
 | 
			
		||||
export const ORIENTATION_ANGLES: { [key: string]: number } = {
 | 
			
		||||
  north: 0,
 | 
			
		||||
  east: Math.PI / 2,
 | 
			
		||||
  south: Math.PI,
 | 
			
		||||
  west: (3 * Math.PI) / 2
 | 
			
		||||
  west: (3 * Math.PI) / 2,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const DIRECTION_INDEXES: { [key: string]: number } = {
 | 
			
		||||
  north: 0,
 | 
			
		||||
  east: 1,
 | 
			
		||||
  south: 2,
 | 
			
		||||
  west: 3,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const DIRECTIONS = ['north', 'east', 'south', 'west']
 | 
			
		||||
 
 | 
			
		||||
@@ -99,8 +99,6 @@ export async function wait(ms: number) {
 | 
			
		||||
  return new Promise((resolve) => setTimeout(resolve, ms))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const DIRECTIONS = ['north', 'east', 'south', 'west']
 | 
			
		||||
 | 
			
		||||
export function isTilePair(tile: TileDto): boolean {
 | 
			
		||||
  return !!(tile.pips && tile.pips[0] === tile.pips[1])
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -2,11 +2,11 @@ import { Application, Container, EventEmitter, Text, Ticker } from 'pixi.js'
 | 
			
		||||
import { Scale, type ScaleFunction } from '@/game/utilities/scale'
 | 
			
		||||
import type { AnimationOptions, Movement, PlayerDto, TileDto } from '@/common/interfaces'
 | 
			
		||||
import { Tile } from '@/game/Tile'
 | 
			
		||||
import { DIRECTIONS, createContainer, isTilePair } from '@/common/helpers'
 | 
			
		||||
import { createContainer, isTilePair } from '@/common/helpers'
 | 
			
		||||
import { createText } from '@/game/utilities/fonts'
 | 
			
		||||
import { LoggingService } from '@/services/LoggingService'
 | 
			
		||||
import { GlowFilter } from 'pixi-filters'
 | 
			
		||||
import { ORIENTATION_ANGLES } from '@/common/constants'
 | 
			
		||||
import { DIRECTION_INDEXES, DIRECTIONS, ORIENTATION_ANGLES } from '@/common/constants'
 | 
			
		||||
import type { OtherHand } from './OtherHand'
 | 
			
		||||
import { sound } from '@pixi/sound'
 | 
			
		||||
import { t } from '@/i18n'
 | 
			
		||||
@@ -181,6 +181,8 @@ export class Board extends EventEmitter {
 | 
			
		||||
    const tileDto = tile.toPlain()
 | 
			
		||||
    let direction = move.type === 'left' ? this.leftDirection : this.rightDirection
 | 
			
		||||
 | 
			
		||||
    move.direction = this.hasSpaceToMove(move)
 | 
			
		||||
 | 
			
		||||
    if (this.tiles.length === 0) {
 | 
			
		||||
      x = 0
 | 
			
		||||
      y = 0
 | 
			
		||||
@@ -592,6 +594,22 @@ export class Board extends EventEmitter {
 | 
			
		||||
    return [canPlayNorth, canPlayEast, canPlaySouth, canPlayWest]
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  hasSpaceToMove(move: Movement): string | undefined {
 | 
			
		||||
    if (move.tile === undefined || move.direction === undefined) {
 | 
			
		||||
      return undefined
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const nextValidMoves = this.nextTileValidMoves(move.tile, move.type)
 | 
			
		||||
    let index = DIRECTION_INDEXES[move.direction]
 | 
			
		||||
    let valid = nextValidMoves[index]
 | 
			
		||||
    while (!valid && index < nextValidMoves.length) {
 | 
			
		||||
      index++
 | 
			
		||||
      valid = nextValidMoves[index % nextValidMoves.length]
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return DIRECTIONS[index]
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  clean() {
 | 
			
		||||
    this.tiles = []
 | 
			
		||||
    this.boneyard = []
 | 
			
		||||
 
 | 
			
		||||
@@ -81,8 +81,7 @@ export class Game extends EventEmitter {
 | 
			
		||||
  iniialStuff(app: Application) {
 | 
			
		||||
    app.stage.addChild(this.backgroundLayer)
 | 
			
		||||
    const background = new Sprite(Assets.get(`bg-${this.options.background}`))
 | 
			
		||||
    background.width = this.app.canvas.width
 | 
			
		||||
    background.height = this.app.canvas.height
 | 
			
		||||
 | 
			
		||||
    this.backgroundLayer.addChild(background)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -44,5 +44,7 @@
 | 
			
		||||
    "starting_game": "Starting game...",
 | 
			
		||||
    "your-turn": "Your turn!",
 | 
			
		||||
    "player-turn": "{0}'s turn!"
 | 
			
		||||
  }
 | 
			
		||||
  },
 | 
			
		||||
  "back": "Back",
 | 
			
		||||
  "session-name": "Session Name"
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -20,7 +20,7 @@
 | 
			
		||||
  "welcome-to-the-user-username-s-home-page": "Bienvenido a la página de inicio de {0}",
 | 
			
		||||
  "available-sessions": "Sesiones disponibles",
 | 
			
		||||
  "background-color": "Color de fondo",
 | 
			
		||||
  "blue-fabric": "Fabrica azul",
 | 
			
		||||
  "blue-fabric": "Tela azul",
 | 
			
		||||
  "cancel": "Cancelar",
 | 
			
		||||
  "copy": "Copiar",
 | 
			
		||||
  "game": {
 | 
			
		||||
@@ -44,5 +44,7 @@
 | 
			
		||||
  "seed": "Semilla",
 | 
			
		||||
  "seed-session-seed": "Semilla: {0}",
 | 
			
		||||
  "start": "Comenzar",
 | 
			
		||||
  "yellow-fabric": "Tela amarilla"
 | 
			
		||||
  "yellow-fabric": "Tela amarilla",
 | 
			
		||||
  "back": "Volver",
 | 
			
		||||
  "session-name": "Nombre de la sesión"
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,8 @@
 | 
			
		||||
import './assets/main.css'
 | 
			
		||||
 | 
			
		||||
import { createApp } from 'vue'
 | 
			
		||||
import { createPinia } from 'pinia'
 | 
			
		||||
 | 
			
		||||
import '../node_modules/bulma/css/bulma.css'
 | 
			
		||||
import './assets/main.css'
 | 
			
		||||
 | 
			
		||||
import App from './App.vue'
 | 
			
		||||
import router from './router'
 | 
			
		||||
 
 | 
			
		||||
@@ -51,7 +51,7 @@ const router = createRouter({
 | 
			
		||||
      ],
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      path: '/game:id',
 | 
			
		||||
      path: '/game/:id',
 | 
			
		||||
      component: AuthenticatedLayout,
 | 
			
		||||
      children: [
 | 
			
		||||
        {
 | 
			
		||||
 
 | 
			
		||||
@@ -15,7 +15,7 @@ let background = ref<string>('green')
 | 
			
		||||
let teamed = ref<boolean>(false)
 | 
			
		||||
let pointsToWin = ref<number>(100)
 | 
			
		||||
let seed = ref<string>('')
 | 
			
		||||
let sessionName = ref('Test Value')
 | 
			
		||||
let sessionName = ref(`Test #${Date.now()}`)
 | 
			
		||||
let matchSessions = ref<MatchSessionDto[]>([])
 | 
			
		||||
let dataInterval: any
 | 
			
		||||
 | 
			
		||||
@@ -130,7 +130,6 @@ async function deleteMatch(id: string) {
 | 
			
		||||
async function loadData() {
 | 
			
		||||
  const listResponse = await gameService.listMatchSessions()
 | 
			
		||||
  matchSessions.value = listResponse.data
 | 
			
		||||
  sessionName.value = `Test #${Date.now()}`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
onMounted(() => {
 | 
			
		||||
@@ -156,7 +155,7 @@ function copy(sessionSeed: string) {
 | 
			
		||||
      </h1>
 | 
			
		||||
      <div class="block" v-if="!isSessionStarted">
 | 
			
		||||
        <div class="field">
 | 
			
		||||
          <label class="label">{{ $t('name') }}</label>
 | 
			
		||||
          <label class="label">{{ $t('session-name') }}</label>
 | 
			
		||||
          <div class="control">
 | 
			
		||||
            <input
 | 
			
		||||
              type="text"
 | 
			
		||||
@@ -174,7 +173,7 @@ function copy(sessionSeed: string) {
 | 
			
		||||
              class="input"
 | 
			
		||||
              style="margin-bottom: 0"
 | 
			
		||||
              v-model="seed"
 | 
			
		||||
              placeholder="$t('seed-placeholder')"
 | 
			
		||||
              :placeholder="$t('seed-placeholder')"
 | 
			
		||||
            />
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
@@ -226,53 +225,64 @@ function copy(sessionSeed: string) {
 | 
			
		||||
          </button>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
      <div class="buttons" v-if="isSessionStarted">
 | 
			
		||||
        <button class="button" @click="setPlayerReady">
 | 
			
		||||
          <span v-if="!readyForStart">{{ $t('ready') }}</span
 | 
			
		||||
          ><span v-else>{{ $t('unready') }}</span>
 | 
			
		||||
        </button>
 | 
			
		||||
        <button class="button" @click="startMatch" v-if="amIHost && readyForStart">
 | 
			
		||||
          <span>{{ $t('start') }}</span>
 | 
			
		||||
        </button>
 | 
			
		||||
      <div class="block" v-if="isSessionStarted">
 | 
			
		||||
        <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 }}</p>
 | 
			
		||||
          <p>{{ player.ready ? 'Ready' : 'Not ready' }}</p>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="buttons mt-6">
 | 
			
		||||
          <button class="button" @click="setPlayerReady">
 | 
			
		||||
            <span v-if="!readyForStart">{{ $t('ready') }}</span
 | 
			
		||||
            ><span v-else>{{ $t('unready') }}</span>
 | 
			
		||||
          </button>
 | 
			
		||||
          <button class="button" @click="startMatch" v-if="amIHost && readyForStart">
 | 
			
		||||
            <span>{{ $t('start') }}</span>
 | 
			
		||||
          </button>
 | 
			
		||||
 | 
			
		||||
        <button class="button" @click="cancelMatch">
 | 
			
		||||
          <span>{{ $t('cancel') }}</span>
 | 
			
		||||
        </button>
 | 
			
		||||
          <button class="button" @click="cancelMatch">
 | 
			
		||||
            <span>{{ $t('cancel') }}</span>
 | 
			
		||||
          </button>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
    </section>
 | 
			
		||||
    <section class="section available-sessions">
 | 
			
		||||
    <section class="section available-sessions" v-if="!isSessionStarted">
 | 
			
		||||
      <h2 class="title is-4">{{ $t('available-sessions') }}</h2>
 | 
			
		||||
      <div class="block">
 | 
			
		||||
        <div v-if="matchSessions.length === 0">
 | 
			
		||||
          <p>{{ $t('no-sessions-available') }}</p>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div v-else class="grid is-col-min-12">
 | 
			
		||||
          <div class="cell" v-for="session in matchSessions" :key="session.id">
 | 
			
		||||
            <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" @click="() => copy(session.seed)">
 | 
			
		||||
                    {{ $t('copy') }}
 | 
			
		||||
                  </button>
 | 
			
		||||
                </p>
 | 
			
		||||
                <p>{{ $t('status-session-status', [session.status]) }}</p>
 | 
			
		||||
                <div class="buttons is-centered mt-4"></div>
 | 
			
		||||
              </div>
 | 
			
		||||
              <div class="card-footer">
 | 
			
		||||
                <p class="card-footer-item">
 | 
			
		||||
                  <a href="#" @click.once.prevent="() => joinMatch(session._id)">
 | 
			
		||||
                    {{ $t('join') }}
 | 
			
		||||
                  </a>
 | 
			
		||||
                </p>
 | 
			
		||||
                <p class="card-footer-item">
 | 
			
		||||
                  <a href="#" @click.once.prevent="() => deleteMatch(session._id)">
 | 
			
		||||
                    {{ $t('delete') }}
 | 
			
		||||
                  </a>
 | 
			
		||||
                </p>
 | 
			
		||||
        <div v-else class="fixed-grid has-3-cols">
 | 
			
		||||
          <div class="grid">
 | 
			
		||||
            <div class="cell" v-for="session in matchSessions" :key="session.id">
 | 
			
		||||
              <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>
 | 
			
		||||
 
 | 
			
		||||
@@ -73,6 +73,11 @@ onBeforeMount(() => {
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="buttons">
 | 
			
		||||
      <button class="button is-primary" @click="router.push({ name: 'home' })">
 | 
			
		||||
        {{ $t('back') }}
 | 
			
		||||
      </button>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="section">
 | 
			
		||||
      <!-- <div>{{ matchSession }}</div> -->
 | 
			
		||||
    </div>
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user