All Downloads are FREE. Search and download functionalities are using the official Maven repository.

view.endscreen-module.EndScreenModule.js Maven / Gradle / Ivy

import { WIDTH, HEIGHT } from '../core/constants.js'
import { lerp, unlerp } from '../core/utils.js'
import { ErrorLog } from '../core/ErrorLog.js'
import { MissingImageError } from './errors/MissingImageError.js'

/* global PIXI */

export class EndScreenModule {
  constructor (assets) {
    this.states = []
    this.scores = []
    this.globalData = {}
    this.atEnd = false
  }

  static get name () {
    return 'endScreen'
  }

  updateScene (previousData, currentData, progress) {
    if (currentData.scores && progress === 1) {
      this.atEnd = true
    } else {
      this.atEnd = false
    }
  }

  handleFrameData (frameInfo, data) {
    let scores = null
    let spriteName = null
    let displayedText = null
    if (data) {
      scores = data[0]
      spriteName = data[1]
      displayedText = data[2]
    }
    const state = {
      number: frameInfo.number,
      scores,
      spriteName,
      displayedText
    }
    if (scores) {
      this.scores = scores
    }
    if (spriteName) {
      this.spriteName = spriteName
    }
    if (displayedText) {
      this.displayedText = displayedText
    }
    this.states.push(state)
    return state
  }

  reinitScene (container, canvasData) {
    this.container = container
    this.endLayer = this.createEndScene(this)
    if (this.atEnd) {
      this.initEndScene()
    }
    this.container.addChild(this.endLayer)
  }

  animateScene (delta) {
    let step = Math.min(32, delta)

    if (this.atEnd) {
      if (!this.animationEnded) {
        this.renderEndScene(step)
      }
    } else {
      if (this.endTime > 0) {
        this.destroyEndScene()
      }
      this.endTime = 0
    }
  }

  destroyEndScene () {
    this.animationEnded = false
    this.endLayer.visible = false
  }

  initEndScene () {
    this.animationEnded = false
    this.endLayer.visible = true
  }

  renderEndScene (step) {
    var endOfEnd = 10000
    if (this.endTime === 0) {
      this.initEndScene()
    }

    var backS = 0
    var backD = 400
    var backP = unlerp(backS, backS + backD, this.endTime)
    this.endLayer.backgroundRanking.alpha = backP * 0.9

    var logoS = 400
    var logoD = 600
    var logoP = unlerp(logoS, logoS + logoD, this.endTime)
    this.endLayer.titleRanking.scale.x = this.endLayer.titleRanking.scale.y = 0.001 + lerp(10, 0.8, logoP)
    this.endLayer.titleRanking.visible = !!logoP

    var rankS = 1000
    var rankD = 300
    for (let i = 0; i < this.finishers.length; ++i) {
      var p = unlerp(rankS + rankD * i, rankS + rankD * i + rankD, this.endTime)
      this.finishers[i].alpha = p
    }

    this.endTime += step

    if (this.endTime >= endOfEnd) {
      this.animationEnded = true
    }
  }

  handleGlobalData (players, globalData) {
    this.globalData = {
      players: players,
      playerCount: players.length
    }
  }

  fitTextInWidth (text, width) {
    let currText = text.text
    while (text.width > width) {
      currText = currText.slice(0, -1)
      text.text = currText + '...'
    }
  }
  generateText (text, size, align, color, maxWidth = null) {
    var textEl
    textEl = new PIXI.Text(text, {
      fontSize: Math.round(size / 1.2) + 'px',
      fontFamily: 'Lato',
      fontWeight: 'bold',
      fill: color
    })
    textEl.lineHeight = Math.round(size / 1.2)
    if (align === 'right') {
      textEl.anchor.x = 1
    } else if (align === 'center') {
      textEl.anchor.x = 0.5
    }
    if (maxWidth !== null) {
      this.fitTextInWidth(textEl, maxWidth)
    }

    return textEl
  }

  createFinisher (finisher) {
    var layer = new PIXI.Container()

    var avatarContainer = new PIXI.Container()
    avatarContainer.y = 0
    avatarContainer.x = 0

    var backgroundAvatar = new PIXI.Graphics()
    backgroundAvatar.beginFill(0xffffff)
    backgroundAvatar.alpha = 0.1
    backgroundAvatar.drawRect(0, 0, 240, 120)
    avatarContainer.addChild(backgroundAvatar)

    var avatarBorder = new PIXI.Graphics()
    avatarBorder.lineStyle(1, 0xffffff)
    avatarBorder.alpha = 0.5
    avatarBorder.drawRect(0, 0, 120, 120)
    avatarContainer.addChild(avatarBorder)

    var avatar = new PIXI.Sprite(finisher.player.avatar)
    avatar.width = avatar.height = 120

    var rank = this.generateText(finisher.rank.toString(), 76, 'center', finisher.player.color)
    rank.anchor.y = 0.5
    rank.position.x = 160
    rank.position.y = 56
    avatarContainer.addChild(rank)

    let rankChars = 'TH'
    if (finisher.rank < 4) {
      rankChars = ['ST', 'ND', 'RD'][finisher.rank - 1]
    }
    var rankLetter = this.generateText(rankChars.toString(), 34, 'left', finisher.player.color)
    rankLetter.position.x = 184
    rankLetter.position.y = 32
    avatarContainer.addChild(rankLetter)

    var hudAvatar = new PIXI.Container()
    hudAvatar.addChild(avatar)

    avatarContainer.addChild(hudAvatar)

    let maxTextWidth
    if (this.globalData.playerCount <= 4) {
      maxTextWidth = 1500
    } else {
      maxTextWidth = 500
    }
    var name = this.generateText(finisher.player.name.toUpperCase(), 50, 'left', finisher.player.color, maxTextWidth)

    const scoreText = finisher.text || ((finisher.score >= 0) ? finisher.score.toString() + ' points' : '-')
    var scoreLabel = this.generateText(scoreText, 64, 'left', finisher.player.color, maxTextWidth)

    name.x = 330
    name.y = -4
    scoreLabel.x = 330
    scoreLabel.y = 50

    layer.addChild(avatarContainer)
    layer.addChild(name)
    layer.addChild(scoreLabel)

    return layer
  }

  createEndScene () {
    var layer = new PIXI.Container()

    var background = new PIXI.Graphics()
    background.beginFill(0, 0.85)
    background.drawRect(0, 0, WIDTH, HEIGHT)
    background.endFill()

    layer.backgroundRanking = background

    let sprite = this.spriteName
    var titleRanking
    if (PIXI.utils.TextureCache[sprite]) {
      titleRanking = PIXI.Sprite.fromFrame(sprite)
    } else {
      ErrorLog.push(new MissingImageError(sprite))
      titleRanking = new PIXI.Sprite(PIXI.Texture.EMPTY)
    }
    titleRanking.anchor.x = titleRanking.anchor.y = 0.5
    layer.titleRanking = titleRanking

    titleRanking.position.x = WIDTH / 2
    titleRanking.position.y = 230

    var podium = []
    for (var i = 0; i < this.globalData.playerCount; ++i) {
      podium.push({
        score: this.scores[i],
        text: (this.displayedText) ? this.displayedText[i] : null,
        player: this.globalData.players[i],
        rank: 0
      })
    }
    podium.sort(function (a, b) {
      return b.score - a.score
    })

    this.finishers = []
    var finishers = new PIXI.Container()

    var elem
    for (i = 0; i < podium.length; ++i) {
      podium[i].rank = podium.filter(p => p.score > podium[i].score).length + 1
      elem = this.createFinisher(podium[i])
      finishers.addChild(elem)
      this.finishers.push(elem)
    }

    if (this.finishers.length <= 4) {
      let maxFinisherWidth = Math.max(...this.finishers.map(f => f.width))
      for (i = 0; i < this.finishers.length; ++i) {
        this.finishers[i].position.x = (WIDTH - maxFinisherWidth) / 2
        this.finishers[i].position.y = i * 150
      }
    } else {
      const margin = 50
      const middle = Math.ceil(this.finishers.length / 2)
      let maxFinisherWidth = Math.max(...this.finishers.slice(0, middle).map(f => f.width))
      for (i = 0; i < this.finishers.length; ++i) {
        if (i < middle) {
          this.finishers[i].position.x = WIDTH / 2 - (maxFinisherWidth + margin)
        } else {
          this.finishers[i].position.x = WIDTH / 2 + margin
        }
        this.finishers[i].position.y = (i % middle) * 150
      }
    }
    finishers.y = 400

    layer.addChild(background)
    layer.addChild(titleRanking)
    layer.addChild(finishers)

    layer.visible = false
    return layer
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy