mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2025-04-29 23:43:51 -04:00
Full-screen playback during real-time mode.
This commit is contained in:
parent
f2adb7ec7f
commit
c79541f669
7 changed files with 91 additions and 12 deletions
app
lib
schemas/subscriptions
styles/play
views/play/level
|
@ -196,7 +196,8 @@ module.exports = class LevelBus extends Bus
|
||||||
|
|
||||||
onNewGoalStates: ({goalStates})->
|
onNewGoalStates: ({goalStates})->
|
||||||
state = @session.get 'state'
|
state = @session.get 'state'
|
||||||
unless utils.kindaEqual state.goalStates, goalStates # Only save when goals really change
|
unless utils.kindaEqual state.goalStates, goalStates # Only save when goals really change
|
||||||
|
# TODO: this log doesn't capture when null-status goals are being set during world streaming. Where can they be coming from?
|
||||||
return console.error("Somehow trying to save null goal states!", goalStates) if _.find(goalStates, (gs) -> not gs.status)
|
return console.error("Somehow trying to save null goal states!", goalStates) if _.find(goalStates, (gs) -> not gs.status)
|
||||||
state.goalStates = goalStates
|
state.goalStates = goalStates
|
||||||
@session.set 'state', state
|
@session.set 'state', state
|
||||||
|
|
|
@ -67,6 +67,8 @@ module.exports = Surface = class Surface extends CocoClass
|
||||||
'level-set-letterbox': 'onSetLetterbox'
|
'level-set-letterbox': 'onSetLetterbox'
|
||||||
'application:idle-changed': 'onIdleChanged'
|
'application:idle-changed': 'onIdleChanged'
|
||||||
'camera:zoom-updated': 'onZoomUpdated'
|
'camera:zoom-updated': 'onZoomUpdated'
|
||||||
|
'playback:real-time-playback-started': 'onRealTimePlaybackStarted'
|
||||||
|
'playback:real-time-playback-ended': 'onRealTimePlaybackEnded'
|
||||||
|
|
||||||
shortcuts:
|
shortcuts:
|
||||||
'ctrl+\\, ⌘+\\': 'onToggleDebug'
|
'ctrl+\\, ⌘+\\': 'onToggleDebug'
|
||||||
|
@ -82,7 +84,7 @@ module.exports = Surface = class Surface extends CocoClass
|
||||||
@options = _.extend(@options, givenOptions) if givenOptions
|
@options = _.extend(@options, givenOptions) if givenOptions
|
||||||
@initEasel()
|
@initEasel()
|
||||||
@initAudio()
|
@initAudio()
|
||||||
@onResize = _.debounce @onResize, 500
|
@onResize = _.debounce @onResize, 250
|
||||||
$(window).on 'resize', @onResize
|
$(window).on 'resize', @onResize
|
||||||
if @world.ended
|
if @world.ended
|
||||||
_.defer => @setWorld @world
|
_.defer => @setWorld @world
|
||||||
|
@ -367,7 +369,8 @@ module.exports = Surface = class Surface extends CocoClass
|
||||||
|
|
||||||
@setWorld event.world
|
@setWorld event.world
|
||||||
@onFrameChanged(true)
|
@onFrameChanged(true)
|
||||||
if @playing and (ffToFrame = Math.min(event.firstChangedFrame, @frameBeforeCast, event.world.frames.length)) and ffToFrame > @currentFrame
|
fastForwardBuffer = 2 # Make sure that real-time playback doesn't need to buffer more than this many seconds.
|
||||||
|
if @playing and (ffToFrame = Math.min(event.firstChangedFrame, @frameBeforeCast, event.world.frames.length)) and ffToFrame > @currentFrame + fastForwardBuffer * @world.frameRate
|
||||||
@fastForwardingToFrame = ffToFrame
|
@fastForwardingToFrame = ffToFrame
|
||||||
@fastForwardingSpeed = Math.max 4, 4 * 90 / (@world.maxTotalFrames * @world.dt)
|
@fastForwardingSpeed = Math.max 4, 4 * 90 / (@world.maxTotalFrames * @world.dt)
|
||||||
|
|
||||||
|
@ -402,8 +405,17 @@ module.exports = Surface = class Surface extends CocoClass
|
||||||
onResize: (e) =>
|
onResize: (e) =>
|
||||||
oldWidth = parseInt @canvas.attr('width'), 10
|
oldWidth = parseInt @canvas.attr('width'), 10
|
||||||
oldHeight = parseInt @canvas.attr('height'), 10
|
oldHeight = parseInt @canvas.attr('height'), 10
|
||||||
newWidth = @canvas.width()
|
aspectRatio = oldWidth / oldHeight
|
||||||
newHeight = @canvas.height()
|
pageWidth = $('#page-container').width() - 17 # 17px nano scroll bar
|
||||||
|
if @realTime
|
||||||
|
pageHeight = $('#page-container').height() - $('#control-bar-view').outerHeight() - $('#playback-view').outerHeight()
|
||||||
|
newWidth = Math.min pageWidth, pageHeight * aspectRatio
|
||||||
|
newHeight = newWidth / aspectRatio
|
||||||
|
else
|
||||||
|
newWidth = 0.55 * pageWidth
|
||||||
|
newHeight = newWidth / aspectRatio
|
||||||
|
@canvas.width newWidth
|
||||||
|
@canvas.height newHeight
|
||||||
return unless newWidth > 0 and newHeight > 0
|
return unless newWidth > 0 and newHeight > 0
|
||||||
#if InstallTrigger? # Firefox rendering performance goes down as canvas size goes up
|
#if InstallTrigger? # Firefox rendering performance goes down as canvas size goes up
|
||||||
# newWidth = Math.min 924, newWidth
|
# newWidth = Math.min 924, newWidth
|
||||||
|
@ -600,6 +612,15 @@ module.exports = Surface = class Surface extends CocoClass
|
||||||
++@totalFramesDrawn
|
++@totalFramesDrawn
|
||||||
@stage.update e
|
@stage.update e
|
||||||
|
|
||||||
|
# Real-time playback
|
||||||
|
onRealTimePlaybackStarted: (e) ->
|
||||||
|
@realTime = true
|
||||||
|
@onResize()
|
||||||
|
|
||||||
|
onRealTimePlaybackEnded: (e) ->
|
||||||
|
@realTime = false
|
||||||
|
@onResize()
|
||||||
|
|
||||||
# paths - TODO: move to SpriteBoss? but only update on frame drawing instead of on every frame update?
|
# paths - TODO: move to SpriteBoss? but only update on frame drawing instead of on every frame update?
|
||||||
|
|
||||||
updatePaths: ->
|
updatePaths: ->
|
||||||
|
|
|
@ -105,6 +105,14 @@ module.exports =
|
||||||
'playback:manually-scrubbed':
|
'playback:manually-scrubbed':
|
||||||
{} # TODO schema
|
{} # TODO schema
|
||||||
|
|
||||||
|
'playback:real-time-playback-started':
|
||||||
|
type: 'object'
|
||||||
|
additionalProperties: false
|
||||||
|
|
||||||
|
'playback:real-time-playback-ended':
|
||||||
|
type: 'object'
|
||||||
|
additionalProperties: false
|
||||||
|
|
||||||
'change:editor-config':
|
'change:editor-config':
|
||||||
{} # TODO schema
|
{} # TODO schema
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,28 @@ body.is-playing
|
||||||
margin: 0 auto
|
margin: 0 auto
|
||||||
@include user-select(none)
|
@include user-select(none)
|
||||||
|
|
||||||
|
&.real-time
|
||||||
|
// Hmm, somehow the #page-container is cutting us off by ~17px on the right, looks a bit off.
|
||||||
|
|
||||||
|
#canvas-wrapper
|
||||||
|
width: 100%
|
||||||
|
canvas#surface
|
||||||
|
margin: 0 auto
|
||||||
|
#control-bar-view, #playback-view
|
||||||
|
width: 100%
|
||||||
|
#code-area, #thang-hud, #goals-view
|
||||||
|
display: none
|
||||||
|
visibility: hidden
|
||||||
|
#gold-view
|
||||||
|
right: 1%
|
||||||
|
#control-bar-view .title
|
||||||
|
left: 0
|
||||||
|
width: 100%
|
||||||
|
text-align: center
|
||||||
|
|
||||||
|
.level-content
|
||||||
|
margin: 0px auto
|
||||||
|
|
||||||
.level-content
|
.level-content
|
||||||
position: relative
|
position: relative
|
||||||
|
|
||||||
|
@ -17,12 +39,13 @@ body.is-playing
|
||||||
width: 55%
|
width: 55%
|
||||||
position: relative
|
position: relative
|
||||||
overflow: hidden
|
overflow: hidden
|
||||||
|
@include transition(0.5s ease-out)
|
||||||
|
|
||||||
canvas#surface
|
canvas#surface
|
||||||
background-color: #333
|
background-color: #333
|
||||||
width: 100%
|
|
||||||
display: block
|
display: block
|
||||||
z-index: 1
|
z-index: 1
|
||||||
|
@include transition(0.5s ease-out)
|
||||||
|
|
||||||
min-width: 1024px
|
min-width: 1024px
|
||||||
position: relative
|
position: relative
|
||||||
|
|
|
@ -19,11 +19,11 @@
|
||||||
.title
|
.title
|
||||||
position: absolute
|
position: absolute
|
||||||
display: inline-block
|
display: inline-block
|
||||||
margin-left: 50%
|
|
||||||
right: 0
|
|
||||||
color: #BEBEBE
|
color: #BEBEBE
|
||||||
line-height: 15px
|
line-height: 15px
|
||||||
left: 0
|
left: 0
|
||||||
|
width: 100%
|
||||||
|
text-align: center
|
||||||
|
|
||||||
max-width: 1920px
|
max-width: 1920px
|
||||||
margin: 0 auto
|
margin: 0 auto
|
||||||
|
|
|
@ -24,6 +24,7 @@ module.exports = class LevelPlaybackView extends CocoView
|
||||||
'god:new-world-created': 'onNewWorld'
|
'god:new-world-created': 'onNewWorld'
|
||||||
'god:streaming-world-updated': 'onNewWorld' # Maybe?
|
'god:streaming-world-updated': 'onNewWorld' # Maybe?
|
||||||
'level-set-letterbox': 'onSetLetterbox'
|
'level-set-letterbox': 'onSetLetterbox'
|
||||||
|
'tome:cast-spells': 'onTomeCast'
|
||||||
|
|
||||||
events:
|
events:
|
||||||
'click #debug-toggle': 'onToggleDebug'
|
'click #debug-toggle': 'onToggleDebug'
|
||||||
|
@ -151,10 +152,19 @@ module.exports = class LevelPlaybackView extends CocoView
|
||||||
@$el.find('#music-button').toggleClass('music-on', me.get('music'))
|
@$el.find('#music-button').toggleClass('music-on', me.get('music'))
|
||||||
|
|
||||||
onSetLetterbox: (e) ->
|
onSetLetterbox: (e) ->
|
||||||
buttons = @$el.find '#play-button, .scrubber-handle'
|
@togglePlaybackControls !e.on
|
||||||
buttons.css 'visibility', if e.on then 'hidden' else 'visible'
|
|
||||||
@disabled = e.on
|
@disabled = e.on
|
||||||
|
|
||||||
|
togglePlaybackControls: (to) ->
|
||||||
|
buttons = @$el.find '#play-button, .scrubber-handle'
|
||||||
|
buttons.css 'visibility', if to then 'visible' else 'hidden'
|
||||||
|
|
||||||
|
onTomeCast: (e) ->
|
||||||
|
return unless e.realTime
|
||||||
|
@realTime = true
|
||||||
|
@togglePlaybackControls false
|
||||||
|
Backbone.Mediator.publish 'playback:real-time-playback-started', {}
|
||||||
|
|
||||||
onWindowResize: (s...) =>
|
onWindowResize: (s...) =>
|
||||||
@barWidth = $('.progress', @$el).width()
|
@barWidth = $('.progress', @$el).width()
|
||||||
|
|
||||||
|
@ -276,11 +286,16 @@ module.exports = class LevelPlaybackView extends CocoView
|
||||||
updateProgress: (progress, world) ->
|
updateProgress: (progress, world) ->
|
||||||
if world.frames.length isnt @lastLoadedFrameCount
|
if world.frames.length isnt @lastLoadedFrameCount
|
||||||
@updateBarWidth world.frames.length, world.maxTotalFrames, world.dt
|
@updateBarWidth world.frames.length, world.maxTotalFrames, world.dt
|
||||||
|
@worldCompletelyLoaded = world.frames.length is world.totalFrames
|
||||||
$('.scrubber .progress-bar', @$el).css('width', "#{progress * 100}%")
|
$('.scrubber .progress-bar', @$el).css('width', "#{progress * 100}%")
|
||||||
|
|
||||||
updatePlayButton: (progress) ->
|
updatePlayButton: (progress) ->
|
||||||
if progress >= 0.99 and @lastProgress < 0.99
|
if @worldCompletelyLoaded and progress >= 0.99 and @lastProgress < 0.99
|
||||||
$('#play-button').removeClass('playing').removeClass('paused').addClass('ended')
|
$('#play-button').removeClass('playing').removeClass('paused').addClass('ended')
|
||||||
|
if @realTime
|
||||||
|
@realTime = false
|
||||||
|
@togglePlaybackControls true
|
||||||
|
Backbone.Mediator.publish 'playback:real-time-playback-ended', {}
|
||||||
if progress < 0.99 and @lastProgress >= 0.99
|
if progress < 0.99 and @lastProgress >= 0.99
|
||||||
b = $('#play-button').removeClass('ended')
|
b = $('#play-button').removeClass('ended')
|
||||||
if @playing then b.addClass('playing') else b.addClass('paused')
|
if @playing then b.addClass('playing') else b.addClass('paused')
|
||||||
|
@ -326,7 +341,7 @@ module.exports = class LevelPlaybackView extends CocoView
|
||||||
return if @shouldIgnore()
|
return if @shouldIgnore()
|
||||||
Backbone.Mediator.publish 'level-set-time', ratio: ratio, scrubDuration: duration
|
Backbone.Mediator.publish 'level-set-time', ratio: ratio, scrubDuration: duration
|
||||||
|
|
||||||
shouldIgnore: -> return @disabled
|
shouldIgnore: -> return @disabled or @realTime
|
||||||
|
|
||||||
onTogglePlay: (e) ->
|
onTogglePlay: (e) ->
|
||||||
e?.preventDefault()
|
e?.preventDefault()
|
||||||
|
|
|
@ -64,6 +64,8 @@ module.exports = class PlayLevelView extends RootView
|
||||||
'level:set-team': 'setTeam'
|
'level:set-team': 'setTeam'
|
||||||
'level:started': 'onLevelStarted'
|
'level:started': 'onLevelStarted'
|
||||||
'level:loading-view-unveiled': 'onLoadingViewUnveiled'
|
'level:loading-view-unveiled': 'onLoadingViewUnveiled'
|
||||||
|
'playback:real-time-playback-started': 'onRealTimePlaybackStarted'
|
||||||
|
'playback:real-time-playback-ended': 'onRealTimePlaybackEnded'
|
||||||
|
|
||||||
events:
|
events:
|
||||||
'click #level-done-button': 'onDonePressed'
|
'click #level-done-button': 'onDonePressed'
|
||||||
|
@ -513,6 +515,15 @@ module.exports = class PlayLevelView extends RootView
|
||||||
continue unless sound = AudioPlayer.soundForDialogue message, thangType.get('soundTriggers')
|
continue unless sound = AudioPlayer.soundForDialogue message, thangType.get('soundTriggers')
|
||||||
AudioPlayer.preloadSoundReference sound
|
AudioPlayer.preloadSoundReference sound
|
||||||
|
|
||||||
|
# Real-time playback
|
||||||
|
onRealTimePlaybackStarted: (e) ->
|
||||||
|
@$el.addClass 'real-time'
|
||||||
|
@onWindowResize()
|
||||||
|
|
||||||
|
onRealTimePlaybackEnded: (e) ->
|
||||||
|
@$el.removeClass 'real-time'
|
||||||
|
@onWindowResize()
|
||||||
|
|
||||||
destroy: ->
|
destroy: ->
|
||||||
@levelLoader?.destroy()
|
@levelLoader?.destroy()
|
||||||
@surface?.destroy()
|
@surface?.destroy()
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue