progress
This commit is contained in:
		
							
								
								
									
										
											BIN
										
									
								
								src/assets/images/backgrounds/bg-blue.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/assets/images/backgrounds/bg-blue.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 3.9 MiB  | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/images/backgrounds/bg-red.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/assets/images/backgrounds/bg-red.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 3.9 MiB  | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/images/backgrounds/bg-yellow.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/assets/images/backgrounds/bg-yellow.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 3.8 MiB  | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/sounds/intro.mp3
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/assets/sounds/intro.mp3
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								src/assets/sounds/move-1.mp3
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/assets/sounds/move-1.mp3
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								src/assets/sounds/move-2.mp3
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/assets/sounds/move-2.mp3
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								src/assets/sounds/move-3.mp3
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/assets/sounds/move-3.mp3
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								src/assets/sounds/move-4.mp3
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/assets/sounds/move-4.mp3
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								src/assets/sounds/move-sprite.mp3
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/assets/sounds/move-sprite.mp3
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								src/assets/sounds/move-sprite.ogg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/assets/sounds/move-sprite.ogg
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							@@ -17,8 +17,13 @@ export function getColorBackground(container: Container, colorName: string, alph
 | 
			
		||||
export function createContainer(options: ContainerOptions) {
 | 
			
		||||
  const opts = { ...DEFAULT_CONTAINER_OPTIONS, ...options }
 | 
			
		||||
  const container = new Container()
 | 
			
		||||
  container.x = opts.x
 | 
			
		||||
  container.y = opts.y
 | 
			
		||||
  if (opts.parent) {
 | 
			
		||||
    opts.parent.addChild(container)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const rect = new Graphics().rect(opts.x, opts.y, opts.width, opts.height)
 | 
			
		||||
  const rect = new Graphics().rect(0, 0, opts.width, opts.height)
 | 
			
		||||
 | 
			
		||||
  if (opts.color) {
 | 
			
		||||
    rect.fill(opts.color)
 | 
			
		||||
@@ -29,19 +34,17 @@ export function createContainer(options: ContainerOptions) {
 | 
			
		||||
 | 
			
		||||
  rect.visible = opts.visible
 | 
			
		||||
  container.addChild(rect)
 | 
			
		||||
  if (opts.parent) {
 | 
			
		||||
    opts.parent.addChild(container)
 | 
			
		||||
  }
 | 
			
		||||
  return container
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function createButton(
 | 
			
		||||
  textStr: string,
 | 
			
		||||
  dimension: Dimension,
 | 
			
		||||
  action: Function,
 | 
			
		||||
export function createButton(options: {
 | 
			
		||||
  text: string
 | 
			
		||||
  dimension: Dimension
 | 
			
		||||
  action: Function
 | 
			
		||||
  parent?: Container
 | 
			
		||||
): Container {
 | 
			
		||||
  const { x, y, width, height } = dimension
 | 
			
		||||
}): Container {
 | 
			
		||||
  const { text: textStr, dimension, action, parent } = options
 | 
			
		||||
  const { x, y, width, height = 25 } = dimension
 | 
			
		||||
  const rectangle = new Graphics().roundRect(x, y, width + 4, height + 4, 5).fill(0xffff00)
 | 
			
		||||
  const text = new Text({
 | 
			
		||||
    text: textStr,
 | 
			
		||||
@@ -50,8 +53,8 @@ export function createButton(
 | 
			
		||||
      fontSize: 12,
 | 
			
		||||
      fontWeight: 'bold',
 | 
			
		||||
      fill: 0x121212,
 | 
			
		||||
      align: 'center'
 | 
			
		||||
    }
 | 
			
		||||
      align: 'center',
 | 
			
		||||
    },
 | 
			
		||||
  })
 | 
			
		||||
  text.anchor = 0.5
 | 
			
		||||
  const container = new Container()
 | 
			
		||||
 
 | 
			
		||||
@@ -34,7 +34,7 @@ export interface MatchSessionDto {
 | 
			
		||||
  maxPlayers: number
 | 
			
		||||
  numPlayers: number
 | 
			
		||||
  waitingSeconds: number
 | 
			
		||||
  scoreboard: Map<string, number>
 | 
			
		||||
  scoreboard: { id: string; name: string; score: number }[]
 | 
			
		||||
  matchWinner: PlayerDto | null
 | 
			
		||||
  matchInProgress: boolean
 | 
			
		||||
  playersReady: number
 | 
			
		||||
@@ -98,3 +98,20 @@ export interface AnimationOptions {
 | 
			
		||||
  width?: number
 | 
			
		||||
  height?: number
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface GameOptions {
 | 
			
		||||
  boardScale?: number
 | 
			
		||||
  handScale?: number
 | 
			
		||||
  width?: number
 | 
			
		||||
  height?: number
 | 
			
		||||
  background?: string
 | 
			
		||||
  teamed?: boolean
 | 
			
		||||
}
 | 
			
		||||
export interface GameSummary {
 | 
			
		||||
  gameId: string
 | 
			
		||||
  isBlocked: boolean
 | 
			
		||||
  isTied: boolean
 | 
			
		||||
  winner: PlayerDto
 | 
			
		||||
  score: { id: string; name: string; score: number }[]
 | 
			
		||||
  players?: PlayerDto[]
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										269
									
								
								src/common/summarymock.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										269
									
								
								src/common/summarymock.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,269 @@
 | 
			
		||||
export const summaryMock = {
 | 
			
		||||
  lastGame: {
 | 
			
		||||
    gameId: '6cd2b32c-185e-4869-a3f9-7a1a718a00f7',
 | 
			
		||||
    isBlocked: true,
 | 
			
		||||
    isTied: false,
 | 
			
		||||
    winner: {
 | 
			
		||||
      id: '23ed7e37-84e2-4e4e-b833-e97aef351f18',
 | 
			
		||||
      name: 'Bob (AI)',
 | 
			
		||||
      score: 14,
 | 
			
		||||
      hand: [
 | 
			
		||||
        {
 | 
			
		||||
          id: '69c6e996-7dd4-4565-a49c-96b7ba35db7d',
 | 
			
		||||
          flipped: false,
 | 
			
		||||
          revealed: true,
 | 
			
		||||
          playerId: '23ed7e37-84e2-4e4e-b833-e97aef351f18',
 | 
			
		||||
        },
 | 
			
		||||
      ],
 | 
			
		||||
      teamedWith: null,
 | 
			
		||||
      ready: true,
 | 
			
		||||
    },
 | 
			
		||||
    score: [
 | 
			
		||||
      {
 | 
			
		||||
        id: '668977662eb15e2ef3bdac3f',
 | 
			
		||||
        name: 'arhuako',
 | 
			
		||||
        score: 0,
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        id: '3494d8b9-6b15-49e5-8fec-15e2d6539f71',
 | 
			
		||||
        name: 'Alice (AI)',
 | 
			
		||||
        score: 0,
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        id: '23ed7e37-84e2-4e4e-b833-e97aef351f18',
 | 
			
		||||
        name: 'Bob (AI)',
 | 
			
		||||
        score: 14,
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        id: '2eda2a20-a97a-4310-a431-3aaef916e602',
 | 
			
		||||
        name: 'Charlie (AI)',
 | 
			
		||||
        score: 0,
 | 
			
		||||
      },
 | 
			
		||||
    ],
 | 
			
		||||
    players: [
 | 
			
		||||
      {
 | 
			
		||||
        id: '668977662eb15e2ef3bdac3f',
 | 
			
		||||
        name: 'arhuako',
 | 
			
		||||
        score: 0,
 | 
			
		||||
        hand: [
 | 
			
		||||
          {
 | 
			
		||||
            id: '93d523a2-ae4d-4b51-a7a7-2e740be2da14',
 | 
			
		||||
            flipped: false,
 | 
			
		||||
            revealed: true,
 | 
			
		||||
            playerId: '668977662eb15e2ef3bdac3f',
 | 
			
		||||
          },
 | 
			
		||||
        ],
 | 
			
		||||
        teamedWith: null,
 | 
			
		||||
        ready: true,
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        id: '3494d8b9-6b15-49e5-8fec-15e2d6539f71',
 | 
			
		||||
        name: 'Alice (AI)',
 | 
			
		||||
        score: 0,
 | 
			
		||||
        hand: [
 | 
			
		||||
          {
 | 
			
		||||
            id: 'cf9a04f9-3d8e-4feb-afa6-34a8a61ec014',
 | 
			
		||||
            flipped: false,
 | 
			
		||||
            revealed: true,
 | 
			
		||||
            playerId: '3494d8b9-6b15-49e5-8fec-15e2d6539f71',
 | 
			
		||||
          },
 | 
			
		||||
        ],
 | 
			
		||||
        teamedWith: null,
 | 
			
		||||
        ready: true,
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        id: '23ed7e37-84e2-4e4e-b833-e97aef351f18',
 | 
			
		||||
        name: 'Bob (AI)',
 | 
			
		||||
        score: 14,
 | 
			
		||||
        hand: [
 | 
			
		||||
          {
 | 
			
		||||
            id: '69c6e996-7dd4-4565-a49c-96b7ba35db7d',
 | 
			
		||||
            flipped: false,
 | 
			
		||||
            revealed: true,
 | 
			
		||||
            playerId: '23ed7e37-84e2-4e4e-b833-e97aef351f18',
 | 
			
		||||
          },
 | 
			
		||||
        ],
 | 
			
		||||
        teamedWith: null,
 | 
			
		||||
        ready: true,
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        id: '2eda2a20-a97a-4310-a431-3aaef916e602',
 | 
			
		||||
        name: 'Charlie (AI)',
 | 
			
		||||
        score: 0,
 | 
			
		||||
        hand: [
 | 
			
		||||
          {
 | 
			
		||||
            id: '22d0a60c-b9e9-43f6-804b-6899221de04d',
 | 
			
		||||
            flipped: false,
 | 
			
		||||
            revealed: true,
 | 
			
		||||
            playerId: '2eda2a20-a97a-4310-a431-3aaef916e602',
 | 
			
		||||
          },
 | 
			
		||||
        ],
 | 
			
		||||
        teamedWith: null,
 | 
			
		||||
        ready: true,
 | 
			
		||||
      },
 | 
			
		||||
    ],
 | 
			
		||||
  },
 | 
			
		||||
  sessionState: {
 | 
			
		||||
    id: '669524c8b78825b8f9ede908',
 | 
			
		||||
    name: 'Test #26',
 | 
			
		||||
    creator: '668977662eb15e2ef3bdac3f',
 | 
			
		||||
    players: [
 | 
			
		||||
      {
 | 
			
		||||
        id: '668977662eb15e2ef3bdac3f',
 | 
			
		||||
        name: 'arhuako',
 | 
			
		||||
        score: 0,
 | 
			
		||||
        hand: [],
 | 
			
		||||
        teamedWith: null,
 | 
			
		||||
        ready: false,
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        id: '3494d8b9-6b15-49e5-8fec-15e2d6539f71',
 | 
			
		||||
        name: 'Alice (AI)',
 | 
			
		||||
        score: 0,
 | 
			
		||||
        hand: [],
 | 
			
		||||
        teamedWith: null,
 | 
			
		||||
        ready: false,
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        id: '23ed7e37-84e2-4e4e-b833-e97aef351f18',
 | 
			
		||||
        name: 'Bob (AI)',
 | 
			
		||||
        score: 0,
 | 
			
		||||
        hand: [],
 | 
			
		||||
        teamedWith: null,
 | 
			
		||||
        ready: false,
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        id: '2eda2a20-a97a-4310-a431-3aaef916e602',
 | 
			
		||||
        name: 'Charlie (AI)',
 | 
			
		||||
        score: 0,
 | 
			
		||||
        hand: [],
 | 
			
		||||
        teamedWith: null,
 | 
			
		||||
        ready: false,
 | 
			
		||||
      },
 | 
			
		||||
    ],
 | 
			
		||||
    playersReady: 0,
 | 
			
		||||
    sessionInProgress: true,
 | 
			
		||||
    maxPlayers: 4,
 | 
			
		||||
    numPlayers: 4,
 | 
			
		||||
    waitingForPlayers: false,
 | 
			
		||||
    waitingSeconds: 0,
 | 
			
		||||
    seed: '1721050312717-840b4s07gb8-8d980dd8',
 | 
			
		||||
    mode: 'classic',
 | 
			
		||||
    pointsToWin: 50,
 | 
			
		||||
    status: 'in progress',
 | 
			
		||||
    scoreboard: [
 | 
			
		||||
      ['arhuako', 0],
 | 
			
		||||
      ['Alice (AI)', 0],
 | 
			
		||||
      ['Bob (AI)', 14],
 | 
			
		||||
      ['Charlie (AI)', 0],
 | 
			
		||||
    ],
 | 
			
		||||
    matchWinner: null,
 | 
			
		||||
    matchInProgress: true,
 | 
			
		||||
    gameSummaries: [
 | 
			
		||||
      {
 | 
			
		||||
        gameId: '6cd2b32c-185e-4869-a3f9-7a1a718a00f7',
 | 
			
		||||
        isBlocked: true,
 | 
			
		||||
        isTied: false,
 | 
			
		||||
        winner: {
 | 
			
		||||
          id: '23ed7e37-84e2-4e4e-b833-e97aef351f18',
 | 
			
		||||
          name: 'Bob (AI)',
 | 
			
		||||
          score: 14,
 | 
			
		||||
          hand: [
 | 
			
		||||
            {
 | 
			
		||||
              id: '69c6e996-7dd4-4565-a49c-96b7ba35db7d',
 | 
			
		||||
              flipped: false,
 | 
			
		||||
              revealed: true,
 | 
			
		||||
              playerId: '23ed7e37-84e2-4e4e-b833-e97aef351f18',
 | 
			
		||||
            },
 | 
			
		||||
          ],
 | 
			
		||||
          teamedWith: null,
 | 
			
		||||
          ready: true,
 | 
			
		||||
        },
 | 
			
		||||
        score: [
 | 
			
		||||
          {
 | 
			
		||||
            id: '668977662eb15e2ef3bdac3f',
 | 
			
		||||
            name: 'arhuako',
 | 
			
		||||
            score: 0,
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            id: '3494d8b9-6b15-49e5-8fec-15e2d6539f71',
 | 
			
		||||
            name: 'Alice (AI)',
 | 
			
		||||
            score: 0,
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            id: '23ed7e37-84e2-4e4e-b833-e97aef351f18',
 | 
			
		||||
            name: 'Bob (AI)',
 | 
			
		||||
            score: 14,
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            id: '2eda2a20-a97a-4310-a431-3aaef916e602',
 | 
			
		||||
            name: 'Charlie (AI)',
 | 
			
		||||
            score: 0,
 | 
			
		||||
          },
 | 
			
		||||
        ],
 | 
			
		||||
        players: [
 | 
			
		||||
          {
 | 
			
		||||
            id: '668977662eb15e2ef3bdac3f',
 | 
			
		||||
            name: 'arhuako',
 | 
			
		||||
            score: 0,
 | 
			
		||||
            hand: [
 | 
			
		||||
              {
 | 
			
		||||
                id: '93d523a2-ae4d-4b51-a7a7-2e740be2da14',
 | 
			
		||||
                flipped: false,
 | 
			
		||||
                revealed: true,
 | 
			
		||||
                playerId: '668977662eb15e2ef3bdac3f',
 | 
			
		||||
              },
 | 
			
		||||
            ],
 | 
			
		||||
            teamedWith: null,
 | 
			
		||||
            ready: true,
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            id: '3494d8b9-6b15-49e5-8fec-15e2d6539f71',
 | 
			
		||||
            name: 'Alice (AI)',
 | 
			
		||||
            score: 0,
 | 
			
		||||
            hand: [
 | 
			
		||||
              {
 | 
			
		||||
                id: 'cf9a04f9-3d8e-4feb-afa6-34a8a61ec014',
 | 
			
		||||
                flipped: false,
 | 
			
		||||
                revealed: true,
 | 
			
		||||
                playerId: '3494d8b9-6b15-49e5-8fec-15e2d6539f71',
 | 
			
		||||
              },
 | 
			
		||||
            ],
 | 
			
		||||
            teamedWith: null,
 | 
			
		||||
            ready: true,
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            id: '23ed7e37-84e2-4e4e-b833-e97aef351f18',
 | 
			
		||||
            name: 'Bob (AI)',
 | 
			
		||||
            score: 14,
 | 
			
		||||
            hand: [
 | 
			
		||||
              {
 | 
			
		||||
                id: '69c6e996-7dd4-4565-a49c-96b7ba35db7d',
 | 
			
		||||
                flipped: false,
 | 
			
		||||
                revealed: true,
 | 
			
		||||
                playerId: '23ed7e37-84e2-4e4e-b833-e97aef351f18',
 | 
			
		||||
              },
 | 
			
		||||
            ],
 | 
			
		||||
            teamedWith: null,
 | 
			
		||||
            ready: true,
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            id: '2eda2a20-a97a-4310-a431-3aaef916e602',
 | 
			
		||||
            name: 'Charlie (AI)',
 | 
			
		||||
            score: 0,
 | 
			
		||||
            hand: [
 | 
			
		||||
              {
 | 
			
		||||
                id: '22d0a60c-b9e9-43f6-804b-6899221de04d',
 | 
			
		||||
                flipped: false,
 | 
			
		||||
                revealed: true,
 | 
			
		||||
                playerId: '2eda2a20-a97a-4310-a431-3aaef916e602',
 | 
			
		||||
              },
 | 
			
		||||
            ],
 | 
			
		||||
            teamedWith: null,
 | 
			
		||||
            ready: true,
 | 
			
		||||
          },
 | 
			
		||||
        ],
 | 
			
		||||
      },
 | 
			
		||||
    ],
 | 
			
		||||
  },
 | 
			
		||||
}
 | 
			
		||||
@@ -5,13 +5,16 @@ import { Game } from '@/game/Game'
 | 
			
		||||
import { useGameStore } from '@/stores/game'
 | 
			
		||||
import { useEventBusStore } from '@/stores/eventBus'
 | 
			
		||||
import { storeToRefs } from 'pinia'
 | 
			
		||||
import { useGameOptionsStore } from '@/stores/gameOptions'
 | 
			
		||||
 | 
			
		||||
const socketService: any = inject('socket')
 | 
			
		||||
 | 
			
		||||
const gameStore = useGameStore()
 | 
			
		||||
const gameOptionsStore = useGameOptionsStore()
 | 
			
		||||
const eventBus = useEventBusStore()
 | 
			
		||||
const { playerState, sessionState } = storeToRefs(gameStore)
 | 
			
		||||
const { updateGameState } = gameStore
 | 
			
		||||
const { gameOptions } = storeToRefs(gameOptionsStore)
 | 
			
		||||
 | 
			
		||||
const minScreenWidth = 800
 | 
			
		||||
const minScreenHeight = 700
 | 
			
		||||
@@ -31,11 +34,12 @@ const game = new Game(
 | 
			
		||||
    width: screenWidth,
 | 
			
		||||
    height: screenHeight,
 | 
			
		||||
    boardScale,
 | 
			
		||||
    handScale: 1
 | 
			
		||||
    handScale: 1,
 | 
			
		||||
    background: gameOptions.value?.background || 'green',
 | 
			
		||||
  },
 | 
			
		||||
  socketService,
 | 
			
		||||
  playerState.value?.id || '',
 | 
			
		||||
  sessionState.value?.id || ''
 | 
			
		||||
  sessionState.value?.id || '',
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// watch(
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,7 @@ import { LoggingService } from '@/services/LoggingService'
 | 
			
		||||
import { GlowFilter } from 'pixi-filters'
 | 
			
		||||
import { ORIENTATION_ANGLES } from '@/common/constants'
 | 
			
		||||
import type { OtherHand } from './OtherHand'
 | 
			
		||||
import { sound } from '@pixi/sound'
 | 
			
		||||
 | 
			
		||||
export class Board extends EventEmitter {
 | 
			
		||||
  private _scale: number = 1
 | 
			
		||||
@@ -46,63 +47,61 @@ export class Board extends EventEmitter {
 | 
			
		||||
  constructor(app: Application) {
 | 
			
		||||
    super()
 | 
			
		||||
    this.ticker = app.ticker
 | 
			
		||||
    this.height = app.canvas.height - 130
 | 
			
		||||
    this.height = app.canvas.height - 130 - 130
 | 
			
		||||
    this.width = app.canvas.width
 | 
			
		||||
    this.scaleX = Scale([0, this.width], [0, this.width])
 | 
			
		||||
    this.scaleY = Scale([0, this.height], [0, this.height])
 | 
			
		||||
    this.calculateScale()
 | 
			
		||||
 | 
			
		||||
    this.container = createContainer({
 | 
			
		||||
      y: 130,
 | 
			
		||||
      width: this.width,
 | 
			
		||||
      height: this.height,
 | 
			
		||||
      parent: app.stage
 | 
			
		||||
      parent: app.stage,
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    const background = new Sprite(Assets.get('bg-green'))
 | 
			
		||||
    // background.width = this.width
 | 
			
		||||
    // background.height = this.height
 | 
			
		||||
    this.container.addChild(background)
 | 
			
		||||
    // this.container.y = 130
 | 
			
		||||
 | 
			
		||||
    this.initialContainer = createContainer({
 | 
			
		||||
      width: this.width,
 | 
			
		||||
      height: this.height,
 | 
			
		||||
      // color: 0x1e2f23,
 | 
			
		||||
      visible: false,
 | 
			
		||||
      parent: this.container
 | 
			
		||||
      parent: this.container,
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    this.tilesContainer = createContainer({
 | 
			
		||||
      width: this.width,
 | 
			
		||||
      height: this.height,
 | 
			
		||||
      // color: 0x1e2f23,
 | 
			
		||||
      parent: this.container
 | 
			
		||||
      parent: this.container,
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    this.interactionContainer = createContainer({
 | 
			
		||||
      width: this.width,
 | 
			
		||||
      height: this.height,
 | 
			
		||||
      parent: this.container,
 | 
			
		||||
      visible: false
 | 
			
		||||
      visible: false,
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    createCrosshair(this.tilesContainer, 0xff0000, {
 | 
			
		||||
      width: this.width,
 | 
			
		||||
      height: this.height,
 | 
			
		||||
      x: this.scaleX(0),
 | 
			
		||||
      y: this.scaleY(0)
 | 
			
		||||
      y: this.scaleY(0),
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    createCrosshair(this.interactionContainer, 0xffff00, {
 | 
			
		||||
      width: this.width,
 | 
			
		||||
      height: this.height,
 | 
			
		||||
      x: this.scaleX(0),
 | 
			
		||||
      y: this.scaleY(0)
 | 
			
		||||
      y: this.scaleY(0),
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    this.textContainer = createContainer({
 | 
			
		||||
      width: this.width,
 | 
			
		||||
      height: this.height,
 | 
			
		||||
      parent: this.container
 | 
			
		||||
      parent: this.container,
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    this.showText('Starting game...')
 | 
			
		||||
@@ -134,7 +133,13 @@ export class Board extends EventEmitter {
 | 
			
		||||
 | 
			
		||||
  showText(text: string) {
 | 
			
		||||
    this.textContainer.removeChildren()
 | 
			
		||||
    this.textContainer.addChild(createText(text, this.scaleX(0), 100))
 | 
			
		||||
    this.textContainer.addChild(
 | 
			
		||||
      createText({
 | 
			
		||||
        text,
 | 
			
		||||
        x: this.scaleX(0),
 | 
			
		||||
        y: -10,
 | 
			
		||||
      }),
 | 
			
		||||
    )
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async setPlayerTurn(player: PlayerDto) {
 | 
			
		||||
@@ -169,7 +174,6 @@ export class Board extends EventEmitter {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async addTile(tile: Tile, move: Movement) {
 | 
			
		||||
    console.log('adding tile', tile.pips)
 | 
			
		||||
    let orientation = ''
 | 
			
		||||
    let x: number =
 | 
			
		||||
      move.type === 'left'
 | 
			
		||||
@@ -234,11 +238,18 @@ export class Board extends EventEmitter {
 | 
			
		||||
    tile.addTo(this.tilesContainer)
 | 
			
		||||
    tile.reScale(this.scale)
 | 
			
		||||
    this.tiles.push(tile)
 | 
			
		||||
 | 
			
		||||
    const moveSound = this.getRandomClickSound()
 | 
			
		||||
    await this.animateTile(tile, x, y, orientation, move)
 | 
			
		||||
    sound.play(moveSound)
 | 
			
		||||
    this.emit('game:tile-animation-ended', tile.toPlain())
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getRandomClickSound() {
 | 
			
		||||
    const sounds = ['snd-move-1', 'snd-move-2', 'snd-move-3', 'snd-move-4']
 | 
			
		||||
    const index = Math.floor(Math.random() * sounds.length)
 | 
			
		||||
    return sounds[index]
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async animateTile(tile: Tile, x: number, y: number, orientation: string, move: Movement) {
 | 
			
		||||
    const targetX = this.scaleX(x)
 | 
			
		||||
    const targetY = this.scaleY(y)
 | 
			
		||||
@@ -246,13 +257,13 @@ export class Board extends EventEmitter {
 | 
			
		||||
      x: targetX,
 | 
			
		||||
      y: targetY,
 | 
			
		||||
      rotation: ORIENTATION_ANGLES[orientation],
 | 
			
		||||
      duration: 20
 | 
			
		||||
      duration: 20,
 | 
			
		||||
    }
 | 
			
		||||
    const tempAlpha = tile.alpha
 | 
			
		||||
    tile.alpha = 0
 | 
			
		||||
    const clonedTile = tile.clone()
 | 
			
		||||
    clonedTile.addTo(this.tilesContainer)
 | 
			
		||||
    const pos = this.getAnimationInitialPoosition(move)
 | 
			
		||||
    const pos = this.getAnimationInitialPosition(move)
 | 
			
		||||
    clonedTile.setPosition(this.scaleX(pos.x), this.scaleY(pos.y))
 | 
			
		||||
    await clonedTile.animateTo(animation)
 | 
			
		||||
    clonedTile.removeFromParent()
 | 
			
		||||
@@ -261,7 +272,7 @@ export class Board extends EventEmitter {
 | 
			
		||||
    tile.alpha = tempAlpha
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getAnimationInitialPoosition(move: Movement): { x: number; y: number } {
 | 
			
		||||
  getAnimationInitialPosition(move: Movement): { x: number; y: number } {
 | 
			
		||||
    const otherHand = this.otherPlayerHands.find((h) => h.player?.id === move.playerId)
 | 
			
		||||
    if (otherHand === undefined) {
 | 
			
		||||
      return { x: 0, y: this.scaleY.inverse(this.height + 50) }
 | 
			
		||||
@@ -271,9 +282,9 @@ export class Board extends EventEmitter {
 | 
			
		||||
      case 'left':
 | 
			
		||||
        return { x: this.scaleX.inverse(100), y: this.scaleY.inverse(100) }
 | 
			
		||||
      case 'right':
 | 
			
		||||
        return { x: 0, y: this.scaleY.inverse(100) }
 | 
			
		||||
      case 'top':
 | 
			
		||||
        return { x: this.scaleX.inverse(this.width - 100), y: this.scaleY.inverse(20) }
 | 
			
		||||
      case 'top':
 | 
			
		||||
        return { x: 0, y: this.scaleY.inverse(100) }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -365,7 +376,7 @@ export class Board extends EventEmitter {
 | 
			
		||||
  nextTileValidPoints(
 | 
			
		||||
    tile: TileDto,
 | 
			
		||||
    side: string,
 | 
			
		||||
    validMoves: boolean[]
 | 
			
		||||
    validMoves: boolean[],
 | 
			
		||||
  ): ([number, number] | undefined)[] {
 | 
			
		||||
    const isLeft = side === 'left'
 | 
			
		||||
    const end = isLeft ? this.leftTile : this.rightTile
 | 
			
		||||
@@ -450,8 +461,8 @@ export class Board extends EventEmitter {
 | 
			
		||||
        outerStrength: 1,
 | 
			
		||||
        innerStrength: 0,
 | 
			
		||||
        color: 0xffffff,
 | 
			
		||||
        quality: 0.5
 | 
			
		||||
      })
 | 
			
		||||
        quality: 0.5,
 | 
			
		||||
      }),
 | 
			
		||||
    ])
 | 
			
		||||
    dot.setOrientation(direction ?? 'north')
 | 
			
		||||
    // const dot = new Dot(this.ticker, this.scale)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,13 +1,30 @@
 | 
			
		||||
import { Application, Assets } from 'pixi.js'
 | 
			
		||||
import { Application, Assets, Container, Sprite } from 'pixi.js'
 | 
			
		||||
import { Board } from '@/game/Board'
 | 
			
		||||
import { assets } from '@/game/utilities/assets'
 | 
			
		||||
import { Tile } from '@/game/Tile'
 | 
			
		||||
import { Hand } from '@/game/Hand'
 | 
			
		||||
import type { GameDto, Movement, PlayerDto, TileDto } from '@/common/interfaces'
 | 
			
		||||
import type {
 | 
			
		||||
  GameDto,
 | 
			
		||||
  GameSummary,
 | 
			
		||||
  MatchSessionDto,
 | 
			
		||||
  Movement,
 | 
			
		||||
  PlayerDto,
 | 
			
		||||
  TileDto,
 | 
			
		||||
} from '@/common/interfaces'
 | 
			
		||||
import type { SocketIoClientService } from '@/services/SocketIoClientService'
 | 
			
		||||
import { wait } from '@/common/helpers'
 | 
			
		||||
import { Actions } from 'pixi-actions'
 | 
			
		||||
import { OtherHand } from './OtherHand'
 | 
			
		||||
import { GameSummayView } from './GameSummayView'
 | 
			
		||||
import { summaryMock } from '@/common/summarymock'
 | 
			
		||||
 | 
			
		||||
interface GameOptions {
 | 
			
		||||
  boardScale: number
 | 
			
		||||
  handScale: number
 | 
			
		||||
  width: number
 | 
			
		||||
  height: number
 | 
			
		||||
  background: string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class Game {
 | 
			
		||||
  public board!: Board
 | 
			
		||||
@@ -16,17 +33,20 @@ export class Game {
 | 
			
		||||
  private selectedTile: TileDto | undefined
 | 
			
		||||
  private currentMove: Movement | undefined
 | 
			
		||||
  private otherHands: OtherHand[] = []
 | 
			
		||||
  private backgroundLayer: Container = new Container()
 | 
			
		||||
  private gameSummaryView!: GameSummayView
 | 
			
		||||
 | 
			
		||||
  constructor(
 | 
			
		||||
    private options: { boardScale: number; handScale: number; width: number; height: number } = {
 | 
			
		||||
    private options: GameOptions = {
 | 
			
		||||
      boardScale: 1,
 | 
			
		||||
      handScale: 1,
 | 
			
		||||
      width: 1200,
 | 
			
		||||
      height: 800
 | 
			
		||||
      height: 800,
 | 
			
		||||
      background: 'bg-green',
 | 
			
		||||
    },
 | 
			
		||||
    private socketService: SocketIoClientService,
 | 
			
		||||
    private playerId: string,
 | 
			
		||||
    private sessionId: string
 | 
			
		||||
    private sessionId: string,
 | 
			
		||||
  ) {}
 | 
			
		||||
 | 
			
		||||
  async setup(): Promise<HTMLCanvasElement> {
 | 
			
		||||
@@ -39,14 +59,16 @@ export class Game {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async start(players: PlayerDto[] = []) {
 | 
			
		||||
    this.iniialStuff(this.app)
 | 
			
		||||
    this.board = new Board(this.app)
 | 
			
		||||
    this.hand = new Hand(this.app)
 | 
			
		||||
    this.otherHands = [
 | 
			
		||||
      new OtherHand(this.app, 'left'),
 | 
			
		||||
      new OtherHand(this.app, 'top'),
 | 
			
		||||
      new OtherHand(this.app, 'right')
 | 
			
		||||
      new OtherHand(this.app, 'right'),
 | 
			
		||||
    ]
 | 
			
		||||
    this.initOtherHands(players)
 | 
			
		||||
    this.gameSummaryView = new GameSummayView(this.app)
 | 
			
		||||
    this.hand.scale = this.options.handScale
 | 
			
		||||
    this.board.scale = this.options.boardScale
 | 
			
		||||
    this.setBoardEvents()
 | 
			
		||||
@@ -55,10 +77,18 @@ export class Game {
 | 
			
		||||
    wait(3000)
 | 
			
		||||
    this.socketService.sendMessage('client:set-client-ready', {
 | 
			
		||||
      sessionId: this.sessionId,
 | 
			
		||||
      userId: this.playerId
 | 
			
		||||
      userId: this.playerId,
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  iniialStuff(app: Application) {
 | 
			
		||||
    app.stage.addChild(this.backgroundLayer)
 | 
			
		||||
    const background = new Sprite(Assets.get(`bg-${this.options.background}`))
 | 
			
		||||
    background.width = this.app.canvas.width
 | 
			
		||||
    background.height = this.app.canvas.height
 | 
			
		||||
    this.backgroundLayer.addChild(background)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  initOtherHands(players: PlayerDto[]) {
 | 
			
		||||
    const myIndex = players.findIndex((player) => player.id === this.playerId)
 | 
			
		||||
    const copy = [...players]
 | 
			
		||||
@@ -107,25 +137,36 @@ export class Game {
 | 
			
		||||
      const move: Movement = {
 | 
			
		||||
        id: '',
 | 
			
		||||
        type: 'pass',
 | 
			
		||||
        playerId: this.playerId
 | 
			
		||||
        playerId: this.playerId,
 | 
			
		||||
      }
 | 
			
		||||
      this.socketService.sendMessage('client:player-move', {
 | 
			
		||||
        sessionId: this.sessionId,
 | 
			
		||||
        move: move
 | 
			
		||||
        move: move,
 | 
			
		||||
      })
 | 
			
		||||
      await this.board.updateBoard(move, undefined)
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    this.hand.on('nextClick', () => {
 | 
			
		||||
    this.gameSummaryView.on('nextClick', (data) => {
 | 
			
		||||
      this.updateScoreboard(data.sessionState)
 | 
			
		||||
      this.socketService.sendMessage('client:set-client-ready-for-next-game', {
 | 
			
		||||
        userId: this.playerId,
 | 
			
		||||
        sessionId: this.sessionId
 | 
			
		||||
        sessionId: this.sessionId,
 | 
			
		||||
      })
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    this.hand.on('hand-initialized', () => {})
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private updateScoreboard(sessionState: MatchSessionDto) {
 | 
			
		||||
    const scoreboard = sessionState.scoreboard
 | 
			
		||||
    this.otherHands.forEach((hand) => {
 | 
			
		||||
      const player: PlayerDto | undefined = hand.player
 | 
			
		||||
      const name: string = player?.name || ''
 | 
			
		||||
      const score = scoreboard.find((d) => d.name === name)?.score || 0
 | 
			
		||||
      hand.setScore(score)
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  highlightMoves(tile: TileDto) {
 | 
			
		||||
    this.selectedTile = tile
 | 
			
		||||
    if (tile !== undefined) {
 | 
			
		||||
@@ -168,7 +209,7 @@ export class Game {
 | 
			
		||||
        tile: this.selectedTile,
 | 
			
		||||
        type: 'left',
 | 
			
		||||
        playerId: this.playerId,
 | 
			
		||||
        ...data
 | 
			
		||||
        ...data,
 | 
			
		||||
      }
 | 
			
		||||
      this.currentMove = move
 | 
			
		||||
      const tile = this.hand.tileMoved(this.selectedTile)
 | 
			
		||||
@@ -181,7 +222,7 @@ export class Game {
 | 
			
		||||
        tile: this.selectedTile,
 | 
			
		||||
        type: 'right',
 | 
			
		||||
        playerId: this.playerId,
 | 
			
		||||
        ...data
 | 
			
		||||
        ...data,
 | 
			
		||||
      }
 | 
			
		||||
      this.currentMove = move
 | 
			
		||||
      const tile = this.hand.tileMoved(this.selectedTile)
 | 
			
		||||
@@ -192,12 +233,12 @@ export class Game {
 | 
			
		||||
      if (tile !== null && tile !== undefined && tile.playerId === this.playerId) {
 | 
			
		||||
        this.socketService.sendMessage('client:player-move', {
 | 
			
		||||
          sessionId: this.sessionId,
 | 
			
		||||
          move: this.currentMove
 | 
			
		||||
          move: this.currentMove,
 | 
			
		||||
        })
 | 
			
		||||
      } else {
 | 
			
		||||
        this.socketService.sendMessage('client:animation-ended', {
 | 
			
		||||
          sessionId: this.sessionId,
 | 
			
		||||
          userId: this.playerId
 | 
			
		||||
          userId: this.playerId,
 | 
			
		||||
        })
 | 
			
		||||
      }
 | 
			
		||||
    })
 | 
			
		||||
@@ -206,11 +247,13 @@ export class Game {
 | 
			
		||||
  gameFinished(data: any) {
 | 
			
		||||
    this.hand.gameFinished()
 | 
			
		||||
    this.board.gameFinished(data)
 | 
			
		||||
    this.gameSummaryView.setGameSummary(data, 'round')
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  matchFinished(data: any) {
 | 
			
		||||
    // this.hand.matchFinished()
 | 
			
		||||
    this.board.matchFinished(data)
 | 
			
		||||
    this.gameSummaryView.setGameSummary(data, 'match')
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  serverPlayerMove(data: any, playerId: string) {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										168
									
								
								src/game/GameSummayView.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										168
									
								
								src/game/GameSummayView.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,168 @@
 | 
			
		||||
import { createButton, createContainer } from '@/common/helpers'
 | 
			
		||||
import type { GameSummary, MatchSessionDto } from '@/common/interfaces'
 | 
			
		||||
import { EventEmitter, type Application, type Container } from 'pixi.js'
 | 
			
		||||
import { createText, whiteStyle, yellowStyle } from './utilities/fonts'
 | 
			
		||||
 | 
			
		||||
export class GameSummayView extends EventEmitter {
 | 
			
		||||
  public width: number
 | 
			
		||||
  public height: number
 | 
			
		||||
  container!: Container
 | 
			
		||||
  layer!: Container
 | 
			
		||||
  gameSummary!: GameSummary
 | 
			
		||||
  matchState!: MatchSessionDto
 | 
			
		||||
  type: 'round' | 'match' = 'round'
 | 
			
		||||
 | 
			
		||||
  constructor(app: Application) {
 | 
			
		||||
    super()
 | 
			
		||||
    this.width = 500
 | 
			
		||||
    this.height = 400
 | 
			
		||||
    this.container = createContainer({
 | 
			
		||||
      width: this.width,
 | 
			
		||||
      height: this.height,
 | 
			
		||||
      x: app.canvas.width / 2 - this.width / 2,
 | 
			
		||||
      y: app.canvas.height / 2 - this.height / 2,
 | 
			
		||||
      parent: app.stage,
 | 
			
		||||
      alpha: 0.7,
 | 
			
		||||
      color: 0x121212,
 | 
			
		||||
    })
 | 
			
		||||
    this.layer = createContainer({
 | 
			
		||||
      width: this.width,
 | 
			
		||||
      height: this.height,
 | 
			
		||||
      parent: this.container,
 | 
			
		||||
    })
 | 
			
		||||
    console.log('GameSummaryView created!')
 | 
			
		||||
 | 
			
		||||
    this.container.visible = false
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  setGameSummary(data: any, type: 'round' | 'match') {
 | 
			
		||||
    this.type = type
 | 
			
		||||
    this.matchState = data.sessionState
 | 
			
		||||
    this.gameSummary = data.lastGame
 | 
			
		||||
    this.render()
 | 
			
		||||
    this.container.visible = true
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  renderTitle(y: number = 20, title: string): number {
 | 
			
		||||
    const text = createText({
 | 
			
		||||
      text: title,
 | 
			
		||||
      x: this.width / 2,
 | 
			
		||||
      y,
 | 
			
		||||
      style: yellowStyle(24),
 | 
			
		||||
    })
 | 
			
		||||
    this.layer.addChild(text)
 | 
			
		||||
    return y + 24
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  renderWinner(y: number): number {
 | 
			
		||||
    let line = y + 12
 | 
			
		||||
    this.layer.addChild(
 | 
			
		||||
      createText({
 | 
			
		||||
        text: `Winner: ${this.gameSummary.winner.name}`,
 | 
			
		||||
        x: this.width / 2,
 | 
			
		||||
        y: line,
 | 
			
		||||
        style: whiteStyle(20),
 | 
			
		||||
      }),
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    if (this.gameSummary.isBlocked) {
 | 
			
		||||
      line += 30
 | 
			
		||||
      this.container.addChild(
 | 
			
		||||
        createText({
 | 
			
		||||
          text: '(Blocked)',
 | 
			
		||||
          x: this.width / 2,
 | 
			
		||||
          y: line,
 | 
			
		||||
          style: whiteStyle(),
 | 
			
		||||
        }),
 | 
			
		||||
      )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    line += 30
 | 
			
		||||
    this.layer.addChild(
 | 
			
		||||
      createText({
 | 
			
		||||
        text: `Points this round: ${this.gameSummary.winner.score}`,
 | 
			
		||||
        x: this.width / 2,
 | 
			
		||||
        y: line,
 | 
			
		||||
        style: whiteStyle(20),
 | 
			
		||||
      }),
 | 
			
		||||
    )
 | 
			
		||||
    return line + 16
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  renderScores(y: number): number {
 | 
			
		||||
    const scores = this.matchState.scoreboard
 | 
			
		||||
    // this.type === 'round'
 | 
			
		||||
    //   ? this.gameSummary.score
 | 
			
		||||
    //   : this.matchState.scoreboard.map((d) => ({ name: d[0], score: d[1] }))
 | 
			
		||||
    let line = y + 30
 | 
			
		||||
    scores.forEach((score: any) => {
 | 
			
		||||
      line = line + 30
 | 
			
		||||
      this.layer.addChild(
 | 
			
		||||
        createText({
 | 
			
		||||
          text: `${score.name}:`,
 | 
			
		||||
          x: 130,
 | 
			
		||||
          y: line,
 | 
			
		||||
          style: whiteStyle(18),
 | 
			
		||||
          align: 'left',
 | 
			
		||||
        }),
 | 
			
		||||
      )
 | 
			
		||||
      this.layer.addChild(
 | 
			
		||||
        createText({
 | 
			
		||||
          text: `${score.score}`,
 | 
			
		||||
          x: 330,
 | 
			
		||||
          y: line,
 | 
			
		||||
          style: whiteStyle(18),
 | 
			
		||||
          align: 'right',
 | 
			
		||||
        }),
 | 
			
		||||
      )
 | 
			
		||||
    })
 | 
			
		||||
    return line
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  renderButtons() {
 | 
			
		||||
    if (this.type === 'round') {
 | 
			
		||||
      this.layer.addChild(
 | 
			
		||||
        createButton({
 | 
			
		||||
          text: 'Next',
 | 
			
		||||
          dimension: {
 | 
			
		||||
            x: this.width / 2 - 25,
 | 
			
		||||
            y: this.height - 50,
 | 
			
		||||
            width: 60,
 | 
			
		||||
            height: 25,
 | 
			
		||||
          },
 | 
			
		||||
          action: () => {
 | 
			
		||||
            this.emit('nextClick', { sessionState: this.matchState })
 | 
			
		||||
            this.container.visible = false
 | 
			
		||||
          },
 | 
			
		||||
          parent: this.layer,
 | 
			
		||||
        }),
 | 
			
		||||
      )
 | 
			
		||||
    } else {
 | 
			
		||||
      this.layer.addChild(
 | 
			
		||||
        createButton({
 | 
			
		||||
          text: 'Finish',
 | 
			
		||||
          dimension: {
 | 
			
		||||
            x: this.width / 2 - 25,
 | 
			
		||||
            y: this.height - 50,
 | 
			
		||||
            width: 60,
 | 
			
		||||
            height: 25,
 | 
			
		||||
          },
 | 
			
		||||
          action: () => {
 | 
			
		||||
            this.emit('finishClick', this.gameSummary)
 | 
			
		||||
            this.container.visible = false
 | 
			
		||||
          },
 | 
			
		||||
          parent: this.layer,
 | 
			
		||||
        }),
 | 
			
		||||
      )
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  render() {
 | 
			
		||||
    const title: string = this.type === 'round' ? 'Round Summary' : 'Match Finished!'
 | 
			
		||||
    this.layer.removeChildren()
 | 
			
		||||
    let y = this.renderTitle(30, title.toUpperCase())
 | 
			
		||||
    y = this.renderWinner(y)
 | 
			
		||||
    this.renderScores(y)
 | 
			
		||||
    this.renderButtons()
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -11,7 +11,6 @@ export class Hand extends EventEmitter {
 | 
			
		||||
  tiles: Tile[] = []
 | 
			
		||||
  container: Container = new Container()
 | 
			
		||||
  buttonPass: Container = new Container()
 | 
			
		||||
  buttonNext: Container = new Container()
 | 
			
		||||
  height: number
 | 
			
		||||
  width: number
 | 
			
		||||
  ticker: Ticker
 | 
			
		||||
@@ -27,6 +26,7 @@ export class Hand extends EventEmitter {
 | 
			
		||||
  availableTiles: Tile[] = []
 | 
			
		||||
  tilesLayer!: Container
 | 
			
		||||
  interactionsLayer!: Container
 | 
			
		||||
  score: number = 0
 | 
			
		||||
 | 
			
		||||
  constructor(app: Application) {
 | 
			
		||||
    super()
 | 
			
		||||
@@ -49,14 +49,14 @@ export class Hand extends EventEmitter {
 | 
			
		||||
      height: this.height,
 | 
			
		||||
      x: 0,
 | 
			
		||||
      y: 0,
 | 
			
		||||
      parent: this.container
 | 
			
		||||
      parent: this.container,
 | 
			
		||||
    })
 | 
			
		||||
    this.interactionsLayer = createContainer({
 | 
			
		||||
      width: this.width,
 | 
			
		||||
      height: this.height,
 | 
			
		||||
      x: 0,
 | 
			
		||||
      y: 0,
 | 
			
		||||
      parent: this.container
 | 
			
		||||
      parent: this.container,
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -65,16 +65,6 @@ export class Hand extends EventEmitter {
 | 
			
		||||
    this.tiles = []
 | 
			
		||||
 | 
			
		||||
    this.initialized = false
 | 
			
		||||
    this.buttonNext = createButton(
 | 
			
		||||
      'NEXT',
 | 
			
		||||
      { x: this.width / 2 - 25, y: this.height / 2, width: 50, height: 20 },
 | 
			
		||||
      () => {
 | 
			
		||||
        this.tilesLayer.removeChildren()
 | 
			
		||||
        this.interactionsLayer.removeChild(this.buttonNext)
 | 
			
		||||
        this.emit('nextClick')
 | 
			
		||||
      },
 | 
			
		||||
      this.interactionsLayer
 | 
			
		||||
    )
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  get canMove() {
 | 
			
		||||
@@ -98,7 +88,7 @@ export class Hand extends EventEmitter {
 | 
			
		||||
    this.availableTiles.forEach((tile) => {
 | 
			
		||||
      tile.animateTo({
 | 
			
		||||
        x: tile.x,
 | 
			
		||||
        y: tile.y - 10
 | 
			
		||||
        y: tile.y - 10,
 | 
			
		||||
      })
 | 
			
		||||
      tile.interactive = true
 | 
			
		||||
    })
 | 
			
		||||
@@ -108,7 +98,7 @@ export class Hand extends EventEmitter {
 | 
			
		||||
    this.availableTiles.forEach((tile) => {
 | 
			
		||||
      tile.animateTo({
 | 
			
		||||
        x: tile.x,
 | 
			
		||||
        y: tile.y + 10
 | 
			
		||||
        y: tile.y + 10,
 | 
			
		||||
      })
 | 
			
		||||
      tile.setPosition(tile.x, tile.y + 10)
 | 
			
		||||
      tile.interactive = false
 | 
			
		||||
@@ -128,7 +118,7 @@ export class Hand extends EventEmitter {
 | 
			
		||||
  initialize(playerState: PlayerDto) {
 | 
			
		||||
    this.tiles = this.createTiles(playerState)
 | 
			
		||||
    this.initialized = this.tiles.length > 0
 | 
			
		||||
    this.renderTiles()
 | 
			
		||||
    this.render()
 | 
			
		||||
    this.emit('hand-updated', this.tiles)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -187,31 +177,32 @@ export class Hand extends EventEmitter {
 | 
			
		||||
  private createPassButton() {
 | 
			
		||||
    const lastTile = this.tiles[this.tiles.length - 1]
 | 
			
		||||
    const x = lastTile ? lastTile.x + lastTile.width : this.scaleX(0)
 | 
			
		||||
    this.buttonPass = createButton(
 | 
			
		||||
      'PASS',
 | 
			
		||||
      { x, y: this.height / 2, width: 50, height: 20 },
 | 
			
		||||
      () => {
 | 
			
		||||
    this.buttonPass = createButton({
 | 
			
		||||
      text: 'PASS',
 | 
			
		||||
      dimension: { x, y: this.height / 2, width: 50, height: 20 },
 | 
			
		||||
      action: () => {
 | 
			
		||||
        this.interactionsLayer.removeChild(this.buttonPass)
 | 
			
		||||
        this.emit('game:button-pass-click')
 | 
			
		||||
      },
 | 
			
		||||
      this.interactionsLayer
 | 
			
		||||
    )
 | 
			
		||||
      parent: this.interactionsLayer,
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  update(playerState: PlayerDto) {
 | 
			
		||||
    this.tilesLayer.removeChildren()
 | 
			
		||||
    if (!this.initialized) {
 | 
			
		||||
      this.initialize(playerState)
 | 
			
		||||
      return
 | 
			
		||||
    }
 | 
			
		||||
    const missing: Tile | undefined = this.tiles.find(
 | 
			
		||||
      (tile: Tile) => !playerState.hand.find((t) => t.id === tile.id)
 | 
			
		||||
      (tile: Tile) => !playerState.hand.find((t) => t.id === tile.id),
 | 
			
		||||
    )
 | 
			
		||||
    if (missing) {
 | 
			
		||||
      this.tilesLayer.removeChild(missing.getSprite())
 | 
			
		||||
      this.tiles = this.tiles.filter((tile) => tile.id !== missing.id)
 | 
			
		||||
      this.emit('hand-updated', this.tiles)
 | 
			
		||||
    }
 | 
			
		||||
    this.renderTiles()
 | 
			
		||||
    this.render()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private createTiles(playerState: PlayerDto) {
 | 
			
		||||
@@ -230,8 +221,8 @@ export class Hand extends EventEmitter {
 | 
			
		||||
            outerStrength: 2,
 | 
			
		||||
            innerStrength: 1,
 | 
			
		||||
            color: 0xffffff,
 | 
			
		||||
            quality: 0.5
 | 
			
		||||
          })
 | 
			
		||||
            quality: 0.5,
 | 
			
		||||
          }),
 | 
			
		||||
        ])
 | 
			
		||||
      })
 | 
			
		||||
      newTile.on('pointerout', () => {
 | 
			
		||||
@@ -251,4 +242,13 @@ export class Hand extends EventEmitter {
 | 
			
		||||
      tile.setPosition(deltaX + tile.width / 2 + i * (tile.width + 5), tile.height / 2 + 20)
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  renderScore() {
 | 
			
		||||
    //this.scoreLayer.removeChildren()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  render() {
 | 
			
		||||
    this.renderTiles()
 | 
			
		||||
    this.renderScore()
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,7 @@ import { Scale, type ScaleFunction } from './utilities/scale'
 | 
			
		||||
import { Tile } from './Tile'
 | 
			
		||||
import type { Movement, PlayerDto, TileDto } from '@/common/interfaces'
 | 
			
		||||
import { createContainer } from '@/common/helpers'
 | 
			
		||||
import { createText, playerNameText } from './utilities/fonts'
 | 
			
		||||
import { createText, playerNameText, scoreText } from './utilities/fonts'
 | 
			
		||||
 | 
			
		||||
export class OtherHand {
 | 
			
		||||
  tilesInitialNumber: number = 7
 | 
			
		||||
@@ -22,10 +22,12 @@ export class OtherHand {
 | 
			
		||||
  logger: LoggingService = new LoggingService()
 | 
			
		||||
  tilesLayer!: Container
 | 
			
		||||
  interactionsLayer!: Container
 | 
			
		||||
  scoreLayer: Container = new Container()
 | 
			
		||||
  score: number = 0
 | 
			
		||||
 | 
			
		||||
  constructor(
 | 
			
		||||
    private app: Application,
 | 
			
		||||
    public position: 'left' | 'right' | 'top' = 'left'
 | 
			
		||||
    public position: 'left' | 'right' | 'top' = 'left',
 | 
			
		||||
  ) {
 | 
			
		||||
    this.height = 100
 | 
			
		||||
    this.width = 300
 | 
			
		||||
@@ -37,11 +39,23 @@ export class OtherHand {
 | 
			
		||||
    this.container.y = y
 | 
			
		||||
    this.calculateScale()
 | 
			
		||||
    this.initLayers()
 | 
			
		||||
    this.render()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  setPlayer(player: PlayerDto) {
 | 
			
		||||
    this.player = player
 | 
			
		||||
    this.container.addChild(createText(`${player.name}`, this.width / 2, 12, playerNameText))
 | 
			
		||||
    this.container.addChild(
 | 
			
		||||
      createText({
 | 
			
		||||
        text: `${player.name}`,
 | 
			
		||||
        x: this.width / 2,
 | 
			
		||||
        y: 12,
 | 
			
		||||
        style: playerNameText,
 | 
			
		||||
      }),
 | 
			
		||||
    )
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  setScore(score: number) {
 | 
			
		||||
    this.score = score
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  setHand(tiles: TileDto[]) {
 | 
			
		||||
@@ -54,7 +68,19 @@ export class OtherHand {
 | 
			
		||||
    this.render()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private render() {
 | 
			
		||||
  private renderScore() {
 | 
			
		||||
    this.scoreLayer.removeChildren()
 | 
			
		||||
    const text = createText({
 | 
			
		||||
      text: `${this.score}`,
 | 
			
		||||
      x: this.width - 5,
 | 
			
		||||
      y: 50,
 | 
			
		||||
      style: scoreText,
 | 
			
		||||
    })
 | 
			
		||||
    text.anchor.set(1, 0.5)
 | 
			
		||||
    this.scoreLayer.addChild(text)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private renderTiles() {
 | 
			
		||||
    this.tilesLayer.removeChildren()
 | 
			
		||||
    const x = -9
 | 
			
		||||
    this.hand.forEach((tile, index) => {
 | 
			
		||||
@@ -63,6 +89,11 @@ export class OtherHand {
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private render() {
 | 
			
		||||
    this.renderTiles()
 | 
			
		||||
    this.renderScore()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private addBg() {
 | 
			
		||||
    const bg = new Sprite(Texture.WHITE)
 | 
			
		||||
    bg.alpha = 0.08
 | 
			
		||||
@@ -96,17 +127,17 @@ export class OtherHand {
 | 
			
		||||
      height: this.height,
 | 
			
		||||
      x: 0,
 | 
			
		||||
      y: 0,
 | 
			
		||||
      parent: this.container
 | 
			
		||||
      parent: this.container,
 | 
			
		||||
    })
 | 
			
		||||
    this.interactionsLayer = createContainer({
 | 
			
		||||
      width: this.width,
 | 
			
		||||
      height: this.height,
 | 
			
		||||
      x: 0,
 | 
			
		||||
      y: 0,
 | 
			
		||||
      parent: this.container
 | 
			
		||||
      parent: this.container,
 | 
			
		||||
    })
 | 
			
		||||
    this.container.addChild(this.tilesLayer)
 | 
			
		||||
    this.container.addChild(this.interactionsLayer)
 | 
			
		||||
    // this.container.addChild(this.tilesLayer)
 | 
			
		||||
    this.container.addChild(this.scoreLayer)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private calculateScale() {
 | 
			
		||||
 
 | 
			
		||||
@@ -27,10 +27,16 @@ import tile6_3 from '@/assets/images/tiles/6-3.png'
 | 
			
		||||
import tile6_4 from '@/assets/images/tiles/6-4.png'
 | 
			
		||||
import tile6_5 from '@/assets/images/tiles/6-5.png'
 | 
			
		||||
import tile6_6 from '@/assets/images/tiles/6-6.png'
 | 
			
		||||
import dot from '@/assets/images/circle.png'
 | 
			
		||||
import bgWood_1 from '@/assets/images/backgrounds/wood-1.jpg'
 | 
			
		||||
import bg_1 from '@/assets/images/backgrounds/bg-1.png'
 | 
			
		||||
import bg_green from '@/assets/images/backgrounds/bg-green.png'
 | 
			
		||||
import bg_red from '@/assets/images/backgrounds/bg-red.png'
 | 
			
		||||
import bg_yellow from '@/assets/images/backgrounds/bg-yellow.png'
 | 
			
		||||
import snd_move_1 from '@/assets/sounds/move-1.mp3'
 | 
			
		||||
import snd_move_2 from '@/assets/sounds/move-2.mp3'
 | 
			
		||||
import snd_move_3 from '@/assets/sounds/move-3.mp3'
 | 
			
		||||
import snd_move_4 from '@/assets/sounds/move-4.mp3'
 | 
			
		||||
import snd_intro from '@/assets/sounds/intro.mp3'
 | 
			
		||||
 | 
			
		||||
export const assets = [
 | 
			
		||||
  { alias: 'tile-back', src: tileBack },
 | 
			
		||||
@@ -62,8 +68,14 @@ export const assets = [
 | 
			
		||||
  { alias: 'tile-6_4', src: tile6_4 },
 | 
			
		||||
  { alias: 'tile-6_5', src: tile6_5 },
 | 
			
		||||
  { alias: 'tile-6_6', src: tile6_6 },
 | 
			
		||||
  { alias: 'dot', src: dot },
 | 
			
		||||
  { alias: 'bg-wood-1', src: bgWood_1 },
 | 
			
		||||
  { alias: 'bg-1', src: bg_1 },
 | 
			
		||||
  { alias: 'bg-green', src: bg_green }
 | 
			
		||||
  { alias: 'bg-gray', src: bg_1 },
 | 
			
		||||
  { alias: 'bg-green', src: bg_green },
 | 
			
		||||
  { alias: 'bg-red', src: bg_red },
 | 
			
		||||
  { alias: 'bg-yellow', src: bg_yellow },
 | 
			
		||||
  { alias: 'snd-move-1', src: snd_move_1 },
 | 
			
		||||
  { alias: 'snd-move-2', src: snd_move_2 },
 | 
			
		||||
  { alias: 'snd-move-3', src: snd_move_3 },
 | 
			
		||||
  { alias: 'snd-move-4', src: snd_move_4 },
 | 
			
		||||
  { alias: 'snd-intro', src: snd_intro },
 | 
			
		||||
]
 | 
			
		||||
 
 | 
			
		||||
@@ -1,19 +1,27 @@
 | 
			
		||||
import { Text, TextStyle } from 'pixi.js'
 | 
			
		||||
import {
 | 
			
		||||
  Container,
 | 
			
		||||
  Text,
 | 
			
		||||
  TextStyle,
 | 
			
		||||
  type TextStyleAlign,
 | 
			
		||||
  type TextStyleFontStyle,
 | 
			
		||||
  type TextStyleFontWeight,
 | 
			
		||||
  type TextStyleOptions,
 | 
			
		||||
} from 'pixi.js'
 | 
			
		||||
 | 
			
		||||
export const dropShadowStyle = {
 | 
			
		||||
  alpha: 0.5,
 | 
			
		||||
  angle: 0.3,
 | 
			
		||||
  blur: 5,
 | 
			
		||||
  distance: 4
 | 
			
		||||
  distance: 4,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const mainText = new TextStyle({
 | 
			
		||||
  dropShadow: dropShadowStyle,
 | 
			
		||||
  fill: '#b71a1a',
 | 
			
		||||
  fill: '#aaaaaa',
 | 
			
		||||
  fontFamily: 'Arial, Helvetica, sans-serif',
 | 
			
		||||
  fontWeight: 'bold',
 | 
			
		||||
  letterSpacing: 1,
 | 
			
		||||
  stroke: '#658f56'
 | 
			
		||||
  stroke: '#565656',
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
export const playerNameText = new TextStyle({
 | 
			
		||||
@@ -23,12 +31,104 @@ export const playerNameText = new TextStyle({
 | 
			
		||||
  letterSpacing: 1,
 | 
			
		||||
  stroke: '#565656',
 | 
			
		||||
  fontSize: 15,
 | 
			
		||||
  fontWeight: 'bold'
 | 
			
		||||
  fontWeight: 'bold',
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
export function createText(str: string, x: number, y: number, style: TextStyle = mainText) {
 | 
			
		||||
export const summaryTitle = new TextStyle({
 | 
			
		||||
  dropShadow: dropShadowStyle,
 | 
			
		||||
  fill: '#a2a2a2',
 | 
			
		||||
  fontFamily: 'Arial, Helvetica, sans-serif',
 | 
			
		||||
  letterSpacing: 1,
 | 
			
		||||
  stroke: '#565656',
 | 
			
		||||
  fontSize: 15,
 | 
			
		||||
  fontWeight: 'bold',
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
export const scoreText = new TextStyle({
 | 
			
		||||
  dropShadow: dropShadowStyle,
 | 
			
		||||
  fill: '#aaaaaa',
 | 
			
		||||
  fontFamily: 'Arial, Helvetica, sans-serif',
 | 
			
		||||
  letterSpacing: 1,
 | 
			
		||||
  stroke: '#565656',
 | 
			
		||||
  fontSize: 32,
 | 
			
		||||
  fontWeight: 'bold',
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
function getStyle(styleOptions: TextStyleOptions = {}) {
 | 
			
		||||
  const {
 | 
			
		||||
    fill = 0xa2a2a2,
 | 
			
		||||
    stroke = 0x565656,
 | 
			
		||||
    fontSize = 15,
 | 
			
		||||
    fontFamily = 'Arial, Helvetica, sans-serif',
 | 
			
		||||
    fontWeight = 'normal',
 | 
			
		||||
    fontStyle = 'normal',
 | 
			
		||||
    dropShadow,
 | 
			
		||||
    letterSpacing = 1,
 | 
			
		||||
  } = styleOptions
 | 
			
		||||
  const style = new TextStyle({
 | 
			
		||||
    fill,
 | 
			
		||||
    fontFamily,
 | 
			
		||||
    letterSpacing,
 | 
			
		||||
    stroke,
 | 
			
		||||
    fontSize,
 | 
			
		||||
    fontStyle,
 | 
			
		||||
    fontWeight: fontWeight as any,
 | 
			
		||||
    dropShadow: dropShadow ? dropShadowStyle : undefined,
 | 
			
		||||
  })
 | 
			
		||||
  return style
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const whiteStyle = (
 | 
			
		||||
  fontSize: number = 15,
 | 
			
		||||
  fontWeight: TextStyleFontWeight = 'normal',
 | 
			
		||||
  dropShadow: boolean = true,
 | 
			
		||||
) =>
 | 
			
		||||
  getStyle({
 | 
			
		||||
    fontSize,
 | 
			
		||||
    fontWeight,
 | 
			
		||||
    dropShadow,
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
export const yellowStyle = (
 | 
			
		||||
  fontSize: number = 15,
 | 
			
		||||
  fontWeight: TextStyleFontWeight = 'normal',
 | 
			
		||||
  dropShadow: boolean = true,
 | 
			
		||||
) =>
 | 
			
		||||
  getStyle({
 | 
			
		||||
    fill: 0xffff00,
 | 
			
		||||
    fontSize,
 | 
			
		||||
    fontWeight,
 | 
			
		||||
    dropShadow,
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
interface TextOptions {
 | 
			
		||||
  text: string
 | 
			
		||||
  x: number
 | 
			
		||||
  y: number
 | 
			
		||||
  style?: TextStyle
 | 
			
		||||
  container?: Container
 | 
			
		||||
  align?: 'left' | 'center' | 'right'
 | 
			
		||||
  fontStyle?: TextStyleFontStyle
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function createText(textOptions: TextOptions) {
 | 
			
		||||
  const defaultOptions = { style: whiteStyle(), align: 'center' }
 | 
			
		||||
  const { text: str, x, y, style, container, align } = { ...defaultOptions, ...textOptions }
 | 
			
		||||
  const text = new Text({ text: str, style })
 | 
			
		||||
  text.anchor.set(0.5, 0.5)
 | 
			
		||||
 | 
			
		||||
  switch (align) {
 | 
			
		||||
    case 'center':
 | 
			
		||||
      text.anchor.set(0.5, 0.5)
 | 
			
		||||
      break
 | 
			
		||||
    case 'left':
 | 
			
		||||
      text.anchor.set(0, 0.5)
 | 
			
		||||
      break
 | 
			
		||||
    case 'right':
 | 
			
		||||
      text.anchor.set(1, 0.5)
 | 
			
		||||
      break
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (container) container.addChild(text)
 | 
			
		||||
  text.x = x
 | 
			
		||||
  text.y = y
 | 
			
		||||
  return text
 | 
			
		||||
 
 | 
			
		||||
@@ -23,8 +23,8 @@ export class SocketIoClientService extends ServiceBase {
 | 
			
		||||
      }
 | 
			
		||||
      this.socket = io(this.url, {
 | 
			
		||||
        auth: {
 | 
			
		||||
          token: jwt.value
 | 
			
		||||
        }
 | 
			
		||||
          token: jwt.value,
 | 
			
		||||
        },
 | 
			
		||||
      })
 | 
			
		||||
      this.socket.on('connect', () => {
 | 
			
		||||
        if (this.socket && this.socket.recovered) {
 | 
			
		||||
@@ -62,14 +62,6 @@ export class SocketIoClientService extends ServiceBase {
 | 
			
		||||
 | 
			
		||||
    // Custom events
 | 
			
		||||
 | 
			
		||||
    // this.socket.on('makeMove', async (data: any, callback: any) => {
 | 
			
		||||
    //   callback(await this.gameEventManager.handleCanMakeMoveEvent(data))
 | 
			
		||||
    // })
 | 
			
		||||
 | 
			
		||||
    // this.socket.on('chooseTile', async (data: any, callback: any) => {
 | 
			
		||||
    //   callback(await this.gameEventManager.handleCanSelectTileEvent())
 | 
			
		||||
    // })
 | 
			
		||||
 | 
			
		||||
    this.socket.on('server:game-event', (data: any) => {
 | 
			
		||||
      this.gameEventManager.handleGameEvent(data)
 | 
			
		||||
    })
 | 
			
		||||
@@ -77,6 +69,13 @@ export class SocketIoClientService extends ServiceBase {
 | 
			
		||||
    this.socket.on('server:game-event-ack', async (data: any, callback: any) => {
 | 
			
		||||
      await this.gameEventManager.handleGameEventAck(data, callback)
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    this.socket.onAny((eventName, eventData) => {
 | 
			
		||||
      if (eventName === 'server:game-event' || eventName === 'server:game-event-ack') {
 | 
			
		||||
        const { event, data } = eventData
 | 
			
		||||
        this.logger.debug(`Received event: ${event}`, data)
 | 
			
		||||
      }
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  sendMessage(event: string, data: any): void {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										9
									
								
								src/stores/gameOptions.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/stores/gameOptions.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
import type { GameOptions } from '@/common/interfaces'
 | 
			
		||||
import { defineStore } from 'pinia'
 | 
			
		||||
import { ref } from 'vue'
 | 
			
		||||
 | 
			
		||||
export const useGameOptionsStore = defineStore('gameOptions', () => {
 | 
			
		||||
  const gameOptions = ref<GameOptions>()
 | 
			
		||||
 | 
			
		||||
  return { gameOptions }
 | 
			
		||||
})
 | 
			
		||||
@@ -40,20 +40,16 @@ function copySeed() {
 | 
			
		||||
<template>
 | 
			
		||||
  <div class="block">
 | 
			
		||||
    <section class="block info">
 | 
			
		||||
      <p>
 | 
			
		||||
        Running: {{ sessionState?.sessionInProgress }} Seed: {{ sessionState?.seed }}
 | 
			
		||||
        <button @click="copySeed">Copy!</button>
 | 
			
		||||
      </p>
 | 
			
		||||
      <p>Running: {{ sessionState?.sessionInProgress }}</p>
 | 
			
		||||
      <p>Seed: {{ sessionState?.seed }}</p>
 | 
			
		||||
      <p>
 | 
			
		||||
        FreeEnds: {{ gameState?.boardFreeEnds }} - Current Player:{{
 | 
			
		||||
          gameState?.currentPlayer?.name
 | 
			
		||||
        }}
 | 
			
		||||
        - Score: {{ sessionState?.scoreboard }}
 | 
			
		||||
      </p>
 | 
			
		||||
      <p v-if="sessionState?.id">
 | 
			
		||||
        SessionID: {{ sessionState.id }} PlayerID: {{ playerState?.id }} - canMakeMove
 | 
			
		||||
        {{ canMakeMove }}
 | 
			
		||||
      </p>
 | 
			
		||||
      <p>Score: {{ sessionState?.scoreboard }}</p>
 | 
			
		||||
      <p v-if="sessionState?.id">SessionID: {{ sessionState.id }}</p>
 | 
			
		||||
      <p>PlayerID: {{ playerState?.id }}</p>
 | 
			
		||||
    </section>
 | 
			
		||||
    <section class="block">
 | 
			
		||||
      <div class="game-container">
 | 
			
		||||
@@ -107,9 +103,12 @@ function copySeed() {
 | 
			
		||||
  justify-content: center;
 | 
			
		||||
}
 | 
			
		||||
.info {
 | 
			
		||||
  position: absolute;
 | 
			
		||||
  top: 0;
 | 
			
		||||
  left: 0;
 | 
			
		||||
  color: white;
 | 
			
		||||
  opacity: 0.1;
 | 
			
		||||
  position: fixed;
 | 
			
		||||
  top: 200px;
 | 
			
		||||
  left: 10px;
 | 
			
		||||
  z-index: 20;
 | 
			
		||||
  pointer-events: none;
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
 
 | 
			
		||||
@@ -9,25 +9,27 @@ import type { MatchSessionDto } from '@/common/interfaces'
 | 
			
		||||
import { useEventBusStore } from '@/stores/eventBus'
 | 
			
		||||
import { useAuthStore } from '@/stores/auth'
 | 
			
		||||
import { copyToclipboard } from '@/common/helpers'
 | 
			
		||||
import { useGameOptionsStore } from '@/stores/gameOptions'
 | 
			
		||||
 | 
			
		||||
let background = ref<string>('green')
 | 
			
		||||
let teamed = ref<boolean>(false)
 | 
			
		||||
let seed = ref<string>('')
 | 
			
		||||
let sessionName = ref('Test Value')
 | 
			
		||||
let sessionId = ref('')
 | 
			
		||||
let matchSessions = ref<MatchSessionDto[]>([])
 | 
			
		||||
let dataInterval: any
 | 
			
		||||
 | 
			
		||||
const router = useRouter()
 | 
			
		||||
const gameStore = useGameStore()
 | 
			
		||||
const auth = useAuthStore()
 | 
			
		||||
const gameOptionsStore = useGameOptionsStore()
 | 
			
		||||
 | 
			
		||||
const socketService: any = inject('socket')
 | 
			
		||||
const gameService: GameService = inject<GameService>('game') as GameService
 | 
			
		||||
const logger: LoggingService = inject<LoggingService>('logger') as LoggingService
 | 
			
		||||
 | 
			
		||||
const { readyForStart, sessionState, isSessionStarted, playerState, amIHost } =
 | 
			
		||||
  storeToRefs(gameStore)
 | 
			
		||||
const { updateSessionState, updatePlayerState, updateGameState } = gameStore
 | 
			
		||||
const { sessionState, isSessionStarted, playerState, amIHost } = storeToRefs(gameStore)
 | 
			
		||||
const { user } = storeToRefs(auth)
 | 
			
		||||
const { gameOptions } = storeToRefs(gameOptionsStore)
 | 
			
		||||
 | 
			
		||||
// function setPlayerReady() {
 | 
			
		||||
//   logger.debug('Starting game')
 | 
			
		||||
@@ -53,26 +55,12 @@ eventBus.subscribe('window-before-unload', () => {
 | 
			
		||||
async function createMatch() {
 | 
			
		||||
  logger.debug('Creating match')
 | 
			
		||||
  await socketService.connect()
 | 
			
		||||
  gameOptions.value = { background: background.value }
 | 
			
		||||
  const id = await gameService.createMatchSession(sessionName.value, seed.value)
 | 
			
		||||
  logger.debug('Match created successfully')
 | 
			
		||||
  router.push({ name: 'match', params: { id } })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function cancelMatch() {
 | 
			
		||||
  logger.debug('Cancelling match')
 | 
			
		||||
  await gameService.cancelMatchSession(sessionId.value)
 | 
			
		||||
  await socketService.disconnect()
 | 
			
		||||
  sessionId.value = ''
 | 
			
		||||
  seed.value = ''
 | 
			
		||||
  sessionName.value = ''
 | 
			
		||||
  updateSessionState(undefined)
 | 
			
		||||
  updatePlayerState(undefined)
 | 
			
		||||
  updateGameState(undefined)
 | 
			
		||||
 | 
			
		||||
  logger.debug('Match cancelled successfully')
 | 
			
		||||
  loadData()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function joinMatch(id: string) {
 | 
			
		||||
  if (id) {
 | 
			
		||||
    await socketService.connect()
 | 
			
		||||
@@ -90,19 +78,16 @@ async function deleteMatch(id: string) {
 | 
			
		||||
 | 
			
		||||
async function loadData() {
 | 
			
		||||
  const listResponse = await gameService.listMatchSessions()
 | 
			
		||||
  console.log('listResponse :>> ', listResponse)
 | 
			
		||||
  matchSessions.value = listResponse.data
 | 
			
		||||
  sessionName.value = `Test #${listResponse.pagination.total + 1}`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
onMounted(() => {
 | 
			
		||||
  logger.debug('Home view mounted')
 | 
			
		||||
  loadData()
 | 
			
		||||
  dataInterval = setInterval(loadData, 5000)
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
onUnmounted(() => {
 | 
			
		||||
  logger.debug('Home view unmounted')
 | 
			
		||||
  clearInterval(dataInterval)
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
@@ -117,32 +102,58 @@ function copy(sessionSeed: string) {
 | 
			
		||||
    <section class="section">
 | 
			
		||||
      <h1 class="title is-2">Welcome to the {{ user.username }}'s Home Page</h1>
 | 
			
		||||
      <div class="block">
 | 
			
		||||
        <p>This is a protected route.</p>
 | 
			
		||||
        <p>{{ sessionState || 'No session' }}</p>
 | 
			
		||||
        <p>{{ playerState || 'No player state' }}</p>
 | 
			
		||||
        <p>Session started: {{ isSessionStarted }}</p>
 | 
			
		||||
        <p>Host: {{ amIHost }}</p>
 | 
			
		||||
      </div>
 | 
			
		||||
      <div class="block" v-if="!isSessionStarted">
 | 
			
		||||
        <div class="grid">
 | 
			
		||||
          <div class="cell">
 | 
			
		||||
            <input
 | 
			
		||||
              class="input"
 | 
			
		||||
              style="margin-bottom: 0"
 | 
			
		||||
              v-model="sessionName"
 | 
			
		||||
              placeholder="Session Name"
 | 
			
		||||
            />
 | 
			
		||||
          </div>
 | 
			
		||||
          <div class="cell">
 | 
			
		||||
            <input class="input" style="margin-bottom: 0" v-model="seed" placeholder="Seed" />
 | 
			
		||||
        <div class="field">
 | 
			
		||||
          <label class="label">Name</label>
 | 
			
		||||
          <div class="control">
 | 
			
		||||
            <input type="text" class="input" v-model="sessionName" placeholder="Session Name" />
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="field">
 | 
			
		||||
          <label class="label">Seed</label>
 | 
			
		||||
          <div class="control">
 | 
			
		||||
            <input
 | 
			
		||||
              type="text"
 | 
			
		||||
              class="input"
 | 
			
		||||
              style="margin-bottom: 0"
 | 
			
		||||
              v-model="seed"
 | 
			
		||||
              placeholder="Type the session seed here!"
 | 
			
		||||
            />
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <div class="grid">
 | 
			
		||||
          <div class="cell">
 | 
			
		||||
            <div class="field">
 | 
			
		||||
              <label for="background" class="label">Background color</label>
 | 
			
		||||
              <div class="control">
 | 
			
		||||
                <div class="select">
 | 
			
		||||
                  <select v-model="background" name="background">
 | 
			
		||||
                    <option value="green">Green Fabric</option>
 | 
			
		||||
                    <option value="gray">Gray Fabric</option>
 | 
			
		||||
                    <option value="blue">Blue Fabric</option>
 | 
			
		||||
                    <option value="yellow">Yellow Fabric</option>
 | 
			
		||||
                    <option value="red">Red Fabric</option>
 | 
			
		||||
                  </select>
 | 
			
		||||
                </div>
 | 
			
		||||
              </div>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class="field">
 | 
			
		||||
              <div class="control">
 | 
			
		||||
                <label for="teamed" class="checkbox">
 | 
			
		||||
                  <input v-model="teamed" name="teamed" type="checkbox" />
 | 
			
		||||
                  Crossed game ({{ teamed }})
 | 
			
		||||
                </label>
 | 
			
		||||
              </div>
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
          <div class="cell"></div>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
      <button class="button" @click="createMatch" v-if="!isSessionStarted">
 | 
			
		||||
        Create Match Session
 | 
			
		||||
      </button>
 | 
			
		||||
      <div class="block" v-if="!isSessionStarted"></div>
 | 
			
		||||
 | 
			
		||||
      <button class="button is-primary" @click.once="createMatch">Create Match Session</button>
 | 
			
		||||
    </section>
 | 
			
		||||
    <section class="section available-sessions" v-if="!isSessionStarted">
 | 
			
		||||
    <section class="section available-sessions">
 | 
			
		||||
      <h2 class="title is-4">Available Sessions</h2>
 | 
			
		||||
      <div class="block">
 | 
			
		||||
        <div v-if="matchSessions.length === 0">
 | 
			
		||||
@@ -150,16 +161,27 @@ function copy(sessionSeed: string) {
 | 
			
		||||
        </div>
 | 
			
		||||
        <div v-else class="grid is-col-min-12">
 | 
			
		||||
          <div class="cell" v-for="session in matchSessions" :key="session.id">
 | 
			
		||||
            <p class="title is-6">{{ session.name }}</p>
 | 
			
		||||
            <p>ID: {{ session._id }}</p>
 | 
			
		||||
            <p>Players: {{ session.players.length }}</p>
 | 
			
		||||
            <p>
 | 
			
		||||
              Seed: {{ session.seed }}
 | 
			
		||||
              <button @click="() => copy(session.seed)">Copy</button>
 | 
			
		||||
            </p>
 | 
			
		||||
            <p>Status: {{ session.status }}</p>
 | 
			
		||||
            <button class="button" @click="() => joinMatch(session._id)">Join</button>
 | 
			
		||||
            <button class="button" @click="() => deleteMatch(session._id)">Delete</button>
 | 
			
		||||
            <div class="card">
 | 
			
		||||
              <div class="card-content">
 | 
			
		||||
                <p class="title is-6">{{ session.name }}</p>
 | 
			
		||||
                <p>ID: {{ session._id }}</p>
 | 
			
		||||
                <p>Players: {{ session.players.length }}</p>
 | 
			
		||||
                <p>
 | 
			
		||||
                  Seed: {{ session.seed }}
 | 
			
		||||
                  <button class="button is-small" @click="() => copy(session.seed)">Copy</button>
 | 
			
		||||
                </p>
 | 
			
		||||
                <p>Status: {{ session.status }}</p>
 | 
			
		||||
                <div class="buttons is-centered mt-4"></div>
 | 
			
		||||
              </div>
 | 
			
		||||
              <div class="card-footer">
 | 
			
		||||
                <p class="card-footer-item">
 | 
			
		||||
                  <a href="#" @click.once.prevent="() => joinMatch(session._id)"> Join </a>
 | 
			
		||||
                </p>
 | 
			
		||||
                <p class="card-footer-item">
 | 
			
		||||
                  <a href="#" @click.once.prevent="() => deleteMatch(session._id)"> Delete </a>
 | 
			
		||||
                </p>
 | 
			
		||||
              </div>
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
 
 | 
			
		||||
@@ -4,6 +4,7 @@ import type { GameService } from '@/services/GameService'
 | 
			
		||||
import type { LoggingService } from '@/services/LoggingService'
 | 
			
		||||
import { useEventBusStore } from '@/stores/eventBus'
 | 
			
		||||
import { useGameStore } from '@/stores/game'
 | 
			
		||||
import { useGameOptionsStore } from '@/stores/gameOptions'
 | 
			
		||||
import { storeToRefs } from 'pinia'
 | 
			
		||||
import { inject, onBeforeMount, ref } from 'vue'
 | 
			
		||||
import { useRoute, useRouter } from 'vue-router'
 | 
			
		||||
@@ -12,6 +13,7 @@ const route = useRoute()
 | 
			
		||||
const router = useRouter()
 | 
			
		||||
const gameStore = useGameStore()
 | 
			
		||||
const eventBus = useEventBusStore()
 | 
			
		||||
const gameOptionsStore = useGameOptionsStore()
 | 
			
		||||
const socketService: any = inject('socket')
 | 
			
		||||
const gameService: GameService = inject<GameService>('game') as GameService
 | 
			
		||||
const logger: LoggingService = inject<LoggingService>('logger') as LoggingService
 | 
			
		||||
@@ -22,6 +24,7 @@ let matchSession = ref<MatchSessionDto | undefined>(undefined)
 | 
			
		||||
const { readyForStart, sessionState, isSessionStarted, playerState, amIHost } =
 | 
			
		||||
  storeToRefs(gameStore)
 | 
			
		||||
const { updateSessionState, updatePlayerState, updateGameState } = gameStore
 | 
			
		||||
const { gameOptions } = storeToRefs(gameOptionsStore)
 | 
			
		||||
 | 
			
		||||
async function setPlayerReady() {
 | 
			
		||||
  logger.debug('Starting game')
 | 
			
		||||
@@ -35,7 +38,7 @@ async function setPlayerReady() {
 | 
			
		||||
  }
 | 
			
		||||
  await socketService.sendMessage('client:set-player-ready', {
 | 
			
		||||
    userId: playerState.value.id,
 | 
			
		||||
    sessionId: sessionState.value.id
 | 
			
		||||
    sessionId: sessionState.value.id,
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -45,7 +48,7 @@ async function startMatch() {
 | 
			
		||||
  if (sessionId) {
 | 
			
		||||
    await socketService.sendMessageWithAck('client:start-session', {
 | 
			
		||||
      sessionId: sessionId,
 | 
			
		||||
      playerId: playerId
 | 
			
		||||
      playerId: playerId,
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -89,7 +92,7 @@ onBeforeMount(() => {
 | 
			
		||||
 | 
			
		||||
<template>
 | 
			
		||||
  <div>
 | 
			
		||||
    <h1 class="title is-2">Match Page</h1>
 | 
			
		||||
    <h1 class="title is-2">Match Page {{ isSessionStarted }}</h1>
 | 
			
		||||
    <div class="block" v-if="matchSession">
 | 
			
		||||
      <p>Session ID: {{ matchSession._id }}</p>
 | 
			
		||||
      <p>Session Name: {{ matchSession.name }}</p>
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user