Press Ctrl+Shift+Enter to cast in real-time, and click on the ground to use your wizard as a flag. OMG, it not only works, but it is really fun!

This commit is contained in:
Nick Winter 2014-08-22 21:35:08 -07:00
parent 6fee881efd
commit 9c0869e566
9 changed files with 63 additions and 98 deletions

View file

@ -346,6 +346,7 @@ self.runWorld = function runWorld(args) {
self.world.loadFromLevel(args.level, true);
self.world.preloading = args.preload;
self.world.headless = args.headless;
self.world.realTime = args.realTime;
self.goalManager = new GoalManager(self.world);
self.goalManager.setGoals(args.goals);
self.goalManager.setCode(args.userCodeMap);
@ -458,6 +459,11 @@ self.finalizePreload = function finalizePreload() {
self.world.finalizePreload(self.onWorldLoaded);
};
self.updateFlags = function updateFlags(flags) {
if(!self.world || self.world.framesSerializedSoFar == self.world.frames.length) return;
self.world.updateFlags(flags);
};
self.addEventListener('message', function(event) {
self[event.data.func](event.data.args);
});

View file

@ -12,6 +12,9 @@ module.exports = class Angel extends CocoClass
infiniteLoopTimeoutDuration: 7500 # wait this long for a response when checking
abortTimeoutDuration: 500 # give in-process or dying workers this long to give up
subscriptions:
'self-wizard:target-changed': 'onSelfWizardTargetChanged'
constructor: (@shared) ->
super()
@say 'Got my wings.'
@ -205,6 +208,11 @@ module.exports = class Angel extends CocoClass
@worker.addEventListener 'message', @onWorkerMessage
@worker.creationTime = new Date()
onSelfWizardTargetChanged: (e) ->
return unless @running and @work.realTime
targetPos = e.sender.targetPos
@worker.postMessage func: 'updateFlags', args: [{type: 'wizard', targetPos: targetPos}]
#### Synchronous code for running worlds on main thread (profiling / IE9) ####
simulateSync: (work) =>

View file

@ -53,9 +53,9 @@ module.exports = class God extends CocoClass
setWorldClassMap: (worldClassMap) -> @angelsShare.worldClassMap = worldClassMap
onTomeCast: (e) ->
@createWorld e.spells, e.preload
@createWorld e.spells, e.preload, e.realTime
createWorld: (spells, preload=false) ->
createWorld: (spells, preload=false, realTime=false) ->
console.log "#{@nick}: Let there be light upon #{@level.name}! (preload: #{preload})"
userCodeMap = @getUserCodeMap spells
@ -84,6 +84,7 @@ module.exports = class God extends CocoClass
headless: @angelsShare.headless
preload: preload
synchronous: not Worker? # Profiling world simulation is easier on main thread, or we are IE9.
realTime: realTime
angel.workIfIdle() for angel in @angelsShare.angels
getUserCodeMap: (spells) ->

View file

@ -197,6 +197,7 @@ module.exports = class LevelBus extends Bus
onNewGoalStates: ({goalStates})->
state = @session.get 'state'
unless utils.kindaEqual state.goalStates, goalStates # Only save when goals really change
return console.error("Somehow trying to save null goal states!", goalStates) if _.find(goalStates, (gs) -> not gs.status)
state.goalStates = goalStates
@session.set 'state', state
@changedSessionProperties.state = true

View file

@ -1,81 +0,0 @@
CocoClass = require 'lib/CocoClass'
module.exports = class CastingScreen extends CocoClass
subscriptions:
'tome:cast-spells': 'onCastingBegins'
'god:new-world-created': 'onCastingEnds'
'god:world-load-progress-changed': 'onWorldLoadProgressChanged'
constructor: (options) ->
super()
options ?= {}
@camera = options.camera
@layer = options.layer
console.error @toString(), 'needs a camera.' unless @camera
console.error @toString(), 'needs a layer.' unless @layer
@build()
onCastingBegins: (e) -> @show() unless e.preload
onCastingEnds: (e) -> @hide()
toString: -> '<CastingScreen>'
build: ->
@dimLayer = new createjs.Container()
@dimLayer.mouseEnabled = @dimLayer.mouseChildren = false
@dimLayer.layerIndex = -11
@dimLayer.addChild @dimScreen = new createjs.Shape()
@dimScreen.graphics.beginFill('rgba(0,0,0,0.5)').rect 0, 0, @camera.canvasWidth, @camera.canvasHeight
@dimLayer.alpha = 0
@layer.addChild @dimLayer
@dimLayer.addChild @makeProgressBar()
@dimLayer.addChild @makeCastingText()
onWorldLoadProgressChanged: (e) ->
if new Date().getTime() - @t0 > 500
createjs.Tween.removeTweens @progressBar
createjs.Tween.get(@progressBar).to({scaleX: e.progress}, 200)
makeProgressBar: ->
BAR_PIXEL_HEIGHT = 3
BAR_PCT_WIDTH = .75
pixelWidth = parseInt(@camera.canvasWidth * BAR_PCT_WIDTH)
pixelMargin = (@camera.canvasWidth - (@camera.canvasWidth * BAR_PCT_WIDTH)) / 2
barY = 3 * (@camera.canvasHeight / 5)
g = new createjs.Graphics()
g.beginFill(createjs.Graphics.getRGB(255, 255, 255))
g.drawRoundRect(0, 0, pixelWidth, BAR_PIXEL_HEIGHT, 3)
@progressBar = new createjs.Shape(g)
@progressBar.x = pixelMargin
@progressBar.y = barY
@progressBar.scaleX = 0
@dimLayer.addChild(@progressBar)
makeCastingText: ->
size = @camera.canvasHeight / 15
text = new createjs.Text('Casting', "#{size}px cursive", '#aaaaaa')
text.regX = text.getMeasuredWidth() / 2
text.regY = text.getMeasuredHeight() / 2
text.x = @camera.canvasWidth / 2
text.y = @camera.canvasHeight / 2
@text = text
return text
show: ->
return if @showing
@showing = true
@t0 = new Date().getTime()
@progressBar.scaleX = 0
@dimLayer.alpha = 0
createjs.Tween.removeTweens @dimLayer
createjs.Tween.get(@dimLayer).to({alpha: 1}, 500)
hide: ->
return unless @showing
@showing = false
createjs.Tween.removeTweens @progressBar
createjs.Tween.removeTweens @dimLayer
createjs.Tween.get(@dimLayer).to({alpha: 0}, 500)

View file

@ -8,7 +8,6 @@ CameraBorder = require './CameraBorder'
Layer = require './Layer'
Letterbox = require './Letterbox'
Dimmer = require './Dimmer'
CastingScreen = require './CastingScreen'
PlaybackOverScreen = require './PlaybackOverScreen'
DebugDisplay = require './DebugDisplay'
CoordinateDisplay = require './CoordinateDisplay'
@ -97,7 +96,6 @@ module.exports = Surface = class Surface extends CocoClass
@spriteBoss.destroy()
@chooser?.destroy()
@dimmer?.destroy()
@castingScreen?.destroy()
@playbackOverScreen?.destroy()
@stage.clear()
@musicPlayer?.destroy()
@ -403,7 +401,6 @@ module.exports = Surface = class Surface extends CocoClass
@surfaceLayer.addChild @cameraBorder = new CameraBorder bounds: @camera.bounds
@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
#@castingScreen ?= new CastingScreen camera: @camera, layer: @screenLayer # STREAM: Not needed with world streaming.
@playbackOverScreen ?= new PlaybackOverScreen camera: @camera, layer: @screenLayer
@stage.enableMouseOver(10)
@stage.addEventListener 'stagemousemove', @onMouseMove

View file

@ -12,6 +12,8 @@ Component = require 'lib/world/component'
System = require 'lib/world/system'
PROGRESS_UPDATE_INTERVAL = 100
DESERIALIZATION_INTERVAL = 10
REAL_TIME_BUFFER_MIN = 2 * PROGRESS_UPDATE_INTERVAL
REAL_TIME_BUFFER_MAX = 5 * PROGRESS_UPDATE_INTERVAL
ITEM_ORIGINAL = '53e12043b82921000051cdf9'
module.exports = class World
@ -89,6 +91,8 @@ module.exports = class World
console.log 'Warning: loadFrames called on empty World (no thangs).'
t1 = now()
@t0 ?= t1
@worldLoadStartTime ?= t1
@lastRealTimeUpdate ?= 0
if loadUntilFrame
frameToLoadUntil = loadUntilFrame + 1
else
@ -112,8 +116,16 @@ module.exports = class World
return unless errorCallback error # errorCallback tells us whether the error is recoverable
@unhandledRuntimeErrors = []
t2 = now()
if t2 - t1 > PROGRESS_UPDATE_INTERVAL
loadProgressCallback? i / @totalFrames unless @preloading
if @realTime
shouldUpdateProgress = @shouldUpdateRealTimePlayback t2
shouldDelayRealTimeSimulation = not shouldUpdateProgress and @shouldDelayRealTimeSimulation t2
else
shouldUpdateProgress = t2 - t1 > PROGRESS_UPDATE_INTERVAL
shouldDelayRealTimeSimulation = false
if shouldUpdateProgress or shouldDelayRealTimeSimulation
if shouldUpdateProgress
@lastRealTimeUpdate = i * @dt if @realTime
loadProgressCallback? i / @totalFrames unless @preloading
t1 = t2
if t2 - @t0 > 1000
console.log ' Loaded', i, 'of', @totalFrames, '(+' + (t2 - @t0).toFixed(0) + 'ms)'
@ -127,7 +139,8 @@ module.exports = class World
if skipDeferredLoading
continueFn()
else
setTimeout(continueFn, 0)
delay = if shouldDelayRealTimeSimulation then PROGRESS_UPDATE_INTERVAL else 0
setTimeout(continueFn, delay)
return
unless @debugging
@ended = true
@ -136,6 +149,19 @@ module.exports = class World
loadProgressCallback? 1
loadedCallback()
shouldDelayRealTimeSimulation: (t) ->
return false unless @realTime
timeSinceStart = t - @worldLoadStartTime
timeLoaded = @frames.length * @dt * 1000
timeBuffered = timeLoaded - timeSinceStart
timeBuffered > REAL_TIME_BUFFER_MAX
shouldUpdateRealTimePlayback: (t) ->
return false unless @realTime
timeSinceStart = t - @worldLoadStartTime
remainingBuffer = @lastRealTimeUpdate * 1000 - timeSinceStart
remainingBuffer < REAL_TIME_BUFFER_MIN
finalizePreload: (loadedCallback) ->
@preloading = false
loadedCallback() if @ended
@ -143,6 +169,9 @@ module.exports = class World
abort: ->
@aborted = true
updateFlags: (@flags) ->
console.log "updated flags", @flags
loadFromLevel: (level, willSimulate=true) ->
@levelComponents = level.levelComponents
@thangTypes = level.thangTypes

View file

@ -114,6 +114,10 @@ module.exports = class SpellView extends CocoView
name: 'run-code'
bindKey: {win: 'Shift-Enter|Ctrl-Enter', mac: 'Shift-Enter|Command-Enter|Ctrl-Enter'}
exec: -> Backbone.Mediator.publish 'tome:manual-cast', {}
addCommand
name: 'run-code-real-time'
bindKey: {win: 'Ctrl-Shift-Enter', mac: 'Command-Shift-Enter|Ctrl-Shift-Enter'}
exec: -> Backbone.Mediator.publish 'tome:manual-cast', {realTime: true}
addCommand
name: 'no-op'
bindKey: {win: 'Ctrl-S', mac: 'Command-S|Ctrl-S'}
@ -269,8 +273,8 @@ module.exports = class SpellView extends CocoView
# @addZatannaSnippets()
@highlightCurrentLine()
cast: (preload=false) ->
Backbone.Mediator.publish 'tome:cast-spell', spell: @spell, thang: @thang, preload: preload
cast: (preload=false, realTime=false) ->
Backbone.Mediator.publish 'tome:cast-spell', spell: @spell, thang: @thang, preload: preload, realTime: realTime
notifySpellChanged: =>
Backbone.Mediator.publish 'tome:spell-changed', spell: @spell
@ -285,7 +289,7 @@ module.exports = class SpellView extends CocoView
onManualCast: (e) ->
cast = @$el.parent().length
@recompile cast
@recompile cast, e.realTime
@focus() if cast
onCodeReload: (e) ->
@ -299,12 +303,12 @@ module.exports = class SpellView extends CocoView
recompileIfNeeded: =>
@recompile() if @recompileNeeded
recompile: (cast=true) ->
recompile: (cast=true, realTime=false) ->
@setRecompileNeeded false
return if @spell.source is @getSource()
@spell.transpile @getSource()
@updateAether true, false
@cast() if cast
@cast(false, realTime) if cast
@notifySpellChanged()
updateACEText: (source) ->
@ -472,7 +476,7 @@ module.exports = class SpellView extends CocoView
onSessionWillSave: (e) ->
return unless @spellHasChanged
setTimeout(=>
unless @spellHasChanged
unless @destroyed or @spellHasChanged
@$el.find('.save-status').finish().show().fadeOut(2000)
, 1000)
@spellHasChanged = false

View file

@ -152,10 +152,10 @@ module.exports = class TomeView extends CocoView
onCastSpell: (e) ->
# A single spell is cast.
# Hmm; do we need to make sure other spells are all cast here?
@cast e?.preload
@cast e?.preload, e?.realTime
cast: (preload=false) ->
Backbone.Mediator.publish 'tome:cast-spells', spells: @spells, preload: preload
cast: (preload=false, realTime=false) ->
Backbone.Mediator.publish 'tome:cast-spells', spells: @spells, preload: preload, realTime: realTime
onToggleSpellList: (e) ->
@spellList.rerenderEntries()