From de85d8c17050f41b930f63ef9294ee744fd972b7 Mon Sep 17 00:00:00 2001 From: Nick Winter Date: Mon, 22 Sep 2014 14:05:13 -0700 Subject: [PATCH] Split cast button into Run and Submit. Slighted adjusted victory modal for new hero levels. Real-time playbck is now required to beat hero levels. Worked around an error in the delta expansion. --- app/lib/DefaultScripts.coffee | 18 --- app/lib/scripts/ScriptManager.coffee | 2 +- app/locale/en.coffee | 7 +- app/styles/play/level/tome/cast_button.sass | 107 +++++++----------- app/templates/play/level/modal/victory.jade | 5 +- .../play/level/tome/cast_button.jade | 8 +- app/views/editor/DeltaView.coffee | 35 +++--- app/views/play/WorldMapView.coffee | 2 +- app/views/play/level/ControlBarView.coffee | 4 +- app/views/play/level/LevelHUDView.coffee | 6 +- app/views/play/level/PlayLevelView.coffee | 18 ++- .../play/level/tome/CastButtonView.coffee | 26 ++--- 12 files changed, 103 insertions(+), 135 deletions(-) diff --git a/app/lib/DefaultScripts.coffee b/app/lib/DefaultScripts.coffee index 8fbd50439..8417ce978 100644 --- a/app/lib/DefaultScripts.coffee +++ b/app/lib/DefaultScripts.coffee @@ -4,22 +4,4 @@ module.exports = [ noteChain: [] 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" - } ] diff --git a/app/lib/scripts/ScriptManager.coffee b/app/lib/scripts/ScriptManager.coffee index 2fc1c000b..746ea2d92 100644 --- a/app/lib/scripts/ScriptManager.coffee +++ b/app/lib/scripts/ScriptManager.coffee @@ -256,7 +256,7 @@ module.exports = ScriptManager = class ScriptManager extends CocoClass @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 ? {} # ENDING NOTES diff --git a/app/locale/en.coffee b/app/locale/en.coffee index bf5299ac6..b761a0946 100644 --- a/app/locale/en.coffee +++ b/app/locale/en.coffee @@ -408,9 +408,10 @@ tome_minion_spells: "Your Minions' Spells" tome_read_only_spells: "Read-Only Spells" tome_other_units: "Other Units" - tome_cast_button_castable: "Cast Spell" - tome_cast_button_casting: "Casting" - tome_cast_button_cast: "Spell Cast" + tome_cast_button_run: "Run" + tome_cast_button_running: "Running" + tome_cast_button_ran: "Ran" + tome_submit_button: "Submit" tome_select_spell: "Select a Spell" tome_select_a_thang: "Select Someone for " tome_available_spells: "Available Spells" diff --git a/app/styles/play/level/tome/cast_button.sass b/app/styles/play/level/tome/cast_button.sass index 604b67d33..1b1e2114f 100644 --- a/app/styles/play/level/tome/cast_button.sass +++ b/app/styles/play/level/tome/cast_button.sass @@ -4,86 +4,55 @@ +keyframes(castablePulse) from @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 50% + @include box-shadow(0px 0px 35px skyblue) color: skyblue 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 #cast-button-view display: none 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) - 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 @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 - -webkit-animation-name: castablePulse - -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) + &.winnable .submit-button + font-weight: bold + -webkit-animation-name: winnablePulse + -webkit-animation-duration: 3s + -webkit-animation-iteration-count: infinite diff --git a/app/templates/play/level/modal/victory.jade b/app/templates/play/level/modal/victory.jade index a58b39a3b..08a753b81 100644 --- a/app/templates/play/level/modal/victory.jade +++ b/app/templates/play/level/modal/victory.jade @@ -16,9 +16,8 @@ block modal-footer-content 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 else if hasNextLevel - button.btn.btn-primary.next-level-button(data-dismiss="modal", data-i18n="play_level.victory_play_next_level") Play Next Level - else - a.btn.btn-primary(href="/", data-dismiss="modal", data-i18n="play_level.victory_go_home") Go Home + button.btn.btn-success.next-level-button(data-dismiss="modal", data-i18n="play_level.victory_play_next_level") Play Next Level + a.btn.btn-primary(href="/play-hero", data-dismiss="modal", data-i18n="play_level.victory_go_home") Go Home if me.get('anonymous') 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 diff --git a/app/templates/play/level/tome/cast_button.jade b/app/templates/play/level/tome/cast_button.jade index 3f3d6669f..bde30b02b 100644 --- a/app/templates/play/level/tome/cast_button.jade +++ b/app/templates/play/level/tome/cast_button.jade @@ -1,5 +1,3 @@ -div.btn-group.btn-group-lg.cast-button-group - .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-inverse.banner.cast-real-time-button(title=castRealTimeVerbose) - i.glyphicon.glyphicon-play \ No newline at end of file +button.btn.btn-lg.btn-inverse.banner.cast-button(title=castVerbose, data-i18n="play_level.tome_run_button_ran") Ran + +button.btn.btn-lg.btn-success.banner.submit-button(title=castRealTimeVerbose) Submit diff --git a/app/views/editor/DeltaView.coffee b/app/views/editor/DeltaView.coffee index 719d52329..76208c76f 100644 --- a/app/views/editor/DeltaView.coffee +++ b/app/views/editor/DeltaView.coffee @@ -7,19 +7,19 @@ TEXTDIFF_OPTIONS = newTextName: "New" contextSize: 5 viewType: 1 - + 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). - + * 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 of changes within model itself. - + ### - + @deltaCounter: 0 className: 'delta-view' template: template @@ -28,26 +28,26 @@ module.exports = class DeltaView extends CocoView super(options) @expandedDeltas = [] @skipPaths = options.skipPaths - + for modelName in ['model', 'headModel', 'comparisonModel'] @[modelName] = options[modelName] continue unless @[modelName] and options.loadModels if not @[modelName].isLoaded @[modelName] = @supermodel.loadModel(@[modelName], 'document').model - + @buildDeltas() if @supermodel.finished() - + onLoaded: -> @buildDeltas() super() - + buildDeltas: -> if @comparisonModel @expandedDeltas = @model.getExpandedDeltaWith(@comparisonModel) else @expandedDeltas = @model.getExpandedDelta() [@expandedDeltas, @skippedDeltas] = @filterDeltas(@expandedDeltas) - + if @headModel @headDeltas = @headModel.getExpandedDelta() @headDeltas = @filterDeltas(@headDeltas)[0] @@ -74,33 +74,34 @@ module.exports = class DeltaView extends CocoView c.counter = DeltaView.deltaCounter DeltaView.deltaCounter += @expandedDeltas.length c - + afterRender: -> deltas = @$el.find('.details') for delta, i in deltas deltaEl = $(delta) deltaData = @expandedDeltas[i] @expandDetails(deltaEl, deltaData) - + conflictDeltas = @$el.find('.conflict-details') conflicts = (delta.conflict for delta in @expandedDeltas when delta.conflict) for delta, i in conflictDeltas deltaEl = $(delta) deltaData = conflicts[i] @expandDetails(deltaEl, deltaData) - + expandDetails: (deltaEl, deltaData) -> treemaOptions = { schema: deltaData.schema or {}, readOnly: true } - + if _.isObject(deltaData.left) and leftEl = deltaEl.find('.old-value') options = _.defaults {data: deltaData.left}, treemaOptions TreemaNode.make(leftEl, options).build() - + if _.isObject(deltaData.right) and rightEl = deltaEl.find('.new-value') options = _.defaults {data: deltaData.right}, treemaOptions TreemaNode.make(rightEl, options).build() - + 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 right = difflib.stringAsLines deltaData.right sm = new difflib.SequenceMatcher(left, right) diff --git a/app/views/play/WorldMapView.coffee b/app/views/play/WorldMapView.coffee index cc5dff687..13bed4dcd 100644 --- a/app/views/play/WorldMapView.coffee +++ b/app/views/play/WorldMapView.coffee @@ -587,7 +587,7 @@ hero = [ difficulty: 1 id: 'kithgard-gates' 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 y: 70.55 } diff --git a/app/views/play/level/ControlBarView.coffee b/app/views/play/level/ControlBarView.coffee index 30a8bbaa3..03aa14540 100644 --- a/app/views/play/level/ControlBarView.coffee +++ b/app/views/play/level/ControlBarView.coffee @@ -53,8 +53,10 @@ module.exports = class ControlBarView extends CocoView c.multiplayerEnabled = @session.get('multiplayer') c.ladderGame = @level.get('type') is 'ladder' 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$/, '' + else if @level.get('type', true) is 'hero' + c.homeLink = '/play-hero' else c.homeLink = '/' c.editorLink = "/editor/level/#{@level.get('slug')}" diff --git a/app/views/play/level/LevelHUDView.coffee b/app/views/play/level/LevelHUDView.coffee index 97cd9cad0..aac9d209a 100644 --- a/app/views/play/level/LevelHUDView.coffee +++ b/app/views/play/level/LevelHUDView.coffee @@ -146,7 +146,11 @@ module.exports = class LevelHUDView extends CocoView createProperties: -> props = @$el.find('.thang-props') 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' nColumns = Math.ceil propNames.length / 5 columns = ($('
').appendTo(props) for i in [0 ... nColumns]) diff --git a/app/views/play/level/PlayLevelView.coffee b/app/views/play/level/PlayLevelView.coffee index 73d6036f5..f1e8fbc5a 100644 --- a/app/views/play/level/PlayLevelView.coffee +++ b/app/views/play/level/PlayLevelView.coffee @@ -430,7 +430,7 @@ module.exports = class PlayLevelView extends RootView onDonePressed: -> @showVictory() onShowVictory: (e) -> - $('#level-done-button').show() + $('#level-done-button').show() unless @level.get('type', true) is 'hero' @showVictory() if e.showModal setTimeout(@preloadNextLevel, 3000) return if @victorySeen @@ -584,8 +584,12 @@ module.exports = class PlayLevelView extends RootView @world.scripts = scripts thangTypes = @supermodel.getModels(ThangType) startFrame = @lastWorldFramesLoaded ? 0 - if @world.frames.length is @world.totalFrames # Finished loading + finishedLoading = @world.frames.length is @world.totalFrames + if finishedLoading @lastWorldFramesLoaded = 0 + if @waitingForSubmissionComplete + @onSubmissionComplete() + @waitingForSubmissionComplete = false else @lastWorldFramesLoaded = @world.frames.length for [spriteName, message] in @world.thangDialogueSounds startFrame @@ -605,15 +609,23 @@ module.exports = class PlayLevelView extends RootView onRealTimePlaybackEnded: (e) -> @$el.removeClass 'real-time' @onWindowResize() + if @world.frames.length is @world.totalFrames + _.delay @onSubmissionComplete, 750 # Wait for transition to end. + else + @waitingForSubmissionComplete = true @onRealTimeMultiplayerPlaybackEnded() + onSubmissionComplete: => + return if @destroyed + @showVictory() if @goalManager.checkOverallStatus() is 'success' + destroy: -> @levelLoader?.destroy() @surface?.destroy() @god?.destroy() @goalManager?.destroy() @scriptManager?.destroy() - @ambientSound.stop() + @ambientSound?.stop() $(window).off 'resize', @onWindowResize delete window.world # not sure where this is set, but this is one way to clean it up clearInterval(@pointerInterval) diff --git a/app/views/play/level/tome/CastButtonView.coffee b/app/views/play/level/tome/CastButtonView.coffee index e3763e5e7..fa026abc9 100644 --- a/app/views/play/level/tome/CastButtonView.coffee +++ b/app/views/play/level/tome/CastButtonView.coffee @@ -8,15 +8,15 @@ module.exports = class CastButtonView extends CocoView events: 'click .cast-button': 'onCastButtonClick' - 'click .cast-real-time-button': 'onCastRealTimeButtonClick' + 'click .submit-button': 'onCastRealTimeButtonClick' subscriptions: 'tome:spell-changed': 'onSpellChanged' 'tome:cast-spells': 'onCastSpells' - 'god:world-load-progress-changed': 'onWorldLoadProgressChanged' 'god:new-world-created': 'onNewWorld' 'real-time-multiplayer:joined-game': 'onJoinedRealTimeMultiplayerGame' 'real-time-multiplayer:left-game': 'onLeftRealTimeMultiplayerGame' + 'goal-manager:new-goal-states': 'onNewGoalStates' constructor: (options) -> super options @@ -37,11 +37,11 @@ module.exports = class CastButtonView extends CocoView afterRender: -> super() @castButton = $('.cast-button', @$el) - @castButtonGroup = $('.cast-button-group', @$el) @castOptions = $('.autocast-delays', @$el) #delay = me.get('autocastDelay') # No more autocast delay = 90019001 @setAutocastDelay delay + @$el.find('.submit-button').hide() if @options.levelID is 'dungeons-of-kithgard' attachTo: (spellView) -> @$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 @hasStartedCastingOnce = true @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) -> @casting = false @@ -85,6 +79,12 @@ module.exports = class CastButtonView extends CocoView @hasCastOnce = true @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: -> return if _.some @spells, (spell) => not spell.loaded @@ -92,13 +92,13 @@ module.exports = class CastButtonView extends CocoView spell.hasChangedSignificantly spell.getSource(), null, callback , (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 - s = $.i18n.t('play_level.tome_cast_button_casting', defaultValue: 'Casting') + s = $.i18n.t('play_level.tome_cast_button_running') 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 - 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.prop 'disabled', not castable