From c6b398aae23d66e3ddcf2fbf3e12022b596c4c86 Mon Sep 17 00:00:00 2001 From: Nick Winter Date: Wed, 8 Oct 2014 09:46:56 -0700 Subject: [PATCH 1/5] Increased prominence of GoalsView when at level playback end. Increased legibility of Surface text when PlaybackOverScreen is in effect, and changed colors of PlaybackOverScreen to be less intense and to indicate goal failure / non-failure. --- app/lib/God.coffee | 2 -- app/lib/surface/PlaybackOverScreen.coffee | 25 ++++++++++++++--- app/lib/surface/Surface.coffee | 1 + app/styles/play/level/goals.sass | 13 ++++++--- app/views/play/level/LevelGoalsView.coffee | 31 ++++++++++++++++------ 5 files changed, 54 insertions(+), 18 deletions(-) diff --git a/app/lib/God.coffee b/app/lib/God.coffee index 8feafbfa1..cf1631689 100644 --- a/app/lib/God.coffee +++ b/app/lib/God.coffee @@ -131,9 +131,7 @@ module.exports = class God extends CocoClass Backbone.Mediator.publish 'god:debug-world-load-progress-changed', progress: event.data.progress onNewWorldCreated: (e) -> - console.log 'filtering', _.cloneDeep e.world.userCodeMap @currentUserCodeMap = @filterUserCodeMapWhenFromWorld e.world.userCodeMap - console.log ' ... filtered into', _.cloneDeep e.world.userCodeMap filterUserCodeMapWhenFromWorld: (worldUserCodeMap) -> newUserCodeMap = {} diff --git a/app/lib/surface/PlaybackOverScreen.coffee b/app/lib/surface/PlaybackOverScreen.coffee index a50dadc78..a129bd45f 100644 --- a/app/lib/surface/PlaybackOverScreen.coffee +++ b/app/lib/surface/PlaybackOverScreen.coffee @@ -1,6 +1,9 @@ CocoClass = require 'lib/CocoClass' module.exports = class PlaybackOverScreen extends CocoClass + subscriptions: + 'goal-manager:new-goal-states': 'onNewGoalStates' + constructor: (options) -> super() options ?= {} @@ -16,15 +19,13 @@ module.exports = class PlaybackOverScreen extends CocoClass @dimLayer = new createjs.Container() @dimLayer.mouseEnabled = @dimLayer.mouseChildren = false @dimLayer.addChild @dimScreen = new createjs.Shape() - @dimScreen.graphics.beginFill('rgba(0,0,0,0.4)').rect 0, 0, @camera.canvasWidth, @camera.canvasHeight - @dimLayer.cache 0, 0, @camera.canvasWidth, @camera.canvasHeight @dimLayer.alpha = 0 @layer.addChild @dimLayer show: -> return if @showing @showing = true - + @updateColor 'rgba(212, 212, 212, 0.4' unless @color # If we haven't caught the goal state for the first run, just do something neutral. @dimLayer.alpha = 0 createjs.Tween.removeTweens @dimLayer createjs.Tween.get(@dimLayer).to({alpha: 1}, 500) @@ -32,6 +33,22 @@ module.exports = class PlaybackOverScreen extends CocoClass hide: -> return unless @showing @showing = false - createjs.Tween.removeTweens @dimLayer createjs.Tween.get(@dimLayer).to({alpha: 0}, 500) + + onNewGoalStates: (e) -> + success = e.overallStatus is 'success' + failure = e.overallStatus is 'failure' + timedOut = e.timedOut + incomplete = not success and not failure and not timedOut + color = if failure then 'rgba(255, 128, 128, 0.4)' else 'rgba(255, 255, 255, 0.4)' + @updateColor color + + updateColor: (color) -> + return if color is @color + @dimScreen.graphics.clear().beginFill(color).rect 0, 0, @camera.canvasWidth, @camera.canvasHeight + if @color + @dimLayer.updateCache() + else + @dimLayer.cache 0, 0, @camera.canvasWidth, @camera.canvasHeight # I wonder if caching is even worth it for just a rect fill. + @color = color diff --git a/app/lib/surface/Surface.coffee b/app/lib/surface/Surface.coffee index 980085db8..b96203fd4 100644 --- a/app/lib/surface/Surface.coffee +++ b/app/lib/surface/Surface.coffee @@ -115,6 +115,7 @@ module.exports = Surface = class Surface extends CocoClass @lankBoss = new LankBoss camera: @camera, webGLStage: @webGLStage, surfaceTextLayer: @surfaceTextLayer, world: @world, thangTypes: @options.thangTypes, choosing: @options.choosing, navigateToSelection: @options.navigateToSelection, showInvisible: @options.showInvisible @countdownScreen = new CountdownScreen camera: @camera, layer: @screenLayer @playbackOverScreen = new PlaybackOverScreen camera: @camera, layer: @screenLayer + @normalStage.addChildAt @playbackOverScreen.dimLayer, 0 # Put this below the other layers, actually, so we can more easily read text on the screen. @waitingScreen = new WaitingScreen camera: @camera, layer: @screenLayer @initCoordinates() @webGLStage.enableMouseOver(10) diff --git a/app/styles/play/level/goals.sass b/app/styles/play/level/goals.sass index 39b5b53a8..9ce1381b2 100644 --- a/app/styles/play/level/goals.sass +++ b/app/styles/play/level/goals.sass @@ -5,19 +5,24 @@ position: absolute left: 10px top: -100px - @include transition(top 0.5s ease-in-out) + @include transition(0.5s ease-in-out) background-color: rgba(200,200,200,1.0) border: black padding: 15px 7px 2px 5px box-sizing: border-box - border: 1px solid #333 + border: 1px solid #333 border-radius: 5px - + z-index: 3 + font-size: 14px + + &.brighter + font-size: 28px + .goals-status - font-size: 14px margin: 0 color: black + .success color: darkgreen .timed-out diff --git a/app/views/play/level/LevelGoalsView.coffee b/app/views/play/level/LevelGoalsView.coffee index 27849d659..04000c569 100644 --- a/app/views/play/level/LevelGoalsView.coffee +++ b/app/views/play/level/LevelGoalsView.coffee @@ -4,9 +4,9 @@ template = require 'templates/play/level/goals' utils = require 'lib/utils' stateIconMap = - incomplete: 'icon-minus' - success: 'icon-ok' - failure: 'icon-remove' + incomplete: 'glyphicon-minus' + success: 'glyphicon-ok' + failure: 'glyphicon-remove' module.exports = class LevelGoalsView extends CocoView id: 'goals-view' @@ -61,32 +61,47 @@ module.exports = class LevelGoalsView extends CocoView # This should really get refactored, along with GoalManager, so that goals have a standard # representation of how many are done, how many are needed, what that means, etc. li = $('
  • ').addClass("status-#{state.status}").text(text) - li.prepend($('').addClass(stateIconMap[state.status])) + li.prepend($('').addClass('glyphicon').addClass(stateIconMap[state.status])) list.append(li) goals.push goal if not firstRun and state.status is 'success' and @previousGoalStatus[goal.id] isnt 'success' - Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'goal-success', volume: 1 + @soundToPlayWhenPlaybackEnded = 'goal-success' else if not firstRun and state.status isnt 'success' and @previousGoalStatus[goal.id] is 'success' - Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'goal-incomplete-again', volume: 1 + @soundToPlayWhenPlaybackEnded = 'goal-incomplete-again' + else + @soundToPlayWhenPlaybackEnded = null @previousGoalStatus[goal.id] = state.status - @$el.removeClass('secret') if goals.length > 0 + if goals.length > 0 and @$el.hasClass 'secret' + @$el.removeClass('secret') + @lastSizeTweenTime = new Date() @updatePlacement() onSurfacePlaybackRestarted: -> @playbackEnded = false @$el.removeClass 'brighter' + @lastSizeTweenTime = new Date() @updatePlacement() onSurfacePlaybackEnded: -> @playbackEnded = true + @updateHeight() @$el.addClass 'brighter' + @lastSizeTweenTime = new Date() @updatePlacement() + if @soundToPlayWhenPlaybackEnded + Backbone.Mediator.publish 'audio-player:play-sound', trigger: @soundToPlayWhenPlaybackEnded, volume: 1 + + updateHeight: -> + return if @$el.hasClass('brighter') or @$el.hasClass('secret') + return if (new Date() - @lastSizeTweenTime) < 500 # Don't measure this while still animating, might get the wrong value. Should match sass transition time. + @normalHeight = @$el.outerHeight() updatePlacement: -> expand = @playbackEnded or @mouseEntered return if expand is @expanded + @updateHeight() sound = if expand then 'goals-expand' else 'goals-collapse' - top = if expand then -10 else 26 - @$el.outerHeight() + top = if expand then -10 else 26 - (@normalHeight ? @$el.outerHeight()) @$el.css 'top', top if @soundTimeout # Don't play the sound we were going to play after all; the transition has reversed. From 589c3b090c245bb938f62e5aaca52f4f2900e138 Mon Sep 17 00:00:00 2001 From: Nick Winter Date: Wed, 8 Oct 2014 10:46:10 -0700 Subject: [PATCH 2/5] Moved DOM highlight arrows from PlayLevelView to CocoView so that everywhere can use them. Added highlight for next level on world map. --- app/lib/surface/PlaybackOverScreen.coffee | 2 +- app/styles/base.sass | 11 ++++ app/styles/play/level.sass | 9 --- app/views/kinds/CocoView.coffee | 73 +++++++++++++++++++++ app/views/play/WorldMapView.coffee | 1 + app/views/play/level/PlayLevelView.coffee | 77 ++--------------------- 6 files changed, 90 insertions(+), 83 deletions(-) diff --git a/app/lib/surface/PlaybackOverScreen.coffee b/app/lib/surface/PlaybackOverScreen.coffee index a129bd45f..49eca6f80 100644 --- a/app/lib/surface/PlaybackOverScreen.coffee +++ b/app/lib/surface/PlaybackOverScreen.coffee @@ -25,7 +25,7 @@ module.exports = class PlaybackOverScreen extends CocoClass show: -> return if @showing @showing = true - @updateColor 'rgba(212, 212, 212, 0.4' unless @color # If we haven't caught the goal state for the first run, just do something neutral. + @updateColor 'rgba(212, 212, 212, 0.4)' unless @color # If we haven't caught the goal state for the first run, just do something neutral. @dimLayer.alpha = 0 createjs.Tween.removeTweens @dimLayer createjs.Tween.get(@dimLayer).to({alpha: 1}, 500) diff --git a/app/styles/base.sass b/app/styles/base.sass index e4e6ea228..10bef6efb 100644 --- a/app/styles/base.sass +++ b/app/styles/base.sass @@ -236,6 +236,17 @@ table.table .ui-state-default, .ui-state-focus, .ui-state-active, .ui-state-highlight, .ui-state-error background-image: none +// DOM highlight pointer arrow + +.highlight-pointer + position: absolute + left: 0 + top: 0 + height: 100px + opacity: 0.0 + pointer-events: none + z-index: 10 + // Fonts .header-font diff --git a/app/styles/play/level.sass b/app/styles/play/level.sass index 389453cbf..b4d5e0548 100644 --- a/app/styles/play/level.sass +++ b/app/styles/play/level.sass @@ -97,15 +97,6 @@ $level-resize-transition-time: 0.5s bottom: 0 @include transition(width $level-resize-transition-time ease-in-out, right $level-resize-transition-time ease-in-out) - #pointer - position: absolute - left: 0 - top: 0 - height: 100px - opacity: 0.0 - pointer-events: none - z-index: 10 - // Level Docs .ui-effects-transfer border: 2px dotted gray diff --git a/app/views/kinds/CocoView.coffee b/app/views/kinds/CocoView.coffee index 18915a7da..3068f8015 100644 --- a/app/views/kinds/CocoView.coffee +++ b/app/views/kinds/CocoView.coffee @@ -59,6 +59,7 @@ module.exports = class CocoView extends Backbone.View @undelegateEvents() # removes both events and subs view.destroy() for id, view of @subviews $('#modal-wrapper .modal').off 'hidden.bs.modal', @modalClosed + @endHighlight() @[key] = undefined for key, value of @ @destroyed = true @off = doNothing @@ -293,6 +294,78 @@ module.exports = class CocoView extends Backbone.View delete @subviews[view.parentKey] view.destroy() + # Pointing stuff out + + highlightElement: (selector, options) -> + @endHighlight() + options ?= {} + if delay = options.delay + delete options.delay + return @pointerDelayTimeout = _.delay((=> @highlightElement selector, options), delay) + $pointer = @getPointer() + $target = $(selector + ':visible') + return if parseFloat($target.css('opacity')) is 0.0 # Don't point out invisible elements. + return unless offset = $target.offset() # Don't point out elements we can't locate. + targetLeft = offset.left + $target.outerWidth() * 0.5 + targetTop = offset.top + $target.outerHeight() * 0.5 + + if options.sides + if 'left' in options.sides then targetLeft = offset.left + if 'right' in options.sides then targetLeft = offset.left + $target.outerWidth() + if 'top' in options.sides then targetTop = offset.top + if 'bottom' in options.sides then targetTop = offset.top + $target.outerHeight() + else + # Aim to hit the side if the target is entirely on one side of the screen. + if offset.left > @$el.outerWidth() * 0.5 + targetLeft = offset.left + else if offset.left + $target.outerWidth() < @$el.outerWidth() * 0.5 + targetLeft = offset.left + $target.outerWidth() + + # Aim to hit the bottom or top if the target is entirely on the top or bottom of the screen. + if offset.top > @$el.outerWidth() * 0.5 + targetTop = offset.top + else if offset.top + $target.outerHeight() < @$el.outerHeight() * 0.5 + targetTop = offset.top + $target.outerHeight() + + if options.offset + targetLeft += options.offset.x + targetTop += options.offset.y + + @pointerRadialDistance = -47 + @pointerRotation = options.rotation ? Math.atan2(@$el.outerWidth() * 0.5 - targetLeft, targetTop - @$el.outerHeight() * 0.5) + $pointer.css + opacity: 1.0 + transition: 'none' + transform: "rotate(#{@pointerRotation}rad) translate(-3px, #{@pointerRadialDistance}px)" + top: targetTop - 50 + left: targetLeft - 50 + _.defer => + return if @destroyed + @animatePointer() + clearInterval @pointerInterval + @pointerInterval = setInterval(@animatePointer, 1200) + if options.duration + @pointerDurationTimeout = _.delay (=> @endHighlight() unless @destroyed), options.duration + + animatePointer: => + $pointer = @getPointer() + $pointer.css transition: 'all 0.6s ease-out', transform: "rotate(#{@pointerRotation}rad) translate(-3px, #{@pointerRadialDistance-50}px)" + #Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'dom_highlight', volume: 0.75 # Never mind, this is currently so annoying + setTimeout (=> $pointer.css transition: 'all 0.4s ease-in', transform: "rotate(#{@pointerRotation}rad) translate(-3px, #{@pointerRadialDistance}px)"), 800 + + endHighlight: -> + @getPointer().css('opacity', 0.0) + clearInterval @pointerInterval + clearTimeout @pointerDelayTimeout + clearTimeout @pointerDurationTimeout + @pointerInterval = @pointerDelayTimeout = @pointerDurationTimeout = null + + getPointer: -> + return $pointer if ($pointer = @$el.find('.highlight-pointer')) and $pointer.length + $pointer = $('') + @$el.append($pointer) + $pointer + # Utilities getQueryVariable: (param, defaultValue) -> CocoView.getQueryVariable(param, defaultValue) diff --git a/app/views/play/WorldMapView.coffee b/app/views/play/WorldMapView.coffee index 43bac56ce..7b63c53e9 100644 --- a/app/views/play/WorldMapView.coffee +++ b/app/views/play/WorldMapView.coffee @@ -89,6 +89,7 @@ module.exports = class WorldMapView extends RootView @$el.find('.level').tooltip() @$el.addClass _.string.slugify @terrain @updateVolume() + @highlightElement '.level.next', delay: 8000, duration: 20000, rotation: 0, sides: ['top'] onSessionsLoaded: (e) -> for session in @sessions.models diff --git a/app/views/play/level/PlayLevelView.coffee b/app/views/play/level/PlayLevelView.coffee index 91acdaff8..9d4028b38 100644 --- a/app/views/play/level/PlayLevelView.coffee +++ b/app/views/play/level/PlayLevelView.coffee @@ -49,7 +49,7 @@ module.exports = class PlayLevelView extends RootView 'level:set-volume': (e) -> createjs.Sound.setVolume(if e.volume is 1 then 0.6 else e.volume) # Quieter for now until individual sound FX controls work again. 'level:show-victory': 'onShowVictory' 'level:restart': 'onRestartLevel' - 'level:highlight-dom': 'onHighlightDom' + 'level:highlight-dom': 'onHighlightDOM' 'level:end-highlight-dom': 'onEndHighlight' 'level:focus-dom': 'onFocusDom' 'level:disable-controls': 'onDisableControls' @@ -430,9 +430,7 @@ module.exports = class PlayLevelView extends RootView viewArgs: [{supermodel: @supermodel, autoUnveil: true}, @levelID] } - onWindowResize: (s...) -> - $('#pointer').css('opacity', 0.0) - clearInterval(@pointerInterval) + onWindowResize: (e) -> @endHighlight() onDisableControls: (e) -> return if e.controls and not ('level' in e.controls) @@ -500,73 +498,12 @@ module.exports = class PlayLevelView extends RootView return null unless @getNextLevelID() "/play/level/#{@getNextLevelID()}" - onHighlightDom: (e) -> - if e.delay - delay = e.delay - delete e.delay - @pointerInterval = _.delay((=> @onHighlightDom e), delay) - return - @addPointer() - selector = e.selector + ':visible' - dom = $(selector) - return if parseFloat(dom.css('opacity')) is 0.0 - offset = dom.offset() - return if not offset - target_left = offset.left + dom.outerWidth() * 0.5 - target_top = offset.top + dom.outerHeight() * 0.5 + onHighlightDOM: (e) -> @highlightElement e.selector, delay: e.delay, sides: e.sides, offset: e.offset, rotation: e.rotation - if e.sides - if 'left' in e.sides then target_left = offset.left - if 'right' in e.sides then target_left = offset.left + dom.outerWidth() - if 'top' in e.sides then target_top = offset.top - if 'bottom' in e.sides then target_top = offset.top + dom.outerHeight() - else - # aim to hit the side if the target is entirely on one side of the screen - if offset.left > @$el.outerWidth()*0.5 - target_left = offset.left - else if offset.left + dom.outerWidth() < @$el.outerWidth()*0.5 - target_left = offset.left + dom.outerWidth() - - # aim to hit the bottom or top if the target is entirely on the top or bottom of the screen - if offset.top > @$el.outerWidth()*0.5 - target_top = offset.top - else if offset.top + dom.outerHeight() < @$el.outerHeight()*0.5 - target_top = offset.top + dom.outerHeight() - - if e.offset - target_left += e.offset.x - target_top += e.offset.y - - @pointerRadialDistance = -47 # - Math.sqrt(Math.pow(dom.outerHeight()*0.5, 2), Math.pow(dom.outerWidth()*0.5)) - @pointerRotation = e.rotation ? Math.atan2(@$el.outerWidth()*0.5 - target_left, target_top - @$el.outerHeight()*0.5) - pointer = $('#pointer') - pointer - .css('opacity', 1.0) - .css('transition', 'none') - .css('transform', "rotate(#{@pointerRotation}rad) translate(-3px, #{@pointerRadialDistance}px)") - .css('top', target_top - 50) - .css('left', target_left - 50) - setTimeout(()=> - return if @destroyed - @animatePointer() - clearInterval(@pointerInterval) - @pointerInterval = setInterval(@animatePointer, 1200) - , 1) - - animatePointer: => - pointer = $('#pointer') - pointer.css('transition', 'all 0.6s ease-out') - pointer.css('transform', "rotate(#{@pointerRotation}rad) translate(-3px, #{@pointerRadialDistance-50}px)") - #Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'dom_highlight', volume: 0.75 # Never mind, this is currently so annoying - setTimeout((=> - pointer.css('transform', "rotate(#{@pointerRotation}rad) translate(-3px, #{@pointerRadialDistance}px)").css('transition', 'all 0.4s ease-in')), 800) + onEndHighlight: -> @endHighlight() onFocusDom: (e) -> $(e.selector).focus() - onEndHighlight: -> - $('#pointer').css('opacity', 0.0) - clearInterval(@pointerInterval) - onMultiplayerChanged: (e) -> if @session.get('multiplayer') @bus.connect() @@ -574,11 +511,6 @@ module.exports = class PlayLevelView extends RootView @bus.removeFirebaseData => @bus.disconnect() - addPointer: -> - p = $('#pointer') - return if p.length - @$el.append($('')) - preloadNextLevel: => # TODO: Loading models in the middle of gameplay causes stuttering. Most of the improvement in loading time is simply from passing the supermodel from this level to the next, but if we can find a way to populate the level early without it being noticeable, that would be even better. # return if @destroyed @@ -652,7 +584,6 @@ module.exports = class PlayLevelView extends RootView createjs.Tween.get(ambientSound).to({volume: 0.0}, 1500).call -> 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) @bus?.destroy() #@instance.save() unless @instance.loading delete window.nextLevelURL From f9409488ff73f281b91f1803082f3fe662d7768b Mon Sep 17 00:00:00 2001 From: Nick Winter Date: Wed, 8 Oct 2014 12:38:23 -0700 Subject: [PATCH 3/5] Fixed a bug with jerky camera udpates due to dropped frames and camera updating before positions update. Fleshed out default level scripts. Tweaked style on Dropper code. --- app/lib/DefaultScripts.coffee | 17 +++++++++++++++-- app/lib/surface/Dropper.coffee | 21 ++++++++++----------- app/lib/surface/Surface.coffee | 4 ++-- 3 files changed, 27 insertions(+), 15 deletions(-) diff --git a/app/lib/DefaultScripts.coffee b/app/lib/DefaultScripts.coffee index 8417ce978..818feee91 100644 --- a/app/lib/DefaultScripts.coffee +++ b/app/lib/DefaultScripts.coffee @@ -1,7 +1,20 @@ module.exports = [ { - channel: "god:new-world-created" - noteChain: [] id: "Introduction" + channel: "god:new-world-created" + noteChain: [ + name: "Set camera, start music." + surface: + focus: + bounds: [{x: 0, y: 0}, {x: 80, y: 68}] + target: "Hero Placeholder" + zoom: 2 + sound: + music: + file: "/music/music_level_2" + play: true + script: + duration: 1 + ] } ] diff --git a/app/lib/surface/Dropper.coffee b/app/lib/surface/Dropper.coffee index e7ea3c6cd..f074c702b 100644 --- a/app/lib/surface/Dropper.coffee +++ b/app/lib/surface/Dropper.coffee @@ -1,28 +1,27 @@ Dropper = class Dropper - lost_frames: 0.0 - drop_counter: 0 + lostFrames: 0.0 + dropCounter: 0 constructor: -> @listener = (e) => @tick(e) tick: -> unless @tickedOnce - @tickedOnce = true # Can't get measured FPS on the 0th frame + @tickedOnce = true # Can't get measured FPS on the 0th frame. return - # decrement drop counter - @drop_counter -= 1 if @drop_counter > 0 + --@dropCounter if @dropCounter > 0 - # track number of frames we've lost since the last tick + # Track number of frames we've lost since the last tick. fps = createjs.Ticker.getFPS() actual = createjs.Ticker.getMeasuredFPS(1) - @lost_frames += (fps - actual) / fps + @lostFrames += (fps - actual) / fps - # if lost_frames > 1, drop that number for the next tick - @drop_counter += parseInt(@lost_frames) - @lost_frames = @lost_frames % 1 + # If lostFrames > 1, drop that number for the next tick. + @dropCounter += parseInt @lostFrames + @lostFrames = @lostFrames % 1 drop: -> - return @drop_counter > 0 + return @dropCounter > 0 module.exports = new Dropper() diff --git a/app/lib/surface/Surface.coffee b/app/lib/surface/Surface.coffee index b96203fd4..617dbd279 100644 --- a/app/lib/surface/Surface.coffee +++ b/app/lib/surface/Surface.coffee @@ -193,7 +193,7 @@ module.exports = Surface = class Surface extends CocoClass @currentFrame = Math.min @currentFrame, lastFrame newWorldFrame = Math.floor @currentFrame if Dropper.drop() - framesDropped += 1 + ++framesDropped else worldFrameAdvanced = newWorldFrame isnt oldWorldFrame if worldFrameAdvanced @@ -232,8 +232,8 @@ module.exports = Surface = class Surface extends CocoClass # world state must have been restored in @restoreWorldState if @playing and @currentFrame < @world.frames.length - 1 and @heroLank and not @mouseIsDown and @camera.newTarget isnt @heroLank.sprite and @camera.target isnt @heroLank.sprite @camera.zoomTo @heroLank.sprite, @camera.zoom, 750 - @camera.updateZoom() @lankBoss.update frameChanged + @camera.updateZoom() # Make sure to do this right after the LankBoss updates, not before, so it can properly target sprite positions. @dimmer?.setSprites @lankBoss.lanks drawCurrentFrame: (e) -> From b6f37694e17ccad23ddc2d758d06dc1243b15e8f Mon Sep 17 00:00:00 2001 From: Matt Lott Date: Wed, 8 Oct 2014 14:22:11 -0700 Subject: [PATCH 4/5] Autocomplete line endings for javascript Fixes #1651 --- app/views/play/level/tome/SpellView.coffee | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/views/play/level/tome/SpellView.coffee b/app/views/play/level/tome/SpellView.coffee index e21cb4f0a..c2785f490 100644 --- a/app/views/play/level/tome/SpellView.coffee +++ b/app/views/play/level/tome/SpellView.coffee @@ -199,6 +199,8 @@ module.exports = class SpellView extends CocoView completers: keywords: false text: false + autoLineEndings: + javascript: ';' updateAutocomplete: (@autocomplete) -> @zatanna?.set 'snippets', @autocomplete From 45471b803059aee17842b476f25fe6260b344cdb Mon Sep 17 00:00:00 2001 From: Nick Winter Date: Thu, 9 Oct 2014 09:53:57 -0700 Subject: [PATCH 5/5] Fix for streaming new serialized Aethers into worlds where their Thangs hadn't existed before. --- app/lib/world/world.coffee | 2 ++ app/views/play/level/tome/DocFormatter.coffee | 8 ++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/app/lib/world/world.coffee b/app/lib/world/world.coffee index fb4ed80bd..90d1bd3fa 100644 --- a/app/lib/world/world.coffee +++ b/app/lib/world/world.coffee @@ -448,6 +448,8 @@ module.exports = class World for thangID, methods of o.userCodeMap for methodName, serializedAether of methods for aetherStateKey in ['flow', 'metrics', 'style', 'problems'] + w.userCodeMap[thangID] ?= {} + w.userCodeMap[thangID][methodName] ?= {} w.userCodeMap[thangID][methodName][aetherStateKey] = serializedAether[aetherStateKey] else w = new World o.userCodeMap, classMap diff --git a/app/views/play/level/tome/DocFormatter.coffee b/app/views/play/level/tome/DocFormatter.coffee index 7b3e53b41..b966a94bc 100644 --- a/app/views/play/level/tome/DocFormatter.coffee +++ b/app/views/play/level/tome/DocFormatter.coffee @@ -107,10 +107,14 @@ module.exports = class DocFormatter v = @options.thang[@doc.name] else v = window[@doc.owner][@doc.name] # grab Math or Vector - if @doc.type is 'number' and not isNaN v + if @doc.type is 'number' and not _.isNaN v if v == Math.round v return v - return v?.toFixed(2) ? 'null' + if _.isNumber v + return v.toFixed 2 + unless v + return 'null' + return '' + v if _.isString v return "\"#{v}\"" if v?.id