diff --git a/app/lib/God.coffee b/app/lib/God.coffee index 4d3a195d8..730c034a0 100644 --- a/app/lib/God.coffee +++ b/app/lib/God.coffee @@ -210,8 +210,11 @@ class Angel @purgatoryTimer = null if @worker worker = @worker - _.defer -> worker.terminate() - @worker.removeEventListener 'message', @onWorkerMessage + onWorkerMessage = @onWorkerMessage + _.delay -> + worker.terminate() + worker.removeEventListener 'message', onWorkerMessage + , 1000 @worker = null @ diff --git a/app/lib/LevelLoader.coffee b/app/lib/LevelLoader.coffee index dfae34788..aba6fae47 100644 --- a/app/lib/LevelLoader.coffee +++ b/app/lib/LevelLoader.coffee @@ -98,7 +98,7 @@ module.exports = class LevelLoader extends CocoClass onSupermodelError: -> msg = $.i18n.t('play_level.level_load_error', defaultValue: "Level could not be loaded.") - @$el.html('<div class="alert">' + msg + '</div>') + $('body').append('<div class="alert">' + msg + '</div>') onSupermodelLoadedOne: (e) -> @update() diff --git a/app/lib/surface/CocoSprite.coffee b/app/lib/surface/CocoSprite.coffee index c843d3f00..6246619db 100644 --- a/app/lib/surface/CocoSprite.coffee +++ b/app/lib/surface/CocoSprite.coffee @@ -140,8 +140,8 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass @show() return @updateActionDirection() unless action.animation or action.container m = if action.container then "gotoAndStop" else "gotoAndPlay" - @imageObject[m] action.name @imageObject.framerate = action.framerate or 20 + @imageObject[m] action.name reg = @getOffset 'registration' @imageObject.regX = -reg.x @imageObject.regY = -reg.y diff --git a/app/lib/surface/Surface.coffee b/app/lib/surface/Surface.coffee index e503250d3..b0b1ee423 100644 --- a/app/lib/surface/Surface.coffee +++ b/app/lib/surface/Surface.coffee @@ -36,6 +36,7 @@ module.exports = Surface = class Surface extends CocoClass worldLoaded: false scrubbing: false debug: false + frameRate: 60 defaults: wizards: true @@ -190,7 +191,7 @@ module.exports = Surface = class Surface extends CocoClass createjs.Tween.removeTweens(@) @currentFrame = @scrubbingTo - @scrubbingTo = parseInt(progress * @world.totalFrames) + @scrubbingTo = Math.floor(progress * @world.totalFrames) @scrubbingPlaybackSpeed = Math.sqrt(Math.abs(@scrubbingTo - @currentFrame) * @world.dt / (scrubDuration or 0.5)) if scrubDuration t = createjs.Tween @@ -227,7 +228,7 @@ module.exports = Surface = class Surface extends CocoClass @onFrameChanged() getCurrentFrame: -> - return Math.max(0, Math.min(parseInt(@currentFrame), @world.totalFrames - 1)) + return Math.max(0, Math.min(Math.floor(@currentFrame), @world.totalFrames - 1)) getProgress: -> @currentFrame / @world.totalFrames @@ -344,8 +345,7 @@ module.exports = Surface = class Surface extends CocoClass @stage.addEventListener 'stagemousedown', @onMouseDown @canvas.on 'mousewheel', @onMouseWheel @hookUpChooseControls() if @options.choosing - console.log "Setting fps", @world.frameRate unless @world.frameRate is 30 - createjs.Ticker.setFPS @world.frameRate + createjs.Ticker.setFPS @frameRate showLevel: -> return if @dead @@ -467,16 +467,16 @@ module.exports = Surface = class Surface extends CocoClass @trailmaster.tick() if @trailmaster # Skip some frame updates unless we're playing and not at end (or we haven't drawn much yet) frameAdvanced = (@playing and @currentFrame < @world.totalFrames) or @totalFramesDrawn < 2 - ++@currentFrame if frameAdvanced + @currentFrame += @world.frameRate / @frameRate if frameAdvanced @updateSpriteSounds() if frameAdvanced break unless Dropper.drop() # these are skipped for dropped frames @updateState @currentFrame isnt oldFrame - @drawCurrentFrame() + @drawCurrentFrame e @onFrameChanged() - @updatePaths() if (@totalFramesDrawn % 2) is 0 or createjs.Ticker.getMeasuredFPS() > createjs.Ticker.getFPS() - 5 - Backbone.Mediator.publish('surface:ticked', {dt: @world.dt}) + @updatePaths() if (@totalFramesDrawn % 4) is 0 or createjs.Ticker.getMeasuredFPS() > createjs.Ticker.getFPS() - 5 + Backbone.Mediator.publish('surface:ticked', {dt: 1 / @frameRate}) mib = @stage.mouseInBounds if @mouseInBounds isnt mib Backbone.Mediator.publish('surface:mouse-' + (if mib then "over" else "out"), {}) @@ -484,6 +484,11 @@ module.exports = Surface = class Surface extends CocoClass updateSpriteSounds: -> @world.getFrame(@getCurrentFrame()).restoreState() + current = Math.max(0, Math.min(@currentFrame, @world.totalFrames - 1)) + if current - Math.floor(current) > 0.01 + next = Math.ceil current + ratio = current % 1 + @world.frames[next].restorePartialState ratio if next > 1 @spriteBoss.updateSounds() updateState: (frameChanged) -> @@ -492,9 +497,9 @@ module.exports = Surface = class Surface extends CocoClass @spriteBoss.update frameChanged @dimmer?.setSprites @spriteBoss.sprites - drawCurrentFrame: -> + drawCurrentFrame: (e) -> ++@totalFramesDrawn - @stage.update() + @stage.update e # paths - TODO: move to SpriteBoss? but only update on frame drawing instead of on every frame update? diff --git a/app/lib/world/thang_state.coffee b/app/lib/world/thang_state.coffee index a6ca7546c..964f25eda 100644 --- a/app/lib/world/thang_state.coffee +++ b/app/lib/world/thang_state.coffee @@ -67,7 +67,7 @@ module.exports = class ThangState restore: -> # Restore trackedProperties' values to @thang, retrieving them from @trackedPropertyValues if needed. Optimize it. - return @ if @thang._state is @ + return @ if @thang._state is @ and not @thang.partialState unless @hasRestored # Restoring in a deserialized World for first time props = [] for prop, propIndex in @trackedPropertyKeys @@ -81,6 +81,26 @@ module.exports = class ThangState else # Restoring later times for prop, propIndex in @trackedPropertyKeys @thang[prop] = @props[propIndex] + @thang.partialState = false + @ + + restorePartial: (ratio) -> + inverse = 1 - ratio + for prop, propIndex in @trackedPropertyKeys when prop is "pos" or prop is "rotation" + if @hasRestored + value = @props[propIndex] + else + type = @trackedPropertyTypes[propIndex] + storage = @trackedPropertyValues[propIndex] + value = @getStoredProp propIndex, type, storage + if prop is "pos" + @thang.pos = @thang.pos.copy() + @thang.pos.x = inverse * @thang.pos.x + ratio * value.x + @thang.pos.y = inverse * @thang.pos.y + ratio * value.y + @thang.pos.z = inverse * @thang.pos.z + ratio * value.z + else if prop is "rotation" + @thang.rotation = inverse * @thang.rotation + ratio * value + @thang.partialState = true @ serialize: (frameIndex, trackedPropertyIndices, trackedPropertyTypes, trackedPropertyValues, specialValuesToKeys, specialKeysToValues) -> diff --git a/app/lib/world/world_frame.coffee b/app/lib/world/world_frame.coffee index 997cf548f..fcac050a8 100644 --- a/app/lib/world/world_frame.coffee +++ b/app/lib/world/world_frame.coffee @@ -25,6 +25,9 @@ module.exports = class WorldFrame #console.log "Frame", @time, "restoring state for", thang.id, "and saying it don't exist" thang.exists = false + restorePartialState: (ratio) -> + thangState.restorePartial ratio for thangID, thangState of @thangStateMap + restoreStateForThang: (thang) -> thangState = @thangStateMap[thang.id] if not thangState