Implemented new HUD design. Removed more unneeded stuff. Changed reload button to restart level. Still need restart confirmation and to redo the sprite dialogue area.
Before Width: | Height: | Size: 290 B |
Before Width: | Height: | Size: 332 B |
Before Width: | Height: | Size: 505 B |
Before Width: | Height: | Size: 3 KiB |
Before Width: | Height: | Size: 312 B |
Before Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 3.7 KiB |
Before Width: | Height: | Size: 3.7 KiB |
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 2.2 KiB |
|
@ -56,6 +56,7 @@ $level-resize-transition-time: 0.5s
|
|||
|
||||
.level-content
|
||||
position: relative
|
||||
overflow: hidden
|
||||
|
||||
#canvas-wrapper
|
||||
top: 50px
|
||||
|
|
|
@ -1,315 +1,199 @@
|
|||
@import "app/styles/mixins"
|
||||
@import "app/styles/bootstrap/variables"
|
||||
|
||||
#thang-hud.no-selection
|
||||
.center
|
||||
> *
|
||||
display: none
|
||||
|
||||
.no-selection-message
|
||||
//display: block // we fade this in
|
||||
margin: auto
|
||||
width: 100%
|
||||
height: 100%
|
||||
text-align: center
|
||||
background: transparent url(/images/level/no_selection_pointer.png) no-repeat 50% 40%
|
||||
background-size: 40px 37px
|
||||
@include opacity(75)
|
||||
|
||||
&:hover
|
||||
@include opacity(100)
|
||||
|
||||
div
|
||||
padding-top: 70px
|
||||
font-size: 14px
|
||||
|
||||
#thang-hud
|
||||
width: 55%
|
||||
height: 120px
|
||||
height: 80px
|
||||
position: relative
|
||||
overflow: visible
|
||||
|
||||
.left-wing
|
||||
.wood-background
|
||||
position: absolute
|
||||
width: 50%
|
||||
height: 100%
|
||||
left: 0
|
||||
top: 0
|
||||
z-index: 0
|
||||
background-image: url(/images/level/hud_left_wing.png)
|
||||
background-position: right
|
||||
top: -2px
|
||||
background: transparent url(/images/level/hud_wood_background.png)
|
||||
background-size: auto 100%
|
||||
width: 100%
|
||||
height: 100px
|
||||
z-index: 2
|
||||
|
||||
.right-wing
|
||||
.hinge
|
||||
position: absolute
|
||||
width: 50%
|
||||
height: 100%
|
||||
right: 0
|
||||
top: -16px
|
||||
background: transparent url(/images/level/hud_hinge.png)
|
||||
width: 27px
|
||||
height: 44px
|
||||
background-size: contain
|
||||
z-index: 2
|
||||
pointer-events: none
|
||||
|
||||
.hinge-0
|
||||
left: 3%
|
||||
.hinge-1
|
||||
left: 12%
|
||||
.hinge-2
|
||||
left: 86%
|
||||
@include scaleX(-1)
|
||||
.hinge-3
|
||||
left: 95%
|
||||
@include scaleX(-1)
|
||||
|
||||
.avatar-wrapper-container
|
||||
position: absolute
|
||||
width: 100px
|
||||
height: 100px
|
||||
top: 0
|
||||
z-index: 0
|
||||
background-image: url(/images/level/hud_right_wing.png)
|
||||
background-position: left
|
||||
left: 18%
|
||||
left: -webkit-calc(50% - (560px - 100px) / 2 - 10px)
|
||||
left: calc(50% - (560px - 100px) / 2 - 10px)
|
||||
z-index: 3
|
||||
|
||||
&.hide-hud-properties .center
|
||||
.thang-props, .thang-actions
|
||||
visibility: hidden
|
||||
position: absolute
|
||||
|
||||
.center
|
||||
width: 560px
|
||||
height: 120px
|
||||
padding: 4px 20px 0 20px
|
||||
margin: auto
|
||||
background-image: url(/images/level/hud_center.png)
|
||||
color: #BEBEBE
|
||||
font-size: 12px
|
||||
overflow: hidden
|
||||
z-index: 1
|
||||
position: relative
|
||||
|
||||
.no-selection-message
|
||||
display: none
|
||||
|
||||
.thang-canvas-wrapper, .speaker-image-wrapper
|
||||
width: 100px
|
||||
height: 100px
|
||||
margin: 7px 7px 7px 22px
|
||||
float: left
|
||||
overflow: hidden
|
||||
border: 1px solid #888
|
||||
.thang-canvas-wrapper
|
||||
width: 80px
|
||||
height: 80px
|
||||
position: relative
|
||||
border-radius: 4px
|
||||
@include gradient-radial-custom-stops(hsla(205,0%,74%,1), 20%, hsla(205,0%,31%,1), 70%)
|
||||
|
||||
|
||||
&.team-humans
|
||||
border-color: darkred
|
||||
@include gradient-radial-custom-stops(hsla(4,80%,74%,1), 20%, hsla(4,80%,51%,1), 70%)
|
||||
|
||||
|
||||
&.team-ogres
|
||||
border-color: darkblue
|
||||
@include gradient-radial-custom-stops(hsla(205,100%,74%,1), 20%, hsla(205,100%,31%,1), 70%)
|
||||
|
||||
|
||||
&.team-allies, &.team-minions
|
||||
border-color: darkgreen
|
||||
@include gradient-radial-custom-stops(hsla(116,80%,74%,1), 20%, hsla(116,80%,31%,1), 70%)
|
||||
|
||||
.thang-canvas
|
||||
width: 100%
|
||||
|
||||
.avatar-frame
|
||||
position: absolute
|
||||
left: -18%
|
||||
top: -19%
|
||||
width: 145%
|
||||
|
||||
&.hide-hud-properties .center:hover
|
||||
// Don't allow them to hover over confusing HUD stuff until later levels
|
||||
top: 24px
|
||||
|
||||
.center
|
||||
width: 560px
|
||||
height: 166px
|
||||
position: absolute
|
||||
top: 24px
|
||||
left: 13%
|
||||
left: -webkit-calc(50% - 560px / 2)
|
||||
left: calc(50% - 560px / 2)
|
||||
padding: 10px 20px 0 145px
|
||||
background-image: url(/images/level/hud_background.png)
|
||||
color: white
|
||||
text-transform: uppercase
|
||||
font-family: Open Sans Condensed
|
||||
font-weight: bold
|
||||
font-size: 16px
|
||||
z-index: 2
|
||||
@include transition(0.5s ease)
|
||||
|
||||
&:hover
|
||||
top: -36px
|
||||
|
||||
.thang-name
|
||||
font-size: 18px
|
||||
margin: 10px 0 0 0
|
||||
|
||||
.thang-props
|
||||
margin: 8px 8px 0 0
|
||||
margin: 24px 0 0 0
|
||||
float: left
|
||||
@include user-select(text)
|
||||
|
||||
.thang-props-column
|
||||
.prop:not([name="health"])
|
||||
min-width: 120px
|
||||
display: inline-block
|
||||
line-height: 16px
|
||||
|
||||
&.nonexistent
|
||||
visibility: hidden
|
||||
|
||||
.text-prop
|
||||
width: 50%
|
||||
|
||||
.prop-label-icon
|
||||
$iconSize: 16px
|
||||
display: inline-block
|
||||
width: $iconSize
|
||||
height: $iconSize
|
||||
margin-right: 5px
|
||||
background: transparent url(/images/level/hud_info_icons.png) no-repeat
|
||||
background-size: auto $iconSize
|
||||
float: left
|
||||
width: 144px
|
||||
height: 100px
|
||||
@include user-select(text)
|
||||
|
||||
&.nonexistent
|
||||
visibility: hidden
|
||||
|
||||
.text-prop
|
||||
width: 50%
|
||||
&.prop-label-icon-pos
|
||||
background-position: (-1 * $iconSize) 0px
|
||||
&.prop-label-icon-target
|
||||
background-position: (-2 * $iconSize) 0px
|
||||
&.prop-label-icon-collectedThangIDs
|
||||
background-position: (-3 * $iconSize) 0px
|
||||
&.prop-label-icon-visualRange
|
||||
background-position: (-4 * $iconSize) 0px
|
||||
&.prop-label-icon-attackDamage
|
||||
background-position: (-5 * $iconSize) 0px
|
||||
&.prop-label-icon-attackRange, &.prop-label-icon-attackNearbyEnemyRange
|
||||
background-position: (-6 * $iconSize) 0px
|
||||
&.prop-label-icon-maxSpeed
|
||||
background-position: (-7 * $iconSize) 0px
|
||||
&.prop-label-icon-gold, &.prop-label-icon-bountyGold
|
||||
background-position: (-8 * $iconSize) 0px
|
||||
|
||||
.prop-label-icon
|
||||
$iconSize: 16px
|
||||
display: inline-block
|
||||
width: $iconSize
|
||||
height: $iconSize
|
||||
margin-right: 5px
|
||||
background: transparent url(/images/level/hud_info_icons.png) no-repeat
|
||||
background-size: auto $iconSize
|
||||
float: left
|
||||
|
||||
&.prop-label-icon-pos
|
||||
background-position: (-1 * $iconSize) 0px
|
||||
&.prop-label-icon-target
|
||||
background-position: (-2 * $iconSize) 0px
|
||||
&.prop-label-icon-collectedThangIDs
|
||||
background-position: (-3 * $iconSize) 0px
|
||||
&.prop-label-icon-visualRange
|
||||
background-position: (-4 * $iconSize) 0px
|
||||
&.prop-label-icon-attackDamage
|
||||
background-position: (-5 * $iconSize) 0px
|
||||
&.prop-label-icon-attackRange, &.prop-label-icon-attackNearbyEnemyRange
|
||||
background-position: (-6 * $iconSize) 0px
|
||||
&.prop-label-icon-maxSpeed
|
||||
background-position: (-7 * $iconSize) 0px
|
||||
&.prop-label-icon-gold, &.prop-label-icon-bountyGold
|
||||
background-position: (-8 * $iconSize) 0px
|
||||
|
||||
.prop-value.bar-prop
|
||||
width: 100px
|
||||
display: inline-block
|
||||
height: 6px
|
||||
background: #ddd
|
||||
border: 1px solid black
|
||||
border-radius: 6px
|
||||
overflow: hidden
|
||||
|
||||
.bar
|
||||
background: black
|
||||
width: 100%
|
||||
height: 100%
|
||||
|
||||
.prop[name="health"] .bar
|
||||
background: #C5362B
|
||||
|
||||
.message
|
||||
text-align: center
|
||||
display: table
|
||||
height: 100%
|
||||
width: 100%
|
||||
|
||||
p
|
||||
display: table-cell
|
||||
vertical-align: middle
|
||||
font-size: 20px
|
||||
|
||||
.thang-actions
|
||||
width: 212px
|
||||
height: 100px
|
||||
margin: 8px 24px 8px 0
|
||||
float: left
|
||||
overflow-y: auto
|
||||
overflow-x: hidden
|
||||
|
||||
.table-container
|
||||
position: relative
|
||||
|
||||
.progress-indicator
|
||||
position: absolute
|
||||
right: 45px
|
||||
background-color: #fce
|
||||
z-index: 1
|
||||
|
||||
.progress-arrow
|
||||
width: 14px
|
||||
height: 6px
|
||||
top: -6px
|
||||
// Could put top: 0 to make the arrow go inside, but it doesn't have enough contrast over the bar
|
||||
right: 39px
|
||||
background: transparent url(/images/level/action_timeline_indicator.png) no-repeat center
|
||||
|
||||
.progress-line
|
||||
width: 1px
|
||||
height: 100%
|
||||
top: 0
|
||||
background-color: #A4A198
|
||||
|
||||
table
|
||||
border: 1px solid #5B5855
|
||||
border-radius: 2px
|
||||
line-height: 17px
|
||||
width: 100%
|
||||
|
||||
tr
|
||||
border: 1px solid #5b5855
|
||||
|
||||
.action-indicator
|
||||
width: 10px
|
||||
|
||||
.action-label
|
||||
width: 75px
|
||||
|
||||
.action-timeline
|
||||
padding: 0
|
||||
|
||||
.timeline-wrapper
|
||||
position: relative
|
||||
width: 100%
|
||||
height: 19px
|
||||
|
||||
div
|
||||
border-radius: 1px
|
||||
background-color: #6BA1C8
|
||||
height: 100%
|
||||
border-bottom: 2px groove darken(#6BA1C8, 30%)
|
||||
border-right: 1px solid darken(#6BA1C8, 10%)
|
||||
position: absolute
|
||||
top: 0
|
||||
|
||||
.current-action
|
||||
font-weight: bold
|
||||
|
||||
.action-indicator
|
||||
background: #4B4133 url(/images/level/current_action_indicator.png) no-repeat center
|
||||
|
||||
td
|
||||
background-color: #4B4133
|
||||
|
||||
.dialogue-area
|
||||
opacity: 0.0
|
||||
position: relative
|
||||
height: 100%
|
||||
width: 100%
|
||||
z-index: 1
|
||||
|
||||
.bubble
|
||||
.prop[name="health"]
|
||||
position: absolute
|
||||
left: 140px
|
||||
right: 8px
|
||||
top: 8px
|
||||
bottom: 0px
|
||||
padding: 10px
|
||||
color: black
|
||||
font-weight: bold
|
||||
background: #FFFFFF
|
||||
border: black solid 1px
|
||||
border-radius: 10px
|
||||
font-size: 14px
|
||||
right: 35px
|
||||
top: 23px
|
||||
height: 18px
|
||||
line-height: 18px
|
||||
font-size: 18px
|
||||
|
||||
strong
|
||||
color: #09B057
|
||||
.prop-value.bar-prop
|
||||
width: 150px
|
||||
margin: 1px 10px 0 0
|
||||
height: 16px
|
||||
background: rgb(32, 27, 21)
|
||||
padding: 4px
|
||||
border-radius: 8px
|
||||
border: 0
|
||||
|
||||
.hud-hint
|
||||
font-weight: normal
|
||||
color: #999
|
||||
|
||||
.enter
|
||||
position: absolute
|
||||
right: 7px
|
||||
bottom: 7px
|
||||
div.dot
|
||||
background: #337
|
||||
width: 8px
|
||||
.bar
|
||||
background: rgb(234, 35, 45)
|
||||
height: 8px
|
||||
position: absolute
|
||||
right: 8px
|
||||
top: 9px
|
||||
border-radius: 5px
|
||||
border-radius: 4px
|
||||
|
||||
button, .alert
|
||||
padding: 2px 5px
|
||||
.bar-prop-value
|
||||
vertical-align: top
|
||||
|
||||
.enter button.with-dot
|
||||
padding-right: 20px
|
||||
.prop-value.bar-prop
|
||||
width: 100px
|
||||
display: inline-block
|
||||
height: 6px
|
||||
background: #ddd
|
||||
border: 1px solid black
|
||||
border-radius: 6px
|
||||
overflow: hidden
|
||||
|
||||
h3
|
||||
margin: 0
|
||||
font-size: 16px
|
||||
line-height: 16px
|
||||
color: #338
|
||||
.bar
|
||||
background: black
|
||||
width: 100%
|
||||
height: 100%
|
||||
|
||||
button
|
||||
margin-left: 10px
|
||||
.message
|
||||
text-align: center
|
||||
display: table
|
||||
height: 100%
|
||||
width: 100%
|
||||
|
||||
.bubble:after
|
||||
content: ""
|
||||
position: absolute
|
||||
top: 55px
|
||||
left: -27px
|
||||
border-style: solid
|
||||
border-width: 11px 27px 11px 0
|
||||
border-color: transparent #FFFFFF
|
||||
display: block
|
||||
width: 0
|
||||
z-index: 1
|
||||
|
||||
.bubble:before
|
||||
content: ""
|
||||
position: absolute
|
||||
top: 55px
|
||||
left: -28px
|
||||
border-style: solid
|
||||
border-width: 11px 27px 11px 0
|
||||
border-color: transparent black
|
||||
display: block
|
||||
width: 0
|
||||
z-index: 0
|
||||
p
|
||||
display: table-cell
|
||||
vertical-align: middle
|
||||
font-size: 20px
|
||||
|
|
|
@ -1,29 +1,14 @@
|
|||
.center
|
||||
.wood-background
|
||||
|
||||
.hinge.hinge-0
|
||||
.hinge.hinge-1
|
||||
.hinge.hinge-2
|
||||
.hinge.hinge-3
|
||||
|
||||
.avatar-wrapper-container
|
||||
.thang-canvas-wrapper.thang-elem
|
||||
canvas.thang-canvas
|
||||
|
||||
|
||||
.center
|
||||
.thang-name
|
||||
.thang-props.thang-elem
|
||||
.thang-name
|
||||
|
||||
.thang-actions.thang-elem
|
||||
.nano
|
||||
.nano-content
|
||||
.action-header(data-i18n="play_level.action_timeline") Action Timeline
|
||||
.table-container
|
||||
.progress-arrow.progress-indicator
|
||||
.progress-line.progress-indicator
|
||||
table
|
||||
tbody
|
||||
|
||||
.dialogue-area
|
||||
p.bubble.dialogue-bubble
|
||||
|
||||
.no-selection-message
|
||||
div(data-i18n="play_level.click_to_select") Click on a unit to select it.
|
||||
|
||||
.left-wing
|
||||
.gradient#hud-left-gradient.gradient
|
||||
|
||||
.right-wing
|
||||
.gradient#hud-right-gradient.gradient
|
|
@ -1,5 +0,0 @@
|
|||
tr(name="#{action}")
|
||||
td.action-indicator
|
||||
td.action-label #{action}
|
||||
td.action-timeline
|
||||
.timeline-wrapper
|
|
@ -1,25 +1,17 @@
|
|||
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'
|
||||
|
@ -50,69 +42,23 @@ module.exports = class LevelHUDView extends CocoView
|
|||
@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()
|
||||
return if @disabled
|
||||
@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
|
||||
|
||||
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
|
||||
return unless @thang
|
||||
@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) ->
|
||||
|
@ -127,18 +73,19 @@ module.exports = class LevelHUDView extends CocoView
|
|||
options.async = false
|
||||
options.colorConfig = colorConfig if colorConfig
|
||||
wrapper = @$el.find '.thang-canvas-wrapper'
|
||||
team = @thang?.team or @speakerSprite?.thang?.team
|
||||
team = @thang?.team
|
||||
wrapper.removeClass (i, css) -> (css.match(/\bteam-\S+/g) or []).join ' '
|
||||
wrapper.addClass "team-#{team}"
|
||||
if thangType.get('raster')
|
||||
wrapper.empty().append($('<img />').attr('src', '/file/'+thangType.get('raster')))
|
||||
wrapper.empty().append($('<img />').addClass('avatar').attr('src', '/file/'+thangType.get('raster')))
|
||||
else
|
||||
return unless stage = thangType.getPortraitStage options
|
||||
newCanvas = $(stage.canvas).addClass('thang-canvas')
|
||||
return unless stage = thangType.getPortraitStage options, 100
|
||||
newCanvas = $(stage.canvas).addClass('thang-canvas avatar')
|
||||
wrapper.empty().append(newCanvas)
|
||||
stage.update()
|
||||
@stage?.stopTalking()
|
||||
@stage = stage
|
||||
wrapper.append($('<img />').addClass('avatar-frame').attr('src', '/images/level/thang_avatar_frame.png'))
|
||||
|
||||
onThangBeganTalking: (e) ->
|
||||
return unless @stage and @thang is e.thang
|
||||
|
@ -149,136 +96,29 @@ module.exports = class LevelHUDView extends CocoView
|
|||
@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 = ($('<div class="thang-props-column"></div>').appendTo(props) for i in [0 ... nColumns])
|
||||
@$el.find('.thang-name').text name
|
||||
props = @$el.find('.thang-props')
|
||||
props.find('.prop').remove()
|
||||
#propNames = _.without @thang.hudProperties ? [], 'action'
|
||||
propNames = @thang.hudProperties
|
||||
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
|
||||
props.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 <i class='icon-play'></i> in the Markdown
|
||||
message = message.replace /<i class='(.+?)'><\/i>/, "<i class='$1'></i>"
|
||||
clearInterval(@messageInterval) if @messageInterval
|
||||
@bubble = $('.dialogue-bubble', @$el)
|
||||
@bubble.removeClass(@lastMood) if @lastMood
|
||||
@lastMood = mood
|
||||
@bubble.text('')
|
||||
group = $('<div class="enter secret"></div>')
|
||||
@bubble.append(group)
|
||||
if responses
|
||||
@lastResponses = responses
|
||||
for response in responses
|
||||
button = $('<button class="btn btn-small banner"></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('<span class="hud-hint">' + sk + '</span>')
|
||||
group.append($('<button class="btn btn-small banner with-dot">' + s + ' <div class="dot"></div></button>'))
|
||||
@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($("<h3>#{name}</h3>"))
|
||||
@animator = new DialogueAnimator(message, @bubble)
|
||||
@messageInterval = setInterval(@addMoreMessage, 1000 / 30) # 30 FPS
|
||||
|
||||
addMoreMessage: =>
|
||||
if @animator.done()
|
||||
clearInterval(@messageInterval)
|
||||
@messageInterval = null
|
||||
$('.enter', @bubble).removeClass('secret').css('opacity', 0.0).delay(500).animate({opacity: 1.0}, 500, @animateEnterButton)
|
||||
if @lastResponses
|
||||
buttons = $('.enter button')
|
||||
for response, i in @lastResponses
|
||||
channel = response.channel.replace 'level-set-playing', 'level:set-playing' # Easier than migrating all those victory buttons.
|
||||
f = (r) => => setTimeout((-> Backbone.Mediator.publish(channel, r.event or {})), 10)
|
||||
$(buttons[i]).click(f(response))
|
||||
else
|
||||
$('.enter', @bubble).click(-> Backbone.Mediator.publish('script:end-current-script', {}))
|
||||
return
|
||||
@animator.tick()
|
||||
|
||||
onShiftSpacePressed: (e) ->
|
||||
@shiftSpacePressed = (@shiftSpacePressed || 0) + 1
|
||||
# We don't need to handle script:end-current-script--that's done--but if we do have
|
||||
# custom buttons, then we need to trigger the one that should fire (the last one).
|
||||
# If we decide that always having the last one fire is bad, we should make it smarter.
|
||||
return unless @lastResponses?.length
|
||||
r = @lastResponses[@lastResponses.length - 1]
|
||||
channel = r.channel.replace 'level-set-playing', 'level:set-playing'
|
||||
_.delay (-> Backbone.Mediator.publish(channel, r.event or {})), 10
|
||||
|
||||
onEscapePressed: (e) ->
|
||||
@escapePressed = true
|
||||
|
||||
animateEnterButton: =>
|
||||
return unless @bubble
|
||||
button = $('.enter', @bubble)
|
||||
dot = $('.dot', button)
|
||||
dot.animate({opacity: 0.2}, 300).animate({opacity: 1.9}, 600, @animateEnterButton)
|
||||
|
||||
switchToDialogueElements: ->
|
||||
@dialogueMode = true
|
||||
$('.thang-elem', @$el).addClass('secret')
|
||||
@$el.find('.thang-canvas-wrapper').removeClass('secret')
|
||||
$('.dialogue-area', @$el)
|
||||
.removeClass('secret')
|
||||
.animate({opacity: 1.0}, 200)
|
||||
$('.dialogue-bubble', @$el)
|
||||
.css('opacity', 0.0)
|
||||
.delay(200)
|
||||
.animate({opacity: 1.0}, 200)
|
||||
clearTimeout @hintNextSelectionTimeout
|
||||
|
||||
switchToThangElements: ->
|
||||
@dialogueMode = false
|
||||
$('.thang-elem', @$el).removeClass('secret')
|
||||
$('.dialogue-area', @$el).addClass('secret')
|
||||
$('.thang-actions', @$el).toggleClass 'secret', not @showingActions
|
||||
@$el.find('.thang-canvas-wrapper').addClass('secret') if @hidesHUD
|
||||
|
||||
update: ->
|
||||
return unless @thang and not @speaker
|
||||
return unless @thang
|
||||
@$el.find('.thang-props-column').toggleClass 'nonexistent', not @thang.exists
|
||||
if @thang.exists
|
||||
@updatePropElement(prop, @thang[prop]) for prop in @thang.hudProperties ? []
|
||||
# Update action timeline
|
||||
@updateActions()
|
||||
|
||||
createPropElement: (prop) ->
|
||||
if prop in ['maxHealth']
|
||||
|
@ -336,60 +176,6 @@ module.exports = class LevelHUDView extends CocoView
|
|||
return 'No ' + prop
|
||||
return val
|
||||
|
||||
updateActions: ->
|
||||
return unless @thang.world and @showingActions and not _.isEmpty @thang.actions
|
||||
@buildActionTimespans() unless @timespans
|
||||
for actionName, action of @thang.actions
|
||||
@updateActionElement(actionName, @timespans[actionName], @thang.action is actionName)
|
||||
tableContainer = @$el.find('.table-container')
|
||||
arrow = tableContainer.find('.progress-arrow')
|
||||
@timelineWidth ||= tableContainer.find('tr:not(.secret) .action-timeline').width()
|
||||
@actionArrowWidth ||= arrow.width()
|
||||
right = (1 - (@timeProgress ? 0)) * @timelineWidth
|
||||
arrow.css 'right', right - @actionArrowWidth / 2
|
||||
tableContainer.find('.progress-line').css 'right', right
|
||||
|
||||
buildActionTimespans: ->
|
||||
@lastActionTimespans = {}
|
||||
@timespans = {}
|
||||
dt = @thang.world.dt
|
||||
actionHistory = @thang.world.actionsForThang @thang.id, true
|
||||
[lastFrame, lastAction] = [0, null]
|
||||
for hist in actionHistory.concat {frame: @thang.world.totalFrames, name: 'END'}
|
||||
[newFrame, newAction] = [hist.frame, hist.name]
|
||||
continue if newAction is lastAction
|
||||
if newFrame > lastFrame and lastAction
|
||||
# TODO: don't push it if it didn't exist until then
|
||||
(@timespans[lastAction] ?= []).push [lastFrame * dt, newFrame * dt]
|
||||
[lastFrame, lastAction] = [newFrame, newAction]
|
||||
|
||||
createActionElement: (action) ->
|
||||
$(action_template(action: action))
|
||||
|
||||
updateActionElement: (action, timespans, current) ->
|
||||
ael = @$el.find '.thang-actions *[name=' + action + ']'
|
||||
ael.toggleClass 'current-action', current
|
||||
|
||||
timespans ?= []
|
||||
lastTimespans = @lastActionTimespans[action] ? []
|
||||
if @lastActionTimespans and timespans.length is lastTimespans.length
|
||||
changed = false
|
||||
for timespan, i in timespans
|
||||
if timespan[0] isnt lastTimespans[i][0] or timespan[1] isnt lastTimespans[i][1]
|
||||
changed = true
|
||||
break
|
||||
return unless changed
|
||||
ael.toggleClass 'secret', not timespans.length
|
||||
@lastActionTimespans[action] = timespans
|
||||
timeline = ael.find('.action-timeline .timeline-wrapper').empty()
|
||||
lifespan = @thang.world.totalFrames / @thang.world.frameRate
|
||||
scale = timeline.width() / lifespan
|
||||
for [start, end] in timespans
|
||||
bar = $('<div></div>').css left: start * scale, right: (lifespan - end) * scale
|
||||
timeline.append bar
|
||||
|
||||
ael
|
||||
|
||||
destroy: ->
|
||||
@stage?.stopTalking()
|
||||
clearInterval(@messageInterval) if @messageInterval
|
||||
|
|
|
@ -31,7 +31,8 @@ module.exports = class LevelLoadingView extends CocoView
|
|||
eventualCanvasWidth = $('#canvas-wrapper').outerWidth()
|
||||
eventualCanvasHeight = Math.max(eventualCanvasWidth / canvasAspectRatio)
|
||||
currentCanvasHeight = 589
|
||||
@$el.addClass('manually-sized').css('height', @$el.outerHeight() + eventualCanvasHeight - currentCanvasHeight + 2)
|
||||
extraHeight # From control bar (I think)
|
||||
@$el.addClass('manually-sized').css('height', @$el.outerHeight() + eventualCanvasHeight - currentCanvasHeight + 2 + extraHeight)
|
||||
|
||||
onLevelLoaded: (e) ->
|
||||
@level = e.level
|
||||
|
|
|
@ -98,7 +98,9 @@ module.exports = class SpellListTabEntryView extends SpellListEntryView
|
|||
|
||||
onCodeReload: (e) ->
|
||||
return unless @controlsEnabled
|
||||
Backbone.Mediator.publish 'tome:reload-code', spell: @spell
|
||||
#Backbone.Mediator.publish 'tome:reload-code', spell: @spell # Old: just reload the current code
|
||||
Backbone.Mediator.publish 'level:restart', {} # New: prompt them to restart the level
|
||||
# TODO: actually prompt them to restart rather than just doing it
|
||||
|
||||
onBeautifyClick: (e) ->
|
||||
return unless @controlsEnabled
|
||||
|
@ -138,16 +140,6 @@ module.exports = class SpellListTabEntryView extends SpellListEntryView
|
|||
return if enabled is @controlsEnabled
|
||||
@controlsEnabled = enabled
|
||||
@$el.toggleClass 'read-only', not enabled
|
||||
@toggleBackground()
|
||||
|
||||
toggleBackground: =>
|
||||
# TODO: make the palette background an actual background and do the CSS trick
|
||||
# used in spell_list_entry.sass for disabling
|
||||
background = @$el.find('img.spell-tab-image-hidden')[0]
|
||||
if background.naturalWidth is 0 # not loaded yet
|
||||
return _.delay @toggleBackground, 100
|
||||
filters.revertImage background, '.spell-list-entry-view.spell-tab' if @controlsEnabled
|
||||
filters.darkenImage background, '.spell-list-entry-view.spell-tab', 0.8 unless @controlsEnabled
|
||||
|
||||
attachTransitionEventListener: =>
|
||||
transitionListener = ''
|
||||
|
|