From f79a2848fdb5012d3bce955f8546f9cd52a82a7c Mon Sep 17 00:00:00 2001 From: Nick Winter Date: Thu, 28 Aug 2014 14:00:54 -0700 Subject: [PATCH 1/4] Fixed #1038. Focus should now remain on code editor when after clicking on Surface. --- app/lib/surface/CoordinateDisplay.coffee | 1 + app/lib/surface/Surface.coffee | 2 ++ app/schemas/subscriptions/bus.coffee | 2 +- app/schemas/subscriptions/surface.coffee | 1 + app/views/editor/level/LevelSearchView.coffee | 2 +- app/views/play/SpectateView.coffee | 1 - 6 files changed, 6 insertions(+), 3 deletions(-) diff --git a/app/lib/surface/CoordinateDisplay.coffee b/app/lib/surface/CoordinateDisplay.coffee index 8d235cec9..e9928ed60 100644 --- a/app/lib/surface/CoordinateDisplay.coffee +++ b/app/lib/surface/CoordinateDisplay.coffee @@ -53,6 +53,7 @@ module.exports = class CoordinateDisplay extends createjs.Container wop = @camera.screenToWorld x: e.x, y: e.y wop.x = Math.round wop.x wop.y = Math.round wop.y + Backbone.Mediator.publish 'tome:focus-editor', {} Backbone.Mediator.publish 'surface:coordinate-selected', wop onZoomUpdated: (e) -> diff --git a/app/lib/surface/Surface.coffee b/app/lib/surface/Surface.coffee index dcd55cf4c..315a1a1d2 100644 --- a/app/lib/surface/Surface.coffee +++ b/app/lib/surface/Surface.coffee @@ -483,11 +483,13 @@ module.exports = Surface = class Surface extends CocoClass worldPos = @camera.screenToWorld x: e.stageX, y: e.stageY event = onBackground: onBackground, x: e.stageX, y: e.stageY, originalEvent: e, worldPos: worldPos Backbone.Mediator.publish 'surface:stage-mouse-down', event + Backbone.Mediator.publish 'tome:focus-editor', {} onMouseUp: (e) => return if @disabled onBackground = not @stage.hitTest e.stageX, e.stageY Backbone.Mediator.publish 'surface:stage-mouse-up', onBackground: onBackground, x: e.stageX, y: e.stageY, originalEvent: e + Backbone.Mediator.publish 'tome:focus-editor', {} onMouseWheel: (e) => # https://github.com/brandonaaron/jquery-mousewheel diff --git a/app/schemas/subscriptions/bus.coffee b/app/schemas/subscriptions/bus.coffee index f56c18170..53cc6c3f9 100644 --- a/app/schemas/subscriptions/bus.coffee +++ b/app/schemas/subscriptions/bus.coffee @@ -23,5 +23,5 @@ module.exports = bus: {$ref: 'bus'} 'bus:player-states-changed': c.object {title: 'Player state changes', description: 'State of the players has changed'}, - states: c.array {}, {type: 'object'} + states: {type: 'object', additionalProperties: {type: 'object'}} bus: {$ref: 'bus'} diff --git a/app/schemas/subscriptions/surface.coffee b/app/schemas/subscriptions/surface.coffee index 7bc8cea8b..2ed519135 100644 --- a/app/schemas/subscriptions/surface.coffee +++ b/app/schemas/subscriptions/surface.coffee @@ -66,6 +66,7 @@ module.exports = # /app/lib/surface 'surface:coordinate-selected': c.object {required: ['x', 'y']}, x: {type: 'number'} y: {type: 'number'} + z: {type: 'number'} 'surface:coordinates-shown': c.object {} diff --git a/app/views/editor/level/LevelSearchView.coffee b/app/views/editor/level/LevelSearchView.coffee index 3c8f4a727..d64a79cbe 100644 --- a/app/views/editor/level/LevelSearchView.coffee +++ b/app/views/editor/level/LevelSearchView.coffee @@ -6,7 +6,7 @@ module.exports = class LevelSearchView extends SearchView model: require 'models/Level' modelURL: '/db/level' tableTemplate: require 'templates/editor/level/table' - page: 'editor' + page: 'level' getRenderData: -> context = super() diff --git a/app/views/play/SpectateView.coffee b/app/views/play/SpectateView.coffee index 30fb92aca..8cd6a79cb 100644 --- a/app/views/play/SpectateView.coffee +++ b/app/views/play/SpectateView.coffee @@ -221,7 +221,6 @@ module.exports = class SpectateLevelView extends RootView else console.log 'World scripts don\'t exist!' nonVictoryPlaybackScripts = [] - console.log nonVictoryPlaybackScripts @scriptManager = new ScriptManager({scripts: nonVictoryPlaybackScripts, view:@, session: @session}) @scriptManager.loadFromSession() From 107a727d028aa188fe3da8cdcd9e6e2321b69ff1 Mon Sep 17 00:00:00 2001 From: Nick Winter Date: Thu, 28 Aug 2014 14:06:53 -0700 Subject: [PATCH 2/4] Fixed a bug in the WizardSettingsModal being destroyed and still trying to do things. --- app/views/modal/WizardSettingsModal.coffee | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/views/modal/WizardSettingsModal.coffee b/app/views/modal/WizardSettingsModal.coffee index 3e45663c0..8ef6db1d5 100644 --- a/app/views/modal/WizardSettingsModal.coffee +++ b/app/views/modal/WizardSettingsModal.coffee @@ -50,10 +50,11 @@ module.exports = class WizardSettingsModal extends ModalView res.error => errors = JSON.parse(res.responseText) console.warn 'Got errors saving user:', errors + return if @destroyed forms.applyErrorsToForm(@$el, errors) @disableModalInProgress(@$el) res.success (model, response, options) => - @hide() + @hide() unless @destroyed @enableModalInProgress(@$el) From 0fa5d264f4b739b291d58e4f4915a7176ceb11c3 Mon Sep 17 00:00:00 2001 From: Nick Winter Date: Thu, 28 Aug 2014 14:25:37 -0700 Subject: [PATCH 3/4] Fixed #1072 by raising selection state so that we can see it if we're selecting an obstacle. --- app/lib/surface/Dimmer.coffee | 1 - app/lib/surface/Mark.coffee | 8 +++++++- app/lib/surface/PlaybackOverScreen.coffee | 1 - app/lib/surface/PointChooser.coffee | 1 - app/lib/surface/SpriteBoss.coffee | 5 +++++ 5 files changed, 12 insertions(+), 4 deletions(-) diff --git a/app/lib/surface/Dimmer.coffee b/app/lib/surface/Dimmer.coffee index 2d902fb0f..cfac3ba15 100644 --- a/app/lib/surface/Dimmer.coffee +++ b/app/lib/surface/Dimmer.coffee @@ -26,7 +26,6 @@ module.exports = class Dimmer extends CocoClass build: -> @dimLayer = new createjs.Container() @dimLayer.mouseEnabled = @dimLayer.mouseChildren = false - @dimLayer.layerIndex = -10 @dimLayer.addChild @dimScreen = new createjs.Shape() @dimLayer.addChild @dimMask = new createjs.Shape() @dimScreen.graphics.beginFill('rgba(0,0,0,0.5)').rect 0, 0, @camera.canvasWidth, @camera.canvasHeight diff --git a/app/lib/surface/Mark.coffee b/app/lib/surface/Mark.coffee index b730cabd8..b9103bcce 100644 --- a/app/lib/surface/Mark.coffee +++ b/app/lib/surface/Mark.coffee @@ -45,6 +45,13 @@ module.exports = class Mark extends CocoClass @mark.visible = true @ + setLayer: (layer) -> + return if layer is @layer + wasOn = @on + @toggle false + @layer = layer + @toggle true if wasOn + setSprite: (sprite) -> return if sprite is @sprite @sprite = sprite @@ -131,7 +138,6 @@ module.exports = class Mark extends CocoClass @mark.graphics.endFill() @mark.regX = width / 2 @mark.regY = height / 2 - @mark.layerIndex = 10 @mark.cache -1, -1, width + 2, height + 2 # not actually faster than simple ellipse draw buildRadius: (range) -> diff --git a/app/lib/surface/PlaybackOverScreen.coffee b/app/lib/surface/PlaybackOverScreen.coffee index 9bf05fe7b..a50dadc78 100644 --- a/app/lib/surface/PlaybackOverScreen.coffee +++ b/app/lib/surface/PlaybackOverScreen.coffee @@ -15,7 +15,6 @@ module.exports = class PlaybackOverScreen extends CocoClass build: -> @dimLayer = new createjs.Container() @dimLayer.mouseEnabled = @dimLayer.mouseChildren = false - @dimLayer.layerIndex = -12 @dimLayer.addChild @dimScreen = new createjs.Shape() @dimScreen.graphics.beginFill('rgba(0,0,0,0.4)').rect 0, 0, @camera.canvasWidth, @camera.canvasHeight @dimLayer.cache 0, 0, @camera.canvasWidth, @camera.canvasHeight diff --git a/app/lib/surface/PointChooser.coffee b/app/lib/surface/PointChooser.coffee index 16bacd5c6..38e72231f 100644 --- a/app/lib/surface/PointChooser.coffee +++ b/app/lib/surface/PointChooser.coffee @@ -21,7 +21,6 @@ module.exports = class PointChooser extends CocoClass @shape.mouseEnabled = false @shape.graphics.setStrokeStyle(1, 'round').beginStroke('#000000').beginFill('#fedcba') @shape.graphics.drawCircle(0, 0, 4).endFill() - @shape.layerIndex = 100 onMouseDown: (e) => return unless key.shift diff --git a/app/lib/surface/SpriteBoss.coffee b/app/lib/surface/SpriteBoss.coffee index 511c2830a..361e17318 100644 --- a/app/lib/surface/SpriteBoss.coffee +++ b/app/lib/surface/SpriteBoss.coffee @@ -373,6 +373,11 @@ module.exports = class SpriteBoss extends CocoClass @updateTarget() return unless @selectionMark @selectedSprite = null if @selectedSprite and (@selectedSprite.destroyed or not @selectedSprite.thang) + # The selection mark should be on the ground layer, unless we're not a normal sprite (like a wall), in which case we'll place it higher so we can see it. + if @selectedSprite and @selectedSprite.imageObject.parent isnt @spriteLayers.Default + @selectionMark.setLayer @spriteLayers.Default + else if @selectedSprite + @selectionMark.setLayer @spriteLayers.Ground @selectionMark.toggle @selectedSprite? @selectionMark.setSprite @selectedSprite @selectionMark.update() From e67b4ae064bf70b5cb70608ece01f4ff010544c3 Mon Sep 17 00:00:00 2001 From: Nick Winter Date: Thu, 28 Aug 2014 17:08:05 -0700 Subject: [PATCH 4/4] Fixed #1090. --- app/lib/world/world_utils.coffee | 100 +++++++++--------- app/schemas/subscriptions/surface.coffee | 2 + app/views/play/level/LevelPlaybackView.coffee | 4 +- 3 files changed, 53 insertions(+), 53 deletions(-) diff --git a/app/lib/world/world_utils.coffee b/app/lib/world/world_utils.coffee index 1398c409b..67c1d406a 100644 --- a/app/lib/world/world_utils.coffee +++ b/app/lib/world/world_utils.coffee @@ -92,60 +92,58 @@ module.exports.consolidateThangs = consolidateThangs = (thangs) -> padding = 0 console.log 'got max width', width, 'height', height, 'left', left, 'bottom', bottom, 'of thangs', thangs.length, 'structural', structural.length if debug grid = new Grid structural, width, height, padding, left, bottom - console.log grid.toString() if debug - # Approach: start at bottom left. Go right, then up. At each occupied grid square, find the largest rectangle we can make starting at that corner, add a corresponding Thang to the grid, and unmark all occupied grid squares. - # Since it's not like we're going to do any of these: - # http://stackoverflow.com/questions/5919298/algorithm-for-finding-the-fewest-rectangles-to-cover-a-set-of-rectangles - # http://stackoverflow.com/questions/4701887/find-the-set-of-largest-contiguous-rectangles-to-cover-multiple-areas dissection = [] - for y in grid.columns bottom, height - for x in grid.rows left, width - continue unless grid.grid[y][x].length - rect = largestRectangle grid, y, x, false, debug - vertices = rect.vertices() - for y2 in [vertices[0].y ... vertices[1].y] # maybe ..? - for x2 in [vertices[0].x ... vertices[2].x] # maybe ..? - grid.grid[y2][x2] = [] - console.log grid.toString() if debug - thang = structural[dissection.length] # grab one we already know is configured properly - console.error 'Hmm, our dissection has more Thangs than the original structural Thangs?', dissection.length unless thang - thang.width = rect.width - thang.height = rect.height - thang.pos.x = rect.x - thang.pos.y = rect.y - thang.createBodyDef() - dissection.push thang + addStructuralThang = (rect) -> + thang = structural[dissection.length] # Grab one we already know is configured properly. + console.error 'Hmm, our dissection has more Thangs than the original structural Thangs?', dissection.length unless thang + thang.pos.x = rect.x + thang.pos.y = rect.y + thang.width = rect.width + thang.height = rect.height + thang.createBodyDef() + dissection.push thang + + dissectRectangles grid, addStructuralThang, false, debug + + # Now add the new structural thangs back to thangs and return the ones not in the dissection. console.log 'Turned', structural.length, 'structural Thangs into', dissection.length, 'dissecting Thangs.' thangs.push dissection... structural[dissection.length ... structural.length] -module.exports.largestRectangle = largestRectangle = (grid, bottomY, leftX, wantEmpty, debug) -> - # If wantEmpty, then we try to cover empty rectangles. - # Otherwise, we try to cover occupied rectangles. - coveredRows = [] - shortestCoveredRow = grid.width - leftX - for y in grid.columns bottomY, grid.height - coveredRow = 0 - for x in grid.rows leftX, leftX + shortestCoveredRow - if Boolean(grid.grid[y][x].length) isnt wantEmpty - ++coveredRow - else - break - break unless coveredRow - coveredRows.push coveredRow - shortestCoveredRow = Math.min(shortestCoveredRow, coveredRow) - console.log 'largestRectangle() for', bottomY, leftX, 'got coveredRows', coveredRows if debug - [maxArea, maxAreaRows, maxAreaRowLength, shortestRow] = [0, 0, 0, 0] - for rowLength, rowIndex in coveredRows - shortestRow ||= rowLength - area = rowLength * (rowIndex + 1) - if area > maxArea - maxAreaRows = rowIndex + 1 - maxAreaRowLength = shortestRow - maxArea = area - shortestRow = Math.min(rowLength, shortestRow) - console.log 'So largest rect has area', maxArea, 'with', maxAreaRows, 'rows of length', maxAreaRowLength if debug - rect = new Rectangle leftX + maxAreaRowLength / 2, bottomY + maxAreaRows / 2, maxAreaRowLength, maxAreaRows - console.log 'That corresponds to a rectangle', rect.toString() if debug - rect + +module.exports.dissectRectangles = dissectRectangles = (grid, rectangleCallback, wantEmpty, debug) -> + # Mark Maxham's fast sweeper approach: https://github.com/codecombat/codecombat/issues/1090 + console.log grid.toString() if debug + for x in grid.rows grid.left, grid.left + grid.width + y = grid.clampColumn grid.bottom + while y < grid.clampColumn grid.bottom + grid.height + y2 = y # Note our current y. + ++y2 until occ x, y2, grid, wantEmpty # Sweep through y to expand 1xN rect. + if y2 > y # If we get a hit, sweep X with that swath. + x2 = x + 1 + ++x2 until occCol x2, y, y2, grid, wantEmpty + w = x2 - x + h = y2 - y + rect = addRect grid, x, y, w, h, wantEmpty + rectangleCallback rect + console.log grid.toString() if debug + y = y2 + ++y + +occ = (x, y, grid, wantEmpty) -> + return true if y > grid.bottom + grid.height or x > grid.left + grid.width + console.error 'trying to check invalid coordinates', x, y, 'from grid', grid.bottom, grid.left, grid.width, grid.height unless grid.grid[y]?[x] + Boolean(grid.grid[y][x].length) is wantEmpty + +occCol = (x, y1, y2, grid, wantEmpty) -> + for j in [y1 ... y2] + if occ(x, j, grid, wantEmpty) + return true + false + +addRect = (grid, leftX, bottomY, width, height, wantEmpty) -> + for x in [leftX ... leftX + width] + for y in [bottomY ... bottomY + height] + grid.grid[y][x] = if wantEmpty then [true] else [] + new Rectangle leftX + width / 2, bottomY + height / 2, width, height diff --git a/app/schemas/subscriptions/surface.coffee b/app/schemas/subscriptions/surface.coffee index 2ed519135..73d7116bb 100644 --- a/app/schemas/subscriptions/surface.coffee +++ b/app/schemas/subscriptions/surface.coffee @@ -77,12 +77,14 @@ module.exports = # /app/lib/surface point: c.object {required: ['x', 'y']}, x: {type: 'number'} y: {type: 'number'} + z: {type: 'number'} 'surface:choose-region': c.object {required: ['points']}, points: c.array {minItems: 2, maxItems: 2}, c.object {required: ['x', 'y']}, x: {type: 'number'} y: {type: 'number'} + z: {type: 'number'} 'surface:new-thang-added': c.object {required: ['thang', 'sprite']}, thang: {type: 'object'} diff --git a/app/views/play/level/LevelPlaybackView.coffee b/app/views/play/level/LevelPlaybackView.coffee index cbe7e6633..8e7b299e9 100644 --- a/app/views/play/level/LevelPlaybackView.coffee +++ b/app/views/play/level/LevelPlaybackView.coffee @@ -234,7 +234,7 @@ module.exports = class LevelPlaybackView extends CocoView button.addClass(classes[2]) if e.volume >= 1.0 onScrub: (e, options) -> - e?.preventDefault() + e?.preventDefault?() options.scrubDuration = 500 Backbone.Mediator.publish('level:set-time', options) @@ -347,7 +347,7 @@ module.exports = class LevelPlaybackView extends CocoView shouldIgnore: -> return @disabled or @realTime onTogglePlay: (e) -> - e?.preventDefault() + e?.preventDefault?() return if @shouldIgnore() button = $('#play-button') willPlay = button.hasClass('paused') or button.hasClass('ended')