Merge branch 'master' into production

This commit is contained in:
Nick Winter 2014-09-22 14:05:23 -07:00
commit bf0013ea0a
12 changed files with 103 additions and 135 deletions

View file

@ -4,22 +4,4 @@ module.exports = [
noteChain: [] noteChain: []
id: "Introduction" id: "Introduction"
} }
{
channel: "world:won"
noteChain: []
id: "Victory Playback"
scriptPrereqs: ["Introduction"]
}
{
channel: "level:set-playing"
noteChain: []
scriptPrereqs: ["Victory Playback"]
id: "Victory Playback Started"
}
{
channel: "surface:frame-changed"
noteChain: []
scriptPrereqs: ["Victory Playback Started"]
id: "Show Victory"
}
] ]

View file

@ -256,7 +256,7 @@ module.exports = ScriptManager = class ScriptManager extends CocoClass
@publishNote(note) @publishNote(note)
publishNote: (note) -> publishNote: (note) ->
Backbone.Mediator.publish 'playback:real-time-playback-ended', {} Backbone.Mediator.publish 'playback:real-time-playback-ended', {} unless @session.get('heroConfig') # Only old levels need this, to stop interfering with old victory coolcams.
Backbone.Mediator.publish note.channel, note.event ? {} Backbone.Mediator.publish note.channel, note.event ? {}
# ENDING NOTES # ENDING NOTES

View file

@ -408,9 +408,10 @@
tome_minion_spells: "Your Minions' Spells" tome_minion_spells: "Your Minions' Spells"
tome_read_only_spells: "Read-Only Spells" tome_read_only_spells: "Read-Only Spells"
tome_other_units: "Other Units" tome_other_units: "Other Units"
tome_cast_button_castable: "Cast Spell" tome_cast_button_run: "Run"
tome_cast_button_casting: "Casting" tome_cast_button_running: "Running"
tome_cast_button_cast: "Spell Cast" tome_cast_button_ran: "Ran"
tome_submit_button: "Submit"
tome_select_spell: "Select a Spell" tome_select_spell: "Select a Spell"
tome_select_a_thang: "Select Someone for " tome_select_a_thang: "Select Someone for "
tome_available_spells: "Available Spells" tome_available_spells: "Available Spells"

View file

@ -4,86 +4,55 @@
+keyframes(castablePulse) +keyframes(castablePulse)
from from
@include box-shadow(0px 0px 8px #333) @include box-shadow(0px 0px 8px #333)
50%
@include box-shadow(0px 0px 35px skyblue)
to
@include box-shadow(0px 0px 8px #333)
+keyframes(castablePulseButton)
from
color: white color: white
50% 50%
@include box-shadow(0px 0px 35px skyblue)
color: skyblue color: skyblue
to to
@include box-shadow(0px 0px 8px #333)
color: white
+keyframes(winnablePulse)
from
@include box-shadow(0px 0px 8px #333)
color: white
50%
@include box-shadow(0px 0px 35px #87FFCE)
color: #87FFFF
to
@include box-shadow(0px 0px 8px #333)
color: white color: white
#cast-button-view #cast-button-view
display: none display: none
position: absolute position: absolute
width: 35%
.cast-button-group z-index: 2
z-index: 2 width: 100%
border-radius: 6px
.btn
padding: 3px 10px
height: 40px
.submit-button
margin-left: 20px
.cast-button
margin-left: 10px
@include opacity(0.77) @include opacity(0.77)
width: 100%
border-radius: 6px
.button-progress-overlay
position: absolute
left: -1px
top: -1px
bottom: -1px
border-radius: 6px
z-index: 10
pointer-events: none
// This transition time should roughly match the world progress update interval
@include transition(width .2s linear)
@include transition(box-shadow .2s linear)
&.casting .button-progress-overlay
background-color: green
@include opacity(0.5)
@include box-shadow(0px 0px 15px green)
&:hover, &.castable &:hover, &.castable
@include opacity(1) @include opacity(1)
&:not(.winnable) .cast-button.castable
font-weight: bold
-webkit-animation-name: castablePulse
-webkit-animation-duration: 3s
-webkit-animation-iteration-count: infinite
&.castable &.winnable .submit-button
-webkit-animation-name: castablePulse font-weight: bold
-webkit-animation-duration: 3s -webkit-animation-name: winnablePulse
-webkit-animation-iteration-count: infinite -webkit-animation-duration: 3s
-webkit-animation-iteration-count: infinite
.cast-button
font-weight: bold
-webkit-animation-name: castablePulseButton
-webkit-animation-duration: 3s
-webkit-animation-iteration-count: infinite
&:not(.castable):not(:hover)
.cast-options-button
// Mimic the disabled visuals
@include opacity(0.65)
background-image: none
@include box-shadow(none)
.btn
padding: 3px 10px
height: 40px
.cast-button
width: 80%
width: -webkit-calc(100% - 40px)
width: calc(100% - 40px)
border-top-left-radius: 6px
border-bottom-left-radius: 6px
@media screen and (max-width: 1440px)
font-size: 16px
@media screen and (max-width: 1280px)
font-size: 14px
@media screen and (max-width: 1024px)
font-size: 12px
.cast-real-time-button
width: 20%
width: -webkit-calc(40px)
width: calc(40px)

View file

@ -16,9 +16,8 @@ block modal-footer-content
else if level.get('type') === 'ladder' else if level.get('type') === 'ladder'
a.btn.btn-primary(href="/play/ladder/#{level.get('slug')}#my-matches", data-dismiss="modal", data-i18n="play_level.victory_go_ladder") Return to Ladder a.btn.btn-primary(href="/play/ladder/#{level.get('slug')}#my-matches", data-dismiss="modal", data-i18n="play_level.victory_go_ladder") Return to Ladder
else if hasNextLevel else if hasNextLevel
button.btn.btn-primary.next-level-button(data-dismiss="modal", data-i18n="play_level.victory_play_next_level") Play Next Level button.btn.btn-success.next-level-button(data-dismiss="modal", data-i18n="play_level.victory_play_next_level") Play Next Level
else a.btn.btn-primary(href="/play-hero", data-dismiss="modal", data-i18n="play_level.victory_go_home") Go Home
a.btn.btn-primary(href="/", data-dismiss="modal", data-i18n="play_level.victory_go_home") Go Home
if me.get('anonymous') if me.get('anonymous')
p.sign-up-poke p.sign-up-poke
button.btn.btn-success.sign-up-button.btn-large(data-toggle="coco-modal", data-target="modal/SignupModal", data-i18n="play_level.victory_sign_up") Sign Up to Save Progress button.btn.btn-success.sign-up-button.btn-large(data-toggle="coco-modal", data-target="modal/SignupModal", data-i18n="play_level.victory_sign_up") Sign Up to Save Progress

View file

@ -1,5 +1,3 @@
div.btn-group.btn-group-lg.cast-button-group button.btn.btn-lg.btn-inverse.banner.cast-button(title=castVerbose, data-i18n="play_level.tome_run_button_ran") Ran
.button-progress-overlay
button.btn.btn-inverse.banner.cast-button(title=castVerbose, data-i18n="play_level.tome_cast_button_cast") Spell Cast button.btn.btn-lg.btn-success.banner.submit-button(title=castRealTimeVerbose) Submit
button.btn.btn-inverse.banner.cast-real-time-button(title=castRealTimeVerbose)
i.glyphicon.glyphicon-play

View file

@ -7,19 +7,19 @@ TEXTDIFF_OPTIONS =
newTextName: "New" newTextName: "New"
contextSize: 5 contextSize: 5
viewType: 1 viewType: 1
module.exports = class DeltaView extends CocoView module.exports = class DeltaView extends CocoView
### ###
Takes a CocoModel instance (model) and displays changes since the Takes a CocoModel instance (model) and displays changes since the
last save (attributes vs _revertAttributes). last save (attributes vs _revertAttributes).
* If headModel is included, will look for and display conflicts with the changes in model. * If headModel is included, will look for and display conflicts with the changes in model.
* If comparisonModel is included, will show deltas between model and comparisonModel instead * If comparisonModel is included, will show deltas between model and comparisonModel instead
of changes within model itself. of changes within model itself.
### ###
@deltaCounter: 0 @deltaCounter: 0
className: 'delta-view' className: 'delta-view'
template: template template: template
@ -28,26 +28,26 @@ module.exports = class DeltaView extends CocoView
super(options) super(options)
@expandedDeltas = [] @expandedDeltas = []
@skipPaths = options.skipPaths @skipPaths = options.skipPaths
for modelName in ['model', 'headModel', 'comparisonModel'] for modelName in ['model', 'headModel', 'comparisonModel']
@[modelName] = options[modelName] @[modelName] = options[modelName]
continue unless @[modelName] and options.loadModels continue unless @[modelName] and options.loadModels
if not @[modelName].isLoaded if not @[modelName].isLoaded
@[modelName] = @supermodel.loadModel(@[modelName], 'document').model @[modelName] = @supermodel.loadModel(@[modelName], 'document').model
@buildDeltas() if @supermodel.finished() @buildDeltas() if @supermodel.finished()
onLoaded: -> onLoaded: ->
@buildDeltas() @buildDeltas()
super() super()
buildDeltas: -> buildDeltas: ->
if @comparisonModel if @comparisonModel
@expandedDeltas = @model.getExpandedDeltaWith(@comparisonModel) @expandedDeltas = @model.getExpandedDeltaWith(@comparisonModel)
else else
@expandedDeltas = @model.getExpandedDelta() @expandedDeltas = @model.getExpandedDelta()
[@expandedDeltas, @skippedDeltas] = @filterDeltas(@expandedDeltas) [@expandedDeltas, @skippedDeltas] = @filterDeltas(@expandedDeltas)
if @headModel if @headModel
@headDeltas = @headModel.getExpandedDelta() @headDeltas = @headModel.getExpandedDelta()
@headDeltas = @filterDeltas(@headDeltas)[0] @headDeltas = @filterDeltas(@headDeltas)[0]
@ -74,33 +74,34 @@ module.exports = class DeltaView extends CocoView
c.counter = DeltaView.deltaCounter c.counter = DeltaView.deltaCounter
DeltaView.deltaCounter += @expandedDeltas.length DeltaView.deltaCounter += @expandedDeltas.length
c c
afterRender: -> afterRender: ->
deltas = @$el.find('.details') deltas = @$el.find('.details')
for delta, i in deltas for delta, i in deltas
deltaEl = $(delta) deltaEl = $(delta)
deltaData = @expandedDeltas[i] deltaData = @expandedDeltas[i]
@expandDetails(deltaEl, deltaData) @expandDetails(deltaEl, deltaData)
conflictDeltas = @$el.find('.conflict-details') conflictDeltas = @$el.find('.conflict-details')
conflicts = (delta.conflict for delta in @expandedDeltas when delta.conflict) conflicts = (delta.conflict for delta in @expandedDeltas when delta.conflict)
for delta, i in conflictDeltas for delta, i in conflictDeltas
deltaEl = $(delta) deltaEl = $(delta)
deltaData = conflicts[i] deltaData = conflicts[i]
@expandDetails(deltaEl, deltaData) @expandDetails(deltaEl, deltaData)
expandDetails: (deltaEl, deltaData) -> expandDetails: (deltaEl, deltaData) ->
treemaOptions = { schema: deltaData.schema or {}, readOnly: true } treemaOptions = { schema: deltaData.schema or {}, readOnly: true }
if _.isObject(deltaData.left) and leftEl = deltaEl.find('.old-value') if _.isObject(deltaData.left) and leftEl = deltaEl.find('.old-value')
options = _.defaults {data: deltaData.left}, treemaOptions options = _.defaults {data: deltaData.left}, treemaOptions
TreemaNode.make(leftEl, options).build() TreemaNode.make(leftEl, options).build()
if _.isObject(deltaData.right) and rightEl = deltaEl.find('.new-value') if _.isObject(deltaData.right) and rightEl = deltaEl.find('.new-value')
options = _.defaults {data: deltaData.right}, treemaOptions options = _.defaults {data: deltaData.right}, treemaOptions
TreemaNode.make(rightEl, options).build() TreemaNode.make(rightEl, options).build()
if deltaData.action is 'text-diff' if deltaData.action is 'text-diff'
return console.error "Couldn't show diff for left: #{deltaData.left}, right: #{deltaData.right} of delta:", deltaData unless deltaData.left? and deltaData.right?
left = difflib.stringAsLines deltaData.left left = difflib.stringAsLines deltaData.left
right = difflib.stringAsLines deltaData.right right = difflib.stringAsLines deltaData.right
sm = new difflib.SequenceMatcher(left, right) sm = new difflib.SequenceMatcher(left, right)

View file

@ -587,7 +587,7 @@ hero = [
difficulty: 1 difficulty: 1
id: 'kithgard-gates' id: 'kithgard-gates'
image: '/file/db/level/526fd3043c637ece50001bb2/the_herd_icon.png' image: '/file/db/level/526fd3043c637ece50001bb2/the_herd_icon.png'
description: 'Board up Kithguard and escape into the forest.' description: 'Escape the Kithgard dungeons and don\'t let the guardians get you.'
x: 47.38 x: 47.38
y: 70.55 y: 70.55
} }

View file

@ -53,8 +53,10 @@ module.exports = class ControlBarView extends CocoView
c.multiplayerEnabled = @session.get('multiplayer') c.multiplayerEnabled = @session.get('multiplayer')
c.ladderGame = @level.get('type') is 'ladder' c.ladderGame = @level.get('type') is 'ladder'
c.spectateGame = @spectateGame c.spectateGame = @spectateGame
if @level.get('type') in ['ladder', 'ladder-tutorial'] if @level.get('type', true) in ['ladder', 'ladder-tutorial']
c.homeLink = '/play/ladder/' + @level.get('slug').replace /\-tutorial$/, '' c.homeLink = '/play/ladder/' + @level.get('slug').replace /\-tutorial$/, ''
else if @level.get('type', true) is 'hero'
c.homeLink = '/play-hero'
else else
c.homeLink = '/' c.homeLink = '/'
c.editorLink = "/editor/level/#{@level.get('slug')}" c.editorLink = "/editor/level/#{@level.get('slug')}"

View file

@ -146,7 +146,11 @@ module.exports = class LevelHUDView extends CocoView
createProperties: -> createProperties: ->
props = @$el.find('.thang-props') props = @$el.find('.thang-props')
props.find(':not(.thang-name)').remove() props.find(':not(.thang-name)').remove()
props.find('.thang-name').text(if @thang.type then "#{@thang.id} - #{@thang.type}" else @thang.id) 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' propNames = _.without @thang.hudProperties ? [], 'action'
nColumns = Math.ceil propNames.length / 5 nColumns = Math.ceil propNames.length / 5
columns = ($('<div class="thang-props-column"></div>').appendTo(props) for i in [0 ... nColumns]) columns = ($('<div class="thang-props-column"></div>').appendTo(props) for i in [0 ... nColumns])

View file

@ -430,7 +430,7 @@ module.exports = class PlayLevelView extends RootView
onDonePressed: -> @showVictory() onDonePressed: -> @showVictory()
onShowVictory: (e) -> onShowVictory: (e) ->
$('#level-done-button').show() $('#level-done-button').show() unless @level.get('type', true) is 'hero'
@showVictory() if e.showModal @showVictory() if e.showModal
setTimeout(@preloadNextLevel, 3000) setTimeout(@preloadNextLevel, 3000)
return if @victorySeen return if @victorySeen
@ -584,8 +584,12 @@ module.exports = class PlayLevelView extends RootView
@world.scripts = scripts @world.scripts = scripts
thangTypes = @supermodel.getModels(ThangType) thangTypes = @supermodel.getModels(ThangType)
startFrame = @lastWorldFramesLoaded ? 0 startFrame = @lastWorldFramesLoaded ? 0
if @world.frames.length is @world.totalFrames # Finished loading finishedLoading = @world.frames.length is @world.totalFrames
if finishedLoading
@lastWorldFramesLoaded = 0 @lastWorldFramesLoaded = 0
if @waitingForSubmissionComplete
@onSubmissionComplete()
@waitingForSubmissionComplete = false
else else
@lastWorldFramesLoaded = @world.frames.length @lastWorldFramesLoaded = @world.frames.length
for [spriteName, message] in @world.thangDialogueSounds startFrame for [spriteName, message] in @world.thangDialogueSounds startFrame
@ -605,15 +609,23 @@ module.exports = class PlayLevelView extends RootView
onRealTimePlaybackEnded: (e) -> onRealTimePlaybackEnded: (e) ->
@$el.removeClass 'real-time' @$el.removeClass 'real-time'
@onWindowResize() @onWindowResize()
if @world.frames.length is @world.totalFrames
_.delay @onSubmissionComplete, 750 # Wait for transition to end.
else
@waitingForSubmissionComplete = true
@onRealTimeMultiplayerPlaybackEnded() @onRealTimeMultiplayerPlaybackEnded()
onSubmissionComplete: =>
return if @destroyed
@showVictory() if @goalManager.checkOverallStatus() is 'success'
destroy: -> destroy: ->
@levelLoader?.destroy() @levelLoader?.destroy()
@surface?.destroy() @surface?.destroy()
@god?.destroy() @god?.destroy()
@goalManager?.destroy() @goalManager?.destroy()
@scriptManager?.destroy() @scriptManager?.destroy()
@ambientSound.stop() @ambientSound?.stop()
$(window).off 'resize', @onWindowResize $(window).off 'resize', @onWindowResize
delete window.world # not sure where this is set, but this is one way to clean it up delete window.world # not sure where this is set, but this is one way to clean it up
clearInterval(@pointerInterval) clearInterval(@pointerInterval)

View file

@ -8,15 +8,15 @@ module.exports = class CastButtonView extends CocoView
events: events:
'click .cast-button': 'onCastButtonClick' 'click .cast-button': 'onCastButtonClick'
'click .cast-real-time-button': 'onCastRealTimeButtonClick' 'click .submit-button': 'onCastRealTimeButtonClick'
subscriptions: subscriptions:
'tome:spell-changed': 'onSpellChanged' 'tome:spell-changed': 'onSpellChanged'
'tome:cast-spells': 'onCastSpells' 'tome:cast-spells': 'onCastSpells'
'god:world-load-progress-changed': 'onWorldLoadProgressChanged'
'god:new-world-created': 'onNewWorld' 'god:new-world-created': 'onNewWorld'
'real-time-multiplayer:joined-game': 'onJoinedRealTimeMultiplayerGame' 'real-time-multiplayer:joined-game': 'onJoinedRealTimeMultiplayerGame'
'real-time-multiplayer:left-game': 'onLeftRealTimeMultiplayerGame' 'real-time-multiplayer:left-game': 'onLeftRealTimeMultiplayerGame'
'goal-manager:new-goal-states': 'onNewGoalStates'
constructor: (options) -> constructor: (options) ->
super options super options
@ -37,11 +37,11 @@ module.exports = class CastButtonView extends CocoView
afterRender: -> afterRender: ->
super() super()
@castButton = $('.cast-button', @$el) @castButton = $('.cast-button', @$el)
@castButtonGroup = $('.cast-button-group', @$el)
@castOptions = $('.autocast-delays', @$el) @castOptions = $('.autocast-delays', @$el)
#delay = me.get('autocastDelay') # No more autocast #delay = me.get('autocastDelay') # No more autocast
delay = 90019001 delay = 90019001
@setAutocastDelay delay @setAutocastDelay delay
@$el.find('.submit-button').hide() if @options.levelID is 'dungeons-of-kithgard'
attachTo: (spellView) -> attachTo: (spellView) ->
@$el.detach().prependTo(spellView.toolbarView.$el).show() @$el.detach().prependTo(spellView.toolbarView.$el).show()
@ -71,12 +71,6 @@ module.exports = class CastButtonView extends CocoView
Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'cast', volume: 0.5 Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'cast', volume: 0.5
@hasStartedCastingOnce = true @hasStartedCastingOnce = true
@updateCastButton() @updateCastButton()
@onWorldLoadProgressChanged progress: 0
onWorldLoadProgressChanged: (e) ->
return # trying out showing progress on the canvas instead
overlay = @castButtonGroup.find '.button-progress-overlay'
overlay.css 'width', e.progress * @castButton.outerWidth() + 1
onNewWorld: (e) -> onNewWorld: (e) ->
@casting = false @casting = false
@ -85,6 +79,12 @@ module.exports = class CastButtonView extends CocoView
@hasCastOnce = true @hasCastOnce = true
@updateCastButton() @updateCastButton()
onNewGoalStates: (e) ->
@winnable = e.overallStatus is 'success'
@$el.toggleClass 'winnable', @winnable
if @winnable
@$el.find('.submit-button').show() # In case we hid it, like on the first level.
updateCastButton: -> updateCastButton: ->
return if _.some @spells, (spell) => not spell.loaded return if _.some @spells, (spell) => not spell.loaded
@ -92,13 +92,13 @@ module.exports = class CastButtonView extends CocoView
spell.hasChangedSignificantly spell.getSource(), null, callback spell.hasChangedSignificantly spell.getSource(), null, callback
, (castable) => , (castable) =>
Backbone.Mediator.publish 'tome:spell-has-changed-significantly-calculation', hasChangedSignificantly: castable Backbone.Mediator.publish 'tome:spell-has-changed-significantly-calculation', hasChangedSignificantly: castable
@castButtonGroup.toggleClass('castable', castable).toggleClass('casting', @casting) @castButton.toggleClass('castable', castable).toggleClass('casting', @casting)
if @casting if @casting
s = $.i18n.t('play_level.tome_cast_button_casting', defaultValue: 'Casting') s = $.i18n.t('play_level.tome_cast_button_running')
else if castable else if castable
s = $.i18n.t('play_level.tome_cast_button_castable', defaultValue: 'Cast Spell') + ' ' + @castShortcut s = $.i18n.t('play_level.tome_cast_button_run') + ' ' + @castShortcut
else else
s = $.i18n.t('play_level.tome_cast_button_cast', defaultValue: 'Spell Cast') s = $.i18n.t('play_level.tome_cast_button_ran')
@castButton.text s @castButton.text s
@castButton.prop 'disabled', not castable @castButton.prop 'disabled', not castable