engagementgamelab/CivicSeed

View on GitHub
client/code/game/game.others.js

Summary

Maintainability
D
3 days
Test Coverage
'use strict'
/* global ss, $, $game */

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

    others.js

    - Manages other (on-screen) players
    - TODO: Create a generalized player object that could be extended for
      current player
    - TODO: Extend player object for superadmin / DM roles.

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

var _onScreenPlayers = {}

function Player (player) {
  this.name = player.firstName
  this.id = player._id
  this.isMoving = false
  this.currentStep = 0
  this.currentStepIncX = 0
  this.currentStepIncY = 0
  this.curFrame = 0
  this.numFrames = 4 // TODO: Should be a constant
  this.numSteps = 8 // TODO: Should be a constant
  this.direction = 0
  this.idleCounter = 0
  this.chatId = 'player' + player._id
  this.chatIdSelector = '#player' + player._id
  this.hideTimer = null
  this.isChatting = false
  this.offScreen = true
  this.tilesColored = player.game.tilesColored
  this.rank = player.game.rank
  this.level = player.game.currentLevel
  this.skinSuit = player.game.skinSuit
  this.playerColor = player.game.playerColor
  this.info = {
    x: player.game.position.x,
    y: player.game.position.y,
    srcX: 0,
    srcY: 0,
    offX: 0,
    offY: 0,
    prevOffX: 0,
    prevOffY: 0
  }
  this.renderInfo = {
    kind: 'player',
    id: player._id,
    firstName: player.firstName,
    srcX: 0,
    srcY: 0,
    curX: player.x,
    curY: player.y,
    prevX: player.x,
    prevY: player.y,
    color: null
  }
}

Player.prototype.update = function () {
  if (this.isMoving) {
    this.move()
    this.updateRenderInfo()
  } else {
    if ($game.flags.check('screen-transition') === true) {
      this.updateRenderInfo()
    } else {
      this.idle()
    }
  }
}

Player.prototype.updateRenderInfo = function () {
  var loc = $game.$map.masterToLocal(this.info.x, this.info.y)
  if (loc) {
    var prevX = loc.x * $game.TILE_SIZE + this.info.prevOffX * $game.STEP_PIXELS
    var prevY = loc.y * $game.TILE_SIZE + this.info.prevOffY * $game.STEP_PIXELS
    var curX = loc.x * $game.TILE_SIZE + this.info.offX * $game.STEP_PIXELS
    var curY = loc.y * $game.TILE_SIZE + this.info.offY * $game.STEP_PIXELS

    this.renderInfo.prevX = prevX
    this.renderInfo.prevY = prevY

    this.renderInfo.srcX = this.info.srcX
    this.renderInfo.srcY = this.info.srcY
    this.renderInfo.curX = curX
    this.renderInfo.curY = curY

    this.offScreen = false
  } else {
    this.offScreen = true
  }
}

Player.prototype.idle = function () {
  this.idleCounter += 1

  if (this.idleCounter >= 64) {
    this.idleCounter = 0
    this.info.srcX = 0
    this.info.srcY = 0
    this.updateRenderInfo()
    return true
  } else if (this.idleCounter === 48) {
    this.info.srcX = 32
    this.info.srcY = 0
    this.updateRenderInfo()
  }
}

Player.prototype.clear = function () {
  $game.$render.clearCharacter(this.renderInfo)
}

Player.prototype.slide = function (sX, sY) {
  this.info.prevOffX = sX * this.numSteps
  this.info.prevOffY = sY * this.numSteps
}

Player.prototype.resetRenderValues = function () {
  this.info.prevOffX = 0
  this.info.prevOffY = 0
}

Player.prototype.getRenderInfo = function () {
  if (!this.offScreen) {
    this.renderInfo.color = this.getCSSColor()
    return this.renderInfo
  } else {
    return false
  }
}

Player.prototype.beginMove = function (moves) {
  this.seriesOfMoves = new Array(moves.length)
  this.seriesOfMoves = moves
  this.currentStep = 0
  this.isMoving = true
  this.hideChat()
}

Player.prototype.endMove = function () {
  this.isMoving = false

  this.info.srcX = 0
  this.info.srcY = 0
  this.info.offX = 0
  this.info.offY = 0
  this.info.prevOffX = 0
  this.info.prevOffY = 0

  $game.minimap.updatePlayer(this.id, this.info)
}

Player.prototype.move = function () {
  // if the steps between the tiles has finished,
  // update the master location, and reset steps to go on to next move
  if (this.currentStep >= this.numSteps) {
    this.currentStep = 0
    this.info.x = this.seriesOfMoves[0].masterX
    this.info.y = this.seriesOfMoves[0].masterY

    this.seriesOfMoves.shift()

    // Update player position on minimap
    $game.minimap.updatePlayer(this.id, this.info)
  }

  // Check to see if done
  if (this.seriesOfMoves.length <= 0) {
    this.endMove()
  } else {
    // if not, step through it
    // increment the current step
    this.currentStep += 1

    // if it the first one, then figure out the direction to face
    if (this.currentStep === 1) {
      this.currentStepIncX = this.seriesOfMoves[0].masterX - this.info.x
      this.currentStepIncY = this.seriesOfMoves[0].masterY - this.info.y

      // set the previous offsets to 0 because the last visit
      // was the actual rounded master
      this.info.prevOffX = 0
      this.info.prevOffY = 0

      // set direction for sprite sheets
      // direction refers to the y location on the sprite sheet
      // since the character will be in different rows
      // will be 0,1,2,3
      if (this.currentStepIncX === 1) {
        this.direction = 2
      } else if (this.currentStepIncX === -1) {
        this.direction = 1
      } else if (this.currentStepIncY === -1) {
        this.direction = 4
      } else {
        this.direction = 3
      }
    } else {
      this.info.prevOffX = this.info.offX
      this.info.prevOffY = this.info.offY
    }

    this.info.offX = this.currentStep * this.currentStepIncX
    this.info.offY = this.currentStep * this.currentStepIncY

    // try only changing the src (frame) every X frames
    if ((this.currentStep - 1) % 8 === 0) {
      this.curFrame += 1
      if (this.curFrame >= this.numFrames) {
        this.curFrame = 0
      }
    }

    this.info.srcX = this.curFrame * $game.TILE_SIZE
    this.info.srcY = this.direction * $game.TILE_SIZE * 2
  }
}

Player.prototype.message = function (data) {
  // Display over other player's head if onscreen
  if (!this.offScreen) {
    data.isChatting = this.isChatting
    data.chatId = this.chatId
    data.chatIdSelector = this.chatIdSelector
    data.playerCSSColor = this.getCSSColor()
    data.position = {
      x: this.renderInfo.curX,
      y: this.renderInfo.curY
    }

    clearTimeout(this.hideTimer)
    this.isChatting = true
    var fadeTime = $game.$chat.message(data)
    this.hideTimer = setTimeout(this.hideChat, fadeTime)
  }
}

Player.prototype.hideChat = function () {
  // remove chat from screen
  clearTimeout(this.hideTimer)
  $(this.chatIdSelector).fadeOut('fast', function () {
    $(this).remove()
    this.isChatting = false
  })
}

Player.prototype.setTilesColored = function (num) {
  this.tilesColored = num
}

Player.prototype.showPlayerCard = function () {
  if (!this.offScreen) {
    $game.alert(this.name + ' is a ' + $game.playerRanks[this.level] + ' who has colored ' + this.tilesColored + ' tiles')
  }
}

Player.prototype.changeLevel = function (level) {
  this.level = level
}

Player.prototype.beam = function (position) {
  this.info.x = position.x
  this.info.y = position.y
  this.updateRenderInfo()
  $game.minimap.updatePlayer(this.id, this.info)
}

Player.prototype.skinSuitChange = function (info) {
  this.skinSuit = info.skinSuit
  $game.$render.createCanvasForPlayer(this.id, this.skinSuit, this.playerColor)
}

// Get the player's color index number
Player.prototype.getColorIndex = function () {
  return this.playerColor
}

// Get a color at a given index or use current player color index
Player.prototype.getColor = function () {
  // Returns an object {r, g, b}, values from 0 - 255
  // TODO: something different besides aliasing to $player
  return $game.$player.getColor(this.playerColor)
}

// Get a color hex string at a given index or use current player color index
Player.prototype.getCSSColor = function () {
  var rgb = this.getColor()
  // A quick way of converting to a hex string, e.g. #5599cc
  return '#' + ('0' + (rgb.r.toString(16))).slice(-2) + ('0' + (rgb.g.toString(16))).slice(-2) + ('0' + (rgb.b.toString(16))).slice(-2)
}

module.exports = (function () {
  return {
    ready: false,

    // load in other players
    init: function (callback) {
      ss.rpc('game.player.getOthers', function (response) {
        _onScreenPlayers = {}
        for (var p = 0; p < response.length; p++) {
          this.add(response[p])
        }
        this.ready = true
        callback()
      }.bind(this))
    },

    resetInit: function () {
      _onScreenPlayers = {}
      this.ready = false
    },

    add: function (player) {
      // check if player is on our screen (or near it....)
      // don't add it if its yourself
      if (player._id !== $game.$player.id) {
        // set inview if nearby
        var newbie = new Player(player)
        _onScreenPlayers[newbie.id] = newbie
        newbie.updateRenderInfo()
        $game.$render.createCanvasForPlayer(newbie.id, newbie.skinSuit, newbie.playerColor)
        $game.minimap.addPlayer(newbie.id, player.game.position, newbie.getCSSColor())
      }
    },

    get: function () {
      return _onScreenPlayers
    },

    update: function () {
      for (var i in _onScreenPlayers) {
        _onScreenPlayers[i].update()
      }
    },

    clear: function () {
      for (var i in _onScreenPlayers) {
        _onScreenPlayers[i].clear()
      }
    },

    slide: function (slideX, slideY) {
      for (var i in _onScreenPlayers) {
        _onScreenPlayers[i].slide(slideX, slideY)
      }
    },

    getRenderInfo: function () {
      var all = []
      for (var i in _onScreenPlayers) {
        var info = _onScreenPlayers[i].getRenderInfo()
        if (info) {
          all.push(info)
        }
      }
      return all
    },

    resetRenderValues: function () {
      for (var i in _onScreenPlayers) {
        _onScreenPlayers[i].resetRenderValues()
      }
    },

    hideAllChats: function () {
      for (var i in _onScreenPlayers) {
        _onScreenPlayers[i].hideChat()
      }
    },

    message: function (id, data) {
      var player = _onScreenPlayers[id]
      if (player) {
        player.message(data)
      }
    },

    // Remove a player from the screen
    remove: function (id) {
      // Clear it off screen first, then delete!
      var player = _onScreenPlayers[id]
      if (player) {
        player.clear()
        $game.minimap.removePlayer(id)
        delete _onScreenPlayers[id]
      }
    },

    playerCard: function (x, y) {
      for (var i in _onScreenPlayers) {
        var player = _onScreenPlayers[i]
        if (player.info.x === x && player.info.y === y) {
          player.showPlayerCard()
          return true
        }
      }
      return false
    },

    updateTilesColored: function (id, count) {
      var player = _onScreenPlayers[id]
      if (player) {
        player.setTilesColored(count)
      }
    },

    levelChange: function (id, level) {
      var player = _onScreenPlayers[id]
      if (player) {
        player.changeLevel(level)
      }
    },

    sendMoveInfo: function (id, moves) {
      var player = _onScreenPlayers[id]
      if (player) {
        player.beginMove(moves)
      }
    },

    beam: function (id, position) {
      var player = _onScreenPlayers[id]
      if (player) {
        player.beam(position)
      }
    },

    skinSuitChange: function (id, info) {
      var player = _onScreenPlayers[id]
      if (player) {
        player.skinSuitChange(info)
      }
    },

    disable: function () {
      for (var i in _onScreenPlayers) {
        _onScreenPlayers[i].offScreen = true
      }
    }

  }
}())