2014-08-08 14:36:41 -04:00
|
|
|
CocoView = require 'views/kinds/CocoView'
|
|
|
|
template = require 'templates/game-menu/choose-hero-view'
|
|
|
|
{me} = require 'lib/auth'
|
|
|
|
ThangType = require 'models/ThangType'
|
2014-09-20 01:15:58 -04:00
|
|
|
CocoCollection = require 'collections/CocoCollection'
|
|
|
|
SpriteBuilder = require 'lib/sprites/SpriteBuilder'
|
2014-09-21 02:06:28 -04:00
|
|
|
AudioPlayer = require 'lib/AudioPlayer'
|
2014-08-08 14:36:41 -04:00
|
|
|
|
|
|
|
module.exports = class ChooseHeroView extends CocoView
|
|
|
|
id: 'choose-hero-view'
|
|
|
|
className: 'tab-pane'
|
|
|
|
template: template
|
|
|
|
|
2014-08-10 20:41:18 -04:00
|
|
|
events:
|
2014-08-28 12:27:42 -04:00
|
|
|
'click #restart-level-confirm-button': -> Backbone.Mediator.publish 'level:restart', {}
|
2014-09-20 01:15:58 -04:00
|
|
|
'slide.bs.carousel #hero-carousel': 'onHeroChanged'
|
2014-09-20 19:04:13 -04:00
|
|
|
'change #option-code-language': 'onCodeLanguageChanged'
|
2014-09-20 01:15:58 -04:00
|
|
|
|
|
|
|
shortcuts:
|
2014-09-24 18:39:51 -04:00
|
|
|
'left': -> @$el.find('#hero-carousel').carousel('prev') if @heroes.models.length and not @$el.hasClass 'secret'
|
|
|
|
'right': -> @$el.find('#hero-carousel').carousel('next') if @heroes.models.length and not @$el.hasClass 'secret'
|
2014-09-20 01:15:58 -04:00
|
|
|
|
|
|
|
constructor: (options) ->
|
|
|
|
super options
|
|
|
|
@heroes = new CocoCollection([], {model: ThangType})
|
2014-10-29 18:54:51 -04:00
|
|
|
@heroes.url = '/db/thang.type?view=heroes&project=original,name,slug,soundTriggers,featureImage,gems,heroClass,description'
|
2014-09-20 01:15:58 -04:00
|
|
|
@supermodel.loadCollection(@heroes, 'heroes')
|
|
|
|
@stages = {}
|
|
|
|
|
|
|
|
destroy: ->
|
|
|
|
for heroIndex, stage of @stages
|
|
|
|
createjs.Ticker.removeEventListener "tick", stage
|
|
|
|
stage.removeAllChildren()
|
|
|
|
super()
|
2014-08-10 20:41:18 -04:00
|
|
|
|
2014-08-08 14:36:41 -04:00
|
|
|
getRenderData: (context={}) ->
|
|
|
|
context = super(context)
|
2014-09-20 01:15:58 -04:00
|
|
|
context.heroes = @heroes.models
|
2014-09-26 05:28:54 -04:00
|
|
|
hero.locked = temporaryHeroInfo[hero.get('slug')].status is 'Locked' and not me.earnedHero hero.get('original') for hero in context.heroes
|
2014-09-20 01:15:58 -04:00
|
|
|
context.level = @options.level
|
2014-09-20 19:04:13 -04:00
|
|
|
context.codeLanguages = [
|
2014-10-27 14:17:34 -04:00
|
|
|
{id: 'python', name: 'Python (Default)'}
|
2014-09-20 01:15:58 -04:00
|
|
|
{id: 'javascript', name: 'JavaScript'}
|
|
|
|
{id: 'coffeescript', name: 'CoffeeScript'}
|
|
|
|
{id: 'clojure', name: 'Clojure (Experimental)'}
|
|
|
|
{id: 'lua', name: 'Lua (Experimental)'}
|
|
|
|
{id: 'io', name: 'Io (Experimental)'}
|
|
|
|
]
|
2014-09-20 19:04:13 -04:00
|
|
|
context.codeLanguage = @codeLanguage = @options.session.get('codeLanguage') ? me.get('aceConfig')?.language ? 'python'
|
2014-09-20 01:15:58 -04:00
|
|
|
context.heroInfo = temporaryHeroInfo
|
2014-08-08 14:36:41 -04:00
|
|
|
context
|
|
|
|
|
|
|
|
afterRender: ->
|
|
|
|
super()
|
2014-09-20 01:15:58 -04:00
|
|
|
return unless @supermodel.finished()
|
|
|
|
heroes = @heroes.models
|
|
|
|
@$el.find('.hero-indicator').each ->
|
|
|
|
heroID = $(@).data('hero-id')
|
|
|
|
hero = _.find heroes, (hero) -> hero.get('original') is heroID
|
2014-09-24 22:14:22 -04:00
|
|
|
$(@).find('.hero-avatar').css('background-image', "url(#{hero.getPortraitURL()})").tooltip()
|
2014-09-20 18:18:21 -04:00
|
|
|
_.defer => $(@).addClass 'initialized'
|
2014-09-20 01:15:58 -04:00
|
|
|
@canvasWidth = 313 # @$el.find('canvas').width() # unreliable, whatever
|
|
|
|
@canvasHeight = @$el.find('canvas').height()
|
2014-09-20 18:18:21 -04:00
|
|
|
heroConfig = @options.session.get('heroConfig') ? me.get('heroConfig') ? {}
|
|
|
|
heroIndex = Math.max 0, _.findIndex(heroes, ((hero) -> hero.get('original') is heroConfig.thangType))
|
|
|
|
@$el.find(".hero-item:nth-child(#{heroIndex + 1}), .hero-indicator:nth-child(#{heroIndex + 1})").addClass('active')
|
|
|
|
@onHeroChanged direction: null, relatedTarget: @$el.find('.hero-item')[heroIndex]
|
2014-10-26 00:53:55 -04:00
|
|
|
@$el.find('.hero-stat').tooltip()
|
2014-10-27 14:17:34 -04:00
|
|
|
@buildCodeLanguages()
|
2014-09-20 01:15:58 -04:00
|
|
|
|
|
|
|
onHeroChanged: (e) ->
|
|
|
|
direction = e.direction # 'left' or 'right'
|
|
|
|
heroItem = $(e.relatedTarget)
|
|
|
|
hero = _.find @heroes.models, (hero) -> hero.get('original') is heroItem.data('hero-id')
|
2014-09-24 18:39:51 -04:00
|
|
|
return console.error "Couldn't find hero from heroItem:", heroItem unless hero
|
2014-09-20 01:15:58 -04:00
|
|
|
heroIndex = heroItem.index()
|
|
|
|
@$el.find('.hero-indicator').each ->
|
|
|
|
distance = Math.min 3, Math.abs $(@).index() - heroIndex
|
|
|
|
size = 100 - (50 / 3) * distance
|
|
|
|
$(@).css width: size, height: size, top: -(100 - size) / 2
|
|
|
|
heroInfo = temporaryHeroInfo[hero.get('slug')]
|
2014-09-26 05:28:54 -04:00
|
|
|
locked = heroInfo.status is 'Locked' and not me.earnedHero ThangType.heroes[hero.get('slug')]
|
2014-09-20 01:15:58 -04:00
|
|
|
hero = @loadHero hero, heroIndex
|
2014-09-24 18:39:51 -04:00
|
|
|
@preloadHero heroIndex + 1
|
|
|
|
@preloadHero heroIndex - 1
|
2014-09-20 18:18:21 -04:00
|
|
|
@selectedHero = hero unless locked
|
2014-09-21 02:06:28 -04:00
|
|
|
Backbone.Mediator.publish 'level:hero-selection-updated', hero: @selectedHero
|
2014-09-20 18:18:21 -04:00
|
|
|
$('#choose-inventory-button').prop 'disabled', locked
|
2014-09-20 01:15:58 -04:00
|
|
|
|
2014-09-24 18:39:51 -04:00
|
|
|
getFullHero: (original) ->
|
|
|
|
url = "/db/thang.type/#{original}/version"
|
|
|
|
if fullHero = @supermodel.getModel url
|
|
|
|
return fullHero
|
|
|
|
fullHero = new ThangType()
|
|
|
|
fullHero.setURL url
|
|
|
|
fullHero = (@supermodel.loadModel fullHero, 'thang').model
|
|
|
|
fullHero
|
|
|
|
|
|
|
|
preloadHero: (heroIndex) ->
|
|
|
|
return unless hero = @heroes.models[heroIndex]
|
|
|
|
@loadHero hero, heroIndex, true
|
|
|
|
|
|
|
|
loadHero: (hero, heroIndex, preloading=false) ->
|
2014-09-20 01:15:58 -04:00
|
|
|
createjs.Ticker.removeEventListener 'tick', stage for stage in _.values @stages
|
2014-10-14 12:54:18 -04:00
|
|
|
if featureImage = hero.get 'featureImage'
|
|
|
|
$(".hero-item[data-hero-id='#{hero.get('original')}'] canvas").hide()
|
|
|
|
$(".hero-item[data-hero-id='#{hero.get('original')}'] .hero-feature-image").show().find('img').prop('src', '/file/' + featureImage)
|
|
|
|
@playSelectionSound hero unless preloading
|
|
|
|
return hero
|
2014-09-24 22:14:22 -04:00
|
|
|
createjs.Ticker.setFPS 30 # In case we paused it from being inactive somewhere else
|
2014-09-20 01:15:58 -04:00
|
|
|
if stage = @stages[heroIndex]
|
2014-09-24 18:39:51 -04:00
|
|
|
unless preloading
|
|
|
|
_.defer -> createjs.Ticker.addEventListener 'tick', stage # Deferred, otherwise it won't start updating for some reason.
|
|
|
|
@playSelectionSound hero
|
2014-09-20 01:15:58 -04:00
|
|
|
return hero
|
2014-09-24 18:39:51 -04:00
|
|
|
fullHero = @getFullHero hero.get 'original'
|
2014-09-20 01:15:58 -04:00
|
|
|
onLoaded = =>
|
|
|
|
return unless canvas = $(".hero-item[data-hero-id='#{fullHero.get('original')}'] canvas")
|
2014-10-14 12:54:18 -04:00
|
|
|
canvas.show().prop width: @canvasWidth, height: @canvasHeight
|
2014-09-20 01:15:58 -04:00
|
|
|
builder = new SpriteBuilder(fullHero)
|
|
|
|
movieClip = builder.buildMovieClip(fullHero.get('actions').attack?.animation ? fullHero.get('actions').idle.animation)
|
2014-09-21 02:06:28 -04:00
|
|
|
movieClip.scaleX = movieClip.scaleY = canvas.prop('height') / 120 # Average hero height is ~110px tall at normal resolution
|
|
|
|
if fullHero.get('name') in ['Knight', 'Robot Walker'] # These are too big, so shrink them.
|
|
|
|
movieClip.scaleX *= 0.7
|
|
|
|
movieClip.scaleY *= 0.7
|
2014-09-20 01:15:58 -04:00
|
|
|
movieClip.regX = -fullHero.get('positions').registration.x
|
|
|
|
movieClip.regY = -fullHero.get('positions').registration.y
|
|
|
|
movieClip.x = canvas.prop('width') * 0.5
|
2014-09-21 02:06:28 -04:00
|
|
|
movieClip.y = canvas.prop('height') * 0.925 # This is where the feet go.
|
2014-09-20 01:15:58 -04:00
|
|
|
stage = new createjs.Stage(canvas[0])
|
2014-09-24 18:39:51 -04:00
|
|
|
@stages[heroIndex] = stage
|
2014-09-20 01:15:58 -04:00
|
|
|
stage.addChild movieClip
|
|
|
|
stage.update()
|
|
|
|
movieClip.gotoAndPlay 0
|
2014-09-24 18:39:51 -04:00
|
|
|
unless preloading
|
|
|
|
createjs.Ticker.addEventListener 'tick', stage
|
|
|
|
@playSelectionSound hero
|
2014-09-20 01:15:58 -04:00
|
|
|
if fullHero.loaded
|
|
|
|
_.defer onLoaded
|
|
|
|
else
|
|
|
|
@listenToOnce fullHero, 'sync', onLoaded
|
|
|
|
fullHero
|
|
|
|
|
2014-09-21 02:06:28 -04:00
|
|
|
playSelectionSound: (hero) ->
|
|
|
|
return if @$el.hasClass 'secret'
|
|
|
|
@currentSoundInstance?.stop()
|
|
|
|
return unless sounds = hero.get('soundTriggers')?.selected
|
|
|
|
return unless sound = sounds[Math.floor Math.random() * sounds.length]
|
|
|
|
name = AudioPlayer.nameForSoundReference sound
|
|
|
|
AudioPlayer.preloadSoundReference sound
|
|
|
|
@currentSoundInstance = AudioPlayer.playSound name, 1
|
|
|
|
@currentSoundInstance
|
2014-09-20 18:18:21 -04:00
|
|
|
|
2014-10-27 14:17:34 -04:00
|
|
|
buildCodeLanguages: ->
|
|
|
|
$select = @$el.find('#option-code-language')
|
|
|
|
$select.fancySelect().parent().find('.options li').each ->
|
|
|
|
languageName = $(@).text()
|
|
|
|
languageID = $(@).data('value')
|
|
|
|
blurb = $.i18n.t("choose_hero.#{languageID}_blurb")
|
|
|
|
$(@).text("#{languageName} - #{blurb}")
|
|
|
|
|
2014-09-20 19:04:13 -04:00
|
|
|
onCodeLanguageChanged: (e) ->
|
|
|
|
@codeLanguage = @$el.find('#option-code-language').val()
|
|
|
|
@codeLanguageChanged = true
|
2014-09-20 18:18:21 -04:00
|
|
|
|
2014-09-21 02:06:28 -04:00
|
|
|
onShown: ->
|
|
|
|
# Called when we switch tabs to this within the modal
|
|
|
|
|
|
|
|
onHidden: ->
|
|
|
|
# Called when the modal itself is dismissed
|
|
|
|
|
|
|
|
|
2014-09-20 01:15:58 -04:00
|
|
|
temporaryHeroInfo =
|
|
|
|
knight:
|
|
|
|
fullName: 'Tharin Thunderfist'
|
2014-10-26 00:53:55 -04:00
|
|
|
weapons: 'Swords - Short Range, No Magic'
|
|
|
|
class: 'Warrior'
|
|
|
|
description: 'Beefcake! Beefcaaake!'
|
2014-09-20 01:15:58 -04:00
|
|
|
status: 'Available'
|
2014-10-26 00:53:55 -04:00
|
|
|
attack: 8
|
|
|
|
attackFactor: 1.2
|
|
|
|
health: 8.5
|
|
|
|
healthFactor: 1.4
|
|
|
|
speed: 1.5
|
|
|
|
speedAbsolute: 6
|
|
|
|
|
|
|
|
captain:
|
|
|
|
fullName: 'Captain Anya Weston'
|
|
|
|
weapons: 'Swords - Short Range, No Magic'
|
|
|
|
class: 'Warrior'
|
|
|
|
description: 'Don\'t bother me, I\'m winning this fight for you.'
|
|
|
|
status: 'Available'
|
|
|
|
attack: 8
|
|
|
|
attackFactor: 1.2
|
|
|
|
health: 8.5
|
|
|
|
healthFactor: 1.4
|
|
|
|
speed: 1.5
|
|
|
|
speedAbsolute: 6
|
2014-09-20 01:15:58 -04:00
|
|
|
|
|
|
|
thoktar:
|
|
|
|
fullName: 'Thoktar the Devourer'
|
2014-10-26 00:53:55 -04:00
|
|
|
weapons: 'Wands, Staffs - Long Range, Magic'
|
|
|
|
class: 'Wizard'
|
|
|
|
description: '???'
|
2014-09-20 01:15:58 -04:00
|
|
|
status: 'Locked'
|
2014-10-26 00:53:55 -04:00
|
|
|
attack: 5
|
|
|
|
attackFactor: 2
|
|
|
|
health: 4.5
|
|
|
|
healthFactor: 1.4
|
|
|
|
speed: 2.5
|
|
|
|
speedAbsolute: 7
|
|
|
|
skills: ['summonElemental', 'devour']
|
2014-09-20 01:15:58 -04:00
|
|
|
|
|
|
|
equestrian:
|
|
|
|
fullName: 'Rider Reynaldo'
|
2014-10-26 00:53:55 -04:00
|
|
|
weapons: 'Crossbows, Guns - Long Range, No Magic'
|
|
|
|
class: 'Ranger'
|
|
|
|
description: '???'
|
2014-09-20 01:15:58 -04:00
|
|
|
status: 'Locked'
|
2014-10-26 00:53:55 -04:00
|
|
|
attack: 6
|
|
|
|
attackFactor: 1.4
|
|
|
|
health: 7
|
|
|
|
healthFactor: 1.8
|
|
|
|
speed: 1.5
|
|
|
|
speedAbsolute: 6
|
|
|
|
skills: ['hide']
|
2014-09-20 01:15:58 -04:00
|
|
|
|
|
|
|
'potion-master':
|
|
|
|
fullName: 'Master Snake'
|
2014-10-26 00:53:55 -04:00
|
|
|
weapons: 'Wands, Staffs - Long Range, Magic'
|
|
|
|
class: 'Wizard'
|
|
|
|
description: '???'
|
2014-09-20 01:15:58 -04:00
|
|
|
status: 'Locked'
|
2014-10-26 00:53:55 -04:00
|
|
|
attack: 2
|
|
|
|
attackFactor: 0.833
|
|
|
|
health: 4
|
|
|
|
healthFactor: 1.2
|
|
|
|
speed: 6
|
|
|
|
speedAbsolute: 11
|
|
|
|
skills: ['brewPotion']
|
2014-09-20 01:15:58 -04:00
|
|
|
|
|
|
|
librarian:
|
|
|
|
fullName: 'Hushbaum'
|
2014-10-26 00:53:55 -04:00
|
|
|
weapons: 'Wands, Staffs - Long Range, Magic'
|
|
|
|
class: 'Wizard'
|
|
|
|
description: '???'
|
2014-09-20 01:15:58 -04:00
|
|
|
status: 'Locked'
|
2014-10-26 00:53:55 -04:00
|
|
|
attack: 3
|
|
|
|
attackFactor: 1.2
|
|
|
|
health: 4.5
|
|
|
|
healthFactor: 1.4
|
|
|
|
speed: 2.5
|
|
|
|
speedAbsolute: 7
|
2014-09-20 01:15:58 -04:00
|
|
|
|
|
|
|
'robot-walker':
|
|
|
|
fullName: '???'
|
|
|
|
weapons: '???'
|
2014-10-26 00:53:55 -04:00
|
|
|
class: 'Ranger'
|
|
|
|
description: '???'
|
2014-09-20 01:15:58 -04:00
|
|
|
status: 'Locked'
|
2014-10-26 00:53:55 -04:00
|
|
|
attack: 6.5
|
|
|
|
attackFactor: 1.6
|
|
|
|
health: 5.5
|
|
|
|
healthFactor: 1.2
|
|
|
|
speed: 6
|
|
|
|
speedAbsolute: 11
|
|
|
|
skills: ['???', '???', '???']
|
2014-09-20 01:15:58 -04:00
|
|
|
|
|
|
|
'michael-heasell':
|
|
|
|
fullName: '???'
|
|
|
|
weapons: '???'
|
2014-10-26 00:53:55 -04:00
|
|
|
class: 'Ranger'
|
|
|
|
description: '???'
|
2014-09-20 01:15:58 -04:00
|
|
|
status: 'Locked'
|
2014-10-26 00:53:55 -04:00
|
|
|
attack: 4
|
|
|
|
attackFactor: 0.714
|
|
|
|
health: 5
|
|
|
|
healthFactor: 1
|
|
|
|
speed: 10
|
|
|
|
speedAbsolute: 16
|
|
|
|
skills: ['???', '???']
|
2014-09-20 01:15:58 -04:00
|
|
|
|
|
|
|
'ian-elliott':
|
|
|
|
fullName: '???'
|
2014-10-26 00:53:55 -04:00
|
|
|
weapons: 'Swords - Short Range, No Magic'
|
|
|
|
class: 'Warrior'
|
|
|
|
description: '???'
|
2014-09-20 01:15:58 -04:00
|
|
|
status: 'Locked'
|
2014-10-26 00:53:55 -04:00
|
|
|
attack: 9.5
|
|
|
|
attackFactor: 1.8
|
|
|
|
health: 6.5
|
|
|
|
healthFactor: 0.714
|
|
|
|
speed: 3.5
|
|
|
|
speedAbsolute: 8
|
|
|
|
skills: ['trueStrike']
|