mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2025-05-02 08:53:38 -04:00
Added stop playback button and real-time countdown screen.
This commit is contained in:
parent
ea3ade5bbc
commit
e666ee1dac
12 changed files with 51 additions and 12 deletions
app
assets/javascripts/workers
lib
locale
schemas/subscriptions
styles/play
templates/play/level
views/play/level
|
@ -464,6 +464,11 @@ self.addFlagEvent = function addFlagEvent(flagEvent) {
|
||||||
self.world.addFlagEvent(flagEvent);
|
self.world.addFlagEvent(flagEvent);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
self.stopRealTimePlayback = function stopRealTimePlayback() {
|
||||||
|
if(!self.world) return;
|
||||||
|
self.world.realTime = false;
|
||||||
|
};
|
||||||
|
|
||||||
self.addEventListener('message', function(event) {
|
self.addEventListener('message', function(event) {
|
||||||
self[event.data.func](event.data.args);
|
self[event.data.func](event.data.args);
|
||||||
});
|
});
|
||||||
|
|
|
@ -14,6 +14,7 @@ module.exports = class Angel extends CocoClass
|
||||||
|
|
||||||
subscriptions:
|
subscriptions:
|
||||||
'level:flag-updated': 'onFlagEvent'
|
'level:flag-updated': 'onFlagEvent'
|
||||||
|
'playback:stop-real-time-playback': 'onStopRealTimePlayback'
|
||||||
|
|
||||||
constructor: (@shared) ->
|
constructor: (@shared) ->
|
||||||
super()
|
super()
|
||||||
|
@ -212,6 +213,10 @@ module.exports = class Angel extends CocoClass
|
||||||
return unless @running and @work.realTime
|
return unless @running and @work.realTime
|
||||||
@worker.postMessage func: 'addFlagEvent', args: e
|
@worker.postMessage func: 'addFlagEvent', args: e
|
||||||
|
|
||||||
|
onStopRealTimePlayback: (e) ->
|
||||||
|
return unless @running and @work.realTime
|
||||||
|
@work.realTime = false
|
||||||
|
@worker.postMessage func: 'stopRealTimePlayback'
|
||||||
|
|
||||||
#### Synchronous code for running worlds on main thread (profiling / IE9) ####
|
#### Synchronous code for running worlds on main thread (profiling / IE9) ####
|
||||||
simulateSync: (work) =>
|
simulateSync: (work) =>
|
||||||
|
|
|
@ -65,7 +65,7 @@ module.exports = class God extends CocoClass
|
||||||
isPreloading = angel.running and angel.work.preload and _.isEqual angel.work.userCodeMap, userCodeMap, (a, b) ->
|
isPreloading = angel.running and angel.work.preload and _.isEqual angel.work.userCodeMap, userCodeMap, (a, b) ->
|
||||||
return a.raw is b.raw if a?.raw? and b?.raw?
|
return a.raw is b.raw if a?.raw? and b?.raw?
|
||||||
undefined # Let default equality test suffice.
|
undefined # Let default equality test suffice.
|
||||||
if not hadPreloader and isPreloading
|
if not hadPreloader and isPreloading and not realTime
|
||||||
angel.finalizePreload()
|
angel.finalizePreload()
|
||||||
hadPreloader = true
|
hadPreloader = true
|
||||||
else if preload and angel.running and not angel.work.preload
|
else if preload and angel.running and not angel.work.preload
|
||||||
|
|
|
@ -8,6 +8,7 @@ CameraBorder = require './CameraBorder'
|
||||||
Layer = require './Layer'
|
Layer = require './Layer'
|
||||||
Letterbox = require './Letterbox'
|
Letterbox = require './Letterbox'
|
||||||
Dimmer = require './Dimmer'
|
Dimmer = require './Dimmer'
|
||||||
|
CountdownScreen = require './CountdownScreen'
|
||||||
PlaybackOverScreen = require './PlaybackOverScreen'
|
PlaybackOverScreen = require './PlaybackOverScreen'
|
||||||
DebugDisplay = require './DebugDisplay'
|
DebugDisplay = require './DebugDisplay'
|
||||||
CoordinateDisplay = require './CoordinateDisplay'
|
CoordinateDisplay = require './CoordinateDisplay'
|
||||||
|
@ -100,6 +101,7 @@ module.exports = Surface = class Surface extends CocoClass
|
||||||
@spriteBoss.destroy()
|
@spriteBoss.destroy()
|
||||||
@chooser?.destroy()
|
@chooser?.destroy()
|
||||||
@dimmer?.destroy()
|
@dimmer?.destroy()
|
||||||
|
@countdownScreen?.destroy()
|
||||||
@playbackOverScreen?.destroy()
|
@playbackOverScreen?.destroy()
|
||||||
@stage.clear()
|
@stage.clear()
|
||||||
@musicPlayer?.destroy()
|
@musicPlayer?.destroy()
|
||||||
|
@ -402,6 +404,7 @@ module.exports = Surface = class Surface extends CocoClass
|
||||||
@surfaceLayer.addChild @cameraBorder = new CameraBorder bounds: @camera.bounds
|
@surfaceLayer.addChild @cameraBorder = new CameraBorder bounds: @camera.bounds
|
||||||
@screenLayer.addChild new Letterbox canvasWidth: canvasWidth, canvasHeight: canvasHeight
|
@screenLayer.addChild new Letterbox canvasWidth: canvasWidth, canvasHeight: canvasHeight
|
||||||
@spriteBoss = new SpriteBoss camera: @camera, surfaceLayer: @surfaceLayer, surfaceTextLayer: @surfaceTextLayer, world: @world, thangTypes: @options.thangTypes, choosing: @options.choosing, navigateToSelection: @options.navigateToSelection, showInvisible: @options.showInvisible
|
@spriteBoss = new SpriteBoss camera: @camera, surfaceLayer: @surfaceLayer, 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
|
@playbackOverScreen ?= new PlaybackOverScreen camera: @camera, layer: @screenLayer
|
||||||
@stage.enableMouseOver(10)
|
@stage.enableMouseOver(10)
|
||||||
@stage.addEventListener 'stagemousemove', @onMouseMove
|
@stage.addEventListener 'stagemousemove', @onMouseMove
|
||||||
|
@ -414,6 +417,7 @@ module.exports = Surface = class Surface extends CocoClass
|
||||||
@onResize()
|
@onResize()
|
||||||
|
|
||||||
onResize: (e) =>
|
onResize: (e) =>
|
||||||
|
return if @destroyed
|
||||||
oldWidth = parseInt @canvas.attr('width'), 10
|
oldWidth = parseInt @canvas.attr('width'), 10
|
||||||
oldHeight = parseInt @canvas.attr('height'), 10
|
oldHeight = parseInt @canvas.attr('height'), 10
|
||||||
aspectRatio = oldWidth / oldHeight
|
aspectRatio = oldWidth / oldHeight
|
||||||
|
@ -630,6 +634,7 @@ module.exports = Surface = class Surface extends CocoClass
|
||||||
@realTime = true
|
@realTime = true
|
||||||
@onResize()
|
@onResize()
|
||||||
@spriteBoss.selfWizardSprite?.toggle false
|
@spriteBoss.selfWizardSprite?.toggle false
|
||||||
|
@playing = false # Will start when countdown is done.
|
||||||
|
|
||||||
onRealTimePlaybackEnded: (e) ->
|
onRealTimePlaybackEnded: (e) ->
|
||||||
@realTime = false
|
@realTime = false
|
||||||
|
|
|
@ -15,6 +15,7 @@ DESERIALIZATION_INTERVAL = 10
|
||||||
REAL_TIME_BUFFER_MIN = 2 * PROGRESS_UPDATE_INTERVAL
|
REAL_TIME_BUFFER_MIN = 2 * PROGRESS_UPDATE_INTERVAL
|
||||||
REAL_TIME_BUFFER_MAX = 3 * PROGRESS_UPDATE_INTERVAL
|
REAL_TIME_BUFFER_MAX = 3 * PROGRESS_UPDATE_INTERVAL
|
||||||
REAL_TIME_BUFFERED_WAIT_INTERVAL = 0.5 * PROGRESS_UPDATE_INTERVAL
|
REAL_TIME_BUFFERED_WAIT_INTERVAL = 0.5 * PROGRESS_UPDATE_INTERVAL
|
||||||
|
REAL_TIME_COUNTDOWN_DELAY = 3000 # match CountdownScreen
|
||||||
ITEM_ORIGINAL = '53e12043b82921000051cdf9'
|
ITEM_ORIGINAL = '53e12043b82921000051cdf9'
|
||||||
|
|
||||||
module.exports = class World
|
module.exports = class World
|
||||||
|
@ -93,12 +94,14 @@ module.exports = class World
|
||||||
loadFrames: (loadedCallback, errorCallback, loadProgressCallback, skipDeferredLoading, loadUntilFrame) ->
|
loadFrames: (loadedCallback, errorCallback, loadProgressCallback, skipDeferredLoading, loadUntilFrame) ->
|
||||||
return if @aborted
|
return if @aborted
|
||||||
console.log 'Warning: loadFrames called on empty World (no thangs).' unless @thangs.length
|
console.log 'Warning: loadFrames called on empty World (no thangs).' unless @thangs.length
|
||||||
|
continueLaterFn = =>
|
||||||
|
@loadFrames(loadedCallback, errorCallback, loadProgressCallback, skipDeferredLoading, loadUntilFrame) unless @destroyed
|
||||||
|
if @realTime and not @countdownFinished
|
||||||
|
return setTimeout @finishCountdown(continueLaterFn), REAL_TIME_COUNTDOWN_DELAY
|
||||||
t1 = now()
|
t1 = now()
|
||||||
@t0 ?= t1
|
@t0 ?= t1
|
||||||
@worldLoadStartTime ?= t1
|
@worldLoadStartTime ?= t1
|
||||||
@lastRealTimeUpdate ?= 0
|
@lastRealTimeUpdate ?= 0
|
||||||
continueLaterFn = =>
|
|
||||||
@loadFrames(loadedCallback, errorCallback, loadProgressCallback, skipDeferredLoading, loadUntilFrame) unless @destroyed
|
|
||||||
frameToLoadUntil = if loadUntilFrame then loadUntilFrame + 1 else @totalFrames # Might stop early if debugging.
|
frameToLoadUntil = if loadUntilFrame then loadUntilFrame + 1 else @totalFrames # Might stop early if debugging.
|
||||||
i = @frames.length
|
i = @frames.length
|
||||||
while i < frameToLoadUntil and i < @totalFrames
|
while i < frameToLoadUntil and i < @totalFrames
|
||||||
|
@ -123,6 +126,11 @@ module.exports = class World
|
||||||
loadProgressCallback? 1
|
loadProgressCallback? 1
|
||||||
loadedCallback()
|
loadedCallback()
|
||||||
|
|
||||||
|
finishCountdown: (continueLaterFn) -> =>
|
||||||
|
return if @destroyed
|
||||||
|
@countdownFinished = true
|
||||||
|
continueLaterFn()
|
||||||
|
|
||||||
shouldDelayRealTimeSimulation: (t) ->
|
shouldDelayRealTimeSimulation: (t) ->
|
||||||
return false unless @realTime
|
return false unless @realTime
|
||||||
timeSinceStart = t - @worldLoadStartTime
|
timeSinceStart = t - @worldLoadStartTime
|
||||||
|
|
|
@ -358,6 +358,7 @@
|
||||||
grid: "Grid"
|
grid: "Grid"
|
||||||
customize_wizard: "Customize Wizard"
|
customize_wizard: "Customize Wizard"
|
||||||
home: "Home"
|
home: "Home"
|
||||||
|
stop: "Stop"
|
||||||
game_menu: "Game Menu"
|
game_menu: "Game Menu"
|
||||||
guide: "Guide"
|
guide: "Guide"
|
||||||
restart: "Restart"
|
restart: "Restart"
|
||||||
|
|
|
@ -147,6 +147,10 @@ module.exports =
|
||||||
'playback:manually-scrubbed':
|
'playback:manually-scrubbed':
|
||||||
{} # TODO schema
|
{} # TODO schema
|
||||||
|
|
||||||
|
'playback:stop-real-time-playback':
|
||||||
|
type: 'object'
|
||||||
|
additionalProperties: false
|
||||||
|
|
||||||
'playback:real-time-playback-started':
|
'playback:real-time-playback-started':
|
||||||
type: 'object'
|
type: 'object'
|
||||||
additionalProperties: false
|
additionalProperties: false
|
||||||
|
|
|
@ -19,6 +19,10 @@ body.is-playing
|
||||||
margin: 0 auto
|
margin: 0 auto
|
||||||
#control-bar-view
|
#control-bar-view
|
||||||
width: 100%
|
width: 100%
|
||||||
|
button, h4
|
||||||
|
display: none
|
||||||
|
#stop-real-time-playback-button
|
||||||
|
display: block
|
||||||
#playback-view
|
#playback-view
|
||||||
$flags-width: 200px
|
$flags-width: 200px
|
||||||
width: 90%
|
width: 90%
|
||||||
|
|
|
@ -41,5 +41,5 @@
|
||||||
height: 24px
|
height: 24px
|
||||||
|
|
||||||
|
|
||||||
#level-done-button
|
#level-done-button, #stop-real-time-playback-button
|
||||||
display: none
|
display: none
|
||||||
|
|
|
@ -9,6 +9,9 @@ h4.title
|
||||||
| -
|
| -
|
||||||
a(href=editorLink, data-i18n="nav.editor", title="Open " + worldName + " in the Level Editor") Editor
|
a(href=editorLink, data-i18n="nav.editor", title="Open " + worldName + " in the Level Editor") Editor
|
||||||
|
|
||||||
|
|
||||||
|
button.btn.btn-xs.btn-warning.banner#stop-real-time-playback-button(title="Stop real-time playback", data-i18n="play_level.stop") Stop
|
||||||
|
|
||||||
button.btn.btn-xs.btn-inverse.banner#game-menu-button(title="Show game menu", data-i18n="play_level.game_menu") Game Menu
|
button.btn.btn-xs.btn-inverse.banner#game-menu-button(title="Show game menu", data-i18n="play_level.game_menu") Game Menu
|
||||||
|
|
||||||
button.btn.btn-xs.btn-success.banner#docs-button(title="Show level instructions", data-i18n="play_level.guide") Guide
|
button.btn.btn-xs.btn-success.banner#docs-button(title="Show level instructions", data-i18n="play_level.guide") Guide
|
||||||
|
|
|
@ -16,13 +16,13 @@ module.exports = class ControlBarView extends CocoView
|
||||||
window.tracker?.trackEvent 'Clicked Docs', level: @level.get('name'), label: @level.get('name')
|
window.tracker?.trackEvent 'Clicked Docs', level: @level.get('name'), label: @level.get('name')
|
||||||
@showGuideModal()
|
@showGuideModal()
|
||||||
|
|
||||||
'click #next-game-button': ->
|
'click #next-game-button': -> Backbone.Mediator.publish 'next-game-pressed', {}
|
||||||
Backbone.Mediator.publish 'next-game-pressed'
|
|
||||||
|
|
||||||
'click #game-menu-button': ->
|
'click #game-menu-button': 'showGameMenuModal'
|
||||||
@showGameMenuModal()
|
|
||||||
|
|
||||||
'click': -> Backbone.Mediator.publish 'tome:focus-editor'
|
'click #stop-real-time-playback-button': -> Backbone.Mediator.publish 'playback:stop-real-time-playback', {}
|
||||||
|
|
||||||
|
'click': -> Backbone.Mediator.publish 'tome:focus-editor', {}
|
||||||
|
|
||||||
constructor: (options) ->
|
constructor: (options) ->
|
||||||
@worldName = options.worldName
|
@worldName = options.worldName
|
||||||
|
|
|
@ -26,6 +26,7 @@ module.exports = class LevelPlaybackView extends CocoView
|
||||||
'level-set-letterbox': 'onSetLetterbox'
|
'level-set-letterbox': 'onSetLetterbox'
|
||||||
'tome:cast-spells': 'onTomeCast'
|
'tome:cast-spells': 'onTomeCast'
|
||||||
'playback:real-time-playback-ended': 'onRealTimePlaybackEnded'
|
'playback:real-time-playback-ended': 'onRealTimePlaybackEnded'
|
||||||
|
'playback:stop-real-time-playback': 'onStopRealTimePlayback'
|
||||||
|
|
||||||
events:
|
events:
|
||||||
'click #debug-toggle': 'onToggleDebug'
|
'click #debug-toggle': 'onToggleDebug'
|
||||||
|
@ -34,11 +35,11 @@ module.exports = class LevelPlaybackView extends CocoView
|
||||||
'click #edit-editor-config': 'onEditEditorConfig'
|
'click #edit-editor-config': 'onEditEditorConfig'
|
||||||
'click #view-keyboard-shortcuts': 'onViewKeyboardShortcuts'
|
'click #view-keyboard-shortcuts': 'onViewKeyboardShortcuts'
|
||||||
'click #music-button': 'onToggleMusic'
|
'click #music-button': 'onToggleMusic'
|
||||||
'click #zoom-in-button': -> Backbone.Mediator.publish('camera-zoom-in') unless @shouldIgnore()
|
'click #zoom-in-button': -> Backbone.Mediator.publish('camera-zoom-in', {}) unless @shouldIgnore()
|
||||||
'click #zoom-out-button': -> Backbone.Mediator.publish('camera-zoom-out') unless @shouldIgnore()
|
'click #zoom-out-button': -> Backbone.Mediator.publish('camera-zoom-out', {}) unless @shouldIgnore()
|
||||||
'click #volume-button': 'onToggleVolume'
|
'click #volume-button': 'onToggleVolume'
|
||||||
'click #play-button': 'onTogglePlay'
|
'click #play-button': 'onTogglePlay'
|
||||||
'click': -> Backbone.Mediator.publish 'tome:focus-editor' unless @realTime
|
'click': -> Backbone.Mediator.publish 'tome:focus-editor', {} unless @realTime
|
||||||
'mouseenter #timeProgress': 'onProgressEnter'
|
'mouseenter #timeProgress': 'onProgressEnter'
|
||||||
'mouseleave #timeProgress': 'onProgressLeave'
|
'mouseleave #timeProgress': 'onProgressLeave'
|
||||||
'mousemove #timeProgress': 'onProgressHover'
|
'mousemove #timeProgress': 'onProgressHover'
|
||||||
|
@ -308,6 +309,9 @@ module.exports = class LevelPlaybackView extends CocoView
|
||||||
@realTime = false
|
@realTime = false
|
||||||
@togglePlaybackControls true
|
@togglePlaybackControls true
|
||||||
|
|
||||||
|
onStopRealTimePlayback: (e) ->
|
||||||
|
Backbone.Mediator.publish 'playback:real-time-playback-ended', {}
|
||||||
|
|
||||||
onSetDebug: (e) ->
|
onSetDebug: (e) ->
|
||||||
flag = $('#debug-toggle i.icon-ok')
|
flag = $('#debug-toggle i.icon-ok')
|
||||||
flag.toggleClass 'invisible', not e.debug
|
flag.toggleClass 'invisible', not e.debug
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue