Merge branch 'master' into production

This commit is contained in:
Nick Winter 2014-10-09 09:54:08 -07:00
commit b0aa841976
15 changed files with 180 additions and 117 deletions

View file

@ -1,7 +1,20 @@
module.exports = [ module.exports = [
{ {
channel: "god:new-world-created"
noteChain: []
id: "Introduction" 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
]
} }
] ]

View file

@ -131,9 +131,7 @@ module.exports = class God extends CocoClass
Backbone.Mediator.publish 'god:debug-world-load-progress-changed', progress: event.data.progress Backbone.Mediator.publish 'god:debug-world-load-progress-changed', progress: event.data.progress
onNewWorldCreated: (e) -> onNewWorldCreated: (e) ->
console.log 'filtering', _.cloneDeep e.world.userCodeMap
@currentUserCodeMap = @filterUserCodeMapWhenFromWorld e.world.userCodeMap @currentUserCodeMap = @filterUserCodeMapWhenFromWorld e.world.userCodeMap
console.log ' ... filtered into', _.cloneDeep e.world.userCodeMap
filterUserCodeMapWhenFromWorld: (worldUserCodeMap) -> filterUserCodeMapWhenFromWorld: (worldUserCodeMap) ->
newUserCodeMap = {} newUserCodeMap = {}

View file

@ -1,28 +1,27 @@
Dropper = class Dropper Dropper = class Dropper
lost_frames: 0.0 lostFrames: 0.0
drop_counter: 0 dropCounter: 0
constructor: -> constructor: ->
@listener = (e) => @tick(e) @listener = (e) => @tick(e)
tick: -> tick: ->
unless @tickedOnce 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 return
# decrement drop counter --@dropCounter if @dropCounter > 0
@drop_counter -= 1 if @drop_counter > 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() fps = createjs.Ticker.getFPS()
actual = createjs.Ticker.getMeasuredFPS(1) 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 # If lostFrames > 1, drop that number for the next tick.
@drop_counter += parseInt(@lost_frames) @dropCounter += parseInt @lostFrames
@lost_frames = @lost_frames % 1 @lostFrames = @lostFrames % 1
drop: -> drop: ->
return @drop_counter > 0 return @dropCounter > 0
module.exports = new Dropper() module.exports = new Dropper()

View file

@ -1,6 +1,9 @@
CocoClass = require 'lib/CocoClass' CocoClass = require 'lib/CocoClass'
module.exports = class PlaybackOverScreen extends CocoClass module.exports = class PlaybackOverScreen extends CocoClass
subscriptions:
'goal-manager:new-goal-states': 'onNewGoalStates'
constructor: (options) -> constructor: (options) ->
super() super()
options ?= {} options ?= {}
@ -16,15 +19,13 @@ module.exports = class PlaybackOverScreen extends CocoClass
@dimLayer = new createjs.Container() @dimLayer = new createjs.Container()
@dimLayer.mouseEnabled = @dimLayer.mouseChildren = false @dimLayer.mouseEnabled = @dimLayer.mouseChildren = false
@dimLayer.addChild @dimScreen = new createjs.Shape() @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 @dimLayer.alpha = 0
@layer.addChild @dimLayer @layer.addChild @dimLayer
show: -> show: ->
return if @showing return if @showing
@showing = true @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 @dimLayer.alpha = 0
createjs.Tween.removeTweens @dimLayer createjs.Tween.removeTweens @dimLayer
createjs.Tween.get(@dimLayer).to({alpha: 1}, 500) createjs.Tween.get(@dimLayer).to({alpha: 1}, 500)
@ -32,6 +33,22 @@ module.exports = class PlaybackOverScreen extends CocoClass
hide: -> hide: ->
return unless @showing return unless @showing
@showing = false @showing = false
createjs.Tween.removeTweens @dimLayer createjs.Tween.removeTweens @dimLayer
createjs.Tween.get(@dimLayer).to({alpha: 0}, 500) 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

View file

@ -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 @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 @countdownScreen = new CountdownScreen camera: @camera, layer: @screenLayer
@playbackOverScreen = new PlaybackOverScreen 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 @waitingScreen = new WaitingScreen camera: @camera, layer: @screenLayer
@initCoordinates() @initCoordinates()
@webGLStage.enableMouseOver(10) @webGLStage.enableMouseOver(10)
@ -192,7 +193,7 @@ module.exports = Surface = class Surface extends CocoClass
@currentFrame = Math.min @currentFrame, lastFrame @currentFrame = Math.min @currentFrame, lastFrame
newWorldFrame = Math.floor @currentFrame newWorldFrame = Math.floor @currentFrame
if Dropper.drop() if Dropper.drop()
framesDropped += 1 ++framesDropped
else else
worldFrameAdvanced = newWorldFrame isnt oldWorldFrame worldFrameAdvanced = newWorldFrame isnt oldWorldFrame
if worldFrameAdvanced if worldFrameAdvanced
@ -231,8 +232,8 @@ module.exports = Surface = class Surface extends CocoClass
# world state must have been restored in @restoreWorldState # 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 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.zoomTo @heroLank.sprite, @camera.zoom, 750
@camera.updateZoom()
@lankBoss.update frameChanged @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 @dimmer?.setSprites @lankBoss.lanks
drawCurrentFrame: (e) -> drawCurrentFrame: (e) ->

View file

@ -448,6 +448,8 @@ module.exports = class World
for thangID, methods of o.userCodeMap for thangID, methods of o.userCodeMap
for methodName, serializedAether of methods for methodName, serializedAether of methods
for aetherStateKey in ['flow', 'metrics', 'style', 'problems'] for aetherStateKey in ['flow', 'metrics', 'style', 'problems']
w.userCodeMap[thangID] ?= {}
w.userCodeMap[thangID][methodName] ?= {}
w.userCodeMap[thangID][methodName][aetherStateKey] = serializedAether[aetherStateKey] w.userCodeMap[thangID][methodName][aetherStateKey] = serializedAether[aetherStateKey]
else else
w = new World o.userCodeMap, classMap w = new World o.userCodeMap, classMap

View file

@ -236,6 +236,17 @@ table.table
.ui-state-default, .ui-state-focus, .ui-state-active, .ui-state-highlight, .ui-state-error .ui-state-default, .ui-state-focus, .ui-state-active, .ui-state-highlight, .ui-state-error
background-image: none 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 // Fonts
.header-font .header-font

View file

@ -97,15 +97,6 @@ $level-resize-transition-time: 0.5s
bottom: 0 bottom: 0
@include transition(width $level-resize-transition-time ease-in-out, right $level-resize-transition-time ease-in-out) @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 // Level Docs
.ui-effects-transfer .ui-effects-transfer
border: 2px dotted gray border: 2px dotted gray

View file

@ -5,19 +5,24 @@
position: absolute position: absolute
left: 10px left: 10px
top: -100px 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) background-color: rgba(200,200,200,1.0)
border: black border: black
padding: 15px 7px 2px 5px padding: 15px 7px 2px 5px
box-sizing: border-box box-sizing: border-box
border: 1px solid #333 border: 1px solid #333
border-radius: 5px border-radius: 5px
z-index: 3
font-size: 14px
&.brighter
font-size: 28px
.goals-status .goals-status
font-size: 14px
margin: 0 margin: 0
color: black color: black
.success .success
color: darkgreen color: darkgreen
.timed-out .timed-out

View file

@ -59,6 +59,7 @@ module.exports = class CocoView extends Backbone.View
@undelegateEvents() # removes both events and subs @undelegateEvents() # removes both events and subs
view.destroy() for id, view of @subviews view.destroy() for id, view of @subviews
$('#modal-wrapper .modal').off 'hidden.bs.modal', @modalClosed $('#modal-wrapper .modal').off 'hidden.bs.modal', @modalClosed
@endHighlight()
@[key] = undefined for key, value of @ @[key] = undefined for key, value of @
@destroyed = true @destroyed = true
@off = doNothing @off = doNothing
@ -293,6 +294,78 @@ module.exports = class CocoView extends Backbone.View
delete @subviews[view.parentKey] delete @subviews[view.parentKey]
view.destroy() 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 = $('<img src="/images/level/pointer.png" class="highlight-pointer">')
@$el.append($pointer)
$pointer
# Utilities # Utilities
getQueryVariable: (param, defaultValue) -> CocoView.getQueryVariable(param, defaultValue) getQueryVariable: (param, defaultValue) -> CocoView.getQueryVariable(param, defaultValue)

View file

@ -89,6 +89,7 @@ module.exports = class WorldMapView extends RootView
@$el.find('.level').tooltip() @$el.find('.level').tooltip()
@$el.addClass _.string.slugify @terrain @$el.addClass _.string.slugify @terrain
@updateVolume() @updateVolume()
@highlightElement '.level.next', delay: 8000, duration: 20000, rotation: 0, sides: ['top']
onSessionsLoaded: (e) -> onSessionsLoaded: (e) ->
for session in @sessions.models for session in @sessions.models

View file

@ -4,9 +4,9 @@ template = require 'templates/play/level/goals'
utils = require 'lib/utils' utils = require 'lib/utils'
stateIconMap = stateIconMap =
incomplete: 'icon-minus' incomplete: 'glyphicon-minus'
success: 'icon-ok' success: 'glyphicon-ok'
failure: 'icon-remove' failure: 'glyphicon-remove'
module.exports = class LevelGoalsView extends CocoView module.exports = class LevelGoalsView extends CocoView
id: 'goals-view' 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 # 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. # representation of how many are done, how many are needed, what that means, etc.
li = $('<li></li>').addClass("status-#{state.status}").text(text) li = $('<li></li>').addClass("status-#{state.status}").text(text)
li.prepend($('<i></i>').addClass(stateIconMap[state.status])) li.prepend($('<i></i>').addClass('glyphicon').addClass(stateIconMap[state.status]))
list.append(li) list.append(li)
goals.push goal goals.push goal
if not firstRun and state.status is 'success' and @previousGoalStatus[goal.id] isnt 'success' 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' 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 @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() @updatePlacement()
onSurfacePlaybackRestarted: -> onSurfacePlaybackRestarted: ->
@playbackEnded = false @playbackEnded = false
@$el.removeClass 'brighter' @$el.removeClass 'brighter'
@lastSizeTweenTime = new Date()
@updatePlacement() @updatePlacement()
onSurfacePlaybackEnded: -> onSurfacePlaybackEnded: ->
@playbackEnded = true @playbackEnded = true
@updateHeight()
@$el.addClass 'brighter' @$el.addClass 'brighter'
@lastSizeTweenTime = new Date()
@updatePlacement() @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: -> updatePlacement: ->
expand = @playbackEnded or @mouseEntered expand = @playbackEnded or @mouseEntered
return if expand is @expanded return if expand is @expanded
@updateHeight()
sound = if expand then 'goals-expand' else 'goals-collapse' 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 @$el.css 'top', top
if @soundTimeout if @soundTimeout
# Don't play the sound we were going to play after all; the transition has reversed. # Don't play the sound we were going to play after all; the transition has reversed.

View file

@ -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: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:show-victory': 'onShowVictory'
'level:restart': 'onRestartLevel' 'level:restart': 'onRestartLevel'
'level:highlight-dom': 'onHighlightDom' 'level:highlight-dom': 'onHighlightDOM'
'level:end-highlight-dom': 'onEndHighlight' 'level:end-highlight-dom': 'onEndHighlight'
'level:focus-dom': 'onFocusDom' 'level:focus-dom': 'onFocusDom'
'level:disable-controls': 'onDisableControls' 'level:disable-controls': 'onDisableControls'
@ -430,9 +430,7 @@ module.exports = class PlayLevelView extends RootView
viewArgs: [{supermodel: @supermodel, autoUnveil: true}, @levelID] viewArgs: [{supermodel: @supermodel, autoUnveil: true}, @levelID]
} }
onWindowResize: (s...) -> onWindowResize: (e) -> @endHighlight()
$('#pointer').css('opacity', 0.0)
clearInterval(@pointerInterval)
onDisableControls: (e) -> onDisableControls: (e) ->
return if e.controls and not ('level' in e.controls) return if e.controls and not ('level' in e.controls)
@ -500,73 +498,12 @@ module.exports = class PlayLevelView extends RootView
return null unless @getNextLevelID() return null unless @getNextLevelID()
"/play/level/#{@getNextLevelID()}" "/play/level/#{@getNextLevelID()}"
onHighlightDom: (e) -> onHighlightDOM: (e) -> @highlightElement e.selector, delay: e.delay, sides: e.sides, offset: e.offset, rotation: e.rotation
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
if e.sides onEndHighlight: -> @endHighlight()
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)
onFocusDom: (e) -> $(e.selector).focus() onFocusDom: (e) -> $(e.selector).focus()
onEndHighlight: ->
$('#pointer').css('opacity', 0.0)
clearInterval(@pointerInterval)
onMultiplayerChanged: (e) -> onMultiplayerChanged: (e) ->
if @session.get('multiplayer') if @session.get('multiplayer')
@bus.connect() @bus.connect()
@ -574,11 +511,6 @@ module.exports = class PlayLevelView extends RootView
@bus.removeFirebaseData => @bus.removeFirebaseData =>
@bus.disconnect() @bus.disconnect()
addPointer: ->
p = $('#pointer')
return if p.length
@$el.append($('<img src="/images/level/pointer.png" id="pointer">'))
preloadNextLevel: => 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. # 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 # 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() createjs.Tween.get(ambientSound).to({volume: 0.0}, 1500).call -> 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)
@bus?.destroy() @bus?.destroy()
#@instance.save() unless @instance.loading #@instance.save() unless @instance.loading
delete window.nextLevelURL delete window.nextLevelURL

View file

@ -107,10 +107,14 @@ module.exports = class DocFormatter
v = @options.thang[@doc.name] v = @options.thang[@doc.name]
else else
v = window[@doc.owner][@doc.name] # grab Math or Vector 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 if v == Math.round v
return v return v
return v?.toFixed(2) ? 'null' if _.isNumber v
return v.toFixed 2
unless v
return 'null'
return '' + v
if _.isString v if _.isString v
return "\"#{v}\"" return "\"#{v}\""
if v?.id if v?.id

View file

@ -199,6 +199,8 @@ module.exports = class SpellView extends CocoView
completers: completers:
keywords: false keywords: false
text: false text: false
autoLineEndings:
javascript: ';'
updateAutocomplete: (@autocomplete) -> updateAutocomplete: (@autocomplete) ->
@zatanna?.set 'snippets', @autocomplete @zatanna?.set 'snippets', @autocomplete