mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2024-11-28 18:15:52 -05:00
Merge branch 'master' into production
This commit is contained in:
commit
b0aa841976
15 changed files with 180 additions and 117 deletions
|
@ -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
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -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 = {}
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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) ->
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue