v0,2,1
This commit is contained in:
		@@ -8,7 +8,7 @@
 | 
				
			|||||||
  },
 | 
					  },
 | 
				
			||||||
  "package": {
 | 
					  "package": {
 | 
				
			||||||
    "productName": "domino-client",
 | 
					    "productName": "domino-client",
 | 
				
			||||||
    "version": "0.2.0"
 | 
					    "version": "0.2.1"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "tauri": {
 | 
					  "tauri": {
 | 
				
			||||||
    "allowlist": {
 | 
					    "allowlist": {
 | 
				
			||||||
@@ -72,7 +72,7 @@
 | 
				
			|||||||
        "fullscreen": false,
 | 
					        "fullscreen": false,
 | 
				
			||||||
        "height": 720,
 | 
					        "height": 720,
 | 
				
			||||||
        "resizable": true,
 | 
					        "resizable": true,
 | 
				
			||||||
        "title": "Domino v0.2.0",
 | 
					        "title": "Domino v0.2.1",
 | 
				
			||||||
        "width": 1280,
 | 
					        "width": 1280,
 | 
				
			||||||
        "minHeight": 720,
 | 
					        "minHeight": 720,
 | 
				
			||||||
        "minWidth": 1280,
 | 
					        "minWidth": 1280,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -15,9 +15,3 @@
 | 
				
			|||||||
  --bulma-danger-s: 74%;
 | 
					  --bulma-danger-s: 74%;
 | 
				
			||||||
  --bulma-danger-l: 37%;
 | 
					  --bulma-danger-l: 37%;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
.tabs li.is-disabled {
 | 
					 | 
				
			||||||
  pointer-events: none;
 | 
					 | 
				
			||||||
  cursor: default;
 | 
					 | 
				
			||||||
  opacity: 0.3;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,35 @@
 | 
				
			|||||||
@import './base.css';
 | 
					@import './base.css';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
html {
 | 
					html,
 | 
				
			||||||
 | 
					body,
 | 
				
			||||||
 | 
					#app {
 | 
				
			||||||
  font-size: 16px;
 | 
					  font-size: 16px;
 | 
				
			||||||
  overflow-y: auto;
 | 
					  overflow-y: auto;
 | 
				
			||||||
  overflow-x: hidden;
 | 
					  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;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -143,3 +143,10 @@ export function createStringMatrix(
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  return matrix
 | 
					  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,
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,7 +19,7 @@ let options = ref<MatchSessionOptions>({
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
const winTargetPointsList = [20, 50, 80, 100, 150, 200]
 | 
					const winTargetPointsList = [20, 50, 80, 100, 150, 200]
 | 
				
			||||||
const winTargetRoundsList = [1, 2, 3, 4, 5, 6]
 | 
					const winTargetRoundsList = [1, 2, 3, 4, 5, 6]
 | 
				
			||||||
const turnWaitSecondsList = [15, 20, 30, 40, 50, 60]
 | 
					const turnWaitSecondsList = [5, 10, 15, 20, 30, 40, 50, 60]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const backgroundOptiopnList = [
 | 
					const backgroundOptiopnList = [
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
@@ -58,86 +58,90 @@ function startSingleMatch() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
  <div>
 | 
					  <div>
 | 
				
			||||||
    <div class="grid">
 | 
					    <div class="fixed-grid has-3-cols has-1-cols-mobile">
 | 
				
			||||||
      <div class="cell">
 | 
					      <div class="grid">
 | 
				
			||||||
        <div class="field">
 | 
					        <div class="cell">
 | 
				
			||||||
          <label class="label">{{ $t('mode') }}</label>
 | 
					          <div class="field">
 | 
				
			||||||
          <div class="control">
 | 
					            <label class="label">{{ $t('mode') }}</label>
 | 
				
			||||||
            <div class="buttons has-addons">
 | 
					            <div class="control">
 | 
				
			||||||
              <button
 | 
					              <div class="buttons has-addons">
 | 
				
			||||||
                class="button"
 | 
					                <button
 | 
				
			||||||
                :class="{ 'is-primary is-selected': isSinglePlayer }"
 | 
					                  class="button"
 | 
				
			||||||
                @click="
 | 
					                  :class="{ 'is-primary is-selected': isSinglePlayer }"
 | 
				
			||||||
                  () => {
 | 
					                  @click="
 | 
				
			||||||
                    console.log('options :>> ', options)
 | 
					                    () => {
 | 
				
			||||||
                    options.numPlayers = 1
 | 
					                      console.log('options :>> ', options)
 | 
				
			||||||
                  }
 | 
					                      options.numPlayers = 1
 | 
				
			||||||
                "
 | 
					                    }
 | 
				
			||||||
              >
 | 
					                  "
 | 
				
			||||||
                {{ $t('singleplayer') }}
 | 
					                >
 | 
				
			||||||
              </button>
 | 
					                  {{ $t('singleplayer') }}
 | 
				
			||||||
              <button
 | 
					                </button>
 | 
				
			||||||
                class="button"
 | 
					                <button
 | 
				
			||||||
                :class="{ 'is-primary is-selected': isMultiPlayer }"
 | 
					                  class="button"
 | 
				
			||||||
                @click="
 | 
					                  :class="{ 'is-primary is-selected': isMultiPlayer }"
 | 
				
			||||||
                  () => {
 | 
					                  @click="
 | 
				
			||||||
                    console.log('options :>> ', options)
 | 
					                    () => {
 | 
				
			||||||
                    options.numPlayers = 2
 | 
					                      console.log('options :>> ', options)
 | 
				
			||||||
                  }
 | 
					                      options.numPlayers = 2
 | 
				
			||||||
                "
 | 
					                    }
 | 
				
			||||||
              >
 | 
					                  "
 | 
				
			||||||
                {{ $t('multiplayer') }}
 | 
					                >
 | 
				
			||||||
              </button>
 | 
					                  {{ $t('multiplayer') }}
 | 
				
			||||||
 | 
					                </button>
 | 
				
			||||||
 | 
					              </div>
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
          </div>
 | 
					          </div>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
      </div>
 | 
					        <div class="cell">
 | 
				
			||||||
      <div class="cell">
 | 
					          <div class="field" v-if="isMultiPlayer">
 | 
				
			||||||
        <div class="field" v-if="isMultiPlayer">
 | 
					            <label class="label">{{ $t('players-number') }}</label>
 | 
				
			||||||
          <label class="label">{{ $t('players-number') }}</label>
 | 
					            <div class="control">
 | 
				
			||||||
          <div class="control">
 | 
					              <div class="buttons has-addons">
 | 
				
			||||||
            <div class="buttons has-addons">
 | 
					                <button
 | 
				
			||||||
              <button
 | 
					                  class="button is-primary"
 | 
				
			||||||
                class="button is-primary"
 | 
					                  @click="options.numPlayers--"
 | 
				
			||||||
                @click="options.numPlayers--"
 | 
					                  :disabled="options.numPlayers <= 2"
 | 
				
			||||||
                :disabled="options.numPlayers <= 2"
 | 
					                >
 | 
				
			||||||
              >
 | 
					                  -
 | 
				
			||||||
                -
 | 
					                </button>
 | 
				
			||||||
              </button>
 | 
					                <button class="button is-primary" disabled>{{ options.numPlayers }}</button>
 | 
				
			||||||
              <button class="button is-primary" disabled>{{ options.numPlayers }}</button>
 | 
					                <button
 | 
				
			||||||
              <button
 | 
					                  class="button is-primary"
 | 
				
			||||||
                class="button is-primary"
 | 
					                  @click="options.numPlayers++"
 | 
				
			||||||
                @click="options.numPlayers++"
 | 
					                  :disabled="options.numPlayers >= 4"
 | 
				
			||||||
                :disabled="options.numPlayers >= 4"
 | 
					                >
 | 
				
			||||||
              >
 | 
					                  +
 | 
				
			||||||
                +
 | 
					                </button>
 | 
				
			||||||
              </button>
 | 
					              </div>
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
          </div>
 | 
					          </div>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
      </div>
 | 
					        <div class="cell">
 | 
				
			||||||
      <div class="cell">
 | 
					          <div class="field" v-if="isMultiPlayer">
 | 
				
			||||||
        <div class="field" v-if="isMultiPlayer">
 | 
					            <div class="control">
 | 
				
			||||||
          <div class="control">
 | 
					              <label for="teamed" class="checkbox">
 | 
				
			||||||
            <label for="teamed" class="checkbox">
 | 
					                <input v-model="options.teamed" name="teamed" type="checkbox" />
 | 
				
			||||||
              <input v-model="options.teamed" name="teamed" type="checkbox" />
 | 
					                {{ $t('crossed-game-teamed') }}
 | 
				
			||||||
              {{ $t('crossed-game-teamed') }}
 | 
					              </label>
 | 
				
			||||||
            </label>
 | 
					            </div>
 | 
				
			||||||
          </div>
 | 
					          </div>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
    <div class="grid" v-if="info?.development">
 | 
					    <div class="flex-grid has-2-cols has-1-cols-mobile" v-if="info?.development">
 | 
				
			||||||
      <div class="cell">
 | 
					      <div class="grid">
 | 
				
			||||||
        <div class="field">
 | 
					        <div class="cell">
 | 
				
			||||||
          <label class="label">{{ $t('session-name') }}</label>
 | 
					          <div class="field">
 | 
				
			||||||
          <div class="control">
 | 
					            <label class="label">{{ $t('session-name') }}</label>
 | 
				
			||||||
            <input
 | 
					            <div class="control">
 | 
				
			||||||
              type="text"
 | 
					              <input
 | 
				
			||||||
              class="input"
 | 
					                type="text"
 | 
				
			||||||
              v-model="options.sessionName"
 | 
					                class="input"
 | 
				
			||||||
              placeholder="$t('session-name-placeholder')"
 | 
					                v-model="options.sessionName"
 | 
				
			||||||
            />
 | 
					                placeholder="$t('session-name-placeholder')"
 | 
				
			||||||
 | 
					              />
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
          </div>
 | 
					          </div>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										86
									
								
								src/components/MultiplayerSetupComponent.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								src/components/MultiplayerSetupComponent.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,86 @@
 | 
				
			|||||||
 | 
					<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>
 | 
				
			||||||
							
								
								
									
										69
									
								
								src/components/SessionBoxComponent.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								src/components/SessionBoxComponent.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,69 @@
 | 
				
			|||||||
 | 
					<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>
 | 
				
			||||||
@@ -102,7 +102,7 @@ if (info.tauri) {
 | 
				
			|||||||
                >Check for update</a
 | 
					                >Check for update</a
 | 
				
			||||||
              >
 | 
					              >
 | 
				
			||||||
              <hr class="navbar-divider" />
 | 
					              <hr class="navbar-divider" />
 | 
				
			||||||
              <div class="navbar-item">Version 0.2.0</div>
 | 
					              <div class="navbar-item">Version 0.2.1</div>
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
          </div>
 | 
					          </div>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										10
									
								
								src/detect.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/detect.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
				
			|||||||
 | 
					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,
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -16,6 +16,7 @@ import { DIRECTION_INDEXES, DIRECTIONS, ORIENTATION_ANGLES } from '@/common/cons
 | 
				
			|||||||
import type { OtherHand } from './OtherHand'
 | 
					import type { OtherHand } from './OtherHand'
 | 
				
			||||||
import { SoundManager } from '@/game/utilities/SoundManager'
 | 
					import { SoundManager } from '@/game/utilities/SoundManager'
 | 
				
			||||||
import { t } from '@/i18n'
 | 
					import { t } from '@/i18n'
 | 
				
			||||||
 | 
					import { gsap } from 'gsap'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class Board extends EventEmitter {
 | 
					export class Board extends EventEmitter {
 | 
				
			||||||
  private _scale: number = 1
 | 
					  private _scale: number = 1
 | 
				
			||||||
@@ -225,7 +226,7 @@ export class Board extends EventEmitter {
 | 
				
			|||||||
        const availableMoves = this.nextTileValidMoves(tileDto, move.type)
 | 
					        const availableMoves = this.nextTileValidMoves(tileDto, move.type)
 | 
				
			||||||
        const availablePositions = this.nextTileValidPoints(tileDto, move.type, availableMoves)
 | 
					        const availablePositions = this.nextTileValidPoints(tileDto, move.type, availableMoves)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let directionIndex = DIRECTIONS.indexOf(direction)
 | 
					        let directionIndex = DIRECTION_INDEXES[direction]
 | 
				
			||||||
        let availablePosition: [number, number] | undefined = availablePositions[directionIndex]
 | 
					        let availablePosition: [number, number] | undefined = availablePositions[directionIndex]
 | 
				
			||||||
        let endlessLoop: number = 0
 | 
					        let endlessLoop: number = 0
 | 
				
			||||||
        while (endlessLoop < 4 && availablePosition === undefined) {
 | 
					        while (endlessLoop < 4 && availablePosition === undefined) {
 | 
				
			||||||
@@ -234,7 +235,10 @@ export class Board extends EventEmitter {
 | 
				
			|||||||
          endlessLoop++
 | 
					          endlessLoop++
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        if (endlessLoop >= 4) {
 | 
					        if (endlessLoop >= 4) {
 | 
				
			||||||
          throw new Error('No available position')
 | 
					          console.log('availableMoves :>>', availableMoves)
 | 
				
			||||||
 | 
					          console.log('availablePositions :>>', availablePositions)
 | 
				
			||||||
 | 
					          // throw new Error('No available position')
 | 
				
			||||||
 | 
					          directionIndex = DIRECTION_INDEXES[direction]
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        direction = DIRECTIONS[directionIndex]
 | 
					        direction = DIRECTIONS[directionIndex]
 | 
				
			||||||
        isLeft ? (this.leftDirection = direction) : (this.rightDirection = direction)
 | 
					        isLeft ? (this.leftDirection = direction) : (this.rightDirection = direction)
 | 
				
			||||||
@@ -260,23 +264,37 @@ export class Board extends EventEmitter {
 | 
				
			|||||||
  async animateTile(tile: Tile, x: number, y: number, orientation: string, move: Movement) {
 | 
					  async animateTile(tile: Tile, x: number, y: number, orientation: string, move: Movement) {
 | 
				
			||||||
    const targetX = this.scaleX(x)
 | 
					    const targetX = this.scaleX(x)
 | 
				
			||||||
    const targetY = this.scaleY(y)
 | 
					    const targetY = this.scaleY(y)
 | 
				
			||||||
    const animation: AnimationOptions = {
 | 
					 | 
				
			||||||
      x: targetX,
 | 
					 | 
				
			||||||
      y: targetY,
 | 
					 | 
				
			||||||
      rotation: ORIENTATION_ANGLES[orientation],
 | 
					 | 
				
			||||||
      duration: 20,
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    const tempAlpha = tile.alpha
 | 
					    const tempAlpha = tile.alpha
 | 
				
			||||||
    tile.alpha = 0
 | 
					    tile.alpha = 0
 | 
				
			||||||
    const clonedTile = tile.clone()
 | 
					    const clonedTile = tile.clone()
 | 
				
			||||||
    clonedTile.addTo(this.tilesContainer)
 | 
					    clonedTile.addTo(this.tilesContainer)
 | 
				
			||||||
    const pos = this.getAnimationInitialPosition(move)
 | 
					    const pos = this.getAnimationInitialPosition(move)
 | 
				
			||||||
    clonedTile.setPosition(this.scaleX(pos.x), this.scaleY(pos.y))
 | 
					    clonedTile.setPosition(this.scaleX(pos.x), this.scaleY(pos.y))
 | 
				
			||||||
    await clonedTile.animateTo(animation)
 | 
					
 | 
				
			||||||
    clonedTile.removeFromParent()
 | 
					    return new Promise((resolve) => {
 | 
				
			||||||
    tile.setOrientation(orientation)
 | 
					      gsap.timeline({ repeat: 0 }).fromTo(
 | 
				
			||||||
    tile.setPosition(targetX, targetY)
 | 
					        clonedTile.getSprite(),
 | 
				
			||||||
    tile.alpha = tempAlpha
 | 
					        {
 | 
				
			||||||
 | 
					          x: this.scaleX(pos.x),
 | 
				
			||||||
 | 
					          y: this.scaleY(pos.y),
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          x: targetX,
 | 
				
			||||||
 | 
					          y: targetY,
 | 
				
			||||||
 | 
					          duration: 1,
 | 
				
			||||||
 | 
					          rotation: ORIENTATION_ANGLES[orientation],
 | 
				
			||||||
 | 
					          // rotate: ,
 | 
				
			||||||
 | 
					          onComplete: () => {
 | 
				
			||||||
 | 
					            clonedTile.removeFromParent()
 | 
				
			||||||
 | 
					            tile.setOrientation(orientation)
 | 
				
			||||||
 | 
					            tile.setPosition(targetX, targetY)
 | 
				
			||||||
 | 
					            tile.alpha = tempAlpha
 | 
				
			||||||
 | 
					            resolve('done')
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          ease: 'power4.out',
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  getAnimationInitialPosition(move: Movement): { x: number; y: number } {
 | 
					  getAnimationInitialPosition(move: Movement): { x: number; y: number } {
 | 
				
			||||||
@@ -345,6 +363,7 @@ export class Board extends EventEmitter {
 | 
				
			|||||||
    try {
 | 
					    try {
 | 
				
			||||||
      // const { tileDto: tileDto } = move
 | 
					      // const { tileDto: tileDto } = move
 | 
				
			||||||
      // const tile = this.getTileInHand(tileDto?.id ?? '')
 | 
					      // const tile = this.getTileInHand(tileDto?.id ?? '')
 | 
				
			||||||
 | 
					      this.interactionContainer.removeChildren()
 | 
				
			||||||
      this.movements.push(move)
 | 
					      this.movements.push(move)
 | 
				
			||||||
      if (tile === undefined) {
 | 
					      if (tile === undefined) {
 | 
				
			||||||
        return
 | 
					        return
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,5 @@
 | 
				
			|||||||
import type { Dimension } from '@/common/interfaces'
 | 
					import type { Dimension } from '@/common/interfaces'
 | 
				
			||||||
import { BlurFilter, Container, Graphics, Point, Text, TextStyle } from 'pixi.js'
 | 
					import { BlurFilter, Container, Graphics, Point, Text } from 'pixi.js'
 | 
				
			||||||
import { gsap } from 'gsap'
 | 
					import { gsap } from 'gsap'
 | 
				
			||||||
import { SoundManager } from '@/game/utilities/SoundManager'
 | 
					import { SoundManager } from '@/game/utilities/SoundManager'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -20,6 +20,8 @@ import Config from './Config'
 | 
				
			|||||||
import { createText, grayStyle } from './utilities/fonts'
 | 
					import { createText, grayStyle } from './utilities/fonts'
 | 
				
			||||||
import { t } from '@/i18n'
 | 
					import { t } from '@/i18n'
 | 
				
			||||||
import { DIRECTION_INDEXES, DIRECTIONS } from '@/common/constants'
 | 
					import { DIRECTION_INDEXES, DIRECTIONS } from '@/common/constants'
 | 
				
			||||||
 | 
					import { SoundManager } from './utilities/SoundManager'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class Game extends EventEmitter {
 | 
					export class Game extends EventEmitter {
 | 
				
			||||||
  public board!: Board
 | 
					  public board!: Board
 | 
				
			||||||
  public hand!: Hand
 | 
					  public hand!: Hand
 | 
				
			||||||
@@ -30,6 +32,7 @@ export class Game extends EventEmitter {
 | 
				
			|||||||
  private backgroundLayer: Container = new Container()
 | 
					  private backgroundLayer: Container = new Container()
 | 
				
			||||||
  private gameSummaryView!: GameSummayView
 | 
					  private gameSummaryView!: GameSummayView
 | 
				
			||||||
  private players: PlayerDto[] = []
 | 
					  private players: PlayerDto[] = []
 | 
				
			||||||
 | 
					  private soundManager: SoundManager = new SoundManager()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  constructor(
 | 
					  constructor(
 | 
				
			||||||
    private options: MatchSessionOptions,
 | 
					    private options: MatchSessionOptions,
 | 
				
			||||||
@@ -197,7 +200,6 @@ export class Game extends EventEmitter {
 | 
				
			|||||||
      return
 | 
					      return
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    const freeEnds = this.board.freeEnds
 | 
					    const freeEnds = this.board.freeEnds
 | 
				
			||||||
    console.log('freeEnds :>> ', freeEnds, this.playerId)
 | 
					 | 
				
			||||||
    const move: Movement = {
 | 
					    const move: Movement = {
 | 
				
			||||||
      id: '',
 | 
					      id: '',
 | 
				
			||||||
      tile,
 | 
					      tile,
 | 
				
			||||||
@@ -215,8 +217,6 @@ export class Game extends EventEmitter {
 | 
				
			|||||||
        dirIndex += 1
 | 
					        dirIndex += 1
 | 
				
			||||||
        safeCount -= 1
 | 
					        safeCount -= 1
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      console.log('validMoves :>> ', validMoves)
 | 
					 | 
				
			||||||
      console.log('validPoints :>> ', validPoints)
 | 
					 | 
				
			||||||
      const validPoint = validPoints[dirIndex % 4]
 | 
					      const validPoint = validPoints[dirIndex % 4]
 | 
				
			||||||
      if (validPoint !== undefined) {
 | 
					      if (validPoint !== undefined) {
 | 
				
			||||||
        move.x = validPoint[0]
 | 
					        move.x = validPoint[0]
 | 
				
			||||||
@@ -227,6 +227,7 @@ export class Game extends EventEmitter {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
    this.currentMove = move
 | 
					    this.currentMove = move
 | 
				
			||||||
    this.board.updateBoard(move, this.hand.tileMoved(tile))
 | 
					    this.board.updateBoard(move, this.hand.tileMoved(tile))
 | 
				
			||||||
 | 
					    this.hand.afterMove()
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private async sendPassEvent() {
 | 
					  private async sendPassEvent() {
 | 
				
			||||||
@@ -236,7 +237,7 @@ export class Game extends EventEmitter {
 | 
				
			|||||||
      playerId: this.playerId,
 | 
					      playerId: this.playerId,
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    this.socketService &&
 | 
					    this.socketService &&
 | 
				
			||||||
      this.socketService.sendMessage('client:player-move', {
 | 
					      this.socketService.send('client:player-move', {
 | 
				
			||||||
        sessionId: this.sessionId,
 | 
					        sessionId: this.sessionId,
 | 
				
			||||||
        move: move,
 | 
					        move: move,
 | 
				
			||||||
      })
 | 
					      })
 | 
				
			||||||
@@ -287,6 +288,7 @@ export class Game extends EventEmitter {
 | 
				
			|||||||
      this.hand.setActive(true)
 | 
					      this.hand.setActive(true)
 | 
				
			||||||
      this.hand.prepareForMove(this.board.count === 0, this.board.freeEnds)
 | 
					      this.hand.prepareForMove(this.board.count === 0, this.board.freeEnds)
 | 
				
			||||||
      this.board.setPlayerTurn(currentPlayer)
 | 
					      this.board.setPlayerTurn(currentPlayer)
 | 
				
			||||||
 | 
					      this.soundManager.play('snd-ding-1')
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      this.board.setServerPlayerTurn(currentPlayer)
 | 
					      this.board.setServerPlayerTurn(currentPlayer)
 | 
				
			||||||
      this.highLightPlayer(currentPlayer)
 | 
					      this.highLightPlayer(currentPlayer)
 | 
				
			||||||
@@ -323,17 +325,20 @@ export class Game extends EventEmitter {
 | 
				
			|||||||
    this.board.on('game:tile-animation-ended', async (tile) => {
 | 
					    this.board.on('game:tile-animation-ended', async (tile) => {
 | 
				
			||||||
      if (tile !== null && tile !== undefined && tile.playerId === this.playerId) {
 | 
					      if (tile !== null && tile !== undefined && tile.playerId === this.playerId) {
 | 
				
			||||||
        this.socketService &&
 | 
					        this.socketService &&
 | 
				
			||||||
          this.socketService.sendMessage('client:player-move', {
 | 
					          this.socketService.send('client:player-move', {
 | 
				
			||||||
            sessionId: this.sessionId,
 | 
					            sessionId: this.sessionId,
 | 
				
			||||||
            move: this.currentMove,
 | 
					            move: this.currentMove,
 | 
				
			||||||
          })
 | 
					          })
 | 
				
			||||||
      } else {
 | 
					        // this.socketService.sendMessage('client:player-move', {
 | 
				
			||||||
        this.socketService &&
 | 
					        //   sessionId: this.sessionId,
 | 
				
			||||||
          this.socketService.sendMessage('client:animation-ended', {
 | 
					        //   move: this.currentMove,
 | 
				
			||||||
            sessionId: this.sessionId,
 | 
					        // })
 | 
				
			||||||
            userId: this.playerId,
 | 
					 | 
				
			||||||
          })
 | 
					 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					      this.socketService &&
 | 
				
			||||||
 | 
					        this.socketService.sendMessage('client:animation-ended', {
 | 
				
			||||||
 | 
					          sessionId: this.sessionId,
 | 
				
			||||||
 | 
					          userId: this.playerId,
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -18,6 +18,7 @@ import { createText, playerNameText, whiteStyle } from './utilities/fonts'
 | 
				
			|||||||
import Config from '@/game/Config'
 | 
					import Config from '@/game/Config'
 | 
				
			||||||
import { TimerText } from './TimerText'
 | 
					import { TimerText } from './TimerText'
 | 
				
			||||||
import { Button } from './Button'
 | 
					import { Button } from './Button'
 | 
				
			||||||
 | 
					import gsap from 'gsap'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class Hand extends EventEmitter {
 | 
					export class Hand extends EventEmitter {
 | 
				
			||||||
  tiles: Tile[] = []
 | 
					  tiles: Tile[] = []
 | 
				
			||||||
@@ -112,10 +113,16 @@ export class Hand extends EventEmitter {
 | 
				
			|||||||
      this.buttonPass.disabled = false
 | 
					      this.buttonPass.disabled = false
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    this.availableTiles.forEach((tile) => {
 | 
					    this.availableTiles.forEach((tile) => {
 | 
				
			||||||
      tile.animateTo({
 | 
					      gsap.to(tile.getSprite(), {
 | 
				
			||||||
        x: tile.x,
 | 
					        duration: 0.3,
 | 
				
			||||||
        y: tile.y - 10,
 | 
					        y: tile.y - 10,
 | 
				
			||||||
 | 
					        alpha: 1,
 | 
				
			||||||
 | 
					        ease: 'power4.out',
 | 
				
			||||||
      })
 | 
					      })
 | 
				
			||||||
 | 
					      // tile.animateTo({
 | 
				
			||||||
 | 
					      //   x: tile.x,
 | 
				
			||||||
 | 
					      //   y: tile.y - 10,
 | 
				
			||||||
 | 
					      // })
 | 
				
			||||||
      tile.interactive = true
 | 
					      tile.interactive = true
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
    if (this.timer) {
 | 
					    if (this.timer) {
 | 
				
			||||||
@@ -146,15 +153,21 @@ export class Hand extends EventEmitter {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  afterMove() {
 | 
					  afterMove() {
 | 
				
			||||||
    this.availableTiles.forEach((tile) => {
 | 
					 | 
				
			||||||
      tile.animateTo({
 | 
					 | 
				
			||||||
        x: tile.x,
 | 
					 | 
				
			||||||
        y: tile.y + 10,
 | 
					 | 
				
			||||||
      })
 | 
					 | 
				
			||||||
      tile.setPosition(tile.x, tile.y + 10)
 | 
					 | 
				
			||||||
      tile.interactive = false
 | 
					 | 
				
			||||||
    })
 | 
					 | 
				
			||||||
    this.timer && this.timer.reset()
 | 
					    this.timer && this.timer.reset()
 | 
				
			||||||
 | 
					    this.availableTiles.forEach((tile) => {
 | 
				
			||||||
 | 
					      if (tile.selected) return
 | 
				
			||||||
 | 
					      gsap.to(tile.getSprite(), {
 | 
				
			||||||
 | 
					        duration: 0.3,
 | 
				
			||||||
 | 
					        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)
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  hasMoves(tile: TileDto, freeEnds?: [number, number]): boolean {
 | 
					  hasMoves(tile: TileDto, freeEnds?: [number, number]): boolean {
 | 
				
			||||||
@@ -215,7 +228,12 @@ export class Hand extends EventEmitter {
 | 
				
			|||||||
    if (!tile) return
 | 
					    if (!tile) return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    this.afterMove()
 | 
					    this.afterMove()
 | 
				
			||||||
    this.tiles = this.tiles.filter((t) => t.id !== tileDto.id)
 | 
					    this.tiles = this.tiles.filter((t) => t.id !== tile.id)
 | 
				
			||||||
 | 
					    this.tiles.forEach((t) => {
 | 
				
			||||||
 | 
					      t.interactive = false
 | 
				
			||||||
 | 
					      t.alpha = 0.7
 | 
				
			||||||
 | 
					      t.clearFilters()
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    tile.interactive = false
 | 
					    tile.interactive = false
 | 
				
			||||||
    tile.clearFilters()
 | 
					    tile.clearFilters()
 | 
				
			||||||
@@ -290,9 +308,9 @@ export class Hand extends EventEmitter {
 | 
				
			|||||||
        newTile.setFilters([
 | 
					        newTile.setFilters([
 | 
				
			||||||
          new GlowFilter({
 | 
					          new GlowFilter({
 | 
				
			||||||
            distance: 10,
 | 
					            distance: 10,
 | 
				
			||||||
            outerStrength: 2,
 | 
					            outerStrength: 1,
 | 
				
			||||||
            innerStrength: 1,
 | 
					            innerStrength: 1,
 | 
				
			||||||
            color: 0xffffff,
 | 
					            color: 0xffff66,
 | 
				
			||||||
            quality: 0.5,
 | 
					            quality: 0.5,
 | 
				
			||||||
          }),
 | 
					          }),
 | 
				
			||||||
        ])
 | 
					        ])
 | 
				
			||||||
@@ -300,7 +318,7 @@ export class Hand extends EventEmitter {
 | 
				
			|||||||
      newTile.on('pointerout', () => {
 | 
					      newTile.on('pointerout', () => {
 | 
				
			||||||
        if (!newTile.selected) {
 | 
					        if (!newTile.selected) {
 | 
				
			||||||
          this.emit('tileHover')
 | 
					          this.emit('tileHover')
 | 
				
			||||||
          newTile.alpha = 0.7
 | 
					          newTile.alpha = 1
 | 
				
			||||||
          newTile.getSprite().filters = []
 | 
					          newTile.getSprite().filters = []
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      })
 | 
					      })
 | 
				
			||||||
@@ -319,18 +337,18 @@ export class Hand extends EventEmitter {
 | 
				
			|||||||
    this.scoreLayer.removeChildren()
 | 
					    this.scoreLayer.removeChildren()
 | 
				
			||||||
    const name = createText({
 | 
					    const name = createText({
 | 
				
			||||||
      text: this.player?.name ?? '-',
 | 
					      text: this.player?.name ?? '-',
 | 
				
			||||||
      x: 100,
 | 
					      x: 65,
 | 
				
			||||||
      y: 50,
 | 
					      y: 25,
 | 
				
			||||||
      style: playerNameText,
 | 
					      style: playerNameText,
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
    const text = createText({
 | 
					    const text = createText({
 | 
				
			||||||
      text: `${this.score}`,
 | 
					      text: `${this.score}`,
 | 
				
			||||||
      x: 100,
 | 
					      x: 65,
 | 
				
			||||||
      // x: this.width - 5,
 | 
					      // x: this.width - 5,
 | 
				
			||||||
      y: 80,
 | 
					      y: 75,
 | 
				
			||||||
      style: whiteStyle(36, 'bold'),
 | 
					      style: whiteStyle(60, 'bold'),
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
    text.anchor.set(1, 0.5)
 | 
					    text.anchor.set(0.5, 0.5)
 | 
				
			||||||
    this.scoreLayer.addChild(name)
 | 
					    this.scoreLayer.addChild(name)
 | 
				
			||||||
    this.scoreLayer.addChild(text)
 | 
					    this.scoreLayer.addChild(text)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,7 +9,7 @@ export abstract class SpriteBase {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  constructor(
 | 
					  constructor(
 | 
				
			||||||
    protected ticker?: Ticker,
 | 
					    protected ticker?: Ticker,
 | 
				
			||||||
    protected scale: number = 1
 | 
					    protected scale: number = 1,
 | 
				
			||||||
  ) {
 | 
					  ) {
 | 
				
			||||||
    this.ticker = ticker
 | 
					    this.ticker = ticker
 | 
				
			||||||
    this.scale = scale
 | 
					    this.scale = scale
 | 
				
			||||||
@@ -84,58 +84,6 @@ export abstract class SpriteBase {
 | 
				
			|||||||
    this.sprite.filters = []
 | 
					    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) {
 | 
					  addTo(container: any) {
 | 
				
			||||||
    this.container = container
 | 
					    this.container = container
 | 
				
			||||||
    container.addChild(this.sprite)
 | 
					    container.addChild(this.sprite)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,9 +2,10 @@ import { Container, Point, Text, TextStyle } from 'pixi.js'
 | 
				
			|||||||
import { createText, timerStyle } from './utilities/fonts'
 | 
					import { createText, timerStyle } from './utilities/fonts'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { gsap } from 'gsap'
 | 
					import { gsap } from 'gsap'
 | 
				
			||||||
 | 
					import { Timer } from './utilities/Timer'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class TimerText extends Container {
 | 
					export class TimerText extends Container {
 | 
				
			||||||
  private intervalHandle?: any
 | 
					  private timer: Timer
 | 
				
			||||||
  private text?: Text
 | 
					  private text?: Text
 | 
				
			||||||
  animation: boolean = false
 | 
					  animation: boolean = false
 | 
				
			||||||
  textStyle: TextStyle = timerStyle()
 | 
					  textStyle: TextStyle = timerStyle()
 | 
				
			||||||
@@ -16,41 +17,35 @@ export class TimerText extends Container {
 | 
				
			|||||||
    private point: Point,
 | 
					    private point: Point,
 | 
				
			||||||
  ) {
 | 
					  ) {
 | 
				
			||||||
    super()
 | 
					    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.countdown = seconds
 | 
				
			||||||
    this.render()
 | 
					    this.render()
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  reset() {
 | 
					  reset() {
 | 
				
			||||||
    this.stop()
 | 
					    this.timer.stop()
 | 
				
			||||||
 | 
					    this.timer.reset()
 | 
				
			||||||
    this.countdown = this.seconds
 | 
					    this.countdown = this.seconds
 | 
				
			||||||
    this.isInitiated = false
 | 
					    this.isInitiated = false
 | 
				
			||||||
    this.render()
 | 
					    this.render()
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  start() {
 | 
					  start() {
 | 
				
			||||||
    clearInterval(this.intervalHandle)
 | 
					    this.timer.reset()
 | 
				
			||||||
    this.isInitiated = true
 | 
					    this.timer.start()
 | 
				
			||||||
    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() {
 | 
					  private render() {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -15,6 +15,7 @@ export class Timer extends EventEmitter {
 | 
				
			|||||||
      this.countdown--
 | 
					      this.countdown--
 | 
				
			||||||
      if (this.countdown === 0) {
 | 
					      if (this.countdown === 0) {
 | 
				
			||||||
        clearInterval(this.intervalHandle)
 | 
					        clearInterval(this.intervalHandle)
 | 
				
			||||||
 | 
					        console.log('tic')
 | 
				
			||||||
        this.emit('timeout')
 | 
					        this.emit('timeout')
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
        this.emit('tick', this.countdown)
 | 
					        this.emit('tick', this.countdown)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,11 +14,11 @@
 | 
				
			|||||||
  "scoreboard": "Scoreboard",
 | 
					  "scoreboard": "Scoreboard",
 | 
				
			||||||
  "available-sessions": "Available Sessions",
 | 
					  "available-sessions": "Available Sessions",
 | 
				
			||||||
  "no-sessions-available": "No sessions available",
 | 
					  "no-sessions-available": "No sessions available",
 | 
				
			||||||
  "id-session-_id": "ID: {0}",
 | 
					  "id-session-_id": "ID:",
 | 
				
			||||||
  "players-session-players-length": "Players: {0}",
 | 
					  "players-session-players-length": "Players:",
 | 
				
			||||||
  "copy": "Copy",
 | 
					  "copy": "Copy",
 | 
				
			||||||
  "seed-session-seed": "Seed: {0}",
 | 
					  "seed-session-seed": "Seed:",
 | 
				
			||||||
  "status-session-status": "Status: {0}",
 | 
					  "status-session-status": "Status:",
 | 
				
			||||||
  "delete": "Delete",
 | 
					  "delete": "Delete",
 | 
				
			||||||
  "join": "Join",
 | 
					  "join": "Join",
 | 
				
			||||||
  "welcome-to-the-user-username-s-home-page": "Welcome to the {0}'s Home Page",
 | 
					  "welcome-to-the-user-username-s-home-page": "Welcome to the {0}'s Home Page",
 | 
				
			||||||
@@ -35,7 +35,7 @@
 | 
				
			|||||||
  "crossed-game-teamed": "Crossed game",
 | 
					  "crossed-game-teamed": "Crossed game",
 | 
				
			||||||
  "create-match-session": "Create Match Session",
 | 
					  "create-match-session": "Create Match Session",
 | 
				
			||||||
  "ready": "Ready",
 | 
					  "ready": "Ready",
 | 
				
			||||||
  "unready": "Unready",
 | 
					  "unready": "Not ready",
 | 
				
			||||||
  "start": "Start",
 | 
					  "start": "Start",
 | 
				
			||||||
  "cancel": "Cancel",
 | 
					  "cancel": "Cancel",
 | 
				
			||||||
  "game": {
 | 
					  "game": {
 | 
				
			||||||
@@ -59,16 +59,18 @@
 | 
				
			|||||||
  "n-rounds": "One Round|{count} Rounds",
 | 
					  "n-rounds": "One Round|{count} Rounds",
 | 
				
			||||||
  "n-of-m-rounds": "{0} of {1} Rounds",
 | 
					  "n-of-m-rounds": "{0} of {1} Rounds",
 | 
				
			||||||
  "create-session": "Create Session",
 | 
					  "create-session": "Create Session",
 | 
				
			||||||
  "join-a-multiplayer-session": "Join a Multiplayer Session (No sessions)|Join a Multiplayer Session ({count})|Join a Multiplayer Session ({count})",
 | 
					  "join-a-multiplayer-session": "Multiplayer (0)|Multiplayer ({count})|Multiplayer ({count})",
 | 
				
			||||||
  "tournaments": "Tournaments",
 | 
					  "tournaments": "Tournaments",
 | 
				
			||||||
  "start-game": "Start Game",
 | 
					  "start-game": "Start Game",
 | 
				
			||||||
  "player": "Player",
 | 
					  "player": "Player",
 | 
				
			||||||
  "final-score": "Final Score",
 | 
					  "final-score": "Final Score",
 | 
				
			||||||
  "round-index": "Round #{0}",
 | 
					  "round-index": "Round #{0}",
 | 
				
			||||||
  "first-actor-to-win-this-options-wintarget-this-options-wintype": "First {0} to win {1}",
 | 
					  "first-actor-to-win-this-options-wintarget-this-options-wintype": "First {0} to reach {1}",
 | 
				
			||||||
  "team": "team",
 | 
					  "team": "team",
 | 
				
			||||||
  "winner-name": "Winner: {0}",
 | 
					  "winner-name": "Winner: {0}",
 | 
				
			||||||
  "blocked": "Blocked",
 | 
					  "blocked": "Blocked",
 | 
				
			||||||
  "round-summary": "Round Summary",
 | 
					  "round-summary": "Round Summary",
 | 
				
			||||||
  "match-finished": "Match Finished"
 | 
					  "match-finished": "Match Finished",
 | 
				
			||||||
 | 
					  "host-session-host": "Host:",
 | 
				
			||||||
 | 
					  "players": "Players"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,10 +12,10 @@
 | 
				
			|||||||
  "scoreboard": "Marcador",
 | 
					  "scoreboard": "Marcador",
 | 
				
			||||||
  "winner": "Ganador",
 | 
					  "winner": "Ganador",
 | 
				
			||||||
  "final-scoreboard": "Puntaje Final",
 | 
					  "final-scoreboard": "Puntaje Final",
 | 
				
			||||||
  "id-session-_id": "ID: {0}",
 | 
					  "id-session-_id": "ID:",
 | 
				
			||||||
  "seed-placeholder": "¡Escriba la semilla de la sesión aquí!",
 | 
					  "seed-placeholder": "¡Escriba la semilla de la sesión aquí!",
 | 
				
			||||||
  "session-name-placeholder": "Nombre de la sesión",
 | 
					  "session-name-placeholder": "Nombre de la sesión",
 | 
				
			||||||
  "status-session-status": "Estado: {0}",
 | 
					  "status-session-status": "Estado:",
 | 
				
			||||||
  "unready": "No preparado",
 | 
					  "unready": "No preparado",
 | 
				
			||||||
  "welcome-to-the-user-username-s-home-page": "Bienvenido a la página de inicio de {0}",
 | 
					  "welcome-to-the-user-username-s-home-page": "Bienvenido a la página de inicio de {0}",
 | 
				
			||||||
  "available-sessions": "Sesiones disponibles",
 | 
					  "available-sessions": "Sesiones disponibles",
 | 
				
			||||||
@@ -38,11 +38,11 @@
 | 
				
			|||||||
  "join": "Unirse",
 | 
					  "join": "Unirse",
 | 
				
			||||||
  "name": "Nombre",
 | 
					  "name": "Nombre",
 | 
				
			||||||
  "no-sessions-available": "No hay sesiones disponibles",
 | 
					  "no-sessions-available": "No hay sesiones disponibles",
 | 
				
			||||||
  "players-session-players-length": "Jugadores: {0}",
 | 
					  "players-session-players-length": "Jugadores:",
 | 
				
			||||||
  "ready": "Listo",
 | 
					  "ready": "Listo",
 | 
				
			||||||
  "red-fabric": "Tela roja",
 | 
					  "red-fabric": "Tela roja",
 | 
				
			||||||
  "seed": "Semilla",
 | 
					  "seed": "Semilla",
 | 
				
			||||||
  "seed-session-seed": "Semilla: {0}",
 | 
					  "seed-session-seed": "Semilla:",
 | 
				
			||||||
  "start": "Comenzar",
 | 
					  "start": "Comenzar",
 | 
				
			||||||
  "yellow-fabric": "Tela amarilla",
 | 
					  "yellow-fabric": "Tela amarilla",
 | 
				
			||||||
  "back": "Volver",
 | 
					  "back": "Volver",
 | 
				
			||||||
@@ -70,5 +70,7 @@
 | 
				
			|||||||
  "blocked": "Cerrado",
 | 
					  "blocked": "Cerrado",
 | 
				
			||||||
  "round-summary": "Resumen de la ronda",
 | 
					  "round-summary": "Resumen de la ronda",
 | 
				
			||||||
  "match-finished": "Partida terminado",
 | 
					  "match-finished": "Partida terminado",
 | 
				
			||||||
  "team": "equipo"
 | 
					  "team": "equipo",
 | 
				
			||||||
 | 
					  "host-session-host": "Creada por:",
 | 
				
			||||||
 | 
					  "players": "Jugadores"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -105,4 +105,16 @@ 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
 | 
					export default router
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -50,9 +50,10 @@ export class SocketIoClientService extends ServiceBase {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  addEvents(): void {
 | 
					  addEvents(): void {
 | 
				
			||||||
    this.socket.on('disconnect', () => {
 | 
					    this.socket.on('disconnect', (reason) => {
 | 
				
			||||||
      this.isConnected = false
 | 
					      this.isConnected = false
 | 
				
			||||||
      console.log('SOCKET: Disconnected from server')
 | 
					      console.log('SOCKET: Disconnected from server')
 | 
				
			||||||
 | 
					      console.log(`Reason: ${reason}`)
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    this.socket.on('reconnect', () => {
 | 
					    this.socket.on('reconnect', () => {
 | 
				
			||||||
@@ -87,6 +88,15 @@ 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 {
 | 
					  sendMessage(event: string, data: any): void {
 | 
				
			||||||
    if (this.isConnected) {
 | 
					    if (this.isConnected) {
 | 
				
			||||||
      this.socket?.emit('client:event', { event, data })
 | 
					      this.socket?.emit('client:event', { event, data })
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -23,7 +23,6 @@ if (!playerState?.value) {
 | 
				
			|||||||
function makeMove(move: any) {
 | 
					function makeMove(move: any) {
 | 
				
			||||||
  moveToMake.value = move
 | 
					  moveToMake.value = move
 | 
				
			||||||
  canMakeMove.value = false
 | 
					  canMakeMove.value = false
 | 
				
			||||||
  console.log('makemove :>> ', move)
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
onBeforeUnmount(() => {
 | 
					onBeforeUnmount(() => {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,6 +13,8 @@ import MatchConfiguration from '@/components/MatchConfiguration.vue'
 | 
				
			|||||||
import { useI18n } from 'vue-i18n'
 | 
					import { useI18n } from 'vue-i18n'
 | 
				
			||||||
import { SessionExpiredError } from '@/common/errors/SessionExpiredError'
 | 
					import { SessionExpiredError } from '@/common/errors/SessionExpiredError'
 | 
				
			||||||
import type { AuthenticationService } from '@/services/AuthenticationService'
 | 
					import type { AuthenticationService } from '@/services/AuthenticationService'
 | 
				
			||||||
 | 
					import SessionBoxComponent from '@/components/SessionBoxComponent.vue'
 | 
				
			||||||
 | 
					import MultiplayerSetupComponent from '@/components/MultiplayerSetupComponent.vue'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
let teamedWith = ref<string | undefined>(undefined)
 | 
					let teamedWith = ref<string | undefined>(undefined)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -31,8 +33,7 @@ const authService: AuthenticationService = inject<AuthenticationService>(
 | 
				
			|||||||
  'auth',
 | 
					  'auth',
 | 
				
			||||||
) as AuthenticationService
 | 
					) as AuthenticationService
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const { sessionState, isSessionStarted, playerState, amIHost, readyForStart } =
 | 
					const { sessionState, isSessionStarted, playerState, amIHost } = storeToRefs(gameStore)
 | 
				
			||||||
  storeToRefs(gameStore)
 | 
					 | 
				
			||||||
const { gameOptions } = storeToRefs(gameOptionsStore)
 | 
					const { gameOptions } = storeToRefs(gameOptionsStore)
 | 
				
			||||||
const { updateSessionState, updatePlayerState, updateGameState } = gameStore
 | 
					const { updateSessionState, updatePlayerState, updateGameState } = gameStore
 | 
				
			||||||
const { t } = useI18n()
 | 
					const { t } = useI18n()
 | 
				
			||||||
@@ -43,17 +44,13 @@ eventBus.subscribe('window-before-unload', () => {
 | 
				
			|||||||
})
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async function createMatch(options: MatchSessionOptions) {
 | 
					async function createMatch(options: MatchSessionOptions) {
 | 
				
			||||||
  logger.debug('Creating match')
 | 
					 | 
				
			||||||
  await socketService.connect()
 | 
					  await socketService.connect()
 | 
				
			||||||
  gameOptions.value = options
 | 
					  gameOptions.value = options
 | 
				
			||||||
  const sessionId = await gameService.createMatchSession(options)
 | 
					  const sessionId = await gameService.createMatchSession(options)
 | 
				
			||||||
  socketService.joinRoom(`room-${sessionId}`)
 | 
					  socketService.joinRoom(`room-${sessionId}`)
 | 
				
			||||||
  logger.debug('Match created successfully')
 | 
					 | 
				
			||||||
  // router.push({ name: 'match', params: { id } })
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async function setPlayerReady() {
 | 
					async function setPlayerReady() {
 | 
				
			||||||
  logger.debug('Starting game')
 | 
					 | 
				
			||||||
  if (!sessionState.value) {
 | 
					  if (!sessionState.value) {
 | 
				
			||||||
    logger.error('No session found')
 | 
					    logger.error('No session found')
 | 
				
			||||||
    return
 | 
					    return
 | 
				
			||||||
@@ -68,14 +65,15 @@ async function setPlayerReady() {
 | 
				
			|||||||
  })
 | 
					  })
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async function startMatch() {
 | 
					async function startMatch(teamedWith?: string) {
 | 
				
			||||||
 | 
					  console.log('teamedWith :>> ', teamedWith)
 | 
				
			||||||
  const sessionId = sessionState?.value?.id
 | 
					  const sessionId = sessionState?.value?.id
 | 
				
			||||||
  const playerId = playerState?.value?.id
 | 
					  const playerId = playerState?.value?.id
 | 
				
			||||||
  if (sessionId) {
 | 
					  if (sessionId) {
 | 
				
			||||||
    await socketService.sendMessageWithAck('client:start-session', {
 | 
					    await socketService.sendMessageWithAck('client:start-session', {
 | 
				
			||||||
      sessionId: sessionId,
 | 
					      sessionId: sessionId,
 | 
				
			||||||
      playerId: playerId,
 | 
					      playerId: playerId,
 | 
				
			||||||
      teamedWith: teamedWith.value,
 | 
					      teamedWith: teamedWith,
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -117,20 +115,6 @@ 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(
 | 
					const isMultiplayer = computed(
 | 
				
			||||||
  () => (sessionState?.value?.options?.numPlayers || gameOptions.value?.numPlayers || 0) > 1,
 | 
					  () => (sessionState?.value?.options?.numPlayers || gameOptions.value?.numPlayers || 0) > 1,
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
@@ -167,18 +151,18 @@ function copy(sessionSeed: string) {
 | 
				
			|||||||
  copyToclipboard(sessionSeed)
 | 
					  copyToclipboard(sessionSeed)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
let selectedTab = ref('create-tab')
 | 
					let selectedTab = ref('join-tab')
 | 
				
			||||||
const isCreateTab = computed(() => selectedTab.value === 'create-tab')
 | 
					const isCreateTab = computed(() => selectedTab.value === 'create-tab')
 | 
				
			||||||
const isJoinTab = computed(() => selectedTab.value === 'join-tab')
 | 
					const isJoinTab = computed(() => selectedTab.value === 'join-tab')
 | 
				
			||||||
const isTournamentTab = computed(() => selectedTab.value === 'torunaments-tab')
 | 
					const isTournamentTab = computed(() => selectedTab.value === 'torunaments-tab')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const tabs = computed<any[]>((): any => [
 | 
					const tabs = computed<any[]>((): any => [
 | 
				
			||||||
  { label: t('create-session'), id: 'create-tab', disabled: false },
 | 
					 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    label: t('join-a-multiplayer-session', matchSessions.value.length),
 | 
					    label: t('join-a-multiplayer-session', matchSessions.value?.length ?? 0),
 | 
				
			||||||
    id: 'join-tab',
 | 
					    id: 'join-tab',
 | 
				
			||||||
    disabled: matchSessions.value.length <= 0,
 | 
					    disabled: false, //matchSessions.value.length <= 0,
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
					  { label: t('create-session'), id: 'create-tab', disabled: false },
 | 
				
			||||||
  { label: t('tournaments'), id: 'torunaments-tab', disabled: true },
 | 
					  { label: t('tournaments'), id: 'torunaments-tab', disabled: true },
 | 
				
			||||||
])
 | 
					])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -229,38 +213,28 @@ async function onStartSingleMatch(options: MatchSessionOptions) {
 | 
				
			|||||||
        <section class="section available-sessions" v-if="isJoinTab">
 | 
					        <section class="section available-sessions" v-if="isJoinTab">
 | 
				
			||||||
          <div class="block">
 | 
					          <div class="block">
 | 
				
			||||||
            <div v-if="matchSessions.length === 0">
 | 
					            <div v-if="matchSessions.length === 0">
 | 
				
			||||||
              <p>{{ $t('no-sessions-available') }}</p>
 | 
					              <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>
 | 
				
			||||||
 | 
					              </div>
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
            <div v-else class="fixed-grid has-3-cols">
 | 
					            <div v-else>
 | 
				
			||||||
              <div class="grid">
 | 
					              <div class="buttons is-justify-content-center mt-6">
 | 
				
			||||||
                <div class="cell" v-for="session in matchSessions" :key="session.id">
 | 
					                <button class="button is-primary is-small" @click="() => tabClick(tabs[1])">
 | 
				
			||||||
                  <div class="card">
 | 
					                  {{ $t('create-session') }}
 | 
				
			||||||
                    <div class="card-content">
 | 
					                </button>
 | 
				
			||||||
                      <p class="title is-6">{{ session.name }}</p>
 | 
					              </div>
 | 
				
			||||||
                      <p>{{ $t('id-session-_id', [session._id]) }}</p>
 | 
					              <div class="fixed-grid has-3-cols">
 | 
				
			||||||
                      <p>{{ $t('players-session-players-length', [session.players.length]) }}</p>
 | 
					                <div class="grid">
 | 
				
			||||||
                      <p>
 | 
					                  <div class="cell" v-for="session in matchSessions" :key="session.id">
 | 
				
			||||||
                        {{ $t('seed-session-seed', [session.seed]) }}
 | 
					                    <SessionBoxComponent
 | 
				
			||||||
                        <button class="button is-small is-ghost" @click="() => copy(session.seed)">
 | 
					                      :session="session"
 | 
				
			||||||
                          {{ $t('copy') }}
 | 
					                      @join="joinMatch"
 | 
				
			||||||
                        </button>
 | 
					                      @delete="deleteMatch"
 | 
				
			||||||
                      </p>
 | 
					                      @copy="copy"
 | 
				
			||||||
                      <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>
 | 
					                </div>
 | 
				
			||||||
              </div>
 | 
					              </div>
 | 
				
			||||||
@@ -272,45 +246,12 @@ async function onStartSingleMatch(options: MatchSessionOptions) {
 | 
				
			|||||||
        <section class="section" v-if="isTournamentTab"></section>
 | 
					        <section class="section" v-if="isTournamentTab"></section>
 | 
				
			||||||
        <!-- Tournaments End -->
 | 
					        <!-- Tournaments End -->
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
      <div class="block" v-if="isSessionStarted && isMultiplayer">
 | 
					      <MultiplayerSetupComponent
 | 
				
			||||||
        <h2 class="title is-4">{{ sessionState?.name }}</h2>
 | 
					        v-if="isSessionStarted && isMultiplayer"
 | 
				
			||||||
        <h6 class="title is-size-5">Players</h6>
 | 
					        @ready="setPlayerReady"
 | 
				
			||||||
        <div v-for="player in sessionState?.players" :key="player.id">
 | 
					        @start="startMatch"
 | 
				
			||||||
          <p>{{ player.name }} ({{ player.ready ? 'Ready' : 'Not ready' }})</p>
 | 
					        @cancel="cancelMatch"
 | 
				
			||||||
        </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>
 | 
					    </section>
 | 
				
			||||||
  </div>
 | 
					  </div>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -52,45 +52,56 @@ async function login() {
 | 
				
			|||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
  <div class="login">
 | 
					  <div class="container login">
 | 
				
			||||||
    <h1 class="title">{{ $t('login') }}</h1>
 | 
					    <div class="is-flex is-justify-content-center is-align-items-center">
 | 
				
			||||||
    <div class="message is-danger">
 | 
					      <div class="login-form-container box is-no-shadow" style="width: 400px">
 | 
				
			||||||
      <div class="message-body" v-if="errorLogin">
 | 
					        <div class="message is-danger">
 | 
				
			||||||
        {{ $t('invalid-username-or-password') }}
 | 
					          <div class="message-body" v-if="errorLogin">
 | 
				
			||||||
 | 
					            {{ $t('invalid-username-or-password') }}
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					        <form class="form" @submit.prevent="login">
 | 
				
			||||||
 | 
					          <div class="field">
 | 
				
			||||||
 | 
					            <label class="label">{{ $t('username') }}</label>
 | 
				
			||||||
 | 
					            <div class="control">
 | 
				
			||||||
 | 
					              <input
 | 
				
			||||||
 | 
					                class="input"
 | 
				
			||||||
 | 
					                type="text"
 | 
				
			||||||
 | 
					                v-model="username"
 | 
				
			||||||
 | 
					                :placeholder="t('username-placeholder')"
 | 
				
			||||||
 | 
					              />
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					          <div class="field">
 | 
				
			||||||
 | 
					            <label class="label">{{ $t('password') }}</label>
 | 
				
			||||||
 | 
					            <div class="control">
 | 
				
			||||||
 | 
					              <input
 | 
				
			||||||
 | 
					                class="input"
 | 
				
			||||||
 | 
					                type="password"
 | 
				
			||||||
 | 
					                v-model="password"
 | 
				
			||||||
 | 
					                :placeholder="t('password-placeholder')"
 | 
				
			||||||
 | 
					              />
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          <div class="field is-grouped">
 | 
				
			||||||
 | 
					            <div class="control">
 | 
				
			||||||
 | 
					              <button class="button is-primary" type="submit">{{ $t('login-button') }}</button>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					        </form>
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
    <form class="form" @submit.prevent="login">
 | 
					 | 
				
			||||||
      <div class="field">
 | 
					 | 
				
			||||||
        <label class="label">{{ $t('username') }}</label>
 | 
					 | 
				
			||||||
        <div class="control">
 | 
					 | 
				
			||||||
          <input
 | 
					 | 
				
			||||||
            class="input"
 | 
					 | 
				
			||||||
            type="text"
 | 
					 | 
				
			||||||
            v-model="username"
 | 
					 | 
				
			||||||
            :placeholder="t('username-placeholder')"
 | 
					 | 
				
			||||||
          />
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
      </div>
 | 
					 | 
				
			||||||
      <div class="field">
 | 
					 | 
				
			||||||
        <label class="label">{{ $t('password') }}</label>
 | 
					 | 
				
			||||||
        <div class="control">
 | 
					 | 
				
			||||||
          <input
 | 
					 | 
				
			||||||
            class="input"
 | 
					 | 
				
			||||||
            type="password"
 | 
					 | 
				
			||||||
            v-model="password"
 | 
					 | 
				
			||||||
            :placeholder="t('password-placeholder')"
 | 
					 | 
				
			||||||
          />
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
      </div>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      <div class="field is-grouped">
 | 
					 | 
				
			||||||
        <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>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<style scoped>
 | 
				
			||||||
 | 
					.login,
 | 
				
			||||||
 | 
					.login > div {
 | 
				
			||||||
 | 
					  height: 100%;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.login-form-container {
 | 
				
			||||||
 | 
					  width: 400px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					</style>
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										1
									
								
								types/type.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								types/type.d.ts
									
									
									
									
										vendored
									
									
								
							@@ -6,4 +6,5 @@ declare module 'socket.io' {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
declare interface Window {
 | 
					declare interface Window {
 | 
				
			||||||
  __TAURI__: any
 | 
					  __TAURI__: any
 | 
				
			||||||
 | 
					  opera: any
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user