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