CocoView = require 'views/kinds/CocoView' template = require 'templates/play/level/hud' prop_template = require 'templates/play/level/hud_prop' action_template = require 'templates/play/level/hud_action' DialogueAnimator = require './DialogueAnimator' module.exports = class LevelHUDView extends CocoView id: 'thang-hud' template: template dialogueMode: false showingActions: false subscriptions: 'surface:frame-changed': 'onFrameChanged' 'level:disable-controls': 'onDisableControls' 'level:enable-controls': 'onEnableControls' 'surface:sprite-selected': 'onSpriteSelected' 'sprite:speech-updated': 'onSpriteDialogue' 'level:sprite-clear-dialogue': 'onSpriteClearDialogue' 'level:shift-space-pressed': 'onShiftSpacePressed' 'level:escape-pressed': 'onEscapePressed' 'sprite:dialogue-sound-completed': 'onDialogueSoundCompleted' 'sprite:thang-began-talking': 'onThangBeganTalking' 'sprite:thang-finished-talking': 'onThangFinishedTalking' 'god:new-world-created': 'onNewWorld' events: 'click': 'onClick' afterRender: -> super() @$el.addClass 'no-selection' if @options.level.get('slug') in ['dungeons-of-kithgard', 'gems-in-the-deep', 'forgetful-gemsmith', 'shadow-guard', 'kounter-kithwise', 'crawlways-of-kithgard', 'true-names', 'favorable-odds', 'the-raised-sword', 'the-first-kithmaze', 'haunted-kithmaze', 'descending-further', 'the-second-kithmaze', 'dread-door', 'known-enemy', 'master-of-names', 'lowly-kithmen', 'closing-the-distance', 'tactical-strike', 'the-final-kithmaze', 'the-gauntlet'] @hidesHUD = true @$el.addClass 'hide-hud-properties' onClick: (e) -> Backbone.Mediator.publish 'tome:focus-editor', {} unless $(e.target).parents('.thang-props').length onFrameChanged: (e) -> @timeProgress = e.progress @update() onDisableControls: (e) -> return if e.controls and not ('hud' in e.controls) @disabled = true onEnableControls: (e) -> return if e.controls and not ('hud' in e.controls) @disabled = false onSpriteSelected: (e) -> # TODO: this allows the surface and HUD selection to get out of sync if we select another unit while in dialogue mode return if @disabled or @dialogueMode @switchToThangElements() @setThang e.thang, e.sprite?.thangType onSpriteDialogue: (e) -> return unless e.message spriteID = e.sprite.thang.id @setSpeaker e.sprite @stage?.startTalking() @setMessage(e.message, e.mood, e.responses) window.tracker?.trackEvent 'Heard Sprite', {speaker: spriteID, message: e.message, label: e.message}, ['Google Analytics'] onDialogueSoundCompleted: -> @stage?.stopTalking() onSpriteClearDialogue: -> @clearSpeaker() onNewWorld: (e) -> hadThang = @thang @thang = e.world.thangMap[@thang.id] if @thang if hadThang and not @thang @setThang null, null else if @thang @createActions() # Make sure it updates its actions. setThang: (thang, thangType) -> unless @speaker if not thang? and not @thang? then return if thang? and @thang? and thang.id is @thang.id then return @thang = thang @thangType = thangType @$el.toggleClass 'no-selection', not @thang? clearTimeout @hintNextSelectionTimeout @$el.find('.no-selection-message').hide() if not @thang unless @options.level.get('type', true) in ['hero', 'hero-ladder', 'hero-coop'] @hintNextSelectionTimeout = _.delay((=> @$el.find('.no-selection-message').slideDown('slow')), 10000) return @createAvatar thangType, @thang @createProperties() @createActions() @update() @speaker = null setSpeaker: (speakerSprite) -> return if speakerSprite is @speakerSprite @speakerSprite = speakerSprite @speaker = @speakerSprite.thang.id @createAvatar @speakerSprite.thangType, @speakerSprite.thang, @speakerSprite.options.colorConfig @$el.removeClass 'no-selection' @switchToDialogueElements() clearSpeaker: -> if not @thang @$el.addClass 'no-selection' @setThang @thang, @thangType @switchToThangElements() @speaker = null @speakerSprite = null @bubble = null @update() createAvatar: (thangType, thang, colorConfig) -> unless thangType.isFullyLoaded() args = arguments unless @listeningToCreateAvatar @listenToOnce thangType, 'sync', -> @createAvatar(args...) @listeningToCreateAvatar = true return @listeningToCreateAvatar = false options = thang.getLankOptions() or {} options.async = false options.colorConfig = colorConfig if colorConfig wrapper = @$el.find '.thang-canvas-wrapper' team = @thang?.team or @speakerSprite?.thang?.team wrapper.removeClass (i, css) -> (css.match(/\bteam-\S+/g) or []).join ' ' wrapper.addClass "team-#{team}" if thangType.get('raster') wrapper.empty().append($('').attr('src', '/file/'+thangType.get('raster'))) else return unless stage = thangType.getPortraitStage options newCanvas = $(stage.canvas).addClass('thang-canvas') wrapper.empty().append(newCanvas) stage.update() @stage?.stopTalking() @stage = stage onThangBeganTalking: (e) -> return unless @stage and @thang is e.thang @stage?.startTalking() onThangFinishedTalking: (e) -> return unless @stage and @thang is e.thang @stage?.stopTalking() createProperties: -> props = @$el.find('.thang-props') props.find(':not(.thang-name)').remove() if @thang.id is 'Hero Placeholder' name = {knight: 'Tharin', captain: 'Anya'}[@thang.type] ? 'Hero' else name = if @thang.type then "#{@thang.id} - #{@thang.type}" else @thang.id props.find('.thang-name').text name propNames = _.without @thang.hudProperties ? [], 'action' nColumns = Math.ceil propNames.length / 5 columns = ($('
').appendTo(props) for i in [0 ... nColumns]) for prop, i in propNames continue if prop is 'action' pel = @createPropElement prop continue unless pel? if pel.find('.bar').is('*') and props.find('.bar').is('*') props.find('.bar-prop').last().after pel # Keep bars together else columns[i % nColumns].append pel null createActions: -> actions = @$el.find('.thang-actions tbody').empty() showActions = @thang.world and not @thang.notOfThisWorld and not _.isEmpty(@thang.actions) and 'action' in (@thang.hudProperties ? []) @$el.find('.thang-actions').toggleClass 'secret', not showActions @showingActions = showActions return unless showActions @buildActionTimespans() for actionName, action of @thang.actions actions.append @createActionElement(actionName) @lastActionTimespans[actionName] = {} setMessage: (message, mood, responses) -> message = marked message # Fix old HTML icons like in the Markdown message = message.replace /<i class='(.+?)'><\/i>/, "" clearInterval(@messageInterval) if @messageInterval @bubble = $('.dialogue-bubble', @$el) @bubble.removeClass(@lastMood) if @lastMood @lastMood = mood @bubble.text('') group = $('') @bubble.append(group) if responses @lastResponses = responses for response in responses button = $('').text(response.text) button.addClass response.buttonClass if response.buttonClass group.append(button) response.button = $('button:last', group) else if @options.level.get('type', true) in ['hero', 'hero-ladder', 'hero-coop'] s = $.i18n.t('play_level.hud_continue_short', defaultValue: 'Continue') else s = $.i18n.t('play_level.hud_continue', defaultValue: 'Continue (shift+space)') # Get rid of eventually sk = $.i18n.t('play_level.skip_tutorial', defaultValue: 'skip: esc') if not @escapePressed group.append('' + sk + '') group.append($('')) @lastResponses = null if @speaker is 'Hero Placeholder' # Doesn't work if it fires from a script; we don't really know who we are then. name = {knight: 'Tharin', captain: 'Anya'}[@speakerSprite?.thang?.id] ? 'Hero' else name = @speaker @bubble.append($("