mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2025-02-17 00:40:56 -05:00
Added keyboard shortcuts to move, resize, minor-rotate, and toggle collision for Thangs in the level editor. Fixed some issues with stretchy Thangs and collision shapes not updating. Fixed #1699. Fixed #57. Colored collision overlays according to collision categories.
This commit is contained in:
parent
a52bf08792
commit
e4c6d07a4a
5 changed files with 88 additions and 29 deletions
|
@ -287,21 +287,21 @@ module.exports = Lank = class Lank extends CocoClass
|
||||||
# Let the pending flags know we're here (but not this call stack, they need to delete themselves, and we may be iterating sprites).
|
# Let the pending flags know we're here (but not this call stack, they need to delete themselves, and we may be iterating sprites).
|
||||||
_.defer => Backbone.Mediator.publish 'surface:flag-appeared', sprite: @
|
_.defer => Backbone.Mediator.publish 'surface:flag-appeared', sprite: @
|
||||||
|
|
||||||
updateScale: ->
|
updateScale: (force) ->
|
||||||
return unless @sprite
|
return unless @sprite
|
||||||
if @thangType.get('matchWorldDimensions') and @thang
|
if @thangType.get('matchWorldDimensions') and @thang and @options.camera
|
||||||
if @thang.width isnt @lastThangWidth or @thang.height isnt @lastThangHeight
|
if force or @thang.width isnt @lastThangWidth or @thang.height isnt @lastThangHeight or @thang.rotation isnt @lastThangRotation
|
||||||
bounds = @sprite.getBounds()
|
bounds = @sprite.getBounds()
|
||||||
return unless bounds
|
return unless bounds
|
||||||
@sprite.scaleX = @thang.width * Camera.PPM / bounds.width
|
@sprite.scaleX = @thang.width * Camera.PPM / bounds.width * (@options.camera.y2x + (1 - @options.camera.y2x) * Math.abs Math.cos @thang.rotation)
|
||||||
@sprite.scaleY = @thang.height * Camera.PPM * @options.camera.y2x / bounds.height
|
@sprite.scaleY = @thang.height * Camera.PPM / bounds.height * (@options.camera.y2x + (1 - @options.camera.y2x) * Math.abs Math.sin @thang.rotation)
|
||||||
@sprite.regX = bounds.width / 2
|
@sprite.regX = bounds.width * 3 / 4 # Why not / 2? I don't know.
|
||||||
@sprite.regY = bounds.height / 2
|
@sprite.regY = bounds.height * 3 / 4 # Why not / 2? I don't know.
|
||||||
|
|
||||||
unless @thang.spriteName is 'Beam'
|
unless @thang.spriteName is 'Beam'
|
||||||
@sprite.scaleX *= @thangType.get('scale') ? 1
|
@sprite.scaleX *= @thangType.get('scale') ? 1
|
||||||
@sprite.scaleY *= @thangType.get('scale') ? 1
|
@sprite.scaleY *= @thangType.get('scale') ? 1
|
||||||
[@lastThangWidth, @lastThangHeight] = [@thang.width, @thang.height]
|
[@lastThangWidth, @lastThangHeight, @lastThangRotation] = [@thang.width, @thang.height, @thang.rotation]
|
||||||
return
|
return
|
||||||
|
|
||||||
scaleX = scaleY = 1
|
scaleX = scaleY = 1
|
||||||
|
|
|
@ -500,6 +500,7 @@ module.exports = LayerAdapter = class LayerAdapter extends CocoClass
|
||||||
lank.setSprite(sprite)
|
lank.setSprite(sprite)
|
||||||
lank.update(true)
|
lank.update(true)
|
||||||
@container.addChild(sprite)
|
@container.addChild(sprite)
|
||||||
|
lank.updateScale true if lank.thangType.get 'matchWorldDimensions' # Otherwise it's at the wrong scale for some reason.
|
||||||
|
|
||||||
renderGroupingKey: (thangType, grouping, colorConfig) ->
|
renderGroupingKey: (thangType, grouping, colorConfig) ->
|
||||||
key = thangType.get('slug')
|
key = thangType.get('slug')
|
||||||
|
|
|
@ -216,11 +216,19 @@ module.exports = class Mark extends CocoClass
|
||||||
|
|
||||||
buildDebug: ->
|
buildDebug: ->
|
||||||
shapeName = if @lank.thang.shape in ['ellipsoid', 'disc'] then 'ellipse' else 'rect'
|
shapeName = if @lank.thang.shape in ['ellipsoid', 'disc'] then 'ellipse' else 'rect'
|
||||||
key = "#{shapeName}-debug"
|
key = "#{shapeName}-debug-#{@lank.thang.collisionCategory}"
|
||||||
DEBUG_SIZE = 10
|
DEBUG_SIZE = 10
|
||||||
unless key in @layer.spriteSheet.getAnimations()
|
unless key in @layer.spriteSheet.getAnimations()
|
||||||
shape = new createjs.Shape()
|
shape = new createjs.Shape()
|
||||||
shape.graphics.beginFill 'rgba(171,205,239,0.5)'
|
debugColor = {
|
||||||
|
none: 'rgba(224,255,239,0.25)'
|
||||||
|
ground: 'rgba(239,171,205,0.5)'
|
||||||
|
air: 'rgba(131,205,255,0.5)'
|
||||||
|
ground_and_air: 'rgba(2391,140,239,0.5)'
|
||||||
|
obstacles: 'rgba(88,88,88,0.5)'
|
||||||
|
dead: 'rgba(89,171,100,0.25)'
|
||||||
|
}[@lank.thang.collisionCategory] or 'rgba(171,205,239,0.5)'
|
||||||
|
shape.graphics.beginFill debugColor
|
||||||
bounds = [-DEBUG_SIZE / 2, -DEBUG_SIZE / 2, DEBUG_SIZE, DEBUG_SIZE]
|
bounds = [-DEBUG_SIZE / 2, -DEBUG_SIZE / 2, DEBUG_SIZE, DEBUG_SIZE]
|
||||||
if shapeName is 'ellipse'
|
if shapeName is 'ellipse'
|
||||||
shape.graphics.drawEllipse bounds...
|
shape.graphics.drawEllipse bounds...
|
||||||
|
@ -232,9 +240,11 @@ module.exports = class Mark extends CocoClass
|
||||||
@sprite = new createjs.Sprite(@layer.spriteSheet)
|
@sprite = new createjs.Sprite(@layer.spriteSheet)
|
||||||
@sprite.gotoAndStop(key)
|
@sprite.gotoAndStop(key)
|
||||||
PX = 3
|
PX = 3
|
||||||
[w, h] = [Math.max(PX, @lank.thang.width * Camera.PPM), Math.max(PX, @lank.thang.height * Camera.PPM) * @camera.y2x] # TODO: doesn't work with rotation
|
w = Math.max(PX, @lank.thang.width * Camera.PPM) * (@camera.y2x + (1 - @camera.y2x) * Math.abs Math.cos @lank.thang.rotation)
|
||||||
|
h = Math.max(PX, @lank.thang.height * Camera.PPM) * (@camera.y2x + (1 - @camera.y2x) * Math.abs Math.sin @lank.thang.rotation)
|
||||||
@sprite.scaleX = w / (@layer.resolutionFactor * DEBUG_SIZE)
|
@sprite.scaleX = w / (@layer.resolutionFactor * DEBUG_SIZE)
|
||||||
@sprite.scaleY = h / (@layer.resolutionFactor * DEBUG_SIZE)
|
@sprite.scaleY = h / (@layer.resolutionFactor * DEBUG_SIZE)
|
||||||
|
@sprite.rotation = -@lank.thang.rotation * 180 / Math.PI
|
||||||
|
|
||||||
buildSprite: ->
|
buildSprite: ->
|
||||||
if _.isString @thangType
|
if _.isString @thangType
|
||||||
|
|
|
@ -57,14 +57,23 @@ module.exports = class ThangsTabView extends CocoView
|
||||||
shortcuts:
|
shortcuts:
|
||||||
'esc': 'selectAddThang'
|
'esc': 'selectAddThang'
|
||||||
'delete, del, backspace': 'deleteSelectedExtantThang'
|
'delete, del, backspace': 'deleteSelectedExtantThang'
|
||||||
'left': -> @moveAddThangSelection -1
|
|
||||||
'right': -> @moveAddThangSelection 1
|
|
||||||
'ctrl+z, ⌘+z': 'undo'
|
'ctrl+z, ⌘+z': 'undo'
|
||||||
'ctrl+shift+z, ⌘+shift+z': 'redo'
|
'ctrl+shift+z, ⌘+shift+z': 'redo'
|
||||||
'alt+left': -> @rotateSelectedThangBy(Math.PI)
|
'alt+c': 'toggleSelectedThangCollision'
|
||||||
'alt+right': -> @rotateSelectedThangBy(0)
|
'left': -> @moveSelectedThangBy -1, 0
|
||||||
'alt+up': -> @rotateSelectedThangBy(-Math.PI/2)
|
'right': -> @moveSelectedThangBy 1, 0
|
||||||
'alt+down': -> @rotateSelectedThangBy(Math.PI/2)
|
'up': -> @moveSelectedThangBy 0, 1
|
||||||
|
'down': -> @moveSelectedThangBy 0, -1
|
||||||
|
'alt+left': -> @rotateSelectedThangTo Math.PI unless key.shift
|
||||||
|
'alt+right': -> @rotateSelectedThangTo 0 unless key.shift
|
||||||
|
'alt+up': -> @rotateSelectedThangTo -Math.PI / 2
|
||||||
|
'alt+down': -> @rotateSelectedThangTo Math.PI / 2
|
||||||
|
'alt+shift+left': -> @rotateSelectedThangBy Math.PI / 16
|
||||||
|
'alt+shift+right': -> @rotateSelectedThangBy -Math.PI / 16
|
||||||
|
'shift+left': -> @resizeSelectedThangBy -1, 0
|
||||||
|
'shift+right': -> @resizeSelectedThangBy 1, 0
|
||||||
|
'shift+up': -> @resizeSelectedThangBy 0, 1
|
||||||
|
'shift+down': -> @resizeSelectedThangBy 0, -1
|
||||||
|
|
||||||
constructor: (options) ->
|
constructor: (options) ->
|
||||||
super options
|
super options
|
||||||
|
@ -516,7 +525,7 @@ module.exports = class ThangsTabView extends CocoView
|
||||||
prefix += segment
|
prefix += segment
|
||||||
if not @thangsTreema.get(prefix) then @thangsTreema.set(prefix, {})
|
if not @thangsTreema.get(prefix) then @thangsTreema.set(prefix, {})
|
||||||
|
|
||||||
onThangsChanged: =>
|
onThangsChanged: (skipSerialization) =>
|
||||||
return if @hush
|
return if @hush
|
||||||
|
|
||||||
# keep the thangs in the same order as before, roughly
|
# keep the thangs in the same order as before, roughly
|
||||||
|
@ -527,6 +536,7 @@ module.exports = class ThangsTabView extends CocoView
|
||||||
|
|
||||||
@level.set 'thangs', thangs
|
@level.set 'thangs', thangs
|
||||||
return if @editThangView
|
return if @editThangView
|
||||||
|
return if skipSerialization
|
||||||
serializedLevel = @level.serialize @supermodel, null, null, true
|
serializedLevel = @level.serialize @supermodel, null, null, true
|
||||||
try
|
try
|
||||||
@world.loadFromLevel serializedLevel, false
|
@world.loadFromLevel serializedLevel, false
|
||||||
|
@ -634,18 +644,56 @@ module.exports = class ThangsTabView extends CocoView
|
||||||
onClickRotationButton: (e) ->
|
onClickRotationButton: (e) ->
|
||||||
$('#contextmenu').hide()
|
$('#contextmenu').hide()
|
||||||
rotation = parseFloat($(e.target).closest('button').data('rotation'))
|
rotation = parseFloat($(e.target).closest('button').data('rotation'))
|
||||||
@rotateSelectedThangBy rotation * Math.PI
|
@rotateSelectedThangTo rotation * Math.PI
|
||||||
|
|
||||||
|
modifySelectedThangComponentConfig: (thang, componentOriginal, modificationFunction) ->
|
||||||
|
return unless thang
|
||||||
|
@hush = true
|
||||||
|
thangData = @getThangByID thang.id
|
||||||
|
thangData = $.extend true, {}, thangData
|
||||||
|
unless component = _.find thangData.components, {original: componentOriginal}
|
||||||
|
component = original: componentOriginal, config: {}
|
||||||
|
thangData.components.push component
|
||||||
|
modificationFunction component
|
||||||
|
@thangsTreema.set @pathForThang(thangData), thangData
|
||||||
|
@hush = false
|
||||||
|
@onThangsChanged true
|
||||||
|
thang.stateChanged = true
|
||||||
|
lank = @surface.lankBoss.lanks[thang.id]
|
||||||
|
lank.update true
|
||||||
|
lank.marks.debug?.destroy()
|
||||||
|
lank.marks.debug = null
|
||||||
|
lank.setDebug true
|
||||||
|
|
||||||
|
rotateSelectedThangTo: (radians) ->
|
||||||
|
@modifySelectedThangComponentConfig @selectedExtantThang, LevelComponent.PhysicalID, (component) =>
|
||||||
|
component.config.rotation = radians
|
||||||
|
@selectedExtantThang.rotation = component.config.rotation
|
||||||
|
|
||||||
rotateSelectedThangBy: (radians) ->
|
rotateSelectedThangBy: (radians) ->
|
||||||
return unless @selectedExtantThang
|
@modifySelectedThangComponentConfig @selectedExtantThang, LevelComponent.PhysicalID, (component) =>
|
||||||
@hush = true
|
component.config.rotation = ((component.config.rotation ? 0) + radians) % (2 * Math.PI)
|
||||||
thangData = @getThangByID(@selectedExtantThang.id)
|
@selectedExtantThang.rotation = component.config.rotation
|
||||||
thangData = $.extend(true, {}, thangData)
|
|
||||||
component = _.find thangData.components, {original: LevelComponent.PhysicalID}
|
moveSelectedThangBy: (xDir, yDir) ->
|
||||||
component.config.rotation = radians
|
@modifySelectedThangComponentConfig @selectedExtantThang, LevelComponent.PhysicalID, (component) =>
|
||||||
@thangsTreema.set(@pathForThang(thangData), thangData)
|
component.config.pos.x += 0.5 * xDir
|
||||||
@hush = false
|
component.config.pos.y += 0.5 * yDir
|
||||||
@onThangsChanged()
|
@selectedExtantThang.pos.x = component.config.pos.x
|
||||||
|
@selectedExtantThang.pos.y = component.config.pos.y
|
||||||
|
|
||||||
|
resizeSelectedThangBy: (xDir, yDir) ->
|
||||||
|
@modifySelectedThangComponentConfig @selectedExtantThang, LevelComponent.PhysicalID, (component) =>
|
||||||
|
component.config.width = (component.config.width ? 4) + 0.5 * xDir
|
||||||
|
component.config.height = (component.config.height ? 4) + 0.5 * yDir
|
||||||
|
@selectedExtantThang.width = component.config.width
|
||||||
|
@selectedExtantThang.height = component.config.height
|
||||||
|
|
||||||
|
toggleSelectedThangCollision: ->
|
||||||
|
@modifySelectedThangComponentConfig @selectedExtantThang, LevelComponent.CollidesID, (component) =>
|
||||||
|
component.config ?= {}
|
||||||
|
component.config.collisionCategory = if component.config.collisionCategory is 'none' then 'ground' else 'none'
|
||||||
|
@selectedExtantThang.collisionCategory = component.config.collisionCategory
|
||||||
|
|
||||||
toggleThangsContainer: (e) ->
|
toggleThangsContainer: (e) ->
|
||||||
$('#all-thangs').toggleClass('hide')
|
$('#all-thangs').toggleClass('hide')
|
||||||
|
|
|
@ -63,7 +63,7 @@ setupExpressMiddleware = (app) ->
|
||||||
else
|
else
|
||||||
express.logger.format('dev', developmentLogging)
|
express.logger.format('dev', developmentLogging)
|
||||||
app.use(express.logger('dev'))
|
app.use(express.logger('dev'))
|
||||||
app.use(express.static(path.join(__dirname, 'public'), maxAge: 30 * 60 * 1000))
|
app.use(express.static(path.join(__dirname, 'public'), maxAge: 0)) # CloudFlare overrides maxAge, and we don't want local development caching.
|
||||||
app.use(useragent.express())
|
app.use(useragent.express())
|
||||||
|
|
||||||
app.use(express.favicon())
|
app.use(express.favicon())
|
||||||
|
|
Loading…
Reference in a new issue