working flow
This commit is contained in:
		@@ -1,27 +1,18 @@
 | 
			
		||||
import {
 | 
			
		||||
  Application,
 | 
			
		||||
  Assets,
 | 
			
		||||
  Container,
 | 
			
		||||
  EventEmitter,
 | 
			
		||||
  Graphics,
 | 
			
		||||
  Sprite,
 | 
			
		||||
  Text,
 | 
			
		||||
  Ticker
 | 
			
		||||
} from 'pixi.js'
 | 
			
		||||
import { Application, Assets, Container, EventEmitter, Sprite, Text, Ticker } from 'pixi.js'
 | 
			
		||||
import { Scale, type ScaleFunction } from '@/game/utilities/scale'
 | 
			
		||||
import type { AnimationOptions, Movement, PlayerDto, TileDto } from '@/common/interfaces'
 | 
			
		||||
import { Tile } from '@/game/Tile'
 | 
			
		||||
import { DIRECTIONS, createContainer, isTilePair } from '@/common/helpers'
 | 
			
		||||
import { DIRECTIONS, createContainer, createCrosshair, isTilePair } from '@/common/helpers'
 | 
			
		||||
import { createText } from '@/game/utilities/fonts'
 | 
			
		||||
import { LoggingService } from '@/services/LoggingService'
 | 
			
		||||
import { inject } from 'vue'
 | 
			
		||||
import { GlowFilter } from 'pixi-filters'
 | 
			
		||||
import { ORIENTATION_ANGLES } from '@/common/constants'
 | 
			
		||||
import type { OtherHand } from './OtherHand'
 | 
			
		||||
 | 
			
		||||
export class Board extends EventEmitter {
 | 
			
		||||
  private _scale: number = 1
 | 
			
		||||
  private _canMove: boolean = false
 | 
			
		||||
  private logger = inject<LoggingService>('logger')!
 | 
			
		||||
  private logger: LoggingService = new LoggingService()
 | 
			
		||||
 | 
			
		||||
  ticker: Ticker
 | 
			
		||||
  height: number
 | 
			
		||||
@@ -50,6 +41,7 @@ export class Board extends EventEmitter {
 | 
			
		||||
  playerHand: Tile[] = []
 | 
			
		||||
  firstTile?: Tile
 | 
			
		||||
  currentPlayer!: PlayerDto
 | 
			
		||||
  otherPlayerHands: OtherHand[] = []
 | 
			
		||||
 | 
			
		||||
  constructor(app: Application) {
 | 
			
		||||
    super()
 | 
			
		||||
@@ -93,18 +85,19 @@ export class Board extends EventEmitter {
 | 
			
		||||
      visible: false
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    const verticalLine = new Graphics()
 | 
			
		||||
      .moveTo(this.scaleX(0), 0)
 | 
			
		||||
      .lineTo(this.scaleX(0), this.height)
 | 
			
		||||
      .stroke(0xff0000)
 | 
			
		||||
    const horizontalLine = new Graphics()
 | 
			
		||||
      .moveTo(0, this.scaleY(0))
 | 
			
		||||
      .lineTo(this.width, this.scaleY(0))
 | 
			
		||||
      .stroke(0xff0000)
 | 
			
		||||
    verticalLine.alpha = 0.2
 | 
			
		||||
    horizontalLine.alpha = 0.2
 | 
			
		||||
    this.tilesContainer.addChild(verticalLine)
 | 
			
		||||
    this.tilesContainer.addChild(horizontalLine)
 | 
			
		||||
    createCrosshair(this.tilesContainer, 0xff0000, {
 | 
			
		||||
      width: this.width,
 | 
			
		||||
      height: this.height,
 | 
			
		||||
      x: this.scaleX(0),
 | 
			
		||||
      y: this.scaleY(0)
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    createCrosshair(this.interactionContainer, 0xffff00, {
 | 
			
		||||
      width: this.width,
 | 
			
		||||
      height: this.height,
 | 
			
		||||
      x: this.scaleX(0),
 | 
			
		||||
      y: this.scaleY(0)
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    this.textContainer = createContainer({
 | 
			
		||||
      width: this.width,
 | 
			
		||||
@@ -135,15 +128,6 @@ export class Board extends EventEmitter {
 | 
			
		||||
    this.calculateScale()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  get canMove() {
 | 
			
		||||
    return this._canMove
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  set canMove(value: boolean) {
 | 
			
		||||
    this._canMove = value
 | 
			
		||||
    this.updateCanMoveText()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  setPlayerHand(tiles: Tile[]) {
 | 
			
		||||
    this.playerHand = tiles
 | 
			
		||||
  }
 | 
			
		||||
@@ -153,12 +137,8 @@ export class Board extends EventEmitter {
 | 
			
		||||
    this.textContainer.addChild(createText(text, this.scaleX(0), 100))
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private updateCanMoveText() {
 | 
			
		||||
    if (this.canMove) {
 | 
			
		||||
      this.showText('Your turn!')
 | 
			
		||||
    } else {
 | 
			
		||||
      this.showText('Waiting for players')
 | 
			
		||||
    }
 | 
			
		||||
  async setPlayerTurn(player: PlayerDto) {
 | 
			
		||||
    this.showText('Your turn!')
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async setServerPlayerTurn(currentPlayer: PlayerDto) {
 | 
			
		||||
@@ -168,6 +148,9 @@ export class Board extends EventEmitter {
 | 
			
		||||
  async playerMove(move: any, playerId: string) {
 | 
			
		||||
    const { move: lastMove } = move
 | 
			
		||||
    if (lastMove === null) {
 | 
			
		||||
      setTimeout(() => {
 | 
			
		||||
        this.emit('game:tile-animation-ended')
 | 
			
		||||
      }, 500)
 | 
			
		||||
      return
 | 
			
		||||
    }
 | 
			
		||||
    if (
 | 
			
		||||
@@ -252,21 +235,46 @@ export class Board extends EventEmitter {
 | 
			
		||||
    tile.reScale(this.scale)
 | 
			
		||||
    this.tiles.push(tile)
 | 
			
		||||
 | 
			
		||||
    await this.animateTile(tile, x, y, orientation, move)
 | 
			
		||||
    this.emit('game:tile-animation-ended', tile.toPlain())
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  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: this.scaleX(x),
 | 
			
		||||
      y: this.scaleY(y),
 | 
			
		||||
      x: targetX,
 | 
			
		||||
      y: targetY,
 | 
			
		||||
      rotation: ORIENTATION_ANGLES[orientation],
 | 
			
		||||
      duration: 20
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    tile.setPosition(this.scaleX(x), this.scaleY(y))
 | 
			
		||||
    const tempAlpha = tile.alpha
 | 
			
		||||
    tile.alpha = 0
 | 
			
		||||
    const clonedTile = tile.clone()
 | 
			
		||||
    clonedTile.addTo(this.tilesContainer)
 | 
			
		||||
    const pos = this.getAnimationInitialPoosition(move)
 | 
			
		||||
    clonedTile.setPosition(this.scaleX(pos.x), this.scaleY(pos.y))
 | 
			
		||||
    await clonedTile.animateTo(animation)
 | 
			
		||||
    clonedTile.removeFromParent()
 | 
			
		||||
    tile.setOrientation(orientation)
 | 
			
		||||
    tile.setPosition(targetX, targetY)
 | 
			
		||||
    tile.alpha = tempAlpha
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
    // tile.setPosition(this.scaleX(0), this.height + tile.height / 2)
 | 
			
		||||
    // console.log('going to animate', tile.pips)
 | 
			
		||||
    // await tile.animateTo(animation)
 | 
			
		||||
    // console.log('animated', tile.pips)
 | 
			
		||||
    this.emit('game:tile-animation-ended', tile.toPlain())
 | 
			
		||||
  getAnimationInitialPoosition(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) }
 | 
			
		||||
    }
 | 
			
		||||
    const position = otherHand.position
 | 
			
		||||
    switch (position) {
 | 
			
		||||
      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) }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getPlayedTile(id: string): Tile | undefined {
 | 
			
		||||
@@ -315,16 +323,14 @@ export class Board extends EventEmitter {
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async updateBoard(move: Movement) {
 | 
			
		||||
  async updateBoard(move: Movement, tile: Tile | undefined) {
 | 
			
		||||
    try {
 | 
			
		||||
      const { tile: tileDto } = move
 | 
			
		||||
      const tile = this.getTileInHand(tileDto?.id ?? '')
 | 
			
		||||
 | 
			
		||||
      // const { tileDto: tileDto } = move
 | 
			
		||||
      // const tile = this.getTileInHand(tileDto?.id ?? '')
 | 
			
		||||
      this.movements.push(move)
 | 
			
		||||
      if (tile === undefined) {
 | 
			
		||||
        return
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      this.movements.push(move)
 | 
			
		||||
      await this.addTile(tile, move)
 | 
			
		||||
      this.setFreeEnd(move)
 | 
			
		||||
    } catch (error) {
 | 
			
		||||
 
 | 
			
		||||
@@ -5,17 +5,17 @@ import { Tile } from '@/game/Tile'
 | 
			
		||||
import { Hand } from '@/game/Hand'
 | 
			
		||||
import type { GameDto, Movement, PlayerDto, TileDto } from '@/common/interfaces'
 | 
			
		||||
import type { SocketIoClientService } from '@/services/SocketIoClientService'
 | 
			
		||||
import { useEventBusStore } from '@/stores/eventBus'
 | 
			
		||||
import { wait } from '@/common/helpers'
 | 
			
		||||
import { Actions } from 'pixi-actions'
 | 
			
		||||
import { OtherHand } from './OtherHand'
 | 
			
		||||
 | 
			
		||||
export class Game {
 | 
			
		||||
  public board!: Board
 | 
			
		||||
  public hand!: Hand
 | 
			
		||||
  private app: Application = new Application()
 | 
			
		||||
  private selectedTile: TileDto | undefined
 | 
			
		||||
  private eventBus: any = useEventBusStore()
 | 
			
		||||
  private currentMove: Movement | undefined
 | 
			
		||||
  private otherHands: OtherHand[] = []
 | 
			
		||||
 | 
			
		||||
  constructor(
 | 
			
		||||
    private options: { boardScale: number; handScale: number; width: number; height: number } = {
 | 
			
		||||
@@ -24,24 +24,29 @@ export class Game {
 | 
			
		||||
      width: 1200,
 | 
			
		||||
      height: 800
 | 
			
		||||
    },
 | 
			
		||||
    private emit: any,
 | 
			
		||||
    private socketService: SocketIoClientService,
 | 
			
		||||
    private playerId: string,
 | 
			
		||||
    private sessionId: string
 | 
			
		||||
  ) {}
 | 
			
		||||
 | 
			
		||||
  async setup(): Promise<HTMLCanvasElement> {
 | 
			
		||||
    const width = 1200
 | 
			
		||||
    const height = 800
 | 
			
		||||
    const width = this.options.width || 1200
 | 
			
		||||
    const height = this.options.height || 800
 | 
			
		||||
 | 
			
		||||
    await this.app.init({ width, height })
 | 
			
		||||
    this.app.ticker.add((tick) => Actions.tick(tick.deltaTime / 60))
 | 
			
		||||
    return this.app.canvas
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async start() {
 | 
			
		||||
  async start(players: PlayerDto[] = []) {
 | 
			
		||||
    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')
 | 
			
		||||
    ]
 | 
			
		||||
    this.initOtherHands(players)
 | 
			
		||||
    this.hand.scale = this.options.handScale
 | 
			
		||||
    this.board.scale = this.options.boardScale
 | 
			
		||||
    this.setBoardEvents()
 | 
			
		||||
@@ -54,6 +59,32 @@ export class Game {
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  initOtherHands(players: PlayerDto[]) {
 | 
			
		||||
    const myIndex = players.findIndex((player) => player.id === this.playerId)
 | 
			
		||||
    const copy = [...players]
 | 
			
		||||
    const cut = copy.splice(myIndex)
 | 
			
		||||
    cut.shift()
 | 
			
		||||
    const final = cut.concat(copy)
 | 
			
		||||
 | 
			
		||||
    for (let i = 0; i < final.length; i++) {
 | 
			
		||||
      const hand = this.otherHands[i]
 | 
			
		||||
      hand.setPlayer(final[i])
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this.board.otherPlayerHands = this.otherHands
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  updateOtherHands(gameState: GameDto) {
 | 
			
		||||
    const players = gameState.players
 | 
			
		||||
 | 
			
		||||
    players.forEach((player) => {
 | 
			
		||||
      const hand = this.otherHands.find((hand) => hand.player?.id === player.id)
 | 
			
		||||
      if (hand) {
 | 
			
		||||
        hand.setHand(player.hand)
 | 
			
		||||
      }
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async preload() {
 | 
			
		||||
    await Assets.load(assets)
 | 
			
		||||
  }
 | 
			
		||||
@@ -82,11 +113,11 @@ export class Game {
 | 
			
		||||
        sessionId: this.sessionId,
 | 
			
		||||
        move: move
 | 
			
		||||
      })
 | 
			
		||||
      await this.board.updateBoard(move)
 | 
			
		||||
      await this.board.updateBoard(move, undefined)
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    this.hand.on('nextClick', () => {
 | 
			
		||||
      this.socketService.sendMessage('client:set-player-ready', {
 | 
			
		||||
      this.socketService.sendMessage('client:set-client-ready-for-next-game', {
 | 
			
		||||
        userId: this.playerId,
 | 
			
		||||
        sessionId: this.sessionId
 | 
			
		||||
      })
 | 
			
		||||
@@ -120,24 +151,18 @@ export class Game {
 | 
			
		||||
    return validEnds
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public setCanMakeMove(value: boolean) {
 | 
			
		||||
    this.hand.setCanMove(value, this.board.count === 0, this.board.freeEnds)
 | 
			
		||||
    this.board.canMove = value
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async setNextPlayer(state: GameDto) {
 | 
			
		||||
    const currentPlayer = state?.currentPlayer!
 | 
			
		||||
    if (currentPlayer.id !== this.playerId) {
 | 
			
		||||
      this.setCanMakeMove(false)
 | 
			
		||||
      this.board.setServerPlayerTurn(currentPlayer)
 | 
			
		||||
    if (currentPlayer.id === this.playerId) {
 | 
			
		||||
      this.hand.prepareForMove(this.board.count === 0, this.board.freeEnds)
 | 
			
		||||
      this.board.setPlayerTurn(currentPlayer)
 | 
			
		||||
    } else {
 | 
			
		||||
      this.setCanMakeMove(true)
 | 
			
		||||
      this.board.setServerPlayerTurn(currentPlayer)
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private setBoardEvents() {
 | 
			
		||||
    this.board.on('game:board-left-action-click', async (data) => {
 | 
			
		||||
      console.log('left data :>> ', data)
 | 
			
		||||
      if (this.selectedTile === undefined) return
 | 
			
		||||
      const move: Movement = {
 | 
			
		||||
        tile: this.selectedTile,
 | 
			
		||||
@@ -146,12 +171,11 @@ export class Game {
 | 
			
		||||
        ...data
 | 
			
		||||
      }
 | 
			
		||||
      this.currentMove = move
 | 
			
		||||
      this.hand.tileMoved(this.selectedTile)
 | 
			
		||||
      await this.board.updateBoard({ ...move, tile: this.selectedTile })
 | 
			
		||||
      const tile = this.hand.tileMoved(this.selectedTile)
 | 
			
		||||
      await this.board.updateBoard({ ...move, tile: this.selectedTile }, tile)
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    this.board.on('game:board-right-action-click', async (data) => {
 | 
			
		||||
      console.log('right data :>> ', data)
 | 
			
		||||
      if (this.selectedTile === undefined) return
 | 
			
		||||
      const move: Movement = {
 | 
			
		||||
        tile: this.selectedTile,
 | 
			
		||||
@@ -160,31 +184,25 @@ export class Game {
 | 
			
		||||
        ...data
 | 
			
		||||
      }
 | 
			
		||||
      this.currentMove = move
 | 
			
		||||
      this.hand.tileMoved(this.selectedTile)
 | 
			
		||||
      await this.board.updateBoard({ ...move, tile: this.selectedTile })
 | 
			
		||||
      const tile = this.hand.tileMoved(this.selectedTile)
 | 
			
		||||
      await this.board.updateBoard({ ...move, tile: this.selectedTile }, tile)
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    this.board.on('game:tile-animation-ended', async (tile) => {
 | 
			
		||||
      console.log('animation ended', tile)
 | 
			
		||||
      if (tile.playerId === this.playerId) {
 | 
			
		||||
      if (tile !== null && tile !== undefined && tile.playerId === this.playerId) {
 | 
			
		||||
        this.socketService.sendMessage('client:player-move', {
 | 
			
		||||
          sessionId: this.sessionId,
 | 
			
		||||
          move: this.currentMove
 | 
			
		||||
        })
 | 
			
		||||
      } else {
 | 
			
		||||
        this.socketService.sendMessage('client:animation-ended', {
 | 
			
		||||
          sessionId: this.sessionId,
 | 
			
		||||
          userId: this.playerId
 | 
			
		||||
        })
 | 
			
		||||
      }
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // sendMoveEvent(move: Movement) {
 | 
			
		||||
  //   this.board.on('game:tile-animation-ended', async (tile) => {
 | 
			
		||||
  //     this.eventBus.publish('game:tile-animation-ended', tile)
 | 
			
		||||
  //     // this.socketService.sendMessageWithAck('client:tile-animation-ended', {
 | 
			
		||||
  //     //   sessionId: this.sessionId,
 | 
			
		||||
  //     //   tile
 | 
			
		||||
  //     // })
 | 
			
		||||
  //   })
 | 
			
		||||
  // }
 | 
			
		||||
 | 
			
		||||
  gameFinished(data: any) {
 | 
			
		||||
    this.hand.gameFinished()
 | 
			
		||||
    this.board.gameFinished(data)
 | 
			
		||||
@@ -197,6 +215,13 @@ export class Game {
 | 
			
		||||
 | 
			
		||||
  serverPlayerMove(data: any, playerId: string) {
 | 
			
		||||
    this.board.playerMove(data, playerId)
 | 
			
		||||
 | 
			
		||||
    if (!(data.move === undefined || data.move === null)) {
 | 
			
		||||
      const otherHand = this.otherHands.find((hand) => hand.player?.id === data.move.playerId)
 | 
			
		||||
      if (otherHand) {
 | 
			
		||||
        otherHand.update(data.move)
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private removeBoardEvents() {
 | 
			
		||||
 
 | 
			
		||||
@@ -88,30 +88,31 @@ export class Hand extends EventEmitter {
 | 
			
		||||
    this.scaleY = Scale([-scaleYSteps, scaleYSteps], [0, this.height])
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  setCanMove(value: boolean, isFirstMove: boolean, freeEnds?: [number, number]) {
 | 
			
		||||
    console.log('this.tiles :>> ', this.tiles.length)
 | 
			
		||||
    this.availableTiles =
 | 
			
		||||
      !value || isFirstMove
 | 
			
		||||
        ? this.tiles
 | 
			
		||||
        : this.tiles.filter((tile) => this.hasMoves(tile.toPlain(), freeEnds))
 | 
			
		||||
 | 
			
		||||
    console.log('this.availableTiles :>> ', this.availableTiles.length)
 | 
			
		||||
    if (value && this.availableTiles.length === 0) {
 | 
			
		||||
  prepareForMove(isFirstMove: boolean, freeEnds?: [number, number]) {
 | 
			
		||||
    this.availableTiles = isFirstMove
 | 
			
		||||
      ? this.tiles
 | 
			
		||||
      : this.tiles.filter((tile) => this.hasMoves(tile.toPlain(), freeEnds))
 | 
			
		||||
    if (this.availableTiles.length === 0) {
 | 
			
		||||
      this.createPassButton()
 | 
			
		||||
    } else {
 | 
			
		||||
      this.interactionsLayer.removeChild(this.buttonPass)
 | 
			
		||||
    }
 | 
			
		||||
    this.availableTiles.forEach((tile) => {
 | 
			
		||||
      if (value) {
 | 
			
		||||
        tile.animateTo({
 | 
			
		||||
          x: tile.x,
 | 
			
		||||
          y: tile.y - 10
 | 
			
		||||
        })
 | 
			
		||||
        // const action: Action = Actions.moveTo(tile.getSprite(), tile.x, tile.y - 10, 1,).play()
 | 
			
		||||
      }
 | 
			
		||||
      tile.interactive = value
 | 
			
		||||
      tile.animateTo({
 | 
			
		||||
        x: tile.x,
 | 
			
		||||
        y: tile.y - 10
 | 
			
		||||
      })
 | 
			
		||||
      tile.interactive = true
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  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._canMove = value
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  hasMoves(tile: TileDto, freeEnds?: [number, number]): boolean {
 | 
			
		||||
@@ -166,20 +167,12 @@ export class Hand extends EventEmitter {
 | 
			
		||||
    selected.alpha = 0.7
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public tileMoved(tileDto: TileDto) {
 | 
			
		||||
  public tileMoved(tileDto: TileDto): Tile | undefined {
 | 
			
		||||
    const tile = this.tiles.find((t) => t.id === tileDto.id)
 | 
			
		||||
 | 
			
		||||
    if (!tile) return
 | 
			
		||||
 | 
			
		||||
    this.availableTiles
 | 
			
		||||
      .filter((t) => t.id !== tileDto.id)
 | 
			
		||||
      .forEach((t) => {
 | 
			
		||||
        t.animateTo({
 | 
			
		||||
          x: t.x,
 | 
			
		||||
          y: t.y + 10
 | 
			
		||||
        })
 | 
			
		||||
      })
 | 
			
		||||
 | 
			
		||||
    this.afterMove()
 | 
			
		||||
    this.tiles = this.tiles.filter((t) => t.id !== tileDto.id)
 | 
			
		||||
 | 
			
		||||
    tile.interactive = false
 | 
			
		||||
@@ -187,6 +180,8 @@ export class Hand extends EventEmitter {
 | 
			
		||||
    tile.off('pointerdown')
 | 
			
		||||
    tile.off('pointerover')
 | 
			
		||||
    tile.off('pointerout')
 | 
			
		||||
    this.tilesLayer.removeChild(tile.getSprite())
 | 
			
		||||
    return tile
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private createPassButton() {
 | 
			
		||||
@@ -195,7 +190,10 @@ export class Hand extends EventEmitter {
 | 
			
		||||
    this.buttonPass = createButton(
 | 
			
		||||
      'PASS',
 | 
			
		||||
      { x, y: this.height / 2, width: 50, height: 20 },
 | 
			
		||||
      () => this.emit('game:button-pass-click'),
 | 
			
		||||
      () => {
 | 
			
		||||
        this.interactionsLayer.removeChild(this.buttonPass)
 | 
			
		||||
        this.emit('game:button-pass-click')
 | 
			
		||||
      },
 | 
			
		||||
      this.interactionsLayer
 | 
			
		||||
    )
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										118
									
								
								src/game/OtherHand.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										118
									
								
								src/game/OtherHand.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,118 @@
 | 
			
		||||
import { LoggingService } from '@/services/LoggingService'
 | 
			
		||||
import { Application, Container, Sprite, Texture } from 'pixi.js'
 | 
			
		||||
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'
 | 
			
		||||
 | 
			
		||||
export class OtherHand {
 | 
			
		||||
  tilesInitialNumber: number = 7
 | 
			
		||||
  player?: PlayerDto
 | 
			
		||||
  hand: Tile[] = []
 | 
			
		||||
  container: Container = new Container()
 | 
			
		||||
  height: number
 | 
			
		||||
  width: number
 | 
			
		||||
  scale: number = 0.5
 | 
			
		||||
  scaleY!: ScaleFunction
 | 
			
		||||
  scaleX!: ScaleFunction
 | 
			
		||||
  x: number = 0
 | 
			
		||||
  y: number = 0
 | 
			
		||||
  grain: number = 25
 | 
			
		||||
  logger: LoggingService = new LoggingService()
 | 
			
		||||
  tilesLayer!: Container
 | 
			
		||||
  interactionsLayer!: Container
 | 
			
		||||
 | 
			
		||||
  constructor(
 | 
			
		||||
    private app: Application,
 | 
			
		||||
    public position: 'left' | 'right' | 'top' = 'left'
 | 
			
		||||
  ) {
 | 
			
		||||
    this.height = 100
 | 
			
		||||
    this.width = 300
 | 
			
		||||
    app.stage.addChild(this.container)
 | 
			
		||||
    const { x, y } = this.getPosition()
 | 
			
		||||
    this.container.width = this.width
 | 
			
		||||
    this.container.height = this.height
 | 
			
		||||
    this.container.x = x
 | 
			
		||||
    this.container.y = y
 | 
			
		||||
    this.calculateScale()
 | 
			
		||||
    this.initLayers()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  setPlayer(player: PlayerDto) {
 | 
			
		||||
    this.player = player
 | 
			
		||||
    this.container.addChild(createText(`${player.name}`, this.width / 2, 12, playerNameText))
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  setHand(tiles: TileDto[]) {
 | 
			
		||||
    this.hand = tiles.map((tile) => new Tile(tile.id, this.app.ticker, undefined, this.scale))
 | 
			
		||||
    this.render()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  update(move: Movement) {
 | 
			
		||||
    this.hand = this.hand.filter((tile) => tile.id !== move?.tile?.id)
 | 
			
		||||
    this.render()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private render() {
 | 
			
		||||
    this.tilesLayer.removeChildren()
 | 
			
		||||
    const x = -9
 | 
			
		||||
    this.hand.forEach((tile, index) => {
 | 
			
		||||
      tile.setPosition(this.scaleX(x + index * 2), this.height / 2)
 | 
			
		||||
      this.tilesLayer.addChild(tile.getSprite())
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private addBg() {
 | 
			
		||||
    const bg = new Sprite(Texture.WHITE)
 | 
			
		||||
    bg.alpha = 0.08
 | 
			
		||||
    bg.width = this.width
 | 
			
		||||
    bg.height = this.height
 | 
			
		||||
    this.container.addChild(bg)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private getPosition() {
 | 
			
		||||
    let x = 0
 | 
			
		||||
    let y = 0
 | 
			
		||||
 | 
			
		||||
    if (this.position === 'left') {
 | 
			
		||||
      x = 0
 | 
			
		||||
      y = 30
 | 
			
		||||
    } else if (this.position === 'right') {
 | 
			
		||||
      x = this.app.canvas.width - this.width
 | 
			
		||||
      y = 30
 | 
			
		||||
    } else {
 | 
			
		||||
      x = (this.app.canvas.width - this.width) / 2
 | 
			
		||||
      y = 0
 | 
			
		||||
    }
 | 
			
		||||
    return { x, y }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private initLayers() {
 | 
			
		||||
    this.container.removeChildren()
 | 
			
		||||
    this.addBg()
 | 
			
		||||
    this.tilesLayer = createContainer({
 | 
			
		||||
      width: this.width,
 | 
			
		||||
      height: this.height,
 | 
			
		||||
      x: 0,
 | 
			
		||||
      y: 0,
 | 
			
		||||
      parent: this.container
 | 
			
		||||
    })
 | 
			
		||||
    this.interactionsLayer = createContainer({
 | 
			
		||||
      width: this.width,
 | 
			
		||||
      height: this.height,
 | 
			
		||||
      x: 0,
 | 
			
		||||
      y: 0,
 | 
			
		||||
      parent: this.container
 | 
			
		||||
    })
 | 
			
		||||
    this.container.addChild(this.tilesLayer)
 | 
			
		||||
    this.container.addChild(this.interactionsLayer)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private calculateScale() {
 | 
			
		||||
    const scaleXSteps = Math.floor(this.width / (this.grain * this.scale)) / 2
 | 
			
		||||
    const scaleYSteps = Math.floor(this.height / (this.grain * this.scale)) / 2
 | 
			
		||||
    this.scaleX = Scale([-scaleXSteps, scaleXSteps], [0, this.width])
 | 
			
		||||
    this.scaleY = Scale([-scaleYSteps, scaleYSteps], [0, this.height])
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,10 +1,11 @@
 | 
			
		||||
import type { AnimationOptions } from '@/common/interfaces'
 | 
			
		||||
import { Sprite, Texture, Ticker } from 'pixi.js'
 | 
			
		||||
import { Container, Sprite, Texture, Ticker } from 'pixi.js'
 | 
			
		||||
import { Tile } from './Tile'
 | 
			
		||||
 | 
			
		||||
export abstract class SpriteBase {
 | 
			
		||||
  private _interactive: boolean = false
 | 
			
		||||
  protected sprite: Sprite = new Sprite()
 | 
			
		||||
  private container?: Container
 | 
			
		||||
 | 
			
		||||
  constructor(
 | 
			
		||||
    protected ticker?: Ticker,
 | 
			
		||||
@@ -136,6 +137,11 @@ export abstract class SpriteBase {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  addTo(container: any) {
 | 
			
		||||
    this.container = container
 | 
			
		||||
    container.addChild(this.sprite)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  removeFromParent() {
 | 
			
		||||
    this.container?.removeChild(this.sprite)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -86,4 +86,11 @@ export class Tile extends SpriteBase {
 | 
			
		||||
    }
 | 
			
		||||
    this.orientation = value
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  clone(): Tile {
 | 
			
		||||
    const copy = new Tile(this.id, this.ticker, this.pips, this.scale, this.playerId)
 | 
			
		||||
    copy.selected = this.selected
 | 
			
		||||
    copy.orientation = this.orientation
 | 
			
		||||
    return copy
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -16,6 +16,16 @@ export const mainText = new TextStyle({
 | 
			
		||||
  stroke: '#658f56'
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
export const playerNameText = new TextStyle({
 | 
			
		||||
  dropShadow: dropShadowStyle,
 | 
			
		||||
  fill: '#a2a2a2',
 | 
			
		||||
  fontFamily: 'Arial, Helvetica, sans-serif',
 | 
			
		||||
  letterSpacing: 1,
 | 
			
		||||
  stroke: '#565656',
 | 
			
		||||
  fontSize: 15,
 | 
			
		||||
  fontWeight: 'bold'
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
export function createText(str: string, x: number, y: number, style: TextStyle = mainText) {
 | 
			
		||||
  const text = new Text({ text: str, style })
 | 
			
		||||
  text.anchor.set(0.5, 0.5)
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user