diff --git a/README.md b/README.md index 9ed94553b..192b6ff31 100644 --- a/README.md +++ b/README.md @@ -19,3 +19,9 @@ Whether you're novice or pro, the CodeCombat team is ready to help you implement ### [License](https://github.com/codecombat/codecombat/blob/master/LICENSE) [MIT](https://github.com/codecombat/codecombat/blob/master/LICENSE) for the code, and [CC-BY](http://codecombat.com/legal) for the art and music. Please also [sign the CodeCombat contributor license agreement](http://codecombat.com/cla) so we can accept your pull requests. It is easy. + +---------- + +[](http://codecombat.challengepost.com/?utm_source-github&utm_medium-oswidget&utm_campaign-codecombat) + +[](http://www.google-melange.com/gsoc/homepage/google/gsoc2014) \ No newline at end of file diff --git a/app/lib/surface/Surface.coffee b/app/lib/surface/Surface.coffee index 5d44faa62..7d9cf0c08 100644 --- a/app/lib/surface/Surface.coffee +++ b/app/lib/surface/Surface.coffee @@ -100,7 +100,7 @@ module.exports = Surface = class Surface extends CocoClass @stage.removeAllEventListeners() @stage.enableDOMEvents false @stage.enableMouseOver 0 - @playScrubbedSounds = null + @onFramesScrubbed = null @onMouseMove = null @onMouseDown = null @tick = null @@ -202,32 +202,33 @@ module.exports = Surface = class Surface extends CocoClass .get(@) .to({currentFrame:@scrubbingTo}, scrubDuration, createjs.Ease.sineInOut) .call(onTweenEnd) - t.addEventListener('change', @playScrubbedSounds) + t.addEventListener('change', @onFramesScrubbed) else @currentFrame = @scrubbingTo - @playScrubbedSounds() + @onFramesScrubbed() # For performance, don't play these for instant transitions. onTweenEnd() @updateState true @onFrameChanged() - playScrubbedSounds: (e) => - # gotta play all the sounds, even when scrubbing! - rising = @currentFrame > @lastFrame - actualCurrentFrame = @currentFrame - tempFrame = if rising then Math.ceil(@lastFrame) else Math.floor(@lastFrame) - while true # temporary fix to stop cacophony - break if rising and tempFrame > actualCurrentFrame - break if (not rising) and tempFrame < actualCurrentFrame - @currentFrame = tempFrame - frame = @world.getFrame(@getCurrentFrame()) - frame.restoreState() - for thangID, sprite of @spriteBoss.sprites - sprite.playSounds false, Math.max(0.05, Math.min(1, 1 / @scrubbingPlaybackSpeed)) - tempFrame += if rising then 1 else -1 - @currentFrame = actualCurrentFrame + onFramesScrubbed: (e) => + if e + # Gotta play all the sounds when scrubbing (but not when doing an immediate transition). + rising = @currentFrame > @lastFrame + actualCurrentFrame = @currentFrame + tempFrame = if rising then Math.ceil(@lastFrame) else Math.floor(@lastFrame) + while true # temporary fix to stop cacophony + break if rising and tempFrame > actualCurrentFrame + break if (not rising) and tempFrame < actualCurrentFrame + @currentFrame = tempFrame + frame = @world.getFrame(@getCurrentFrame()) + frame.restoreState() + for thangID, sprite of @spriteBoss.sprites + sprite.playSounds false, Math.max(0.05, Math.min(1, 1 / @scrubbingPlaybackSpeed)) + tempFrame += if rising then 1 else -1 + @currentFrame = actualCurrentFrame - # TODO: are these needed, or perhaps do they duplicate things? + @restoreWorldState() @spriteBoss.update true @onFrameChanged() @@ -325,7 +326,7 @@ module.exports = Surface = class Surface extends CocoClass onNewWorld: (event) -> return unless event.world.name is @world.name @casting = false - + # This has a tendency to break scripts that are waiting for playback to change when the level is loaded # so only run it after the first world is created. Backbone.Mediator.publish 'level-set-playing', { playing: @wasPlayingWhenCastingBegan } unless event.firstWorld @@ -493,14 +494,23 @@ module.exports = Surface = class Surface extends CocoClass tick: (e) => # seems to be a bug where only one object can register with the Ticker... oldFrame = @currentFrame + oldWorldFrame = Math.floor oldFrame while true Dropper.tick() @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 += @world.frameRate / @frameRate if frameAdvanced - @updateSpriteSounds() if frameAdvanced + newWorldFrame = Math.floor @currentFrame + worldFrameAdvanced = newWorldFrame isnt oldWorldFrame + if worldFrameAdvanced + # Only restore world state when it will correspond to an integer WorldFrame, not interpolated frame. + @restoreWorldState() + oldWorldFrame = newWorldFrame break unless Dropper.drop() + if frameAdvanced and not worldFrameAdvanced + # We didn't end the above loop on an integer frame, so do the world state update. + @restoreWorldState() # these are skipped for dropped frames @updateState @currentFrame isnt oldFrame @@ -513,7 +523,7 @@ module.exports = Surface = class Surface extends CocoClass Backbone.Mediator.publish('surface:mouse-' + (if mib then "over" else "out"), {}) @mouseInBounds = mib - updateSpriteSounds: -> + restoreWorldState: -> @world.getFrame(@getCurrentFrame()).restoreState() current = Math.max(0, Math.min(@currentFrame, @world.totalFrames - 1)) if current - Math.floor(current) > 0.01 @@ -523,7 +533,7 @@ module.exports = Surface = class Surface extends CocoClass @spriteBoss.updateSounds() updateState: (frameChanged) -> - # world state must have been restored in @updateSpriteSounds + # world state must have been restored in @restoreWorldState @camera.updateZoom() @spriteBoss.update frameChanged unless @casting @dimmer?.setSprites @spriteBoss.sprites diff --git a/app/views/kinds/SearchView.coffee b/app/views/kinds/SearchView.coffee index 304a9e958..5e150c43a 100644 --- a/app/views/kinds/SearchView.coffee +++ b/app/views/kinds/SearchView.coffee @@ -24,6 +24,7 @@ module.exports = class ThangTypeHomeView extends View 'click button.new-model-submit': 'makeNewModel' 'submit form': 'makeNewModel' 'shown.bs.modal #new-model-modal': 'focusOnName' + 'hidden.bs.modal #new-model-modal': 'onModalHidden' getRenderData: -> c = super() @@ -85,16 +86,21 @@ module.exports = class ThangTypeHomeView extends View res = model.save() return unless res - modal = @$el.find('.modal') + modal = @$el.find('#new-model-modal') forms.clearFormAlerts(modal) @showLoading(modal.find('.modal-body')) res.error => @hideLoading() forms.applyErrorsToForm(modal, JSON.parse(res.responseText)) + that = @ res.success -> - modal.modal('hide') - base = document.location.pathname[1..] + '/' - app.router.navigate(base + (model.get('slug') or model.id), {trigger:true}) + that.model = model + modal.modal('hide') + + onModalHidden: -> + # Can only redirect after the modal hidden event has triggered + base = document.location.pathname[1..] + '/' + app.router.navigate(base + (@model.get('slug') or @model.id), {trigger:true}) focusOnName: -> @$el.find('#name').focus() diff --git a/app/views/play/level/hud_view.coffee b/app/views/play/level/hud_view.coffee index cc62fb047..bb49e2a70 100644 --- a/app/views/play/level/hud_view.coffee +++ b/app/views/play/level/hud_view.coffee @@ -145,7 +145,9 @@ module.exports = class HUDView extends View createActions: -> actions = @$el.find('.thang-actions tbody').empty() - return unless @thang.world and not _.isEmpty @thang.actions + showActions = @thang.world and not _.isEmpty(@thang.actions) and 'action' in @thang.hudProperties ? [] + @$el.find('.thang-actions').toggle showActions + return unless showActions @buildActionTimespans() for actionName, action of @thang.actions actions.append @createActionElement(actionName) diff --git a/app/views/play/level/tome/spell_palette_view.coffee b/app/views/play/level/tome/spell_palette_view.coffee index e045d1842..f4c708a18 100644 --- a/app/views/play/level/tome/spell_palette_view.coffee +++ b/app/views/play/level/tome/spell_palette_view.coffee @@ -44,15 +44,16 @@ module.exports = class SpellPaletteView extends View allDocs = {} for lc in lcs for doc in (lc.get('propertyDocumentation') ? []) - allDocs[doc.name] ?= [] - allDocs[doc.name].push doc + allDocs['__' + doc.name] ?= [] + allDocs['__' + doc.name].push doc if doc.type is 'snippet' then doc.owner = 'snippets' - #allDocs[doc.name] = doc for doc in (lc.get('propertyDocumentation') ? []) for lc in lcs propStorage = 'this': 'programmableProperties' more: 'moreProgrammableProperties' Math: 'programmableMathProperties' + Array: 'programmableArrayProperties' + String: 'programmableStringProperties' Vector: 'programmableVectorProperties' snippets: 'programmableSnippets' count = 0 @@ -66,10 +67,10 @@ module.exports = class SpellPaletteView extends View @entries = [] for owner, props of propGroups for prop in props - doc = _.find (allDocs[prop] ? []), (doc) -> + doc = _.find (allDocs['__' + prop] ? []), (doc) -> return true if doc.owner is owner return (owner is 'this' or owner is 'more') and (not doc.owner? or doc.owner is 'this') - console.log 'could not find doc for', prop, 'from', allDocs[prop], 'for', owner, 'of', propGroups unless doc + console.log 'could not find doc for', prop, 'from', allDocs['__' + prop], 'for', owner, 'of', propGroups unless doc doc ?= prop @entries.push @addEntry(doc, shortenize, tabbify, owner is 'snippets') groupForEntry = (entry) -> diff --git a/server/queues/scoring.coffee b/server/queues/scoring.coffee index 40a6b2f21..08f0794d0 100644 --- a/server/queues/scoring.coffee +++ b/server/queues/scoring.coffee @@ -181,7 +181,8 @@ findNearestBetterSessionID = (sessionTotalScore, opponentSessionTotalScore, oppo $gt:opponentSessionTotalScore + 0.5 _id: $ne: opponentSessionID - levelID: "project-dota" + "level.original": "52d97ecd32362bc86e004e87" + "level.majorVersion": 0 submitted: true submittedCode: $exists: true @@ -280,7 +281,8 @@ updateSessionToSubmit = (sessionToUpdate, callback) -> fetchInitialSessionsToRankAgainst = (opposingTeam, callback) -> console.log "Fetching sessions to rank against for opposing team #{opposingTeam}" findParameters = - levelID: "project-dota" + "level.original": "52d97ecd32362bc86e004e87" + "level.majorVersion": 0 submitted: true submittedCode: $exists: true