reworked event communication
This commit is contained in:
@ -9,13 +9,14 @@ import {
|
||||
Ticker
|
||||
} from 'pixi.js'
|
||||
import { Scale, type ScaleFunction } from '@/game/utilities/scale'
|
||||
import type { GameState, Movement, TileDto } from '@/common/interfaces'
|
||||
import type { AnimationOptions, Movement, PlayerDto, TileDto } from '@/common/interfaces'
|
||||
import { Tile } from '@/game/Tile'
|
||||
import { DIRECTIONS, createContainer, isTilePair, isTileVertical } from '@/common/helpers'
|
||||
import { DIRECTIONS, createContainer, 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'
|
||||
|
||||
export class Board extends EventEmitter {
|
||||
private _scale: number = 1
|
||||
@ -28,7 +29,6 @@ export class Board extends EventEmitter {
|
||||
grain: number = 25
|
||||
scaleY: ScaleFunction
|
||||
scaleX: ScaleFunction
|
||||
state?: GameState
|
||||
container!: Container
|
||||
initialContainer!: Container
|
||||
tilesContainer!: Container
|
||||
@ -49,6 +49,7 @@ export class Board extends EventEmitter {
|
||||
rightDirection: string = 'east'
|
||||
playerHand: Tile[] = []
|
||||
firstTile?: Tile
|
||||
currentPlayer!: PlayerDto
|
||||
|
||||
constructor(app: Application) {
|
||||
super()
|
||||
@ -65,7 +66,7 @@ export class Board extends EventEmitter {
|
||||
parent: app.stage
|
||||
})
|
||||
|
||||
const background = new Sprite(Assets.get('bg-1'))
|
||||
const background = new Sprite(Assets.get('bg-green'))
|
||||
// background.width = this.width
|
||||
// background.height = this.height
|
||||
this.container.addChild(background)
|
||||
@ -160,10 +161,12 @@ export class Board extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
setState(state: GameState, playerId: string) {
|
||||
this.state = state
|
||||
const { lastMove } = state
|
||||
async setServerPlayerTurn(currentPlayer: PlayerDto) {
|
||||
this.showText(`${currentPlayer.name}'s turn!\n Please wait...`)
|
||||
}
|
||||
|
||||
async playerMove(move: any, playerId: string) {
|
||||
const { move: lastMove } = move
|
||||
if (lastMove === null) {
|
||||
return
|
||||
}
|
||||
@ -177,12 +180,13 @@ export class Board extends EventEmitter {
|
||||
this.nextTile = tile
|
||||
lastMove.tile = tile.toPlain()
|
||||
this.movements.push(lastMove)
|
||||
this.addTile(tile, lastMove)
|
||||
await this.addTile(tile, lastMove)
|
||||
this.setFreeEnd(lastMove)
|
||||
}
|
||||
}
|
||||
|
||||
addTile(tile: Tile, move: Movement) {
|
||||
async addTile(tile: Tile, move: Movement) {
|
||||
console.log('adding tile', tile.pips)
|
||||
let orientation = ''
|
||||
let x: number =
|
||||
move.type === 'left'
|
||||
@ -244,14 +248,25 @@ export class Board extends EventEmitter {
|
||||
availablePosition && ([x, y] = availablePosition)
|
||||
}
|
||||
}
|
||||
const endTile = isLeft ? this.leftTile : this.rightTile
|
||||
const isEndVertical = endTile?.isVertical() ?? false
|
||||
const isNextVertical = orientation === 'north' || orientation === 'south'
|
||||
tile.setPosition(this.scaleX(x), this.scaleY(y))
|
||||
tile.setOrientation(orientation)
|
||||
tile.addTo(this.tilesContainer)
|
||||
tile.reScale(this.scale)
|
||||
this.tiles.push(tile)
|
||||
tile.addTo(this.tilesContainer)
|
||||
|
||||
const animation: AnimationOptions = {
|
||||
x: this.scaleX(x),
|
||||
y: this.scaleY(y),
|
||||
rotation: ORIENTATION_ANGLES[orientation],
|
||||
duration: 20
|
||||
}
|
||||
|
||||
tile.setPosition(this.scaleX(x), this.scaleY(y))
|
||||
tile.setOrientation(orientation)
|
||||
|
||||
// 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())
|
||||
}
|
||||
|
||||
getPlayedTile(id: string): Tile | undefined {
|
||||
@ -300,7 +315,7 @@ export class Board extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
updateBoard(move: Movement) {
|
||||
async updateBoard(move: Movement) {
|
||||
try {
|
||||
const { tile: tileDto } = move
|
||||
const tile = this.getTileInHand(tileDto?.id ?? '')
|
||||
@ -310,7 +325,7 @@ export class Board extends EventEmitter {
|
||||
}
|
||||
|
||||
this.movements.push(move)
|
||||
this.addTile(tile, move)
|
||||
await this.addTile(tile, move)
|
||||
this.setFreeEnd(move)
|
||||
} catch (error) {
|
||||
this.logger.error(error, 'Error updating board')
|
||||
@ -437,7 +452,7 @@ export class Board extends EventEmitter {
|
||||
dot.alpha = 0.1
|
||||
dot.interactive = true
|
||||
dot.on('pointerdown', () => {
|
||||
this.emit(`${side}Click`, direction && { direction, x, y })
|
||||
this.emit(`game:board-${side}-action-click`, direction && { direction, x, y })
|
||||
this.cleanInteractions()
|
||||
})
|
||||
dot.on('pointerover', () => {
|
||||
@ -566,7 +581,8 @@ export class Board extends EventEmitter {
|
||||
return [canPlayNorth, canPlayEast, canPlaySouth, canPlayWest]
|
||||
}
|
||||
|
||||
gameFinished() {
|
||||
gameFinished(data: any) {
|
||||
const { lastGame, gameState } = data
|
||||
this.tiles = []
|
||||
this.boneyard = []
|
||||
this.movements = []
|
||||
@ -580,6 +596,10 @@ export class Board extends EventEmitter {
|
||||
this.firstTile = undefined
|
||||
this.tilesContainer.removeChildren()
|
||||
this.interactionContainer.removeChildren()
|
||||
this.showText('Game finished')
|
||||
this.showText(`Game finished \n Winner ${lastGame?.winner?.name ?? 'No winner'}`)
|
||||
}
|
||||
|
||||
matchFinished(data: any) {
|
||||
// this.showText(`Game finished \n Winner ${lastGame?.winner?.name ?? 'No winner'}`)
|
||||
}
|
||||
}
|
||||
|
118
src/game/Game.ts
118
src/game/Game.ts
@ -3,14 +3,19 @@ import { Board } from '@/game/Board'
|
||||
import { assets } from '@/game/utilities/assets'
|
||||
import { Tile } from '@/game/Tile'
|
||||
import { Hand } from '@/game/Hand'
|
||||
import type { Movement, TileDto } from '@/common/interfaces'
|
||||
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'
|
||||
|
||||
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
|
||||
|
||||
constructor(
|
||||
private options: { boardScale: number; handScale: number; width: number; height: number } = {
|
||||
@ -30,16 +35,23 @@ export class Game {
|
||||
const height = 800
|
||||
|
||||
await this.app.init({ width, height })
|
||||
this.app.ticker.add((tick) => Actions.tick(tick.deltaTime / 60))
|
||||
return this.app.canvas
|
||||
}
|
||||
|
||||
start() {
|
||||
async start() {
|
||||
this.board = new Board(this.app)
|
||||
this.hand = new Hand(this.app)
|
||||
this.hand.scale = this.options.handScale
|
||||
this.board.scale = this.options.boardScale
|
||||
this.setBoardEvents()
|
||||
this.setHandEvents()
|
||||
this.initEventBus()
|
||||
wait(3000)
|
||||
this.socketService.sendMessage('client:set-client-ready', {
|
||||
sessionId: this.sessionId,
|
||||
userId: this.playerId
|
||||
})
|
||||
}
|
||||
|
||||
async preload() {
|
||||
@ -52,35 +64,44 @@ export class Game {
|
||||
this.app.destroy()
|
||||
}
|
||||
|
||||
private initEventBus() {}
|
||||
|
||||
private setHandEvents() {
|
||||
this.hand.on('handUpdated', (tiles: Tile[]) => {
|
||||
this.hand.on('hand-updated', (tiles: Tile[]) => {
|
||||
this.board.setPlayerHand(tiles)
|
||||
})
|
||||
this.hand.on('tileClick', (tile: TileDto) => {
|
||||
this.selectedTile = tile
|
||||
if (tile !== undefined) {
|
||||
this.board.setMovesForTile(this.getMoves(tile), tile)
|
||||
} else {
|
||||
this.board.cleanInteractions()
|
||||
}
|
||||
})
|
||||
this.hand.on('game:tile-click', (tile: TileDto) => this.highlightMoves(tile))
|
||||
|
||||
this.hand.on('passClick', () => {
|
||||
this.hand.on('game:button-pass-click', async () => {
|
||||
const move: Movement = {
|
||||
id: '',
|
||||
type: 'pass',
|
||||
playerId: this.playerId
|
||||
}
|
||||
this.emit('move', move)
|
||||
this.board.updateBoard(move)
|
||||
this.socketService.sendMessage('client:player-move', {
|
||||
sessionId: this.sessionId,
|
||||
move: move
|
||||
})
|
||||
await this.board.updateBoard(move)
|
||||
})
|
||||
|
||||
this.hand.on('nextClick', async () => {
|
||||
await this.socketService.sendMessageWithAck('playerReady', {
|
||||
this.hand.on('nextClick', () => {
|
||||
this.socketService.sendMessage('client:set-player-ready', {
|
||||
userId: this.playerId,
|
||||
sessionId: this.sessionId
|
||||
})
|
||||
})
|
||||
|
||||
this.hand.on('hand-initialized', () => {})
|
||||
}
|
||||
|
||||
highlightMoves(tile: TileDto) {
|
||||
this.selectedTile = tile
|
||||
if (tile !== undefined) {
|
||||
this.board.setMovesForTile(this.getMoves(tile), tile)
|
||||
} else {
|
||||
this.board.cleanInteractions()
|
||||
}
|
||||
}
|
||||
|
||||
getMoves(tile: any): [boolean, boolean] {
|
||||
@ -100,12 +121,22 @@ export class Game {
|
||||
}
|
||||
|
||||
public setCanMakeMove(value: boolean) {
|
||||
this.hand.canMove = value
|
||||
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)
|
||||
} else {
|
||||
this.setCanMakeMove(true)
|
||||
}
|
||||
}
|
||||
|
||||
private setBoardEvents() {
|
||||
this.board.on('leftClick', (data) => {
|
||||
this.board.on('game:board-left-action-click', async (data) => {
|
||||
console.log('left data :>> ', data)
|
||||
if (this.selectedTile === undefined) return
|
||||
const move: Movement = {
|
||||
@ -114,12 +145,12 @@ export class Game {
|
||||
playerId: this.playerId,
|
||||
...data
|
||||
}
|
||||
this.emit('move', move)
|
||||
this.currentMove = move
|
||||
this.hand.tileMoved(this.selectedTile)
|
||||
this.board.updateBoard({ ...move, tile: this.selectedTile })
|
||||
await this.board.updateBoard({ ...move, tile: this.selectedTile })
|
||||
})
|
||||
|
||||
this.board.on('rightClick', (data) => {
|
||||
this.board.on('game:board-right-action-click', async (data) => {
|
||||
console.log('right data :>> ', data)
|
||||
if (this.selectedTile === undefined) return
|
||||
const move: Movement = {
|
||||
@ -128,24 +159,53 @@ export class Game {
|
||||
playerId: this.playerId,
|
||||
...data
|
||||
}
|
||||
this.emit('move', move)
|
||||
this.currentMove = move
|
||||
this.hand.tileMoved(this.selectedTile)
|
||||
this.board.updateBoard({ ...move, tile: this.selectedTile })
|
||||
await this.board.updateBoard({ ...move, tile: this.selectedTile })
|
||||
})
|
||||
|
||||
this.board.on('game:tile-animation-ended', async (tile) => {
|
||||
console.log('animation ended', tile)
|
||||
if (tile.playerId === this.playerId) {
|
||||
this.socketService.sendMessage('client:player-move', {
|
||||
sessionId: this.sessionId,
|
||||
move: this.currentMove
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
gameFinished() {
|
||||
// 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()
|
||||
this.board.gameFinished(data)
|
||||
}
|
||||
|
||||
matchFinished(data: any) {
|
||||
// this.hand.matchFinished()
|
||||
this.board.matchFinished(data)
|
||||
}
|
||||
|
||||
serverPlayerMove(data: any, playerId: string) {
|
||||
this.board.playerMove(data, playerId)
|
||||
}
|
||||
|
||||
private removeBoardEvents() {
|
||||
this.board.off('leftClick')
|
||||
this.board.off('rightClick')
|
||||
this.board.off('game:board-left-action-click')
|
||||
this.board.off('game:board-right-action-click')
|
||||
}
|
||||
|
||||
private removeHandEvents() {
|
||||
this.hand.off('tileClick')
|
||||
this.hand.off('passClick')
|
||||
this.hand.off('game:tile-click')
|
||||
this.hand.off('game:button-pass-click')
|
||||
}
|
||||
}
|
||||
|
166
src/game/Hand.ts
166
src/game/Hand.ts
@ -1,18 +1,11 @@
|
||||
import {
|
||||
Application,
|
||||
Container,
|
||||
EventEmitter,
|
||||
Graphics,
|
||||
Sprite,
|
||||
Text,
|
||||
Texture,
|
||||
Ticker
|
||||
} from 'pixi.js'
|
||||
import { Application, Container, EventEmitter, Sprite, Texture, Ticker } from 'pixi.js'
|
||||
import { Tile } from '@/game/Tile'
|
||||
import type { Dimension, PlayerDto, TileDto } from '@/common/interfaces'
|
||||
import type { PlayerDto, TileDto } from '@/common/interfaces'
|
||||
import { GlowFilter } from 'pixi-filters'
|
||||
import { Scale, type ScaleFunction } from './utilities/scale'
|
||||
import { LoggingService } from '@/services/LoggingService'
|
||||
import { createButton, createContainer } from '@/common/helpers'
|
||||
import { Action, Actions } from 'pixi-actions'
|
||||
|
||||
export class Hand extends EventEmitter {
|
||||
tiles: Tile[] = []
|
||||
@ -31,18 +24,40 @@ export class Hand extends EventEmitter {
|
||||
scaleX!: ScaleFunction
|
||||
grain: number = 25
|
||||
logger: LoggingService = new LoggingService()
|
||||
availableTiles: Tile[] = []
|
||||
tilesLayer!: Container
|
||||
interactionsLayer!: Container
|
||||
|
||||
constructor(app: Application) {
|
||||
super()
|
||||
app.stage.addChild(this.container)
|
||||
this.ticker = app.ticker
|
||||
this.height = 130
|
||||
this.height = 130 * this.scale
|
||||
this.width = app.canvas.width
|
||||
this.container.y = app.canvas.height - this.height
|
||||
this.container.width = this.width
|
||||
this.container.height = this.height
|
||||
this.calculateScale()
|
||||
this.initLayers()
|
||||
}
|
||||
|
||||
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
|
||||
})
|
||||
}
|
||||
|
||||
gameFinished() {
|
||||
@ -50,14 +65,15 @@ export class Hand extends EventEmitter {
|
||||
this.tiles = []
|
||||
|
||||
this.initialized = false
|
||||
this.buttonNext = this.createButton(
|
||||
this.buttonNext = createButton(
|
||||
'NEXT',
|
||||
{ x: this.width / 2 - 25, y: this.height / 2, width: 50, height: 20 },
|
||||
() => {
|
||||
this.container.removeChildren()
|
||||
this.container.removeChild(this.buttonNext)
|
||||
this.tilesLayer.removeChildren()
|
||||
this.interactionsLayer.removeChild(this.buttonNext)
|
||||
this.emit('nextClick')
|
||||
}
|
||||
},
|
||||
this.interactionsLayer
|
||||
)
|
||||
}
|
||||
|
||||
@ -72,24 +88,47 @@ export class Hand extends EventEmitter {
|
||||
this.scaleY = Scale([-scaleYSteps, scaleYSteps], [0, this.height])
|
||||
}
|
||||
|
||||
set canMove(value: boolean) {
|
||||
this._canMove = value
|
||||
if (value) {
|
||||
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) {
|
||||
this.createPassButton()
|
||||
} else {
|
||||
this.container.removeChild(this.buttonPass)
|
||||
this.interactionsLayer.removeChild(this.buttonPass)
|
||||
}
|
||||
|
||||
this.tiles.forEach((tile) => {
|
||||
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
|
||||
})
|
||||
this._canMove = value
|
||||
}
|
||||
|
||||
hasMoves(tile: TileDto, freeEnds?: [number, number]): boolean {
|
||||
if (tile === undefined || freeEnds === undefined) return false
|
||||
|
||||
let hasMoves: boolean = false
|
||||
if (tile.pips != undefined) {
|
||||
hasMoves = tile.pips.includes(freeEnds[0]) || tile.pips.includes(freeEnds[1])
|
||||
}
|
||||
return hasMoves
|
||||
}
|
||||
|
||||
initialize(playerState: PlayerDto) {
|
||||
this.tiles = this.createTiles(playerState)
|
||||
this.emit('handUpdated', this.tiles)
|
||||
this.initialized = this.tiles.length > 0
|
||||
this.renderTiles()
|
||||
this.emit('hand-updated', this.tiles)
|
||||
}
|
||||
|
||||
private addBg() {
|
||||
@ -109,8 +148,9 @@ export class Hand extends EventEmitter {
|
||||
const selected = this.tiles.find((t) => t.selected)
|
||||
if (selected) {
|
||||
this.deselectTile(selected)
|
||||
|
||||
if (selected.id === tile.id) {
|
||||
this.emit('tileClick')
|
||||
this.emit('game:tile-click')
|
||||
return
|
||||
}
|
||||
}
|
||||
@ -118,12 +158,10 @@ export class Hand extends EventEmitter {
|
||||
tile.selected = true
|
||||
tile.alpha = 1
|
||||
|
||||
tile.animateTo(tile.x, tile.y - 10)
|
||||
this.emit('tileClick', tile.toPlain())
|
||||
this.emit('game:tile-click', tile.toPlain())
|
||||
}
|
||||
|
||||
private deselectTile(selected: Tile) {
|
||||
selected.animateTo(selected.x, selected.y + 10)
|
||||
selected.selected = false
|
||||
selected.alpha = 0.7
|
||||
}
|
||||
@ -133,6 +171,17 @@ export class Hand extends EventEmitter {
|
||||
|
||||
if (!tile) return
|
||||
|
||||
this.availableTiles
|
||||
.filter((t) => t.id !== tileDto.id)
|
||||
.forEach((t) => {
|
||||
t.animateTo({
|
||||
x: t.x,
|
||||
y: t.y + 10
|
||||
})
|
||||
})
|
||||
|
||||
this.tiles = this.tiles.filter((t) => t.id !== tileDto.id)
|
||||
|
||||
tile.interactive = false
|
||||
tile.clearFilters()
|
||||
tile.off('pointerdown')
|
||||
@ -140,59 +189,14 @@ export class Hand extends EventEmitter {
|
||||
tile.off('pointerout')
|
||||
}
|
||||
|
||||
private createButton(
|
||||
textStr: string,
|
||||
dimension: Dimension,
|
||||
action: string | Function
|
||||
): Container {
|
||||
const { x, y, width, height } = dimension
|
||||
const rectangle = new Graphics().roundRect(x, y, width + 4, height + 4, 5).fill(0xffff00)
|
||||
const text = new Text({
|
||||
text: textStr,
|
||||
style: {
|
||||
fontFamily: 'Arial',
|
||||
fontSize: 12,
|
||||
fontWeight: 'bold',
|
||||
fill: 0x121212,
|
||||
align: 'center'
|
||||
}
|
||||
})
|
||||
text.anchor = 0.5
|
||||
const container = new Container()
|
||||
container.addChild(rectangle)
|
||||
container.addChild(text)
|
||||
|
||||
text.y = y + height / 2
|
||||
text.x = x + width / 2
|
||||
|
||||
container.eventMode = 'static'
|
||||
container.cursor = 'pointer'
|
||||
rectangle.alpha = 0.7
|
||||
text.alpha = 0.7
|
||||
container.on('pointerdown', () => {
|
||||
action instanceof Function ? action() : this.emit(action)
|
||||
})
|
||||
container.on('pointerover', () => {
|
||||
rectangle.alpha = 1
|
||||
text.alpha = 1
|
||||
})
|
||||
|
||||
container.on('pointerout', () => {
|
||||
rectangle.alpha = 0.7
|
||||
text.alpha = 0.7
|
||||
})
|
||||
|
||||
this.container.addChild(container)
|
||||
return container
|
||||
}
|
||||
|
||||
private createPassButton() {
|
||||
const lastTile = this.tiles[this.tiles.length - 1]
|
||||
const x = lastTile ? lastTile.x + lastTile.width : this.scaleX(0)
|
||||
this.buttonPass = this.createButton(
|
||||
this.buttonPass = createButton(
|
||||
'PASS',
|
||||
{ x, y: this.height / 2, width: 50, height: 20 },
|
||||
'passClick'
|
||||
() => this.emit('game:button-pass-click'),
|
||||
this.interactionsLayer
|
||||
)
|
||||
}
|
||||
|
||||
@ -205,21 +209,22 @@ export class Hand extends EventEmitter {
|
||||
(tile: Tile) => !playerState.hand.find((t) => t.id === tile.id)
|
||||
)
|
||||
if (missing) {
|
||||
this.container.removeChild(missing.getSprite())
|
||||
this.tilesLayer.removeChild(missing.getSprite())
|
||||
this.tiles = this.tiles.filter((tile) => tile.id !== missing.id)
|
||||
this.emit('handUpdated', this.tiles)
|
||||
this.emit('hand-updated', this.tiles)
|
||||
}
|
||||
this.renderTiles()
|
||||
}
|
||||
|
||||
private createTiles(playerState: PlayerDto) {
|
||||
return playerState.hand.map((tile) => {
|
||||
const newTile: Tile = new Tile(tile.id, this.ticker, tile.pips, this.scale)
|
||||
return playerState.hand.map((tile: TileDto) => {
|
||||
const newTile: Tile = new Tile(tile.id, this.ticker, tile.pips, this.scale, tile.playerId)
|
||||
newTile.alpha = 0.7
|
||||
newTile.anchor = 0.5
|
||||
newTile.addTo(this.container)
|
||||
newTile.addTo(this.tilesLayer)
|
||||
newTile.on('pointerdown', () => this.onTileClick(newTile))
|
||||
newTile.on('pointerover', () => {
|
||||
this.emit('tileHover', newTile.toPlain())
|
||||
newTile.alpha = 1
|
||||
newTile.setFilters([
|
||||
new GlowFilter({
|
||||
@ -233,6 +238,7 @@ export class Hand extends EventEmitter {
|
||||
})
|
||||
newTile.on('pointerout', () => {
|
||||
if (!newTile.selected) {
|
||||
this.emit('tileHover')
|
||||
newTile.alpha = 0.7
|
||||
newTile.getSprite().filters = []
|
||||
}
|
||||
|
@ -1,4 +1,6 @@
|
||||
import type { AnimationOptions } from '@/common/interfaces'
|
||||
import { Sprite, Texture, Ticker } from 'pixi.js'
|
||||
import { Tile } from './Tile'
|
||||
|
||||
export abstract class SpriteBase {
|
||||
private _interactive: boolean = false
|
||||
@ -81,28 +83,56 @@ export abstract class SpriteBase {
|
||||
this.sprite.filters = []
|
||||
}
|
||||
|
||||
animateTo(x: number, y: number) {
|
||||
const initialX = this.sprite.x
|
||||
const initialY = this.sprite.y
|
||||
animateTo(options: AnimationOptions): Promise<void> {
|
||||
return new Promise((resolve) => {
|
||||
const {
|
||||
x: targetX,
|
||||
y: targetY,
|
||||
rotation: targetRotation,
|
||||
duration = 10,
|
||||
width: targetWidth,
|
||||
height: targetHeight
|
||||
} = options
|
||||
|
||||
const deltaX = x - this.sprite.x
|
||||
const deltaY = y - this.sprite.y
|
||||
let elapsed: number = 0
|
||||
const duration: number = 10
|
||||
const initialX = this.sprite.x
|
||||
const initialY = this.sprite.y
|
||||
const initialRotation = this.sprite.rotation
|
||||
const initialWidth = this.sprite.width
|
||||
const initialHeight = this.sprite.height
|
||||
|
||||
const tick: any = (delta: any) => {
|
||||
elapsed += delta.deltaTime
|
||||
const progress = Math.min(elapsed / duration, 1)
|
||||
const deltaX = targetX ? targetX - this.sprite.x : null
|
||||
const deltaY = targetY ? targetY - this.sprite.y : null
|
||||
const deltaRotation = targetRotation ? targetRotation - this.sprite.rotation : null
|
||||
const deltaWidth = targetWidth ? targetWidth - this.sprite.width : null
|
||||
const deltaHeight = targetHeight ? targetHeight - this.sprite.height : null
|
||||
|
||||
this.sprite.x = initialX + deltaX * progress
|
||||
this.sprite.y = initialY + deltaY * progress
|
||||
let elapsed: number = 0
|
||||
|
||||
if (progress === 1) {
|
||||
this.ticker?.remove(tick)
|
||||
const tick: any = (delta: any) => {
|
||||
elapsed += delta.deltaTime
|
||||
const progress = Math.min(elapsed / duration, 1)
|
||||
|
||||
// Linear interpolation
|
||||
if (deltaX !== null) this.sprite.x = initialX + deltaX * progress
|
||||
if (deltaY !== null) this.sprite.y = initialY + deltaY * progress
|
||||
|
||||
// Rotation interpolation
|
||||
if (deltaRotation !== null)
|
||||
this.sprite.rotation = initialRotation + deltaRotation * progress
|
||||
|
||||
// Scale interpolation
|
||||
// this.sprite.width = initialWidth + deltaWidth * progress
|
||||
// this.sprite.height = initialHeight + deltaHeight * progress
|
||||
|
||||
//
|
||||
if (progress === 1) {
|
||||
this.ticker?.remove(tick)
|
||||
resolve()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.ticker?.add(tick)
|
||||
this.ticker?.add(tick)
|
||||
})
|
||||
}
|
||||
|
||||
addTo(container: any) {
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { Texture, Ticker } from 'pixi.js'
|
||||
import { SpriteBase } from '@/game/SpriteBase'
|
||||
import { ORIENTATION_ANGLES } from '@/common/constants'
|
||||
|
||||
export class Tile extends SpriteBase {
|
||||
selected: boolean = false
|
||||
@ -11,7 +12,8 @@ export class Tile extends SpriteBase {
|
||||
public id: string,
|
||||
ticker?: Ticker,
|
||||
public pips?: [number, number],
|
||||
scale: number = 1
|
||||
scale: number = 1,
|
||||
public playerId?: string
|
||||
) {
|
||||
super(ticker, scale)
|
||||
this.id = id
|
||||
@ -26,6 +28,7 @@ export class Tile extends SpriteBase {
|
||||
|
||||
toPlain() {
|
||||
return {
|
||||
playerId: this.playerId,
|
||||
id: this.id,
|
||||
pips: this.pips,
|
||||
orientation: this.orientation,
|
||||
@ -69,16 +72,16 @@ export class Tile extends SpriteBase {
|
||||
setOrientation(value: string) {
|
||||
switch (value) {
|
||||
case 'north':
|
||||
this.sprite.rotation = 0
|
||||
this.sprite.rotation = ORIENTATION_ANGLES.north
|
||||
break
|
||||
case 'east':
|
||||
this.sprite.rotation = Math.PI / 2
|
||||
this.sprite.rotation = ORIENTATION_ANGLES.east
|
||||
break
|
||||
case 'south':
|
||||
this.sprite.rotation = Math.PI
|
||||
this.sprite.rotation = ORIENTATION_ANGLES.south
|
||||
break
|
||||
case 'west':
|
||||
this.sprite.rotation = (3 * Math.PI) / 2
|
||||
this.sprite.rotation = ORIENTATION_ANGLES.west
|
||||
break
|
||||
}
|
||||
this.orientation = value
|
||||
|
@ -30,6 +30,7 @@ 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'
|
||||
|
||||
export const assets = [
|
||||
{ alias: 'tile-back', src: tileBack },
|
||||
@ -63,5 +64,6 @@ export const assets = [
|
||||
{ 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-1', src: bg_1 },
|
||||
{ alias: 'bg-green', src: bg_green }
|
||||
]
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { Tile } from '../Tile'
|
||||
import type { GameState, Movement } from '../../common/interfaces'
|
||||
import type { GameDto, Movement } from '../../common/interfaces'
|
||||
import type { Game } from '../Game'
|
||||
import { wait } from '../../common/helpers'
|
||||
|
||||
@ -46,7 +46,7 @@ export const playerState = {
|
||||
]
|
||||
}
|
||||
|
||||
export const gameState_0: GameState = {
|
||||
export const gameState_0: GameDto = {
|
||||
id: 'f043051e-6850-444f-857c-b889220fc187',
|
||||
lastMove: {
|
||||
tile: {
|
||||
|
Reference in New Issue
Block a user