engagementgamelab/CivicSeed

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

Summary

Maintainability
C
1 day
Test Coverage
'use strict'
/* global CivicSeed, $, $game */

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

    skins.js

    - Skinventory UI management
    - Skinventory data management
    - Stores outfit data
    - Do not do set/get player data here; that occurs on player.js

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

var _ = require('underscore')
var data = require('/data.skinsuits')

// Returns an array of skin names (sets, not special outfits, not basic colors)
function getSetsList () {
  var outfits = data.outfits
  var list = []

  for (var i in outfits) {
    if (!outfits[i].parts) list.push(outfits[i].id)
  }

  return list
}

// Returns filtered object collection of sets (without special outfits)
function getSets () {
  var outfits = data.outfits

  return _.filter(outfits, function (item) {
    return !item.parts
  })
}

// Returns a particular set
function getSkin (id) {
  return _.find(getSets(), function (item) {
    return item.id === id
  })
}

// Unlock a new skin
function unlockSkin (skin, part) {
  var skinventory = $game.$player.getSkinventory()

  if (part !== undefined) {
    // If part already exists, skip.
    if (!_.contains(skinventory[part], skin)) {
      // Specify a part to unlock
      skinventory[part].push(skin)
      $game.addBadgeCount('.hud-skinventory', 1)
    }
  } else {
    // Assume all parts of the skin is unlocked
    if (!_.contains(skinventory.head, skin)) {
      skinventory.head.push(skin)
      $game.addBadgeCount('.hud-skinventory', 1)
    }
    if (!_.contains(skinventory.torso, skin)) {
      skinventory.torso.push(skin)
      $game.addBadgeCount('.hud-skinventory', 1)
    }
    if (!_.contains(skinventory.legs, skin)) {
      skinventory.legs.push(skin)
      $game.addBadgeCount('.hud-skinventory', 1)
    }
  }

  // Update skinventory
  updateSkinventoryUI(skin)
  $game.$player.setSkinventory(skinventory)
}

// Called when a skin suit is unlocked to render its unlocked appearance.
function updateSkinventoryUI (skin) {
  // Unlock an entire skin easily
  var head = $('.head [data-name="' + skin + '"]')
  var torso = $('.torso [data-name="' + skin + '"]')
  var legs = $('.legs [data-name="' + skin + '"]')
  renderUnlockedPart(head, skin, 'head', true)
  renderUnlockedPart(torso, skin, 'torso', true)
  renderUnlockedPart(legs, skin, 'legs', true)
}

// For debug purposes, reset everything but basic skin
function resetSkinventory () {
  var skinventory = $game.$player.getSkinventory()

  // Reset skinventory unlocked array
  skinventory.head = ['basic']
  skinventory.torso = ['basic']
  skinventory.legs = ['basic']

  $game.$player.setSkinSuit('basic')

  renderSkinventoryUI()          // Re-render with reset skins
  updateSkinventoryUI('basic')   // Then update with basic skin
  $game.$player.setSkinventory(skinventory)
}

// Creates HTML structure of Skinventory
function renderSkinventoryUI () {
  var playerSkin = $game.$player.getSkinSuit()
  var unlocked = $game.$player.getSkinventory()
  var skins = getSets()

  function _render (skin, part) {
    var skinHTML = '<div class="outer locked" data-name="' + skin.id + '"><div class="inner"><i class="fa fa-lock"></i></div><div class="badge-new"><i class="fa fa-star"></i></div></div>'
    var $part = $('.' + part)
    var $el = $(skinHTML)

    $part.append($el)

    // Check if currently selected
    if (skin.id === playerSkin[part]) $el.addClass('equipped')

    // Check if unlocked and set display accordingly
    for (var h = 0; h < unlocked[part].length; h++) {
      if (unlocked[part][h] === skin.id) {
        renderUnlockedPart($el, skin, part)
        break
      }
    }

    // Set tooltip behavior
    $el.tooltip({
      placement: 'bottom',
      container: 'body',
      trigger: 'hover',
      title: '(locked)'
    })

    // Bind actions
    $el.on('click', function () {
      // If marked as 'new', this hides it
      $(this).find('.badge-new:visible').hide()
    })
  }

  // Reset parts
  $('#skinventory .head').empty()
  $('#skinventory .torso').empty()
  $('#skinventory .legs').empty()

  // For each skin, create display element and render
  for (var id in skins) {
    _render(skins[id], 'head')
    _render(skins[id], 'torso')
    _render(skins[id], 'legs')
  }

  // Display skinformation
  renderSkinformation()
}

// Creates HTML content showing information about currently worn parts or outfits.
function renderSkinformation () {
  var playerSkin = $game.$player.getSkinSuit()
  var skins = data.outfits
  var head = playerSkin.head
  var torso = playerSkin.torso
  var legs = playerSkin.legs
  var content = ''

  // Display inventory data
  // The game doesn't store "full set" data, so we compare the parts to see if this is the case
  var outfit = getOutfit(head, torso, legs)

  if (outfit) {
    content += '<strong>' + outfit.name + '</strong><br>' + outfit.description
    if (outfit.effect) {
      content += '<br><strong><span class="color-orange">Outfit bonus:</span> <span class="color-blue">' + outfit.effect + '</span></strong>'
    }
    if (outfit.flag) {
      $game.flags.set(outfit.flag)
    }
  } else {
    content += _createPartString(head, 'head')
    content += '<br>' + _createPartString(torso, 'torso')
    content += '<br>' + _createPartString(legs, 'legs')
  }

  $('.skinformation p').html(content)

  // Private function used to create text that describes the outfit parts
  function _createPartString (skin, part) {
    var string = '<strong>' + skins[skin][part].name + '.</strong>'
    if (skins[skin][part].description) {
      string += ' ' + skins[skin][part].description
    }
    if (skins[skin][part].effect) {
      string += ' <span class="color-orange">Effect:</span> <span class="color-blue">' + skins[skin][part].effect + '</span>'
    }

    // Set individual part flags as well
    if (skins[skin][part].flag) {
      $game.flags.set(skins[skin][part].flag)
    }

    return string
  }
}

function changePlayerSkin (name, part) {
  // Clear & set skin effect flags here
  clearSkinFlags()

  // Update on DB
  $game.$player.setSkinSuit(name, part)

  // Render
  $game.$render.createCanvasForPlayer($game.$player.id, $game.$player.getSkinSuit(), $game.$player.getColorIndex())
  renderSkinformation()

  // Immediately execute anything based on flags
  applyFlags()
}

function applyFlags () {
  // If radar, re-render NPC comments & update minimap
  if (($game.flags.check('local-radar') || $game.flags.check('global-radar'))) {
    $game.$player.displayNpcComments()
    $game.minimap.radar.update()
  }

  // If speed goes up, change player speed
  if ($game.flags.check('speed-max')) {
    $game.$player.setMoveSpeed(2)
  } else if ($game.flags.check('speed-up')) {
    $game.$player.setMoveSpeed(1.5)
  } else {
    // Reset
    $game.$player.setMoveSpeed()
  }
}

/**
  *
  *  PRIVATE FUNCTIONS
  *
 **/

// Clear all effects of skins sets and parts
function clearSkinFlags () {
  // Effects are stored as game flags.
  // Get all flags from skins data.
  var outfits = data.outfits
  var outfitFlags = _.pluck(outfits, 'flag') // Outfit flags
  var partFlags = _.chain(outfits)
    .map(function (value, key, list) {
      if (list[key].head) return [list[key].head.flag, list[key].torso.flag, list[key].legs.flag]
    })
    .flatten()
    .compact()
    .value()

  // Assemble a list from outfit and part-related flags
  var flags = _.compact(_.union(outfitFlags, partFlags))

  // Clear all flags
  _.each(flags, $game.flags.unset, $game.flags)
}

// If parts are part of an outfit, returns an object containing outfit data.
function getOutfit (head, torso, legs) {
  var outfits = data.outfits

  // If everything is equal, player is wearing a full set
  if (head === torso && torso === legs) {
    return outfits[head]
  } else {
    // If everything is not equal, check if player is wearing a special outfit.
    var parts = {
      'head': head,
      'torso': torso,
      'legs': legs
    }
    // Returns outfit data, or undefined if not found.
    return _.find(outfits, function (each) {
      return _.isEqual(each.parts, parts)
    })
  }
}

// If a part is unlocked, display it as such
function renderUnlockedPart ($el, skin, part, isNew) {
  // skin is either the name of the skin or the skin object itself
  // Either way, we want to end up with the skin object.
  if (typeof skin === 'string') {
    skin = data.outfits[skin]
  }

  var playerColor = $game.$player.getColorIndex()
  var filename = skin.id

  if (skin.id === 'basic' && playerColor > 0) {
    filename = 'basic/' + playerColor
  }

  var $inner = $el.find('.inner')
  var bg = CivicSeed.CLOUD_PATH + '/img/game/skins/' + filename + '.png'

  $el.removeClass('locked')
  $el.attr('title', skin[part].name)
  $el.attr('data-original-title', skin[part].name)  // Force Bootstrap to update tooltip
  $inner.css('backgroundImage', 'url(' + bg + ')')
  $inner.find('i').remove()
  $inner.html('')
  if (isNew === true) {
    $el.find('.badge-new').show()
  }
}

// Expose 'public' methods
module.exports = {
  ready: false,
  getSetsList: getSetsList,
  getSets: getSets,
  getSkin: getSkin,
  unlockSkin: unlockSkin,
  resetSkinventory: resetSkinventory,
  renderSkinventoryUI: renderSkinventoryUI,
  renderSkinformation: renderSkinformation,
  changePlayerSkin: changePlayerSkin,
  applyFlags: applyFlags
}