Refactored CocoSprite -> Lank, lank.imageObject -> lank.sprite, SpriteBoss -> LankBoss, spriteLayers -> layerAdapters, sprite -> lank in general. Also got the ThangTypeEditView working again.

This commit is contained in:
Scott Erickson 2014-09-28 14:00:48 -07:00
parent 64a8322ec0
commit f081d9ed4b
22 changed files with 833 additions and 803 deletions

View file

@ -53,11 +53,11 @@ module.exports = class LevelBus extends Bus
super() super()
onSelfWizardCreated: (e) -> onSelfWizardCreated: (e) ->
@selfWizardSprite = e.sprite @selfWizardLank = e.sprite
onSelfWizardTargetChanged: (e) -> onSelfWizardTargetChanged: (e) ->
@wizardRef?.child('targetPos').set(@selfWizardSprite?.targetPos or null) @wizardRef?.child('targetPos').set(@selfWizardLank?.targetPos or null)
@wizardRef?.child('targetSprite').set(@selfWizardSprite?.targetSprite?.thang.id or null) @wizardRef?.child('targetSprite').set(@selfWizardLank?.targetSprite?.thang.id or null)
onMeSynced: => onMeSynced: =>
super() super()
@ -66,8 +66,8 @@ module.exports = class LevelBus extends Bus
join: -> join: ->
super() super()
@wizardRef = @myConnection.child('wizard') @wizardRef = @myConnection.child('wizard')
@wizardRef?.child('targetPos').set(@selfWizardSprite?.targetPos or null) @wizardRef?.child('targetPos').set(@selfWizardLank?.targetPos or null)
@wizardRef?.child('targetSprite').set(@selfWizardSprite?.targetSprite?.thang.id or null) @wizardRef?.child('targetSprite').set(@selfWizardLank?.targetSprite?.thang.id or null)
@wizardRef?.child('wizardColor1').set(me.get('wizardColor1') or 0.0) @wizardRef?.child('wizardColor1').set(me.get('wizardColor1') or 0.0)
disconnect: -> disconnect: ->

View file

@ -66,7 +66,7 @@ module.exports = class Dimmer extends CocoClass
@dimMask.graphics.clear() @dimMask.graphics.clear()
for thangID, sprite of @sprites for thangID, sprite of @sprites
continue unless (thangID in @highlightedThangIDs) or sprite.isTalking?() or sprite.thang?.id is 'My Wizard' continue unless (thangID in @highlightedThangIDs) or sprite.isTalking?() or sprite.thang?.id is 'My Wizard'
sup = x: sprite.imageObject.x, y: sprite.imageObject.y sup = x: sprite.sprite.x, y: sprite.sprite.y
cap = @camera.surfaceToCanvas sup cap = @camera.surfaceToCanvas sup
r = 50 * @camera.zoom # TODO: find better way to get the radius based on the sprite's size r = 50 * @camera.zoom # TODO: find better way to get the radius based on the sprite's size
@dimMask.graphics.beginRadialGradientFill(['rgba(0,0,0,1)', 'rgba(0,0,0,0)'], [0.5, 1], cap.x, cap.y, 0, cap.x, cap.y, r).drawCircle(cap.x, cap.y, r) @dimMask.graphics.beginRadialGradientFill(['rgba(0,0,0,1)', 'rgba(0,0,0,0)'], [0.5, 1], cap.x, cap.y, 0, cap.x, cap.y, r).drawCircle(cap.x, cap.y, r)

View file

@ -1,7 +1,7 @@
IndieSprite = require 'lib/surface/IndieSprite' IndieLank = require 'lib/surface/IndieLank'
{me} = require 'lib/auth' {me} = require 'lib/auth'
module.exports = class FlagSprite extends IndieSprite module.exports = class FlagLank extends IndieLank
subscriptions: subscriptions:
'surface:mouse-moved': 'onMouseMoved' 'surface:mouse-moved': 'onMouseMoved'

View file

@ -1,7 +1,7 @@
Thang = require 'lib/world/thang' Thang = require 'lib/world/thang'
CocoSprite = require 'lib/surface/CocoSprite' Lank = require 'lib/surface/Lank'
module.exports = IndieSprite = class IndieSprite extends CocoSprite module.exports = IndieLank = class IndieLank extends Lank
notOfThisWorld: true notOfThisWorld: true
subscriptions: subscriptions:
'script:note-group-started': 'onNoteGroupStarted' 'script:note-group-started': 'onNoteGroupStarted'

View file

@ -49,11 +49,11 @@ module.exports = class Label extends CocoClass
update: -> update: ->
return unless @text return unless @text
offset = @sprite.getOffset? (if @style in ['dialogue', 'say'] then 'mouth' else 'aboveHead') offset = @sprite.getOffset? (if @style in ['dialogue', 'say'] then 'mouth' else 'aboveHead')
offset ?= x: 0, y: 0 # temp (if not CocoSprite) offset ?= x: 0, y: 0 # temp (if not Lank)
rotation = @sprite.getRotation() rotation = @sprite.getRotation()
offset.x *= -1 if rotation >= 135 or rotation <= -135 offset.x *= -1 if rotation >= 135 or rotation <= -135
@label.x = @background.x = @sprite.imageObject.x + offset.x @label.x = @background.x = @sprite.sprite.x + offset.x
@label.y = @background.y = @sprite.imageObject.y + offset.y @label.y = @background.y = @sprite.sprite.y + offset.y
null null
show: -> show: ->
@ -122,7 +122,7 @@ module.exports = class Label extends CocoClass
pointerWidth += radius # Convenience value including pointer width and border radius pointerWidth += radius # Convenience value including pointer width and border radius
# Figure out the position of the pointer for the bubble # Figure out the position of the pointer for the bubble
sup = x: @sprite.imageObject.x, y: @sprite.imageObject.y # a little more accurate to aim for mouth--how? sup = x: @sprite.sprite.x, y: @sprite.sprite.y # a little more accurate to aim for mouth--how?
cap = @camera.surfaceToCanvas sup cap = @camera.surfaceToCanvas sup
hPos = if cap.x / @camera.canvasWidth > 0.53 then 'right' else 'left' hPos = if cap.x / @camera.canvasWidth > 0.53 then 'right' else 'left'
vPos = if cap.y / @camera.canvasHeight > 0.53 then 'bottom' else 'top' vPos = if cap.y / @camera.canvasHeight > 0.53 then 'bottom' else 'top'

View file

@ -13,10 +13,10 @@ healthColors =
neutral: [64, 212, 128] neutral: [64, 212, 128]
# Sprite: EaselJS-based view/controller for Thang model # Sprite: EaselJS-based view/controller for Thang model
module.exports = CocoSprite = class CocoSprite extends CocoClass module.exports = Lank = class Lank extends CocoClass
thangType: null # ThangType instance thangType: null # ThangType instance
imageObject: null sprite: null
healthBar: null healthBar: null
marks: null marks: null
@ -62,12 +62,12 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass
@options = _.extend($.extend(true, {}, @options), options) @options = _.extend($.extend(true, {}, @options), options)
@setThang @options.thang @setThang @options.thang
if @thang? if @thang?
options = @thang?.getSpriteOptions?() options = @thang?.getLankOptions?()
@options.colorConfig = options.colorConfig if options and options.colorConfig @options.colorConfig = options.colorConfig if options and options.colorConfig
console.error @toString(), 'has no ThangType!' unless @thangType console.error @toString(), 'has no ThangType!' unless @thangType
# this is a stub, use @setImageObject to swap it out for something else later # this is a stub, use @setSprite to swap it out for something else later
@imageObject = new createjs.Container @sprite = new createjs.Container
@actionQueue = [] @actionQueue = []
@marks = {} @marks = {}
@ -78,7 +78,7 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass
@stillLoading = true @stillLoading = true
if @thangType.isFullyLoaded() then @onThangTypeLoaded() else @listenToOnce(@thangType, 'sync', @onThangTypeLoaded) if @thangType.isFullyLoaded() then @onThangTypeLoaded() else @listenToOnce(@thangType, 'sync', @onThangTypeLoaded)
toString: -> "<CocoSprite: #{@thang?.id}>" toString: -> "<Lank: #{@thang?.id}>"
onThangTypeLoaded: -> onThangTypeLoaded: ->
@stillLoading = false @stillLoading = false
@ -97,22 +97,22 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass
@scaleFactorY = @thang.scaleFactor if @thang?.scaleFactor? @scaleFactorY = @thang.scaleFactor if @thang?.scaleFactor?
@updateAction() unless @currentAction @updateAction() unless @currentAction
setImageObject: (newImageObject) -> setSprite: (newSprite) ->
if @imageObject if @sprite
@imageObject.destroy?() @sprite.destroy?()
if parent = @imageObject.parent if parent = @sprite.parent
parent.removeChild @imageObject parent.removeChild @sprite
parent.addChild newImageObject parent.addChild newSprite
# get the cocosprite to update things # get the lank to update things
for prop in ['lastPos', 'currentRootAction'] for prop in ['lastPos', 'currentRootAction']
delete @[prop] delete @[prop]
@imageObject = newImageObject @sprite = newSprite
@configureMouse() @configureMouse()
@imageObject.on 'animationend', @playNextAction @sprite.on 'animationend', @playNextAction
@playAction(@currentAction) if @currentAction and not @stillLoading @playAction(@currentAction) if @currentAction and not @stillLoading
@trigger 'new-image-object', @imageObject @trigger 'new-sprite', @sprite
################################################## ##################################################
# QUEUEING AND PLAYING ACTIONS # QUEUEING AND PLAYING ACTIONS
@ -141,9 +141,9 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass
return @hide() unless action.animation or action.container or action.relatedActions return @hide() unless action.animation or action.container or action.relatedActions
@show() @show()
return @updateActionDirection() unless action.animation or action.container return @updateActionDirection() unless action.animation or action.container
return if @imageObject.placeholder return if @sprite.placeholder
m = if action.container then 'gotoAndStop' else 'gotoAndPlay' m = if action.container then 'gotoAndStop' else 'gotoAndPlay'
@imageObject[m]?(action.name) @sprite[m]?(action.name)
@updateScale() @updateScale()
@updateRotation() @updateRotation()
@ -156,12 +156,12 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass
@updateAlpha() @updateAlpha()
stop: -> stop: ->
@imageObject?.stop?() @sprite?.stop?()
mark.stop() for name, mark of @marks mark.stop() for name, mark of @marks
@stopped = true @stopped = true
play: -> play: ->
@imageObject?.play?() @sprite?.play?()
mark.play() for name, mark of @marks mark.play() for name, mark of @marks
@stopped = false @stopped = false
@ -238,7 +238,7 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass
shadowColor = {humans: '#F00', ogres: '#00F', neutral: '#0F0', common: '#0F0'}[@thang.team] ? '#000' shadowColor = {humans: '#F00', ogres: '#00F', neutral: '#0F0', common: '#0F0'}[@thang.team] ? '#000'
label.shadow = new createjs.Shadow shadowColor, 1, 1, 3 label.shadow = new createjs.Shadow shadowColor, 1, 1, 3
offset = @getOffset 'aboveHead' offset = @getOffset 'aboveHead'
[label.x, label.y] = [@imageObject.x + offset.x - label.getMeasuredWidth() / 2, @imageObject.y + offset.y] [label.x, label.y] = [@sprite.x + offset.x - label.getMeasuredWidth() / 2, @sprite.y + offset.y]
@options.textLayer.addChild label @options.textLayer.addChild label
window.labels ?= [] window.labels ?= []
window.labels.push label window.labels.push label
@ -270,7 +270,7 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass
[p0, p1] = [@lastPos, @thang.pos] [p0, p1] = [@lastPos, @thang.pos]
return if p0 and p0.x is p1.x and p0.y is p1.y and p0.z is p1.z and not @options.camera.tweeningZoomTo and not @thang.bobHeight return if p0 and p0.x is p1.x and p0.y is p1.y and p0.z is p1.z and not @options.camera.tweeningZoomTo and not @thang.bobHeight
sup = @options.camera.worldToSurface wop sup = @options.camera.worldToSurface wop
[@imageObject.x, @imageObject.y] = [sup.x, sup.y] [@sprite.x, @sprite.y] = [sup.x, sup.y]
@lastPos = p1.copy?() or _.clone(p1) unless whileLoading @lastPos = p1.copy?() or _.clone(p1) unless whileLoading
@hasMoved = true @hasMoved = true
if @thangType.get('name') is 'Flag' and not @notOfThisWorld if @thangType.get('name') is 'Flag' and not @notOfThisWorld
@ -278,19 +278,19 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass
_.defer => Backbone.Mediator.publish 'surface:flag-appeared', sprite: @ _.defer => Backbone.Mediator.publish 'surface:flag-appeared', sprite: @
updateScale: -> updateScale: ->
return unless @imageObject return unless @sprite
if @thangType.get('matchWorldDimensions') and @thang if @thangType.get('matchWorldDimensions') and @thang
if @thang.width isnt @lastThangWidth or @thang.height isnt @lastThangHeight if @thang.width isnt @lastThangWidth or @thang.height isnt @lastThangHeight
bounds = @imageObject.getBounds() bounds = @sprite.getBounds()
return unless bounds return unless bounds
@imageObject.scaleX = @thang.width * Camera.PPM / bounds.width @sprite.scaleX = @thang.width * Camera.PPM / bounds.width
@imageObject.scaleY = @thang.height * Camera.PPM * @options.camera.y2x / bounds.height @sprite.scaleY = @thang.height * Camera.PPM * @options.camera.y2x / bounds.height
@imageObject.regX = bounds.width / 2 @sprite.regX = bounds.width / 2
@imageObject.regY = bounds.height / 2 @sprite.regY = bounds.height / 2
unless @thang.spriteName is 'Beam' unless @thang.spriteName is 'Beam'
@imageObject.scaleX *= @thangType.get('scale') ? 1 @sprite.scaleX *= @thangType.get('scale') ? 1
@imageObject.scaleY *= @thangType.get('scale') ? 1 @sprite.scaleY *= @thangType.get('scale') ? 1
[@lastThangWidth, @lastThangHeight] = [@thang.width, @thang.height] [@lastThangWidth, @lastThangHeight] = [@thang.width, @thang.height]
return return
@ -311,8 +311,8 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass
scaleX = 0.5 + 0.5 * (90 - angle) / 90 scaleX = 0.5 + 0.5 * (90 - angle) / 90
# console.error 'No thang for', @ unless @thang # console.error 'No thang for', @ unless @thang
@imageObject.scaleX = @imageObject.baseScaleX * @scaleFactorX * scaleX @sprite.scaleX = @sprite.baseScaleX * @scaleFactorX * scaleX
@imageObject.scaleY = @imageObject.baseScaleY * @scaleFactorY * scaleY @sprite.scaleY = @sprite.baseScaleY * @scaleFactorY * scaleY
newScaleFactorX = @thang?.scaleFactorX ? @thang?.scaleFactor ? 1 newScaleFactorX = @thang?.scaleFactorX ? @thang?.scaleFactor ? 1
newScaleFactorY = @thang?.scaleFactorY ? @thang?.scaleFactor ? 1 newScaleFactorY = @thang?.scaleFactorY ? @thang?.scaleFactor ? 1
@ -323,16 +323,16 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass
createjs.Tween.get(@).to({scaleFactorX: @targetScaleFactorX, scaleFactorY: @targetScaleFactorY}, 2000, createjs.Ease.elasticOut) createjs.Tween.get(@).to({scaleFactorX: @targetScaleFactorX, scaleFactorY: @targetScaleFactorY}, 2000, createjs.Ease.elasticOut)
updateAlpha: -> updateAlpha: ->
@imageObject.alpha = if @hiding then 0 else 1 @sprite.alpha = if @hiding then 0 else 1
return unless @thang?.alpha? return unless @thang?.alpha?
return if @imageObject.alpha is @thang.alpha return if @sprite.alpha is @thang.alpha
@imageObject.alpha = @thang.alpha @sprite.alpha = @thang.alpha
if @options.showInvisible if @options.showInvisible
@imageObject.alpha = Math.max 0.5, @imageObject.alpha @sprite.alpha = Math.max 0.5, @sprite.alpha
mark.updateAlpha @thang.alpha for name, mark of @marks mark.updateAlpha @thang.alpha for name, mark of @marks
@healthBar?.alpha = @thang.alpha @healthBar?.alpha = @thang.alpha
updateRotation: (imageObject) -> updateRotation: (sprite) ->
rotationType = @thangType.get('rotationType') rotationType = @thangType.get('rotationType')
return if rotationType is 'fixed' return if rotationType is 'fixed'
rotation = @getRotation() rotation = @getRotation()
@ -347,9 +347,9 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass
xFactor = Math.cos heading xFactor = Math.cos heading
zFactor = vz / Math.sqrt(vz * vz + vx * vx) zFactor = vz / Math.sqrt(vz * vz + vx * vx)
rotation -= xFactor * zFactor * 45 rotation -= xFactor * zFactor * 45
imageObject ?= @imageObject sprite ?= @sprite
return imageObject.rotation = rotation if rotationType is 'free' or not rotationType return sprite.rotation = rotation if rotationType is 'free' or not rotationType
@updateIsometricRotation(rotation, imageObject) @updateIsometricRotation(rotation, sprite)
getRotation: -> getRotation: ->
thang = if @possessed then @shadow else @thang thang = if @possessed then @shadow else @thang
@ -359,11 +359,11 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass
rotation -= 360 if rotation > 180 rotation -= 360 if rotation > 180
rotation rotation
updateIsometricRotation: (rotation, imageObject) -> updateIsometricRotation: (rotation, sprite) ->
return unless @currentAction return unless @currentAction
return if _.string.endsWith(@currentAction.name, 'back') return if _.string.endsWith(@currentAction.name, 'back')
return if _.string.endsWith(@currentAction.name, 'fore') return if _.string.endsWith(@currentAction.name, 'fore')
imageObject.scaleX *= -1 if Math.abs(rotation) >= 90 sprite.scaleX *= -1 if Math.abs(rotation) >= 90
################################################## ##################################################
updateAction: -> updateAction: ->
@ -455,18 +455,18 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass
@setNameLabel(if @thang.health <= 0 then '' else @thang.id) @setNameLabel(if @thang.health <= 0 then '' else @thang.id)
configureMouse: -> configureMouse: ->
@imageObject.cursor = 'pointer' if @thang?.isSelectable @sprite.cursor = 'pointer' if @thang?.isSelectable
@imageObject.mouseEnabled = @imageObject.mouseChildren = false unless @thang?.isSelectable or @thang?.isLand @sprite.mouseEnabled = @sprite.mouseChildren = false unless @thang?.isSelectable or @thang?.isLand
if @imageObject.mouseEnabled if @sprite.mouseEnabled
@imageObject.on 'mousedown', @onMouseEvent, @, false, 'sprite:mouse-down' @sprite.on 'mousedown', @onMouseEvent, @, false, 'sprite:mouse-down'
@imageObject.on 'click', @onMouseEvent, @, false, 'sprite:clicked' @sprite.on 'click', @onMouseEvent, @, false, 'sprite:clicked'
@imageObject.on 'dblclick', @onMouseEvent, @, false, 'sprite:double-clicked' @sprite.on 'dblclick', @onMouseEvent, @, false, 'sprite:double-clicked'
@imageObject.on 'pressmove', @onMouseEvent, @, false, 'sprite:dragged' @sprite.on 'pressmove', @onMouseEvent, @, false, 'sprite:dragged'
@imageObject.on 'pressup', @onMouseEvent, @, false, 'sprite:mouse-up' @sprite.on 'pressup', @onMouseEvent, @, false, 'sprite:mouse-up'
onMouseEvent: (e, ourEventName) -> onMouseEvent: (e, ourEventName) ->
return if @letterboxOn or not @imageObject return if @letterboxOn or not @sprite
p = @imageObject p = @sprite
p = p.parent while p.parent p = p.parent while p.parent
newEvent = sprite: @, thang: @thang, originalEvent: e, canvas: p.canvas newEvent = sprite: @, thang: @thang, originalEvent: e, canvas: p.canvas
@trigger ourEventName, newEvent @trigger ourEventName, newEvent
@ -505,7 +505,7 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass
pos = x: pos.x, y: pos.y pos = x: pos.x, y: pos.y
if not @isRaster if not @isRaster
scale = @getActionProp 'scale', null, 1 scale = @getActionProp 'scale', null, 1
scale *= @imageObject.parent.resolutionFactor if prop is 'registration' scale *= @sprite.parent.resolutionFactor if prop is 'registration'
pos.x *= scale pos.x *= scale
pos.y *= scale pos.y *= scale
if @thang and prop isnt 'registration' if @thang and prop isnt 'registration'
@ -613,7 +613,7 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass
@labels[name] @labels[name]
addMark: (name, layer, thangType=null) -> addMark: (name, layer, thangType=null) ->
@marks[name] ?= new Mark name: name, sprite: @, camera: @options.camera, layer: layer ? @options.groundLayer, thangType: thangType @marks[name] ?= new Mark name: name, lank: @, camera: @options.camera, layer: layer ? @options.groundLayer, thangType: thangType
@marks[name] @marks[name]
notifySpeechUpdated: (e) -> notifySpeechUpdated: (e) ->
@ -720,7 +720,7 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass
z = @shadow.pos.z z = @shadow.pos.z
@shadow.pos = pos @shadow.pos = pos
@shadow.pos.z = z @shadow.pos.z = z
@imageObject.gotoAndPlay?(endAnimation) @sprite.gotoAndPlay?(endAnimation)
return return
@shadow.action = 'move' @shadow.action = 'move'
@ -736,7 +736,7 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass
endFunc = => endFunc = =>
@lastTween = null @lastTween = null
@imageObject.gotoAndPlay(endAnimation) unless @stillLoading @sprite.gotoAndPlay(endAnimation) unless @stillLoading
@shadow.action = 'idle' @shadow.action = 'idle'
@update true @update true
@possessed = false @possessed = false
@ -762,13 +762,13 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass
return unless @healthBar return unless @healthBar
bounds = @healthBar.getBounds() bounds = @healthBar.getBounds()
offset = @getOffset 'aboveHead' offset = @getOffset 'aboveHead'
@healthBar.x = @imageObject.x - (-offset.x + bounds.width / 2 / @options.floatingLayer.resolutionFactor) @healthBar.x = @sprite.x - (-offset.x + bounds.width / 2 / @options.floatingLayer.resolutionFactor)
@healthBar.y = @imageObject.y - (-offset.y + bounds.height / 2 / @options.floatingLayer.resolutionFactor) @healthBar.y = @sprite.y - (-offset.y + bounds.height / 2 / @options.floatingLayer.resolutionFactor)
destroy: -> destroy: ->
mark.destroy() for name, mark of @marks mark.destroy() for name, mark of @marks
label.destroy() for name, label of @labels label.destroy() for name, label of @labels
p.removeChild @healthBar if p = @healthBar?.parent p.removeChild @healthBar if p = @healthBar?.parent
@imageObject?.off 'animationend', @playNextAction @sprite?.off 'animationend', @playNextAction
clearInterval @effectInterval if @effectInterval clearInterval @effectInterval if @effectInterval
super() super()

View file

@ -0,0 +1,391 @@
CocoClass = require 'lib/CocoClass'
{me} = require 'lib/auth'
LayerAdapter = require './LayerAdapter'
IndieLank = require 'lib/surface/IndieLank'
WizardLank = require 'lib/surface/WizardLank'
FlagLank = require 'lib/surface/FlagLank'
Lank = require 'lib/surface/Lank'
Mark = require './Mark'
Grid = require 'lib/world/Grid'
module.exports = class LankBoss extends CocoClass
subscriptions:
'bus:player-joined': 'onPlayerJoined'
'bus:player-left': 'onPlayerLeft'
'level:set-debug': 'onSetDebug'
'sprite:highlight-sprites': 'onHighlightSprites'
'surface:stage-mouse-down': 'onStageMouseDown'
'level:select-sprite': 'onSelectSprite'
'level:suppress-selection-sounds': 'onSuppressSelectionSounds'
'level:lock-select': 'onSetLockSelect'
'level:restarted': 'onLevelRestarted'
'god:new-world-created': 'onNewWorld'
'god:streaming-world-updated': 'onNewWorld'
'camera:dragged': 'onCameraDragged'
'sprite:loaded': -> @update(true)
'level:flag-color-selected': 'onFlagColorSelected'
'level:flag-updated': 'onFlagUpdated'
'surface:flag-appeared': 'onFlagAppeared'
'surface:remove-selected-flag': 'onRemoveSelectedFlag'
constructor: (@options) ->
super()
@dragged = 0
@options ?= {}
@camera = @options.camera
@webGLStage = @options.webGLStage
@surfaceTextLayer = @options.surfaceTextLayer
@world = options.world
@options.thangTypes ?= []
@lanks = {}
@lankArray = [] # Mirror @lanks, but faster for when we just need to iterate
@selfWizardLank = null
@createLayers()
@pendingFlags = []
destroy: ->
@removeLank lank for thangID, lank of @lanks
@targetMark?.destroy()
@selectionMark?.destroy()
super()
toString: -> "<LankBoss: #{@lankArray.length} lanks>"
thangTypeFor: (type) ->
_.find @options.thangTypes, (m) -> m.get('original') is type or m.get('name') is type
createLayers: ->
@layerAdapters = {}
for [name, priority] in [
['Land', -40]
['Ground', -30]
['Obstacle', -20]
['Path', -10]
['Default', 0]
['Floating', 10]
]
@layerAdapters[name] = new LayerAdapter name: name, webGL: true, layerPriority: priority, transform: LayerAdapter.TRANSFORM_SURFACE, camera: @camera
@webGLStage.addChild (lankLayer.container for lankLayer in _.values(@layerAdapters))...
layerForChild: (child, lank) ->
unless child.layerPriority?
if thang = lank?.thang
child.layerPriority = thang.layerPriority
child.layerPriority ?= 0 if thang.isSelectable
child.layerPriority ?= -40 if thang.isLand
child.layerPriority ?= 0
return @layerAdapters['Default'] unless child.layerPriority
layer = _.findLast @layerAdapters, (layer, name) ->
layer.layerPriority <= child.layerPriority
layer ?= @layerAdapters['Land'] if child.layerPriority < -40
layer ? @layerAdapters['Default']
addLank: (lank, id=null, layer=null) ->
id ?= lank.thang.id
console.error 'Lank collision! Already have:', id if @lanks[id]
@lanks[id] = lank
@lankArray.push lank
layer ?= @layerAdapters['Obstacle'] if lank.thang?.spriteName.search(/(dungeon|indoor).wall/i) isnt -1
layer ?= @layerForChild lank.sprite, lank
layer.addLank lank
layer.updateLayerOrder()
lank
createMarks: ->
@targetMark = new Mark name: 'target', camera: @camera, layer: @layerAdapters['Ground'], thangType: 'target'
@selectionMark = new Mark name: 'selection', camera: @camera, layer: @layerAdapters['Ground'], thangType: 'selection'
createLankOptions: (options) ->
_.extend options, camera: @camera, resolutionFactor: SPRITE_RESOLUTION_FACTOR, groundLayer: @layerAdapters['Ground'], textLayer: @surfaceTextLayer, floatingLayer: @layerAdapters['Floating'], showInvisible: @options.showInvisible
createIndieLanks: (indieLanks, withWizards) ->
unless @indieLanks
@indieLanks = []
@indieLanks = (@createIndieLank indieLank for indieLank in indieLanks) if indieLanks
if withWizards and not @selfWizardLank
@selfWizardLank = @createWizardLank thangID: 'My Wizard', isSelf: true, lanks: @lanks
createIndieLank: (indieLank) ->
unless thangType = @thangTypeFor indieLank.thangType
console.warn "Need to convert #{indieLank.id}'s ThangType #{indieLank.thangType} to a ThangType reference. Until then, #{indieLank.id} won't show up."
return
lank = new IndieLank thangType, @createLankOptions {thangID: indieLank.id, pos: indieLank.pos, lanks: @lanks, team: indieLank.team, teamColors: @world.getTeamColors()}
@addLank lank, lank.thang.id
createOpponentWizard: (opponent) ->
# TODO: colorize name and cloud by team, colorize wizard by user's color config, level-specific wizard spawn points
lank = @createWizardLank thangID: opponent.id, name: opponent.name, codeLanguage: opponent.codeLanguage
if not opponent.levelSlug or opponent.levelSlug is 'brawlwood'
lank.targetPos = if opponent.team is 'ogres' then {x: 52, y: 52} else {x: 28, y: 28}
else if opponent.levelSlug in ['dungeon-arena', 'sky-span']
lank.targetPos = if opponent.team is 'ogres' then {x: 72, y: 39} else {x: 9, y: 39}
else if opponent.levelSlug is 'criss-cross'
lank.targetPos = if opponent.team is 'ogres' then {x: 50, y: 12} else {x: 0, y: 40}
else
lank.targetPos = if opponent.team is 'ogres' then {x: 52, y: 28} else {x: 20, y: 28}
createWizardLank: (options) ->
lank = new WizardLank @thangTypeFor('Wizard'), @createLankOptions(options)
@addLank lank, lank.thang.id, @layerAdapters['Floating']
onPlayerJoined: (e) ->
# Create another WizardLank, unless this player is just me
pid = e.player.id
return if pid is me.id
wiz = @createWizardLank thangID: pid, lanks: @lanks
wiz.animateIn()
state = e.player.wizard or {}
wiz.setInitialState(state.targetPos, @lanks[state.targetLank])
onPlayerLeft: (e) ->
pid = e.player.id
@lanks[pid]?.animateOut => @removeLank @lanks[pid]
onSetDebug: (e) ->
return if e.debug is @debug
@debug = e.debug
lank.setDebug @debug for lank in @lankArray
onHighlightSprites: (e) ->
highlightedIDs = e.thangIDs or []
for thangID, lank of @lanks
lank.setHighlight? thangID in highlightedIDs, e.delay
addThangToLanks: (thang, layer=null) ->
return console.warn 'Tried to add Thang to the surface it already has:', thang.id if @lanks[thang.id]
thangType = _.find @options.thangTypes, (m) ->
return false unless m.get('actions') or m.get('raster')
return m.get('name') is thang.spriteName
thangType ?= _.find @options.thangTypes, (m) -> return m.get('name') is thang.spriteName
return console.error "Couldn't find ThangType for", thang unless thangType
options = @createLankOptions thang: thang
options.resolutionFactor = if thangType.get('kind') is 'Floor' then 2 else SPRITE_RESOLUTION_FACTOR
lank = new Lank thangType, options
@listenTo lank, 'sprite:mouse-up', @onLankMouseUp
@addLank lank, null, layer
lank.setDebug @debug
lank
removeLank: (lank) ->
lank.layer.removeLank(lank)
thang = lank.thang
delete @lanks[lank.thang.id]
@lankArray.splice @lankArray.indexOf(lank), 1
@stopListening lank
lank.destroy()
lank.thang = thang # Keep around so that we know which thang the destroyed thang was for
updateSounds: ->
lank.playSounds() for lank in @lankArray # hmm; doesn't work for lanks which we didn't add yet in adjustLankExistence
update: (frameChanged) ->
@adjustLankExistence() if frameChanged
lank.update frameChanged for lank in @lankArray
@updateSelection()
@layerAdapters['Default'].updateLayerOrder()
@cacheObstacles()
adjustLankExistence: ->
# Add anything new, remove anything old, update everything current
updatedObstacles = []
itemsJustEquipped = []
for thang in @world.thangs when thang.exists and thang.pos
itemsJustEquipped = itemsJustEquipped.concat @equipNewItems thang
if lank = @lanks[thang.id]
lank.setThang thang # make sure Lank has latest Thang
else
lank = @addThangToLanks(thang)
Backbone.Mediator.publish 'surface:new-thang-added', thang: thang, sprite: lank
updatedObstacles.push lank if lank.sprite.parent is @layerAdapters['Obstacle']
lank.playSounds()
item.modifyStats() for item in itemsJustEquipped
for thangID, lank of @lanks
missing = not (lank.notOfThisWorld or @world.thangMap[thangID]?.exists)
isObstacle = lank.sprite.parent is @layerAdapters['Obstacle']
updatedObstacles.push lank if isObstacle and (missing or lank.hasMoved)
lank.hasMoved = false
@removeLank lank if missing
@cacheObstacles updatedObstacles if updatedObstacles.length and @cachedObstacles
# mainly for handling selecting thangs from session when the thang is not always in existence
if @willSelectThang and @lanks[@willSelectThang[0]]
@selectThang @willSelectThang...
equipNewItems: (thang) ->
itemsJustEquipped = []
if thang.equip and not thang.equipped
thang.equip() # Pretty hacky, but needed since initialize may not be called if we're not running Systems.
itemsJustEquipped.push thang
if thang.inventoryIDs
# Even hackier: these items were only created/equipped during simulation, so we reequip here.
for slot, itemID of thang.inventoryIDs
item = @world.getThangByID itemID
unless item.equipped
console.log thang.id, 'equipping', item, 'in', thang.slot, 'Surface-side, but it cannot equip?' unless item.equip
item.equip()
itemsJustEquipped.push item
return itemsJustEquipped
cacheObstacles: (updatedObstacles=null) ->
return if @cachedObstacles and not updatedObstacles
wallLanks = (lank for lank in @lankArray when lank.thangType?.get('name').search(/(dungeon|indoor).wall/i) isnt -1)
return if _.any (s.stillLoading for s in wallLanks)
walls = (lank.thang for lank in wallLanks)
@world.calculateBounds()
wallGrid = new Grid walls, @world.size()...
if updatedObstacles
possiblyUpdatedWallLanks = (lank for lank in wallLanks when _.find updatedObstacles, (w2) -> lank is w2 or (Math.abs(lank.thang.pos.x - w2.thang.pos.x) + Math.abs(lank.thang.pos.y - w2.thang.pos.y)) <= 16)
else
possiblyUpdatedWallLanks = wallLanks
#console.log 'updating up to', possiblyUpdatedWallLanks.length, 'of', wallLanks.length, 'wall lanks from updatedObstacles', updatedObstacles
for wallLank in possiblyUpdatedWallLanks
wallLank.updateActionDirection wallGrid
wallLank.lockAction()
wallLank.updateScale()
wallLank.updatePosition()
#console.log @wallGrid.toString()
@cachedObstacles = true
lankFor: (thangID) -> @lanks[thangID]
onNewWorld: (e) ->
@world = @options.world = e.world
play: ->
lank.play() for lank in @lankArray
@selectionMark?.play()
@targetMark?.play()
stop: ->
lank.stop() for lank in @lankArray
@selectionMark?.stop()
@targetMark?.stop()
# Selection
onSuppressSelectionSounds: (e) -> @suppressSelectionSounds = e.suppress
onSetLockSelect: (e) -> @selectLocked = e.lock
onLevelRestarted: (e) ->
@selectLocked = false
@selectLank e, null
onSelectSprite: (e) ->
@selectThang e.thangID, e.spellName
onCameraDragged: ->
@dragged += 1
onLankMouseUp: (e) ->
return if key.shift #and @options.choosing
return @dragged = 0 if @dragged > 3
@dragged = 0
lank = if e.sprite?.thang?.isSelectable then e.sprite else null
return if @flagCursorLank and lank?.thangType.get('name') is 'Flag'
@selectLank e, lank
onStageMouseDown: (e) ->
return if key.shift #and @options.choosing
@selectLank e if e.onBackground
selectThang: (thangID, spellName=null, treemaThangSelected = null) ->
return @willSelectThang = [thangID, spellName] unless @lanks[thangID]
@selectLank null, @lanks[thangID], spellName, treemaThangSelected
selectLank: (e, lank=null, spellName=null, treemaThangSelected = null) ->
return if e and (@disabled or @selectLocked) # Ignore clicks for selection/panning/wizard movement while disabled or select is locked
worldPos = lank?.thang?.pos
worldPos ?= @camera.screenToWorld {x: e.originalEvent.rawX, y: e.originalEvent.rawY} if e?.originalEvent
if worldPos and (@options.navigateToSelection or not lank or treemaThangSelected) and e?.originalEvent?.nativeEvent?.which isnt 3
@camera.zoomTo(lank?.sprite or @camera.worldToSurface(worldPos), @camera.zoom, 1000, true)
lank = null if @options.choosing # Don't select lanks while choosing
if lank isnt @selectedLank
@selectedLank?.selected = false
lank?.selected = true
@selectedLank = lank
alive = lank and not (lank.thang.health < 0)
Backbone.Mediator.publish 'surface:sprite-selected',
thang: if lank then lank.thang else null
sprite: lank
spellName: spellName ? e?.spellName
originalEvent: e
worldPos: worldPos
@willSelectThang = null if lank # Now that we've done a real selection, don't reselect some other Thang later.
if alive and not @suppressSelectionSounds
instance = lank.playSound 'selected'
if instance?.playState is 'playSucceeded'
Backbone.Mediator.publish 'sprite:thang-began-talking', thang: lank?.thang
instance.addEventListener 'complete', ->
Backbone.Mediator.publish 'sprite:thang-finished-talking', thang: lank?.thang
onFlagColorSelected: (e) ->
@removeLank @flagCursorLank if @flagCursorLank
@flagCursorLank = null
for flagLank in @lankArray when flagLank.thangType.get('name') is 'Flag'
flagLank.sprite.cursor = if e.color then 'crosshair' else 'pointer'
return unless e.color
@flagCursorLank = new FlagLank @thangTypeFor('Flag'), @createLankOptions(thangID: 'Flag Cursor', color: e.color, team: me.team, isCursor: true, pos: e.pos)
@addLank @flagCursorLank, @flagCursorLank.thang.id, @layerAdapters['Floating']
onFlagUpdated: (e) ->
return unless e.active
pendingFlag = new FlagLank @thangTypeFor('Flag'), @createLankOptions(thangID: 'Pending Flag ' + Math.random(), color: e.color, team: e.team, isCursor: false, pos: e.pos)
@addLank pendingFlag, pendingFlag.thang.id, @layerAdapters['Floating']
@pendingFlags.push pendingFlag
onFlagAppeared: (e) ->
# Remove the pending flag that matches this one's color/team/position, and any color/team matches placed earlier.
t1 = e.sprite.thang
pending = (@pendingFlags ? []).slice()
foundExactMatch = false
for i in [pending.length - 1 .. 0] by -1
pendingFlag = pending[i]
t2 = pendingFlag.thang
matchedType = t1.color is t2.color and t1.team is t2.team
matched = matchedType and (foundExactMatch or Math.abs(t1.pos.x - t2.pos.x) < 0.00001 and Math.abs(t1.pos.y - t2.pos.y) < 0.00001)
if matched
foundExactMatch = true
@pendingFlags.splice(i, 1)
@removeLank pendingFlag
e.sprite.sprite.cursor = if @flagCursorLank then 'crosshair' else 'pointer'
null
onRemoveSelectedFlag: (e) ->
# Remove the selected lank if it's a flag, or any flag of the given color if a color is given.
flagLank = _.find [@selectedLank].concat(@lankArray), (lank) ->
lank and lank.thangType.get('name') is 'Flag' and lank.thang.team is me.team and (lank.thang.color is e.color or not e.color) and not lank.notOfThisWorld
return unless flagLank
Backbone.Mediator.publish 'surface:remove-flag', color: flagLank.thang.color
# Marks
updateSelection: ->
if @selectedLank?.thang and (not @selectedLank.thang.exists or not @world.getThangByID @selectedLank.thang.id)
thangID = @selectedLank.thang.id
@selectedLank = null # Don't actually trigger deselection, but remove the selected lank.
@selectionMark?.toggle false
@willSelectThang = [thangID, null]
@updateTarget()
return unless @selectionMark
@selectedLank = null if @selectedLank and (@selectedLank.destroyed or not @selectedLank.thang)
# The selection mark should be on the ground layer, unless we're not a normal lank (like a wall), in which case we'll place it higher so we can see it.
if @selectedLank and @selectedLank.sprite.parent isnt @layerAdapters.Default.container
@selectionMark.setLayer @layerAdapters.Default
else if @selectedLank
@selectionMark.setLayer @layerAdapters.Ground
@selectionMark.toggle @selectedLank?
@selectionMark.setLank @selectedLank
@selectionMark.update()
updateTarget: ->
return unless @targetMark
thang = @selectedLank?.thang
target = thang?.target
targetPos = thang?.targetPos
targetPos = null if targetPos?.isZero?() # Null targetPos get serialized as (0, 0, 0)
@targetMark.setLank if target then @lanks[target.id] else null
@targetMark.toggle @targetMark.lank or targetPos
@targetMark.update if targetPos then @camera.worldToSurface targetPos else null

View file

@ -44,7 +44,7 @@ module.exports = LayerAdapter = class LayerAdapter extends CocoClass
resolutionFactor: SPRITE_RESOLUTION_FACTOR resolutionFactor: SPRITE_RESOLUTION_FACTOR
defaultActions: ['idle', 'die', 'move', 'move', 'attack'] defaultActions: ['idle', 'die', 'move', 'move', 'attack']
numThingsLoading: 0 numThingsLoading: 0
cocoSprites: null lanks: null
spriteSheet: null spriteSheet: null
container: null container: null
customGraphics: null customGraphics: null
@ -70,7 +70,7 @@ module.exports = LayerAdapter = class LayerAdapter extends CocoClass
@container = new createjs.SpriteContainer(@spriteSheet) @container = new createjs.SpriteContainer(@spriteSheet)
@actionRenderState = {} @actionRenderState = {}
@toRenderBundles = [] @toRenderBundles = []
@cocoSprites = [] @lanks = []
@initializing = false @initializing = false
else else
@ -91,13 +91,13 @@ module.exports = LayerAdapter = class LayerAdapter extends CocoClass
# TODO: remove this z stuff # TODO: remove this z stuff
az = a.z or 1000 az = a.z or 1000
bz = b.z or 1000 bz = b.z or 1000
if aSprite = a.sprite if aLank = a.lank
if aThang = aSprite.thang if aThang = aLank.thang
aPos = aThang.pos aPos = aThang.pos
if aThang.health < 0 if aThang.health < 0
--az --az
if bSprite = b.sprite if bLank = b.lank
if bThang = bSprite.thang if bThang = bLank.thang
bPos = bThang.pos bPos = bThang.pos
if bThang.health < 0 if bThang.health < 0
--bz --bz
@ -142,27 +142,27 @@ module.exports = LayerAdapter = class LayerAdapter extends CocoClass
#- Adding, removing children for WebGL layers. #- Adding, removing children for WebGL layers.
addCocoSprite: (cocoSprite) -> addLank: (lank) ->
# TODO: Move this into the production DB rather than setting it dynamically. # TODO: Move this into the production DB rather than setting it dynamically.
if cocoSprite.thangType?.get('name') is 'Highlight' if lank.thangType?.get('name') is 'Highlight'
cocoSprite.thangType.set('spriteType', 'segmented') lank.thangType.set('spriteType', 'segmented')
cocoSprite.options.resolutionFactor = @resolutionFactor lank.options.resolutionFactor = @resolutionFactor
if cocoSprite.layer if lank.layer
console.warn 'CocoSprite being re-added to a layer?' console.warn 'Lank being re-added to a layer?'
cocoSprite.layer = @ lank.layer = @
@listenTo(cocoSprite, 'action-needs-render', @onActionNeedsRender) @listenTo(lank, 'action-needs-render', @onActionNeedsRender)
@cocoSprites.push cocoSprite @lanks.push lank
@loadThangType(cocoSprite.thangType) @loadThangType(lank.thangType)
@addDefaultActionsToRender(cocoSprite) @addDefaultActionsToRender(lank)
@setImageObjectToCocoSprite(cocoSprite) @setSpriteToLank(lank)
@updateLayerOrder() @updateLayerOrder()
cocoSprite.addHealthBar() lank.addHealthBar()
removeCocoSprite: (cocoSprite) -> removeLank: (lank) ->
@stopListening(cocoSprite) @stopListening(lank)
@container.removeChild cocoSprite.imageObject @container.removeChild lank.sprite
@cocoSprites = _.without @cocoSprites, cocoSprite @lanks = _.without @lanks, lank
#- Loading network resources dynamically #- Loading network resources dynamically
@ -180,24 +180,24 @@ module.exports = LayerAdapter = class LayerAdapter extends CocoClass
somethingLoaded: (thangType) -> somethingLoaded: (thangType) ->
@numThingsLoading-- @numThingsLoading--
@loadThangType(thangType) # might need to load the raster image object @loadThangType(thangType) # might need to load the raster image object
for cocoSprite in @cocoSprites for lank in @lanks
if cocoSprite.thangType is thangType if lank.thangType is thangType
@addDefaultActionsToRender(cocoSprite) @addDefaultActionsToRender(lank)
@renderNewSpriteSheet() @renderNewSpriteSheet()
#- Adding to the list of things we need to render #- Adding to the list of things we need to render
onActionNeedsRender: (cocoSprite, action) -> onActionNeedsRender: (lank, action) ->
@upsertActionToRender(cocoSprite.thangType, action.name, cocoSprite.options.colorConfig) @upsertActionToRender(lank.thangType, action.name, lank.options.colorConfig)
addDefaultActionsToRender: (cocoSprite) -> addDefaultActionsToRender: (lank) ->
needToRender = false needToRender = false
if cocoSprite.thangType.get('raster') if lank.thangType.get('raster')
@upsertActionToRender(cocoSprite.thangType) @upsertActionToRender(lank.thangType)
else else
for action in _.values(cocoSprite.thangType.getActions()) for action in _.values(lank.thangType.getActions())
continue unless _.any @defaultActions, (prefix) -> action.name.startsWith(prefix) continue unless _.any @defaultActions, (prefix) -> action.name.startsWith(prefix)
@upsertActionToRender(cocoSprite.thangType, action.name, cocoSprite.options.colorConfig) @upsertActionToRender(lank.thangType, action.name, lank.options.colorConfig)
upsertActionToRender: (thangType, actionName, colorConfig) -> upsertActionToRender: (thangType, actionName, colorConfig) ->
groupKey = @renderGroupingKey(thangType, actionName, colorConfig) groupKey = @renderGroupingKey(thangType, actionName, colorConfig)
@ -279,10 +279,10 @@ module.exports = LayerAdapter = class LayerAdapter extends CocoClass
@spriteSheet.resolutionFactor = @resolutionFactor @spriteSheet.resolutionFactor = @resolutionFactor
oldLayer = @container oldLayer = @container
@container = new createjs.SpriteContainer(@spriteSheet) @container = new createjs.SpriteContainer(@spriteSheet)
for cocoSprite in @cocoSprites for lank in @lanks
console.log 'zombie sprite found on layer', @name if cocoSprite.destroyed console.log 'zombie sprite found on layer', @name if lank.destroyed
continue if cocoSprite.destroyed continue if lank.destroyed
@setImageObjectToCocoSprite(cocoSprite) @setSpriteToLank(lank)
for prop in ['scaleX', 'scaleY', 'regX', 'regY'] for prop in ['scaleX', 'scaleY', 'regX', 'regY']
@container[prop] = oldLayer[prop] @container[prop] = oldLayer[prop]
if parent = oldLayer.parent if parent = oldLayer.parent
@ -291,12 +291,20 @@ module.exports = LayerAdapter = class LayerAdapter extends CocoClass
parent.addChildAt(@container, index) parent.addChildAt(@container, index)
@camera?.updateZoom(true) @camera?.updateZoom(true)
@updateLayerOrder() @updateLayerOrder()
for cocoSprite in @cocoSprites for lank in @lanks
cocoSprite.options.resolutionFactor = @resolutionFactor lank.options.resolutionFactor = @resolutionFactor
cocoSprite.updateScale() lank.updateScale()
cocoSprite.updateRotation() lank.updateRotation()
@trigger 'new-spritesheet' @trigger 'new-spritesheet'
resetSpriteSheet: ->
@removeLank(lank) for lank in @lanks.slice(0)
@toRenderBundles = []
@actionRenderState = {}
@initializing = true
@spriteSheet = @_renderNewSpriteSheet(false) # builds an empty spritesheet
@initializing = false
#- Placeholder #- Placeholder
createPlaceholder: -> createPlaceholder: ->
@ -326,6 +334,7 @@ module.exports = LayerAdapter = class LayerAdapter extends CocoClass
#- Rendering containers for segmented thang types #- Rendering containers for segmented thang types
renderSegmentedThangType: (thangType, colorConfig, actionNames, spriteSheetBuilder) -> renderSegmentedThangType: (thangType, colorConfig, actionNames, spriteSheetBuilder) ->
console.log 'rendering segmented thang type'
containersToRender = {} containersToRender = {}
for actionName in actionNames for actionName in actionNames
action = _.find(thangType.getActions(), {name: actionName}) action = _.find(thangType.getActions(), {name: actionName})
@ -356,6 +365,7 @@ module.exports = LayerAdapter = class LayerAdapter extends CocoClass
#- Rendering sprite sheets for singular thang types #- Rendering sprite sheets for singular thang types
renderSingularThangType: (thangType, colorConfig, actionNames, spriteSheetBuilder) -> renderSingularThangType: (thangType, colorConfig, actionNames, spriteSheetBuilder) ->
console.log 'rendering singular thang type'
actionObjects = _.values(thangType.getActions()) actionObjects = _.values(thangType.getActions())
animationActions = [] animationActions = []
for a in actionObjects for a in actionObjects
@ -441,10 +451,10 @@ module.exports = LayerAdapter = class LayerAdapter extends CocoClass
frame = spriteSheetBuilder.addFrame(bm, null, scale) frame = spriteSheetBuilder.addFrame(bm, null, scale)
spriteSheetBuilder.addAnimation(@renderGroupingKey(thangType), [frame], false) spriteSheetBuilder.addAnimation(@renderGroupingKey(thangType), [frame], false)
#- Distributing new Segmented/Singular/RasterSprites to CocoSprites #- Distributing new Segmented/Singular/RasterSprites to Lanks
setImageObjectToCocoSprite: (cocoSprite) -> setSpriteToLank: (lank) ->
if not cocoSprite.thangType.isFullyLoaded() if not lank.thangType.isFullyLoaded()
# just give a placeholder # just give a placeholder
sprite = new createjs.Sprite(@spriteSheet) sprite = new createjs.Sprite(@spriteSheet)
sprite.gotoAndStop(0) sprite.gotoAndStop(0)
@ -453,26 +463,26 @@ module.exports = LayerAdapter = class LayerAdapter extends CocoClass
sprite.regY = @resolutionFactor * SPRITE_PLACEHOLDER_WIDTH sprite.regY = @resolutionFactor * SPRITE_PLACEHOLDER_WIDTH
sprite.baseScaleX = sprite.baseScaleY = sprite.scaleX = sprite.scaleY = 10 / (@resolutionFactor * SPRITE_PLACEHOLDER_WIDTH) sprite.baseScaleX = sprite.baseScaleY = sprite.scaleX = sprite.scaleY = 10 / (@resolutionFactor * SPRITE_PLACEHOLDER_WIDTH)
else if cocoSprite.thangType.get('raster') else if lank.thangType.get('raster')
sprite = new createjs.Sprite(@spriteSheet) sprite = new createjs.Sprite(@spriteSheet)
scale = cocoSprite.thangType.get('scale') or 1 scale = lank.thangType.get('scale') or 1
reg = cocoSprite.getOffset 'registration' reg = lank.getOffset 'registration'
sprite.regX = -reg.x * scale sprite.regX = -reg.x * scale
sprite.regY = -reg.y * scale sprite.regY = -reg.y * scale
sprite.gotoAndStop(@renderGroupingKey(cocoSprite.thangType)) sprite.gotoAndStop(@renderGroupingKey(lank.thangType))
sprite.baseScaleX = sprite.baseScaleY = 1 sprite.baseScaleX = sprite.baseScaleY = 1
else else
SpriteClass = if (cocoSprite.thangType.get('spriteType') or @defaultSpriteType) is 'segmented' then SegmentedSprite else SingularSprite SpriteClass = if (lank.thangType.get('spriteType') or @defaultSpriteType) is 'segmented' then SegmentedSprite else SingularSprite
prefix = @renderGroupingKey(cocoSprite.thangType, null, cocoSprite.options.colorConfig) + '.' prefix = @renderGroupingKey(lank.thangType, null, lank.options.colorConfig) + '.'
sprite = new SpriteClass(@spriteSheet, cocoSprite.thangType, prefix, @resolutionFactor) sprite = new SpriteClass(@spriteSheet, lank.thangType, prefix, @resolutionFactor)
sprite.sprite = cocoSprite sprite.lank = lank
sprite.camera = @camera sprite.camera = @camera
sprite.layerPriority = cocoSprite.thang?.layerPriority ? cocoSprite.thangType.get 'layerPriority' sprite.layerPriority = lank.thang?.layerPriority ? lank.thangType.get 'layerPriority'
sprite.name = cocoSprite.thang?.spriteName or cocoSprite.thangType.get 'name' sprite.name = lank.thang?.spriteName or lank.thangType.get 'name'
cocoSprite.setImageObject(sprite) lank.setSprite(sprite)
cocoSprite.update(true) lank.update(true)
@container.addChild(sprite) @container.addChild(sprite)
renderGroupingKey: (thangType, grouping, colorConfig) -> renderGroupingKey: (thangType, grouping, colorConfig) ->

View file

@ -11,7 +11,7 @@ module.exports = class Mark extends CocoClass
super() super()
options ?= {} options ?= {}
@name = options.name @name = options.name
@sprite = options.sprite @lank = options.lank
@camera = options.camera @camera = options.camera
@layer = options.layer @layer = options.layer
@thangType = options.thangType @thangType = options.thangType
@ -22,46 +22,46 @@ module.exports = class Mark extends CocoClass
@build() @build()
destroy: -> destroy: ->
createjs.Tween.removeTweens @mark if @mark createjs.Tween.removeTweens @sprite if @sprite
@mark?.parent?.removeChild @mark @sprite?.parent?.removeChild @sprite
if @markSprite if @markLank
@layer.removeCocoSprite(@markSprite) @layer.removeLank(@markLank)
@markSprite.destroy() @markLank.destroy()
@sprite = null @lank = null
super() super()
toString: -> "<Mark #{@name}: Sprite #{@sprite?.thang?.id ? 'None'}>" toString: -> "<Mark #{@name}: Sprite #{@lank?.thang?.id ? 'None'}>"
onLayerMadeSpriteSheet: -> onLayerMadeSpriteSheet: ->
return unless @mark return unless @sprite
return @update() if @markSprite return @update() if @markLank
# need to update the mark display object manually... # need to update the mark display object manually...
@mark = null @sprite = null
@build() @build()
@layer.addChild @mark @layer.addChild @sprite
@layer.updateLayerOrder() @layer.updateLayerOrder()
toggle: (to) -> toggle: (to) ->
to = !!to to = !!to
return @ if to is @on return @ if to is @on
return @toggleTo = to unless @mark return @toggleTo = to unless @sprite
@on = to @on = to
delete @toggleTo delete @toggleTo
if @on if @on
if @markSprite if @markLank
@layer.addCocoSprite(@markSprite) @layer.addLank(@markLank)
else else
@layer.addChild @mark @layer.addChild @sprite
@layer.updateLayerOrder() @layer.updateLayerOrder()
else else
if @markSprite if @markLank
@layer.removeCocoSprite(@markSprite) @layer.removeLank(@markLank)
else else
@layer.removeChild @mark @layer.removeChild @sprite
if @highlightTween if @highlightTween
@highlightDelay = @highlightTween = null @highlightDelay = @highlightTween = null
createjs.Tween.removeTweens @mark createjs.Tween.removeTweens @sprite
@mark.visible = true @sprite.visible = true
@ @
setLayer: (layer) -> setLayer: (layer) ->
@ -71,34 +71,34 @@ module.exports = class Mark extends CocoClass
@layer = layer @layer = layer
@toggle true if wasOn @toggle true if wasOn
setSprite: (sprite) -> setLank: (lank) ->
return if sprite is @sprite return if lank is @lank
@sprite = sprite @lank = lank
@build() @build()
@ @
build: -> build: ->
unless @mark unless @sprite
if @name is 'bounds' then @buildBounds() if @name is 'bounds' then @buildBounds()
else if @name is 'shadow' then @buildShadow() else if @name is 'shadow' then @buildShadow()
else if @name is 'debug' then @buildDebug() else if @name is 'debug' then @buildDebug()
else if @name.match(/.+(Range|Distance|Radius)$/) then @buildRadius(@name) else if @name.match(/.+(Range|Distance|Radius)$/) then @buildRadius(@name)
else if @thangType then @buildSprite() else if @thangType then @buildSprite()
else console.error 'Don\'t know how to build mark for', @name else console.error 'Don\'t know how to build mark for', @name
@mark?.mouseEnabled = false @sprite?.mouseEnabled = false
@ @
buildBounds: -> buildBounds: ->
@mark = new createjs.Container() @sprite = new createjs.Container()
@mark.mouseChildren = false @sprite.mouseChildren = false
style = @sprite.thang.drawsBoundsStyle style = @lank.thang.drawsBoundsStyle
@drawsBoundsIndex = @sprite.thang.drawsBoundsIndex @drawsBoundsIndex = @lank.thang.drawsBoundsIndex
return if style is 'corner-text' and @sprite.thang.world.age is 0 return if style is 'corner-text' and @lank.thang.world.age is 0
# Confusingly make some semi-random colors that'll be consistent based on the drawsBoundsIndex # Confusingly make some semi-random colors that'll be consistent based on the drawsBoundsIndex
colors = (128 + Math.floor(('0.'+Math.sin(3 * @drawsBoundsIndex + i).toString().substr(6)) * 128) for i in [1 ... 4]) colors = (128 + Math.floor(('0.'+Math.sin(3 * @drawsBoundsIndex + i).toString().substr(6)) * 128) for i in [1 ... 4])
color = "rgba(#{colors[0]}, #{colors[1]}, #{colors[2]}, 0.5)" color = "rgba(#{colors[0]}, #{colors[1]}, #{colors[2]}, 0.5)"
[w, h] = [@sprite.thang.width * Camera.PPM, @sprite.thang.height * Camera.PPM * @camera.y2x] [w, h] = [@lank.thang.width * Camera.PPM, @lank.thang.height * Camera.PPM * @camera.y2x]
if style in ['border-text', 'corner-text'] if style in ['border-text', 'corner-text']
@drawsBoundsBorderShape = shape = new createjs.Shape() @drawsBoundsBorderShape = shape = new createjs.Shape()
@ -108,37 +108,37 @@ module.exports = class Mark extends CocoClass
shape.graphics.beginFill color.replace('0.5', '0.25') shape.graphics.beginFill color.replace('0.5', '0.25')
else else
shape.graphics.beginFill color shape.graphics.beginFill color
if @sprite.thang.shape in ['ellipsoid', 'disc'] if @lank.thang.shape in ['ellipsoid', 'disc']
shape.drawEllipse 0, 0, w, h shape.drawEllipse 0, 0, w, h
else else
shape.graphics.drawRect -w / 2, -h / 2, w, h shape.graphics.drawRect -w / 2, -h / 2, w, h
shape.graphics.endStroke() shape.graphics.endStroke()
shape.graphics.endFill() shape.graphics.endFill()
@mark.addChild shape @sprite.addChild shape
if style is 'border-text' if style is 'border-text'
text = new createjs.Text '' + @drawsBoundsIndex, '20px Arial', color.replace('0.5', '1') text = new createjs.Text '' + @drawsBoundsIndex, '20px Arial', color.replace('0.5', '1')
text.regX = text.getMeasuredWidth() / 2 text.regX = text.getMeasuredWidth() / 2
text.regY = text.getMeasuredHeight() / 2 text.regY = text.getMeasuredHeight() / 2
text.shadow = new createjs.Shadow('#000000', 1, 1, 0) text.shadow = new createjs.Shadow('#000000', 1, 1, 0)
@mark.addChild text @sprite.addChild text
else if style is 'corner-text' else if style is 'corner-text'
return if @sprite.thang.world.age is 0 return if @lank.thang.world.age is 0
letter = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'[@drawsBoundsIndex % 26] letter = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'[@drawsBoundsIndex % 26]
text = new createjs.Text letter, '14px Arial', '#333333' # color.replace('0.5', '1') text = new createjs.Text letter, '14px Arial', '#333333' # color.replace('0.5', '1')
text.x = -w / 2 + 2 text.x = -w / 2 + 2
text.y = -h / 2 + 2 text.y = -h / 2 + 2
@mark.addChild text @sprite.addChild text
else else
console.warn @sprite.thang.id, 'didn\'t know how to draw bounds style:', style console.warn @lank.thang.id, 'didn\'t know how to draw bounds style:', style
if w > 0 and h > 0 and style is 'border-text' if w > 0 and h > 0 and style is 'border-text'
@mark.cache -w / 2, -h / 2, w, h, 2 @sprite.cache -w / 2, -h / 2, w, h, 2
@lastWidth = @sprite.thang.width @lastWidth = @lank.thang.width
@lastHeight = @sprite.thang.height @lastHeight = @lank.thang.height
buildShadow: -> buildShadow: ->
shapeName = if @sprite.thang.shape in ['ellipsoid', 'disc'] then 'ellipse' else 'rect' shapeName = if @lank.thang.shape in ['ellipsoid', 'disc'] then 'ellipse' else 'rect'
key = "#{shapeName}-shadow" key = "#{shapeName}-shadow"
SHADOW_SIZE = 10 SHADOW_SIZE = 10
unless key in @layer.spriteSheet.getAnimations() unless key in @layer.spriteSheet.getAnimations()
@ -151,21 +151,21 @@ module.exports = class Mark extends CocoClass
shape.graphics.drawRect bounds... shape.graphics.drawRect bounds...
shape.graphics.endFill() shape.graphics.endFill()
@layer.addCustomGraphic(key, shape, bounds) @layer.addCustomGraphic(key, shape, bounds)
alpha = @sprite.thang?.alpha ? 1 alpha = @lank.thang?.alpha ? 1
width = (@sprite.thang?.width ? 0) + 0.5 width = (@lank.thang?.width ? 0) + 0.5
height = (@sprite.thang?.height ? 0) + 0.5 height = (@lank.thang?.height ? 0) + 0.5
longest = Math.max width, height longest = Math.max width, height
actualLongest = @sprite.thangType.get('shadow') ? longest actualLongest = @lank.thangType.get('shadow') ? longest
width = width * actualLongest / longest width = width * actualLongest / longest
height = height * actualLongest / longest height = height * actualLongest / longest
width *= Camera.PPM width *= Camera.PPM
height *= Camera.PPM * @camera.y2x # TODO: doesn't work with rotation height *= Camera.PPM * @camera.y2x # TODO: doesn't work with rotation
@mark = new createjs.Sprite(@layer.spriteSheet) @sprite = new createjs.Sprite(@layer.spriteSheet)
@mark.gotoAndStop(key) @sprite.gotoAndStop(key)
@mark.mouseEnabled = false @sprite.mouseEnabled = false
@mark.alpha = alpha @sprite.alpha = alpha
@baseScaleX = @mark.scaleX = width / (@layer.resolutionFactor * SHADOW_SIZE) @baseScaleX = @sprite.scaleX = width / (@layer.resolutionFactor * SHADOW_SIZE)
@baseScaleY = @mark.scaleY = height / (@layer.resolutionFactor * SHADOW_SIZE) @baseScaleY = @sprite.scaleY = height / (@layer.resolutionFactor * SHADOW_SIZE)
buildRadius: (range) -> buildRadius: (range) ->
alpha = 0.15 alpha = 0.15
@ -183,36 +183,36 @@ module.exports = class Mark extends CocoClass
] ]
# Find the index of this range, to find the next-smallest radius # Find the index of this range, to find the next-smallest radius
rangeNames = @sprite.ranges.map((range, index) -> rangeNames = @lank.ranges.map((range, index) ->
range['name'] range['name']
) )
i = rangeNames.indexOf(range) i = rangeNames.indexOf(range)
@mark = new createjs.Shape() @sprite = new createjs.Shape()
fillColor = colors[range] ? extraColors[i] fillColor = colors[range] ? extraColors[i]
@mark.graphics.beginFill fillColor @sprite.graphics.beginFill fillColor
# Draw the outer circle # Draw the outer circle
@mark.graphics.drawCircle 0, 0, @sprite.thang[range] * Camera.PPM @sprite.graphics.drawCircle 0, 0, @lank.thang[range] * Camera.PPM
# Cut out the hollow part if necessary # Cut out the hollow part if necessary
if i+1 < @sprite.ranges.length if i+1 < @lank.ranges.length
@mark.graphics.arc 0, 0, @sprite.ranges[i+1]['radius'], Math.PI*2, 0, true @sprite.graphics.arc 0, 0, @lank.ranges[i+1]['radius'], Math.PI*2, 0, true
@mark.graphics.endFill() @sprite.graphics.endFill()
strokeColor = fillColor.replace '' + alpha, '0.75' strokeColor = fillColor.replace '' + alpha, '0.75'
@mark.graphics.setStrokeStyle 2 @sprite.graphics.setStrokeStyle 2
@mark.graphics.beginStroke strokeColor @sprite.graphics.beginStroke strokeColor
@mark.graphics.arc 0, 0, @sprite.thang[range] * Camera.PPM, Math.PI*2, 0, true @sprite.graphics.arc 0, 0, @lank.thang[range] * Camera.PPM, Math.PI*2, 0, true
@mark.graphics.endStroke() @sprite.graphics.endStroke()
# Add perspective # Add perspective
@mark.scaleY *= @camera.y2x @sprite.scaleY *= @camera.y2x
buildDebug: -> buildDebug: ->
shapeName = if @sprite.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"
DEBUG_SIZE = 10 DEBUG_SIZE = 10
unless key in @layer.spriteSheet.getAnimations() unless key in @layer.spriteSheet.getAnimations()
@ -226,12 +226,12 @@ module.exports = class Mark extends CocoClass
shape.graphics.endFill() shape.graphics.endFill()
@layer.addCustomGraphic(key, shape, bounds) @layer.addCustomGraphic(key, shape, bounds)
@mark = new createjs.Sprite(@layer.spriteSheet) @sprite = new createjs.Sprite(@layer.spriteSheet)
@mark.gotoAndStop(key) @sprite.gotoAndStop(key)
PX = 3 PX = 3
[w, h] = [Math.max(PX, @sprite.thang.width * Camera.PPM), Math.max(PX, @sprite.thang.height * Camera.PPM) * @camera.y2x] # TODO: doesn't work with rotation [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
@mark.scaleX = w / (@layer.resolutionFactor * DEBUG_SIZE) @sprite.scaleX = w / (@layer.resolutionFactor * DEBUG_SIZE)
@mark.scaleY = h / (@layer.resolutionFactor * DEBUG_SIZE) @sprite.scaleY = h / (@layer.resolutionFactor * DEBUG_SIZE)
buildSprite: -> buildSprite: ->
if _.isString @thangType if _.isString @thangType
@ -240,13 +240,13 @@ module.exports = class Mark extends CocoClass
@thangType = thangType @thangType = thangType
return @listenToOnce(@thangType, 'sync', @onLoadedThangType) if not @thangType.loaded return @listenToOnce(@thangType, 'sync', @onLoadedThangType) if not @thangType.loaded
CocoSprite = require './CocoSprite' Lank = require './Lank'
# don't bother with making these render async for now, but maybe later for fun and more complexity of code # don't bother with making these render async for now, but maybe later for fun and more complexity of code
markSprite = new CocoSprite @thangType markLank = new Lank @thangType
markSprite.queueAction 'idle' markLank.queueAction 'idle'
@mark = markSprite.imageObject @sprite = markLank.sprite
@markSprite = markSprite @markLank = markLank
@listenTo @markSprite, 'new-image-object', (@mark) -> @listenTo @markLank, 'new-sprite', (@sprite) ->
loadThangType: -> loadThangType: ->
name = @thangType name = @thangType
@ -262,83 +262,83 @@ module.exports = class Mark extends CocoClass
Backbone.Mediator.publish 'sprite:loaded', {sprite: @} Backbone.Mediator.publish 'sprite:loaded', {sprite: @}
update: (pos=null) -> update: (pos=null) ->
return false unless @on and @mark return false unless @on and @sprite
return false if @sprite? and not @sprite.thangType.isFullyLoaded() return false if @lank? and not @lank.thangType.isFullyLoaded()
@mark.visible = not @hidden @sprite.visible = not @hidden
@updatePosition pos @updatePosition pos
@updateRotation() @updateRotation()
@updateScale() @updateScale()
if @name is 'highlight' and @highlightDelay and not @highlightTween if @name is 'highlight' and @highlightDelay and not @highlightTween
@mark.visible = false @sprite.visible = false
@highlightTween = createjs.Tween.get(@mark).to({}, @highlightDelay).call => @highlightTween = createjs.Tween.get(@sprite).to({}, @highlightDelay).call =>
@mark.visible = true @sprite.visible = true
@highlightDelay = @highlightTween = null @highlightDelay = @highlightTween = null
@updateAlpha @alpha if @name in ['shadow', 'bounds'] @updateAlpha @alpha if @name in ['shadow', 'bounds']
true true
updatePosition: (pos) -> updatePosition: (pos) ->
if @sprite?.thang and @name in ['shadow', 'debug', 'target', 'selection', 'repair'] if @lank?.thang and @name in ['shadow', 'debug', 'target', 'selection', 'repair']
pos = @camera.worldToSurface x: @sprite.thang.pos.x, y: @sprite.thang.pos.y pos = @camera.worldToSurface x: @lank.thang.pos.x, y: @lank.thang.pos.y
else else
pos ?= @sprite?.imageObject pos ?= @lank?.sprite
return unless pos return unless pos
@mark.x = pos.x @sprite.x = pos.x
@mark.y = pos.y @sprite.y = pos.y
if @statusEffect or @name is 'highlight' if @statusEffect or @name is 'highlight'
offset = @sprite.getOffset 'aboveHead' offset = @lank.getOffset 'aboveHead'
@mark.x += offset.x @sprite.x += offset.x
@mark.y += offset.y @sprite.y += offset.y
@mark.y -= 3 if @statusEffect @sprite.y -= 3 if @statusEffect
updateAlpha: (@alpha) -> updateAlpha: (@alpha) ->
return if not @mark or @name is 'debug' return if not @sprite or @name is 'debug'
if @name is 'shadow' if @name is 'shadow'
worldZ = @sprite.thang.pos.z - @sprite.thang.depth / 2 + @sprite.getBobOffset() worldZ = @lank.thang.pos.z - @lank.thang.depth / 2 + @lank.getBobOffset()
@mark.alpha = @alpha * 0.451 / Math.sqrt(worldZ / 2 + 1) @sprite.alpha = @alpha * 0.451 / Math.sqrt(worldZ / 2 + 1)
else if @name is 'bounds' else if @name is 'bounds'
@drawsBoundsBorderShape?.alpha = Math.floor @sprite.thang.alpha # Stop drawing bounds as soon as alpha is reduced at all @drawsBoundsBorderShape?.alpha = Math.floor @lank.thang.alpha # Stop drawing bounds as soon as alpha is reduced at all
else else
@mark.alpha = @alpha @sprite.alpha = @alpha
updateRotation: -> updateRotation: ->
if @name is 'debug' or (@name is 'shadow' and @sprite.thang?.shape in ['rectangle', 'box']) if @name is 'debug' or (@name is 'shadow' and @lank.thang?.shape in ['rectangle', 'box'])
@mark.rotation = -@sprite.thang.rotation * 180 / Math.PI @sprite.rotation = -@lank.thang.rotation * 180 / Math.PI
updateScale: -> updateScale: ->
if @name is 'bounds' and ((@sprite.thang.width isnt @lastWidth or @sprite.thang.height isnt @lastHeight) or (@sprite.thang.drawsBoundsIndex isnt @drawsBoundsIndex)) if @name is 'bounds' and ((@lank.thang.width isnt @lastWidth or @lank.thang.height isnt @lastHeight) or (@lank.thang.drawsBoundsIndex isnt @drawsBoundsIndex))
oldMark = @mark oldMark = @sprite
@buildBounds() @buildBounds()
oldMark.parent.addChild @mark oldMark.parent.addChild @sprite
oldMark.parent.swapChildren oldMark, @mark oldMark.parent.swapChildren oldMark, @sprite
oldMark.parent.removeChild oldMark oldMark.parent.removeChild oldMark
if @markSprite? if @markLank?
@markSprite.scaleFactor = 1.2 @markLank.scaleFactor = 1.2
@markSprite.updateScale() @markLank.updateScale()
if @name is 'shadow' and thang = @sprite.thang if @name is 'shadow' and thang = @lank.thang
@mark.scaleX = @baseScaleX * (thang.scaleFactor ? thang.scaleFactorX ? 1) @sprite.scaleX = @baseScaleX * (thang.scaleFactor ? thang.scaleFactorX ? 1)
@mark.scaleY = @baseScaleY * (thang.scaleFactor ? thang.scaleFactorY ? 1) @sprite.scaleY = @baseScaleY * (thang.scaleFactor ? thang.scaleFactorY ? 1)
return unless @name in ['selection', 'target', 'repair', 'highlight'] return unless @name in ['selection', 'target', 'repair', 'highlight']
# scale these marks to 10m (100px). Adjust based on sprite size. # scale these marks to 10m (100px). Adjust based on lank size.
factor = 0.3 # default size: 3m width, most commonly for target when pointing to a location factor = 0.3 # default size: 3m width, most commonly for target when pointing to a location
if @sprite?.imageObject if @lank?.sprite
width = @sprite.imageObject.getBounds()?.width or 0 width = @lank.sprite.getBounds()?.width or 0
width /= @sprite.options.resolutionFactor width /= @lank.options.resolutionFactor
# all targets should be set to have a width of 100px, and then be scaled accordingly # all targets should be set to have a width of 100px, and then be scaled accordingly
factor = width / 100 # normalize factor = width / 100 # normalize
factor *= 1.1 # add margin factor *= 1.1 # add margin
factor = Math.max(factor, 0.3) # lower bound factor = Math.max(factor, 0.3) # lower bound
@mark.scaleX *= factor @sprite.scaleX *= factor
@mark.scaleY *= factor @sprite.scaleY *= factor
if @name in ['selection', 'target', 'repair'] if @name in ['selection', 'target', 'repair']
@mark.scaleY *= @camera.y2x # code applies perspective @sprite.scaleY *= @camera.y2x # code applies perspective
stop: -> @markSprite?.stop() stop: -> @markLank?.stop()
play: -> @markSprite?.play() play: -> @markLank?.play()
hide: -> @hidden = true hide: -> @hidden = true
show: -> @hidden = false show: -> @hidden = false

View file

@ -1,391 +0,0 @@
CocoClass = require 'lib/CocoClass'
{me} = require 'lib/auth'
LayerAdapter = require './LayerAdapter'
IndieSprite = require 'lib/surface/IndieSprite'
WizardSprite = require 'lib/surface/WizardSprite'
FlagSprite = require 'lib/surface/FlagSprite'
CocoSprite = require 'lib/surface/CocoSprite'
Mark = require './Mark'
Grid = require 'lib/world/Grid'
module.exports = class SpriteBoss extends CocoClass
subscriptions:
'bus:player-joined': 'onPlayerJoined'
'bus:player-left': 'onPlayerLeft'
'level:set-debug': 'onSetDebug'
'sprite:highlight-sprites': 'onHighlightSprites'
'surface:stage-mouse-down': 'onStageMouseDown'
'level:select-sprite': 'onSelectSprite'
'level:suppress-selection-sounds': 'onSuppressSelectionSounds'
'level:lock-select': 'onSetLockSelect'
'level:restarted': 'onLevelRestarted'
'god:new-world-created': 'onNewWorld'
'god:streaming-world-updated': 'onNewWorld'
'camera:dragged': 'onCameraDragged'
'sprite:loaded': -> @update(true)
'level:flag-color-selected': 'onFlagColorSelected'
'level:flag-updated': 'onFlagUpdated'
'surface:flag-appeared': 'onFlagAppeared'
'surface:remove-selected-flag': 'onRemoveSelectedFlag'
constructor: (@options) ->
super()
@dragged = 0
@options ?= {}
@camera = @options.camera
@webGLStage = @options.webGLStage
@surfaceTextLayer = @options.surfaceTextLayer
@world = options.world
@options.thangTypes ?= []
@sprites = {}
@spriteArray = [] # Mirror @sprites, but faster for when we just need to iterate
@selfWizardSprite = null
@createLayers()
@pendingFlags = []
destroy: ->
@removeSprite sprite for thangID, sprite of @sprites
@targetMark?.destroy()
@selectionMark?.destroy()
super()
toString: -> "<SpriteBoss: #{@spriteArray.length} sprites>"
thangTypeFor: (type) ->
_.find @options.thangTypes, (m) -> m.get('original') is type or m.get('name') is type
createLayers: ->
@spriteLayers = {}
for [name, priority] in [
['Land', -40]
['Ground', -30]
['Obstacle', -20]
['Path', -10]
['Default', 0]
['Floating', 10]
]
@spriteLayers[name] = new LayerAdapter name: name, webGL: true, layerPriority: priority, transform: LayerAdapter.TRANSFORM_SURFACE, camera: @camera
@webGLStage.addChild (spriteLayer.container for spriteLayer in _.values(@spriteLayers))...
layerForChild: (child, sprite) ->
unless child.layerPriority?
if thang = sprite?.thang
child.layerPriority = thang.layerPriority
child.layerPriority ?= 0 if thang.isSelectable
child.layerPriority ?= -40 if thang.isLand
child.layerPriority ?= 0
return @spriteLayers['Default'] unless child.layerPriority
layer = _.findLast @spriteLayers, (layer, name) ->
layer.layerPriority <= child.layerPriority
layer ?= @spriteLayers['Land'] if child.layerPriority < -40
layer ? @spriteLayers['Default']
addSprite: (sprite, id=null, layer=null) ->
id ?= sprite.thang.id
console.error 'Sprite collision! Already have:', id if @sprites[id]
@sprites[id] = sprite
@spriteArray.push sprite
layer ?= @spriteLayers['Obstacle'] if sprite.thang?.spriteName.search(/(dungeon|indoor).wall/i) isnt -1
layer ?= @layerForChild sprite.imageObject, sprite
layer.addCocoSprite sprite
layer.updateLayerOrder()
sprite
createMarks: ->
@targetMark = new Mark name: 'target', camera: @camera, layer: @spriteLayers['Ground'], thangType: 'target'
@selectionMark = new Mark name: 'selection', camera: @camera, layer: @spriteLayers['Ground'], thangType: 'selection'
createSpriteOptions: (options) ->
_.extend options, camera: @camera, resolutionFactor: SPRITE_RESOLUTION_FACTOR, groundLayer: @spriteLayers['Ground'], textLayer: @surfaceTextLayer, floatingLayer: @spriteLayers['Floating'], showInvisible: @options.showInvisible
createIndieSprites: (indieSprites, withWizards) ->
unless @indieSprites
@indieSprites = []
@indieSprites = (@createIndieSprite indieSprite for indieSprite in indieSprites) if indieSprites
if withWizards and not @selfWizardSprite
@selfWizardSprite = @createWizardSprite thangID: 'My Wizard', isSelf: true, sprites: @sprites
createIndieSprite: (indieSprite) ->
unless thangType = @thangTypeFor indieSprite.thangType
console.warn "Need to convert #{indieSprite.id}'s ThangType #{indieSprite.thangType} to a ThangType reference. Until then, #{indieSprite.id} won't show up."
return
sprite = new IndieSprite thangType, @createSpriteOptions {thangID: indieSprite.id, pos: indieSprite.pos, sprites: @sprites, team: indieSprite.team, teamColors: @world.getTeamColors()}
@addSprite sprite, sprite.thang.id
createOpponentWizard: (opponent) ->
# TODO: colorize name and cloud by team, colorize wizard by user's color config, level-specific wizard spawn points
sprite = @createWizardSprite thangID: opponent.id, name: opponent.name, codeLanguage: opponent.codeLanguage
if not opponent.levelSlug or opponent.levelSlug is 'brawlwood'
sprite.targetPos = if opponent.team is 'ogres' then {x: 52, y: 52} else {x: 28, y: 28}
else if opponent.levelSlug in ['dungeon-arena', 'sky-span']
sprite.targetPos = if opponent.team is 'ogres' then {x: 72, y: 39} else {x: 9, y: 39}
else if opponent.levelSlug is 'criss-cross'
sprite.targetPos = if opponent.team is 'ogres' then {x: 50, y: 12} else {x: 0, y: 40}
else
sprite.targetPos = if opponent.team is 'ogres' then {x: 52, y: 28} else {x: 20, y: 28}
createWizardSprite: (options) ->
sprite = new WizardSprite @thangTypeFor('Wizard'), @createSpriteOptions(options)
@addSprite sprite, sprite.thang.id, @spriteLayers['Floating']
onPlayerJoined: (e) ->
# Create another WizardSprite, unless this player is just me
pid = e.player.id
return if pid is me.id
wiz = @createWizardSprite thangID: pid, sprites: @sprites
wiz.animateIn()
state = e.player.wizard or {}
wiz.setInitialState(state.targetPos, @sprites[state.targetSprite])
onPlayerLeft: (e) ->
pid = e.player.id
@sprites[pid]?.animateOut => @removeSprite @sprites[pid]
onSetDebug: (e) ->
return if e.debug is @debug
@debug = e.debug
sprite.setDebug @debug for sprite in @spriteArray
onHighlightSprites: (e) ->
highlightedIDs = e.thangIDs or []
for thangID, sprite of @sprites
sprite.setHighlight? thangID in highlightedIDs, e.delay
addThangToSprites: (thang, layer=null) ->
return console.warn 'Tried to add Thang to the surface it already has:', thang.id if @sprites[thang.id]
thangType = _.find @options.thangTypes, (m) ->
return false unless m.get('actions') or m.get('raster')
return m.get('name') is thang.spriteName
thangType ?= _.find @options.thangTypes, (m) -> return m.get('name') is thang.spriteName
return console.error "Couldn't find ThangType for", thang unless thangType
options = @createSpriteOptions thang: thang
options.resolutionFactor = if thangType.get('kind') is 'Floor' then 2 else SPRITE_RESOLUTION_FACTOR
sprite = new CocoSprite thangType, options
@listenTo sprite, 'sprite:mouse-up', @onSpriteMouseUp
@addSprite sprite, null, layer
sprite.setDebug @debug
sprite
removeSprite: (sprite) ->
sprite.layer.removeCocoSprite(sprite)
thang = sprite.thang
delete @sprites[sprite.thang.id]
@spriteArray.splice @spriteArray.indexOf(sprite), 1
@stopListening sprite
sprite.destroy()
sprite.thang = thang # Keep around so that we know which thang the destroyed thang was for
updateSounds: ->
sprite.playSounds() for sprite in @spriteArray # hmm; doesn't work for sprites which we didn't add yet in adjustSpriteExistence
update: (frameChanged) ->
@adjustSpriteExistence() if frameChanged
sprite.update frameChanged for sprite in @spriteArray
@updateSelection()
@spriteLayers['Default'].updateLayerOrder()
@cacheObstacles()
adjustSpriteExistence: ->
# Add anything new, remove anything old, update everything current
updatedObstacles = []
itemsJustEquipped = []
for thang in @world.thangs when thang.exists and thang.pos
itemsJustEquipped = itemsJustEquipped.concat @equipNewItems thang
if sprite = @sprites[thang.id]
sprite.setThang thang # make sure Sprite has latest Thang
else
sprite = @addThangToSprites(thang)
Backbone.Mediator.publish 'surface:new-thang-added', thang: thang, sprite: sprite
updatedObstacles.push sprite if sprite.imageObject.parent is @spriteLayers['Obstacle']
sprite.playSounds()
item.modifyStats() for item in itemsJustEquipped
for thangID, sprite of @sprites
missing = not (sprite.notOfThisWorld or @world.thangMap[thangID]?.exists)
isObstacle = sprite.imageObject.parent is @spriteLayers['Obstacle']
updatedObstacles.push sprite if isObstacle and (missing or sprite.hasMoved)
sprite.hasMoved = false
@removeSprite sprite if missing
@cacheObstacles updatedObstacles if updatedObstacles.length and @cachedObstacles
# mainly for handling selecting thangs from session when the thang is not always in existence
if @willSelectThang and @sprites[@willSelectThang[0]]
@selectThang @willSelectThang...
equipNewItems: (thang) ->
itemsJustEquipped = []
if thang.equip and not thang.equipped
thang.equip() # Pretty hacky, but needed since initialize may not be called if we're not running Systems.
itemsJustEquipped.push thang
if thang.inventoryIDs
# Even hackier: these items were only created/equipped during simulation, so we reequip here.
for slot, itemID of thang.inventoryIDs
item = @world.getThangByID itemID
unless item.equipped
console.log thang.id, 'equipping', item, 'in', thang.slot, 'Surface-side, but it cannot equip?' unless item.equip
item.equip()
itemsJustEquipped.push item
return itemsJustEquipped
cacheObstacles: (updatedObstacles=null) ->
return if @cachedObstacles and not updatedObstacles
wallSprites = (sprite for sprite in @spriteArray when sprite.thangType?.get('name').search(/(dungeon|indoor).wall/i) isnt -1)
return if _.any (s.stillLoading for s in wallSprites)
walls = (sprite.thang for sprite in wallSprites)
@world.calculateBounds()
wallGrid = new Grid walls, @world.size()...
if updatedObstacles
possiblyUpdatedWallSprites = (sprite for sprite in wallSprites when _.find updatedObstacles, (w2) -> sprite is w2 or (Math.abs(sprite.thang.pos.x - w2.thang.pos.x) + Math.abs(sprite.thang.pos.y - w2.thang.pos.y)) <= 16)
else
possiblyUpdatedWallSprites = wallSprites
#console.log 'updating up to', possiblyUpdatedWallSprites.length, 'of', wallSprites.length, 'wall sprites from updatedObstacles', updatedObstacles
for wallSprite in possiblyUpdatedWallSprites
wallSprite.updateActionDirection wallGrid
wallSprite.lockAction()
wallSprite.updateScale()
wallSprite.updatePosition()
#console.log @wallGrid.toString()
@cachedObstacles = true
spriteFor: (thangID) -> @sprites[thangID]
onNewWorld: (e) ->
@world = @options.world = e.world
play: ->
sprite.play() for sprite in @spriteArray
@selectionMark?.play()
@targetMark?.play()
stop: ->
sprite.stop() for sprite in @spriteArray
@selectionMark?.stop()
@targetMark?.stop()
# Selection
onSuppressSelectionSounds: (e) -> @suppressSelectionSounds = e.suppress
onSetLockSelect: (e) -> @selectLocked = e.lock
onLevelRestarted: (e) ->
@selectLocked = false
@selectSprite e, null
onSelectSprite: (e) ->
@selectThang e.thangID, e.spellName
onCameraDragged: ->
@dragged += 1
onSpriteMouseUp: (e) ->
return if key.shift #and @options.choosing
return @dragged = 0 if @dragged > 3
@dragged = 0
sprite = if e.sprite?.thang?.isSelectable then e.sprite else null
return if @flagCursorSprite and sprite?.thangType.get('name') is 'Flag'
@selectSprite e, sprite
onStageMouseDown: (e) ->
return if key.shift #and @options.choosing
@selectSprite e if e.onBackground
selectThang: (thangID, spellName=null, treemaThangSelected = null) ->
return @willSelectThang = [thangID, spellName] unless @sprites[thangID]
@selectSprite null, @sprites[thangID], spellName, treemaThangSelected
selectSprite: (e, sprite=null, spellName=null, treemaThangSelected = null) ->
return if e and (@disabled or @selectLocked) # Ignore clicks for selection/panning/wizard movement while disabled or select is locked
worldPos = sprite?.thang?.pos
worldPos ?= @camera.screenToWorld {x: e.originalEvent.rawX, y: e.originalEvent.rawY} if e?.originalEvent
if worldPos and (@options.navigateToSelection or not sprite or treemaThangSelected) and e?.originalEvent?.nativeEvent?.which isnt 3
@camera.zoomTo(sprite?.imageObject or @camera.worldToSurface(worldPos), @camera.zoom, 1000, true)
sprite = null if @options.choosing # Don't select sprites while choosing
if sprite isnt @selectedSprite
@selectedSprite?.selected = false
sprite?.selected = true
@selectedSprite = sprite
alive = sprite and not (sprite.thang.health < 0)
Backbone.Mediator.publish 'surface:sprite-selected',
thang: if sprite then sprite.thang else null
sprite: sprite
spellName: spellName ? e?.spellName
originalEvent: e
worldPos: worldPos
@willSelectThang = null if sprite # Now that we've done a real selection, don't reselect some other Thang later.
if alive and not @suppressSelectionSounds
instance = sprite.playSound 'selected'
if instance?.playState is 'playSucceeded'
Backbone.Mediator.publish 'sprite:thang-began-talking', thang: sprite?.thang
instance.addEventListener 'complete', ->
Backbone.Mediator.publish 'sprite:thang-finished-talking', thang: sprite?.thang
onFlagColorSelected: (e) ->
@removeSprite @flagCursorSprite if @flagCursorSprite
@flagCursorSprite = null
for flagSprite in @spriteArray when flagSprite.thangType.get('name') is 'Flag'
flagSprite.imageObject.cursor = if e.color then 'crosshair' else 'pointer'
return unless e.color
@flagCursorSprite = new FlagSprite @thangTypeFor('Flag'), @createSpriteOptions(thangID: 'Flag Cursor', color: e.color, team: me.team, isCursor: true, pos: e.pos)
@addSprite @flagCursorSprite, @flagCursorSprite.thang.id, @spriteLayers['Floating']
onFlagUpdated: (e) ->
return unless e.active
pendingFlag = new FlagSprite @thangTypeFor('Flag'), @createSpriteOptions(thangID: 'Pending Flag ' + Math.random(), color: e.color, team: e.team, isCursor: false, pos: e.pos)
@addSprite pendingFlag, pendingFlag.thang.id, @spriteLayers['Floating']
@pendingFlags.push pendingFlag
onFlagAppeared: (e) ->
# Remove the pending flag that matches this one's color/team/position, and any color/team matches placed earlier.
t1 = e.sprite.thang
pending = (@pendingFlags ? []).slice()
foundExactMatch = false
for i in [pending.length - 1 .. 0] by -1
pendingFlag = pending[i]
t2 = pendingFlag.thang
matchedType = t1.color is t2.color and t1.team is t2.team
matched = matchedType and (foundExactMatch or Math.abs(t1.pos.x - t2.pos.x) < 0.00001 and Math.abs(t1.pos.y - t2.pos.y) < 0.00001)
if matched
foundExactMatch = true
@pendingFlags.splice(i, 1)
@removeSprite pendingFlag
e.sprite.imageObject.cursor = if @flagCursorSprite then 'crosshair' else 'pointer'
null
onRemoveSelectedFlag: (e) ->
# Remove the selected sprite if it's a flag, or any flag of the given color if a color is given.
flagSprite = _.find [@selectedSprite].concat(@spriteArray), (sprite) ->
sprite and sprite.thangType.get('name') is 'Flag' and sprite.thang.team is me.team and (sprite.thang.color is e.color or not e.color) and not sprite.notOfThisWorld
return unless flagSprite
Backbone.Mediator.publish 'surface:remove-flag', color: flagSprite.thang.color
# Marks
updateSelection: ->
if @selectedSprite?.thang and (not @selectedSprite.thang.exists or not @world.getThangByID @selectedSprite.thang.id)
thangID = @selectedSprite.thang.id
@selectedSprite = null # Don't actually trigger deselection, but remove the selected sprite.
@selectionMark?.toggle false
@willSelectThang = [thangID, null]
@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.container
@selectionMark.setLayer @spriteLayers.Default
else if @selectedSprite
@selectionMark.setLayer @spriteLayers.Ground
@selectionMark.toggle @selectedSprite?
@selectionMark.setSprite @selectedSprite
@selectionMark.update()
updateTarget: ->
return unless @targetMark
thang = @selectedSprite?.thang
target = thang?.target
targetPos = thang?.targetPos
targetPos = null if targetPos?.isZero?() # Null targetPos get serialized as (0, 0, 0)
@targetMark.setSprite if target then @sprites[target.id] else null
@targetMark.toggle @targetMark.sprite or targetPos
@targetMark.update if targetPos then @camera.worldToSurface targetPos else null

View file

@ -14,7 +14,7 @@ WaitingScreen = require './WaitingScreen'
DebugDisplay = require './DebugDisplay' DebugDisplay = require './DebugDisplay'
CoordinateDisplay = require './CoordinateDisplay' CoordinateDisplay = require './CoordinateDisplay'
CoordinateGrid = require './CoordinateGrid' CoordinateGrid = require './CoordinateGrid'
SpriteBoss = require './SpriteBoss' LankBoss = require './LankBoss'
PointChooser = require './PointChooser' PointChooser = require './PointChooser'
RegionChooser = require './RegionChooser' RegionChooser = require './RegionChooser'
MusicPlayer = require './MusicPlayer' MusicPlayer = require './MusicPlayer'
@ -30,7 +30,7 @@ module.exports = Surface = class Surface extends CocoClass
screenLayer: null screenLayer: null
gridLayer: null gridLayer: null
spriteBoss: null lankBoss: null
debugDisplay: null debugDisplay: null
currentFrame: 0 currentFrame: 0
@ -111,7 +111,7 @@ module.exports = Surface = class Surface extends CocoClass
canvasHeight = parseInt @normalCanvas.attr('height'), 10 canvasHeight = parseInt @normalCanvas.attr('height'), 10
@screenLayer.addChild new Letterbox canvasWidth: canvasWidth, canvasHeight: canvasHeight @screenLayer.addChild new Letterbox canvasWidth: canvasWidth, canvasHeight: canvasHeight
@spriteBoss = new SpriteBoss camera: @camera, webGLStage: @webGLStage, surfaceTextLayer: @surfaceTextLayer, world: @world, thangTypes: @options.thangTypes, choosing: @options.choosing, navigateToSelection: @options.navigateToSelection, showInvisible: @options.showInvisible @lankBoss = new LankBoss camera: @camera, webGLStage: @webGLStage, surfaceTextLayer: @surfaceTextLayer, world: @world, thangTypes: @options.thangTypes, choosing: @options.choosing, navigateToSelection: @options.navigateToSelection, showInvisible: @options.showInvisible
@countdownScreen = new CountdownScreen camera: @camera, layer: @screenLayer @countdownScreen = new CountdownScreen camera: @camera, layer: @screenLayer
@playbackOverScreen = new PlaybackOverScreen camera: @camera, layer: @screenLayer @playbackOverScreen = new PlaybackOverScreen camera: @camera, layer: @screenLayer
@waitingScreen = new WaitingScreen camera: @camera, layer: @screenLayer @waitingScreen = new WaitingScreen camera: @camera, layer: @screenLayer
@ -146,7 +146,7 @@ module.exports = Surface = class Surface extends CocoClass
setWorld: (@world) -> setWorld: (@world) ->
@worldLoaded = true @worldLoaded = true
@spriteBoss.world = @world @lankBoss.world = @world
@restoreWorldState() unless @options.choosing @restoreWorldState() unless @options.choosing
@showLevel() @showLevel()
@updateState true if @loaded @updateState true if @loaded
@ -156,15 +156,15 @@ module.exports = Surface = class Surface extends CocoClass
return if @destroyed return if @destroyed
return if @loaded return if @loaded
@loaded = true @loaded = true
@spriteBoss.createMarks() @lankBoss.createMarks()
@spriteBoss.createIndieSprites @world.indieSprites, @options.wizards @lankBoss.createIndieLanks @world.indieSprites, @options.wizards
@updateState true @updateState true
@drawCurrentFrame() @drawCurrentFrame()
createjs.Ticker.addEventListener 'tick', @tick createjs.Ticker.addEventListener 'tick', @tick
Backbone.Mediator.publish 'level:started', {} Backbone.Mediator.publish 'level:started', {}
createOpponentWizard: (opponent) -> createOpponentWizard: (opponent) ->
@spriteBoss.createOpponentWizard opponent @lankBoss.createOpponentWizard opponent
@ -224,15 +224,15 @@ module.exports = Surface = class Surface extends CocoClass
ratio = current % 1 ratio = current % 1
@world.frames[next].restorePartialState ratio if next > 1 @world.frames[next].restorePartialState ratio if next > 1
frame.clearEvents() if parseInt(@currentFrame) is parseInt(@lastFrame) frame.clearEvents() if parseInt(@currentFrame) is parseInt(@lastFrame)
@spriteBoss.updateSounds() if parseInt(@currentFrame) isnt parseInt(@lastFrame) @lankBoss.updateSounds() if parseInt(@currentFrame) isnt parseInt(@lastFrame)
updateState: (frameChanged) -> updateState: (frameChanged) ->
# world state must have been restored in @restoreWorldState # world state must have been restored in @restoreWorldState
if @playing and @heroSprite and not @mouseIsDown and @camera.newTarget isnt @heroSprite.imageObject and @camera.target isnt @heroSprite.imageObject if @playing and @heroSprite and not @mouseIsDown and @camera.newTarget isnt @heroSprite.sprite and @camera.target isnt @heroSprite.sprite
@camera.zoomTo @heroSprite.imageObject, @camera.zoom, 750 @camera.zoomTo @heroSprite.sprite, @camera.zoom, 750
@camera.updateZoom() @camera.updateZoom()
@spriteBoss.update frameChanged @lankBoss.update frameChanged
@dimmer?.setSprites @spriteBoss.sprites @dimmer?.setSprites @lankBoss.sprites
drawCurrentFrame: (e) -> drawCurrentFrame: (e) ->
++@totalFramesDrawn ++@totalFramesDrawn
@ -288,12 +288,12 @@ module.exports = Surface = class Surface extends CocoClass
frame = @world.getFrame(@getCurrentFrame()) frame = @world.getFrame(@getCurrentFrame())
frame.restoreState() frame.restoreState()
volume = Math.max(0.05, Math.min(1, 1 / @scrubbingPlaybackSpeed)) volume = Math.max(0.05, Math.min(1, 1 / @scrubbingPlaybackSpeed))
sprite.playSounds false, volume for sprite in @spriteBoss.spriteArray sprite.playSounds false, volume for sprite in @lankBoss.spriteArray
tempFrame += if rising then 1 else -1 tempFrame += if rising then 1 else -1
@currentFrame = actualCurrentFrame @currentFrame = actualCurrentFrame
@restoreWorldState() @restoreWorldState()
@spriteBoss.update true @lankBoss.update true
@onFrameChanged() @onFrameChanged()
getCurrentFrame: -> getCurrentFrame: ->
@ -310,11 +310,11 @@ module.exports = Surface = class Surface extends CocoClass
@surfacePauseTimeout = @surfaceZoomPauseTimeout = null @surfacePauseTimeout = @surfaceZoomPauseTimeout = null
if paused if paused
@surfacePauseTimeout = _.delay performToggle, 2000 @surfacePauseTimeout = _.delay performToggle, 2000
@spriteBoss.stop() @lankBoss.stop()
@playbackOverScreen.show() @playbackOverScreen.show()
else else
performToggle() performToggle()
@spriteBoss.play() @lankBoss.play()
@playbackOverScreen.hide() @playbackOverScreen.hide()
@ -327,7 +327,7 @@ module.exports = Surface = class Surface extends CocoClass
return if @currentFrame is @lastFrame and not force return if @currentFrame is @lastFrame and not force
progress = @getProgress() progress = @getProgress()
Backbone.Mediator.publish('surface:frame-changed', Backbone.Mediator.publish('surface:frame-changed',
selectedThang: @spriteBoss.selectedSprite?.thang selectedThang: @lankBoss.selectedSprite?.thang
progress: progress progress: progress
frame: @currentFrame frame: @currentFrame
world: @world world: @world
@ -365,7 +365,7 @@ module.exports = Surface = class Surface extends CocoClass
onSetCamera: (e) -> onSetCamera: (e) ->
if e.thangID if e.thangID
return unless target = @spriteBoss.spriteFor(e.thangID)?.imageObject return unless target = @lankBoss.spriteFor(e.thangID)?.sprite
else if e.pos else if e.pos
target = @camera.worldToSurface e.pos target = @camera.worldToSurface e.pos
else else
@ -383,7 +383,7 @@ module.exports = Surface = class Surface extends CocoClass
return if e.controls and not ('surface' in e.controls) return if e.controls and not ('surface' in e.controls)
@setDisabled true @setDisabled true
@dimmer ?= new Dimmer camera: @camera, layer: @screenLayer @dimmer ?= new Dimmer camera: @camera, layer: @screenLayer
@dimmer.setSprites @spriteBoss.sprites @dimmer.setSprites @lankBoss.sprites
onEnableControls: (e) -> onEnableControls: (e) ->
return if e.controls and not ('surface' in e.controls) return if e.controls and not ('surface' in e.controls)
@ -393,7 +393,7 @@ module.exports = Surface = class Surface extends CocoClass
@setDisabled e.on @setDisabled e.on
setDisabled: (@disabled) -> setDisabled: (@disabled) ->
@spriteBoss.disabled = @disabled @lankBoss.disabled = @disabled
onSetPlaying: (e) -> onSetPlaying: (e) ->
@playing = (e ? {}).playing ? true @playing = (e ? {}).playing ? true
@ -433,7 +433,7 @@ module.exports = Surface = class Surface extends CocoClass
onStreamingWorldUpdated: (event) -> onStreamingWorldUpdated: (event) ->
@casting = false @casting = false
@spriteBoss.play() @lankBoss.play()
# This has a tendency to break scripts that are waiting for playback to change when the level is loaded # 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. # so only run it after the first world is created.
@ -535,7 +535,7 @@ module.exports = Surface = class Surface extends CocoClass
#- Camera focus on hero #- Camera focus on hero
focusOnHero: -> focusOnHero: ->
@heroSprite = @spriteBoss.spriteFor 'Hero Placeholder' @heroSprite = @lankBoss.spriteFor 'Hero Placeholder'
#- Real-time playback #- Real-time playback
@ -547,18 +547,18 @@ module.exports = Surface = class Surface extends CocoClass
return if @realTime return if @realTime
@realTime = true @realTime = true
@onResize() @onResize()
@spriteBoss.selfWizardSprite?.toggle false @lankBoss.selfWizardLank?.toggle false
@playing = false # Will start when countdown is done. @playing = false # Will start when countdown is done.
if @heroSprite if @heroSprite
@previousCameraZoom = @camera.zoom @previousCameraZoom = @camera.zoom
@camera.zoomTo @heroSprite.imageObject, 4, 3000 @camera.zoomTo @heroSprite.sprite, 4, 3000
onRealTimePlaybackEnded: (e) -> onRealTimePlaybackEnded: (e) ->
return unless @realTime return unless @realTime
@realTime = false @realTime = false
@onResize() @onResize()
_.delay @onResize, resizeDelay + 100 # Do it again just to be double sure that we don't stay zoomed in due to timing problems. _.delay @onResize, resizeDelay + 100 # Do it again just to be double sure that we don't stay zoomed in due to timing problems.
@spriteBoss.selfWizardSprite?.toggle true @lankBoss.selfWizardLank?.toggle true
@normalCanvas.add(@webGLCanvas).removeClass 'flag-color-selected' @normalCanvas.add(@webGLCanvas).removeClass 'flag-color-selected'
if @previousCameraZoom if @previousCameraZoom
@camera.zoomTo @camera.newTarget or @camera.target, @previousCameraZoom, 3000 @camera.zoomTo @camera.newTarget or @camera.target, @previousCameraZoom, 3000
@ -569,22 +569,22 @@ module.exports = Surface = class Surface extends CocoClass
#- Paths - TODO: move to SpriteBoss? but only update on frame drawing instead of on every frame update? #- Paths - TODO: move to LankBoss? but only update on frame drawing instead of on every frame update?
updatePaths: -> updatePaths: ->
return # TODO: Get paths working again with WebGL return # TODO: Get paths working again with WebGL
return unless @options.paths return unless @options.paths
return if @casting return if @casting
@hidePaths() @hidePaths()
selectedThang = @spriteBoss.selectedSprite?.thang selectedThang = @lankBoss.selectedSprite?.thang
return if @world.showPaths is 'never' return if @world.showPaths is 'never'
return if @world.showPaths is 'paused' and @playing return if @world.showPaths is 'paused' and @playing
return if @world.showPaths is 'selected' and not selectedThang return if @world.showPaths is 'selected' and not selectedThang
@trailmaster ?= new path.Trailmaster @camera @trailmaster ?= new path.Trailmaster @camera
selectedOnly = @playing and @world.showPaths is 'selected' selectedOnly = @playing and @world.showPaths is 'selected'
@paths = @trailmaster.generatePaths @world, @getCurrentFrame(), selectedThang, @spriteBoss.sprites, selectedOnly @paths = @trailmaster.generatePaths @world, @getCurrentFrame(), selectedThang, @lankBoss.sprites, selectedOnly
@paths.name = 'paths' @paths.name = 'paths'
@spriteBoss.spriteLayers['Path'].addChild @paths @lankBoss.layerAdapters['Path'].addChild @paths
hidePaths: -> hidePaths: ->
return if not @paths return if not @paths
@ -681,7 +681,7 @@ module.exports = Surface = class Surface extends CocoClass
createjs.Ticker.removeEventListener('tick', @tick) createjs.Ticker.removeEventListener('tick', @tick)
createjs.Sound.stop() createjs.Sound.stop()
layer.destroy() for layer in @normalLayers layer.destroy() for layer in @normalLayers
@spriteBoss.destroy() @lankBoss.destroy()
@chooser?.destroy() @chooser?.destroy()
@dimmer?.destroy() @dimmer?.destroy()
@countdownScreen?.destroy() @countdownScreen?.destroy()

View file

@ -1,7 +1,7 @@
IndieSprite = require 'lib/surface/IndieSprite' IndieLank = require 'lib/surface/IndieLank'
{me} = require 'lib/auth' {me} = require 'lib/auth'
module.exports = class WizardSprite extends IndieSprite module.exports = class WizardLank extends IndieLank
# Wizard targets are constantly changing, so a simple tween doesn't work. # Wizard targets are constantly changing, so a simple tween doesn't work.
# Instead, the wizard stores its origin point and the (possibly) moving target. # Instead, the wizard stores its origin point and the (possibly) moving target.
# Then it figures out its current position based on tween percentage and # Then it figures out its current position based on tween percentage and
@ -9,7 +9,7 @@ module.exports = class WizardSprite extends IndieSprite
tweenPercentage: 1.0 tweenPercentage: 1.0
originPos: null originPos: null
targetPos: null targetPos: null
targetSprite: null targetLank: null
reachedTarget: true reachedTarget: true
spriteXOffset: 4 # meters from target sprite spriteXOffset: 4 # meters from target sprite
spriteYOffset: 0 # meters from target sprite spriteYOffset: 0 # meters from target sprite
@ -17,8 +17,8 @@ module.exports = class WizardSprite extends IndieSprite
subscriptions: subscriptions:
'bus:player-states-changed': 'onPlayerStatesChanged' 'bus:player-states-changed': 'onPlayerStatesChanged'
'auth:me-synced': 'onMeSynced' 'auth:me-synced': 'onMeSynced'
'surface:sprite-selected': 'onSpriteSelected' 'surface:sprite-selected': 'onLankSelected'
'sprite:echo-all-wizard-sprites': 'onEchoAllWizardSprites' 'sprite:echo-all-wizard-sprites': 'onEchoAllWizardLanks'
shortcuts: shortcuts:
'up': 'onMoveKey' 'up': 'onMoveKey'
@ -57,7 +57,7 @@ module.exports = class WizardSprite extends IndieSprite
super name super name
toggle: (to) -> toggle: (to) ->
@imageObject?.visible = to @sprite?.visible = to
label[if to then 'show' else 'hide']() for name, label of @labels label[if to then 'show' else 'hide']() for name, label of @labels
mark.mark?.visible = to for name, mark of @marks mark.mark?.visible = to for name, mark of @marks
@ -65,37 +65,37 @@ module.exports = class WizardSprite extends IndieSprite
for playerID, state of e.states for playerID, state of e.states
continue unless playerID is @thang.id continue unless playerID is @thang.id
@setEditing state.wizard?.editing @setEditing state.wizard?.editing
continue if playerID is me.id # ignore changes for self wizard sprite continue if playerID is me.id # ignore changes for self wizard lank
@setNameLabel state.name @setNameLabel state.name
continue unless state.wizard? continue unless state.wizard?
if targetID = state.wizard.targetSprite if targetID = state.wizard.targetLank
return console.warn 'Wizard Sprite couldn\'t find target sprite', targetID unless targetID of @options.sprites return console.warn 'Wizard Lank couldn\'t find target lank', targetID unless targetID of @options.lanks
@setTarget @options.sprites[targetID] @setTarget @options.lanks[targetID]
else else
@setTarget state.wizard.targetPos @setTarget state.wizard.targetPos
onMeSynced: (e) -> onMeSynced: (e) ->
return unless @isSelf return unless @isSelf
@setNameLabel me.displayName() if @imageObject.visible # not if we hid the wiz @setNameLabel me.displayName() if @sprite.visible # not if we hid the wiz
newColorConfig = me.get('wizard')?.colorConfig or {} newColorConfig = me.get('wizard')?.colorConfig or {}
shouldUpdate = not _.isEqual(newColorConfig, @options.colorConfig) shouldUpdate = not _.isEqual(newColorConfig, @options.colorConfig)
@options.colorConfig = $.extend(true, {}, newColorConfig) @options.colorConfig = $.extend(true, {}, newColorConfig)
if shouldUpdate if shouldUpdate
@setUpSprite() @setUpLank()
@playAction(@currentAction) if @currentAction @playAction(@currentAction) if @currentAction
onSpriteSelected: (e) -> onLankSelected: (e) ->
return unless @isSelf return unless @isSelf
@setTarget e.sprite or e.worldPos @setTarget e.sprite or e.worldPos
animateIn: -> animateIn: ->
@imageObject.scaleX = @imageObject.scaleY = @imageObject.alpha = 0 @sprite.scaleX = @sprite.scaleY = @sprite.alpha = 0
createjs.Tween.get(@imageObject) createjs.Tween.get(@sprite)
.to({scaleX: 1, scaleY: 1, alpha: 1}, 1000, createjs.Ease.getPowInOut(2.2)) .to({scaleX: 1, scaleY: 1, alpha: 1}, 1000, createjs.Ease.getPowInOut(2.2))
@labels.name?.show() @labels.name?.show()
animateOut: (callback) -> animateOut: (callback) ->
tween = createjs.Tween.get(@imageObject) tween = createjs.Tween.get(@sprite)
.to({scaleX: 0, scaleY: 0, alpha: 0}, 1000, createjs.Ease.getPowInOut(2.2)) .to({scaleX: 0, scaleY: 0, alpha: 0}, 1000, createjs.Ease.getPowInOut(2.2))
tween.call(callback) if callback tween.call(callback) if callback
@labels.name?.hide() @labels.name?.hide()
@ -107,11 +107,11 @@ module.exports = class WizardSprite extends IndieSprite
else else
@thang.action = 'idle' if @thang.action is 'cast' @thang.action = 'idle' if @thang.action is 'cast'
setInitialState: (targetPos, @targetSprite) -> setInitialState: (targetPos, @targetLank) ->
@targetPos = @getPosFromTarget(@targetSprite or targetPos) @targetPos = @getPosFromTarget(@targetLank or targetPos)
@endMoveTween() @endMoveTween()
onEchoAllWizardSprites: (e) -> e.payload.push @ onEchoAllWizardLanks: (e) -> e.payload.push @
defaultPos: -> x: 35, y: 24, z: @thang.depth / 2 + @thang.bobHeight defaultPos: -> x: 35, y: 24, z: @thang.depth / 2 + @thang.bobHeight
move: (pos, duration) -> @setTarget(pos, duration) move: (pos, duration) -> @setTarget(pos, duration)
@ -121,12 +121,12 @@ module.exports = class WizardSprite extends IndieSprite
return if @targetPos and @targetPos.x is targetPos.x and @targetPos.y is targetPos.y return if @targetPos and @targetPos.x is targetPos.x and @targetPos.y is targetPos.y
# ignore selecting sprites you can't control # ignore selecting sprites you can't control
isSprite = newTarget?.thang? isLank = newTarget?.thang?
return if isSprite and not newTarget.thang.isProgrammable return if isLank and not newTarget.thang.isProgrammable
return if isSprite and newTarget is @targetSprite return if isLank and newTarget is @targetLank
@shoveOtherWizards(true) if @targetSprite @shoveOtherWizards(true) if @targetLank
@targetSprite = if isSprite then newTarget else null @targetLank = if isLank then newTarget else null
@targetPos = @boundWizard targetPos @targetPos = @boundWizard targetPos
@beginMoveTween(duration, isLinear) @beginMoveTween(duration, isLinear)
@shoveOtherWizards() @shoveOtherWizards()
@ -175,10 +175,10 @@ module.exports = class WizardSprite extends IndieSprite
@update true @update true
shoveOtherWizards: (removeMe) -> shoveOtherWizards: (removeMe) ->
return unless @targetSprite return unless @targetLank
allWizards = [] allWizards = []
Backbone.Mediator.publish 'sprite:echo-all-wizard-sprites', payload: allWizards Backbone.Mediator.publish 'sprite:echo-all-wizard-sprites', payload: allWizards
allOfUs = (wizard for wizard in allWizards when wizard.targetSprite is @targetSprite) allOfUs = (wizard for wizard in allWizards when wizard.targetLank is @targetLank)
allOfUs = (wizard for wizard in allOfUs when wizard isnt @) if removeMe allOfUs = (wizard for wizard in allOfUs when wizard isnt @) if removeMe
# diagonal lineup pattern # diagonal lineup pattern
@ -210,18 +210,18 @@ module.exports = class WizardSprite extends IndieSprite
@thang.pos = @getCurrentPosition() @thang.pos = @getCurrentPosition()
@faceTarget() @faceTarget()
sup = @options.camera.worldToSurface x: @thang.pos.x, y: @thang.pos.y, z: @thang.pos.z - @thang.depth / 2 sup = @options.camera.worldToSurface x: @thang.pos.x, y: @thang.pos.y, z: @thang.pos.z - @thang.depth / 2
@imageObject.x = sup.x @sprite.x = sup.x
@imageObject.y = sup.y @sprite.y = sup.y
getCurrentPosition: -> getCurrentPosition: ->
""" """
Takes into account whether the wizard is in transit or not, and the bobbing up and down. Takes into account whether the wizard is in transit or not, and the bobbing up and down.
Eventually will also adjust based on where other wizards are. Eventually will also adjust based on where other wizards are.
""" """
@targetPos = @targetSprite.thang.pos if @targetSprite?.thang @targetPos = @targetLank.thang.pos if @targetLank?.thang
pos = _.clone(@targetPos) pos = _.clone(@targetPos)
pos.z = @defaultPos().z + @getBobOffset() pos.z = @defaultPos().z + @getBobOffset()
@adjustPositionToSideOfTarget(pos) if @targetSprite # be off to the side depending on placement in world @adjustPositionToSideOfTarget(pos) if @targetLank # be off to the side depending on placement in world
return pos if @reachedTarget # stick like glue return pos if @reachedTarget # stick like glue
# if here, then the wizard is in transit. Calculate the diff! # if here, then the wizard is in transit. Calculate the diff!
@ -248,11 +248,11 @@ module.exports = class WizardSprite extends IndieSprite
targetPos.y += @spriteYOffset targetPos.y += @spriteYOffset
faceTarget: -> faceTarget: ->
if @targetSprite?.thang if @targetLank?.thang
@pointToward(@targetSprite.thang.pos) @pointToward(@targetLank.thang.pos)
updateMarks: -> updateMarks: ->
super() if @imageObject.visible # not if we hid the wiz super() if @sprite.visible # not if we hid the wiz
onMoveKey: (e) -> onMoveKey: (e) ->
return unless @isSelf return unless @isSelf

View file

@ -144,7 +144,7 @@ module.exports.Trailmaster = class Trailmaster
sprites = [] sprites = []
sprite = @sprites[thang.id] sprite = @sprites[thang.id]
return sprites unless sprite?.actions return sprites unless sprite?.actions
lastPos = @camera.surfaceToWorld x: sprite.imageObject.x, y: sprite.imageObject.y lastPos = @camera.surfaceToWorld x: sprite.sprite.x, y: sprite.sprite.y
minDistance = Math.pow(CLONE_INTERVAL * Camera.MPP, 2) minDistance = Math.pow(CLONE_INTERVAL * Camera.MPP, 2)
actions = @world.actionsForThang(thang.id) actions = @world.actionsForThang(thang.id)
lastAction = null lastAction = null
@ -159,7 +159,7 @@ module.exports.Trailmaster = class Trailmaster
diff += Math.pow(lastPos.y - thang.pos.y, 2) diff += Math.pow(lastPos.y - thang.pos.y, 2)
continue if diff < minDistance and action.name is lastAction continue if diff < minDistance and action.name is lastAction
clone = sprite.imageObject.clone() clone = sprite.sprite.clone()
clonePos = @camera.worldToSurface thang.pos clonePos = @camera.worldToSurface thang.pos
clone.x = clonePos.x clone.x = clonePos.x
clone.y = clonePos.y clone.y = clonePos.y

View file

@ -166,7 +166,7 @@ module.exports = class Thang
serializeForAether: -> serializeForAether: ->
{CN: @constructor.className, id: @id} {CN: @constructor.className, id: @id}
getSpriteOptions: -> getLankOptions: ->
colorConfigs = @teamColors or @world?.getTeamColors() or {} colorConfigs = @teamColors or @world?.getTeamColors() or {}
options = {colorConfig: {}} options = {colorConfig: {}}
if @team and teamColor = colorConfigs[@team] if @team and teamColor = colorConfigs[@team]

View file

@ -66,7 +66,7 @@ class CocoModel extends Backbone.Model
inFlux = @loading or not @loaded inFlux = @loading or not @loaded
@markToRevert() unless inFlux or @_revertAttributes or @project or options?.fromMerge @markToRevert() unless inFlux or @_revertAttributes or @project or options?.fromMerge
res = super attributes, options res = super attributes, options
@saveBackup() if @saveBackups and (not inFlux) and @hasLocalChanges() @saveBackup() if @saveBackups and (not inFlux)
res res
buildAttributesWithDefaults: -> buildAttributesWithDefaults: ->

View file

@ -1,6 +1,6 @@
RootView = require 'views/kinds/RootView' RootView = require 'views/kinds/RootView'
template = require 'templates/home' template = require 'templates/home'
WizardSprite = require 'lib/surface/WizardSprite' WizardLank = require 'lib/surface/WizardLank'
ThangType = require 'models/ThangType' ThangType = require 'models/ThangType'
Simulator = require 'lib/simulator/Simulator' Simulator = require 'lib/simulator/Simulator'
{me} = require '/lib/auth' {me} = require '/lib/auth'

View file

@ -359,9 +359,9 @@ module.exports = class ThangsTabView extends CocoView
@addThangType = type @addThangType = type
if @addThangType if @addThangType
thang = @createAddThang() thang = @createAddThang()
@addThangSprite = @surface.spriteBoss.addThangToSprites thang, @surface.spriteBoss.spriteLayers['Floating'] @addThangSprite = @surface.spriteBoss.addThangToSprites thang, @surface.spriteBoss.layerAdapters['Floating']
@addThangSprite.notOfThisWorld = true @addThangSprite.notOfThisWorld = true
@addThangSprite.imageObject.alpha = 0.75 @addThangSprite.sprite.alpha = 0.75
@addThangSprite.playSound? 'selected' @addThangSprite.playSound? 'selected'
pos ?= x: Math.round(@world.width / 2), y: Math.round(@world.height / 2) pos ?= x: Math.round(@world.width / 2), y: Math.round(@world.height / 2)
@adjustThangPos @addThangSprite, thang, pos @adjustThangPos @addThangSprite, thang, pos
@ -420,11 +420,11 @@ module.exports = class ThangsTabView extends CocoView
onSurfaceMouseOver: (e) -> onSurfaceMouseOver: (e) ->
return unless @addThangSprite return unless @addThangSprite
@addThangSprite.imageObject.visible = true @addThangSprite.sprite.visible = true
onSurfaceMouseOut: (e) -> onSurfaceMouseOut: (e) ->
return unless @addThangSprite return unless @addThangSprite
@addThangSprite.imageObject.visible = false @addThangSprite.sprite.visible = false
calculateMovement: (pctX, pctY, widthHeightRatio) -> calculateMovement: (pctX, pctY, widthHeightRatio) ->
MOVE_TOP_MARGIN = 1.0 - MOVE_MARGIN MOVE_TOP_MARGIN = 1.0 - MOVE_MARGIN

View file

@ -1,7 +1,8 @@
ThangType = require 'models/ThangType' ThangType = require 'models/ThangType'
SpriteParser = require 'lib/sprites/SpriteParser' SpriteParser = require 'lib/sprites/SpriteParser'
SpriteBuilder = require 'lib/sprites/SpriteBuilder' SpriteBuilder = require 'lib/sprites/SpriteBuilder'
CocoSprite = require 'lib/surface/CocoSprite' Lank = require 'lib/surface/Lank'
LayerAdapter = require 'lib/surface/LayerAdapter'
Camera = require 'lib/surface/Camera' Camera = require 'lib/surface/Camera'
DocumentFiles = require 'collections/DocumentFiles' DocumentFiles = require 'collections/DocumentFiles'
@ -58,7 +59,7 @@ module.exports = class ThangTypeEditView extends RootView
@listenToOnce @thangType, 'sync', -> @listenToOnce @thangType, 'sync', ->
@files = @supermodel.loadCollection(new DocumentFiles(@thangType), 'files').model @files = @supermodel.loadCollection(new DocumentFiles(@thangType), 'files').model
@updateFileSize() @updateFileSize()
@refreshAnimation = _.debounce @refreshAnimation, 500 # @refreshAnimation = _.debounce @refreshAnimation, 500
showLoading: ($el) -> showLoading: ($el) ->
$el ?= @$el.find('.outer-content') $el ?= @$el.find('.outer-content')
@ -74,7 +75,8 @@ module.exports = class ThangTypeEditView extends RootView
context context
getAnimationNames: -> getAnimationNames: ->
raw = _.keys(@thangType.get('raw', true).animations) raw = _.keys((@thangType.get('raw') or {}).animations)
return [] unless raw
raw = ("raw:#{name}" for name in raw) raw = ("raw:#{name}" for name in raw)
main = _.keys(@thangType.get('actions') or {}) main = _.keys(@thangType.get('actions') or {})
main.concat(raw) main.concat(raw)
@ -119,6 +121,8 @@ module.exports = class ThangTypeEditView extends RootView
initStage: -> initStage: ->
canvas = @$el.find('#canvas') canvas = @$el.find('#canvas')
@stage = new createjs.Stage(canvas[0]) @stage = new createjs.Stage(canvas[0])
@layerAdapter = new LayerAdapter({name:'Default', webGL: true})
@stage.addChild(@layerAdapter.container)
@camera?.destroy() @camera?.destroy()
@camera = new Camera canvas @camera = new Camera canvas
@ -128,7 +132,7 @@ module.exports = class ThangTypeEditView extends RootView
@groundDot = @makeDot('red') @groundDot = @makeDot('red')
@stage.addChild(@groundDot, @torsoDot, @mouthDot, @aboveHeadDot) @stage.addChild(@groundDot, @torsoDot, @mouthDot, @aboveHeadDot)
@updateGrid() @updateGrid()
@refreshAnimation() _.defer @refreshAnimation
createjs.Ticker.setFPS(30) createjs.Ticker.setFPS(30)
createjs.Ticker.addEventListener('tick', @stage) createjs.Ticker.addEventListener('tick', @stage)
@ -139,11 +143,11 @@ module.exports = class ThangTypeEditView extends RootView
updateDots: -> updateDots: ->
@stage.removeChild(@torsoDot, @mouthDot, @aboveHeadDot, @groundDot) @stage.removeChild(@torsoDot, @mouthDot, @aboveHeadDot, @groundDot)
return unless @currentSprite return unless @currentLank
return unless @showDots return unless @showDots
torso = @currentSprite.getOffset 'torso' torso = @currentLank.getOffset 'torso'
mouth = @currentSprite.getOffset 'mouth' mouth = @currentLank.getOffset 'mouth'
aboveHead = @currentSprite.getOffset 'aboveHead' aboveHead = @currentLank.getOffset 'aboveHead'
@torsoDot.x = CENTER.x + torso.x * @scale @torsoDot.x = CENTER.x + torso.x * @scale
@torsoDot.y = CENTER.y + torso.y * @scale @torsoDot.y = CENTER.y + torso.y * @scale
@mouthDot.x = CENTER.x + mouth.x * @scale @mouthDot.x = CENTER.x + mouth.x * @scale
@ -153,7 +157,7 @@ module.exports = class ThangTypeEditView extends RootView
@stage.addChild(@groundDot, @torsoDot, @mouthDot, @aboveHeadDot) @stage.addChild(@groundDot, @torsoDot, @mouthDot, @aboveHeadDot)
endAnimation: -> endAnimation: ->
@currentSprite?.queueAction('idle') @currentLank?.queueAction('idle')
updateGrid: -> updateGrid: ->
grid = new createjs.Container() grid = new createjs.Container()
@ -223,23 +227,24 @@ module.exports = class ThangTypeEditView extends RootView
# animation select # animation select
refreshAnimation: -> refreshAnimation: =>
return @showRasterImage() if @thangType.get('raster') return @showRasterImage() if @thangType.get('raster')
options = @getSpriteOptions() options = @getLankOptions()
@thangType.resetSpriteSheetCache() console.log 'refresh animation....'
spriteSheet = @thangType.buildSpriteSheet(options) # @thangType.resetSpriteSheetCache()
$('#spritesheets').empty() # spriteSheet = @thangType.buildSpriteSheet(options)
return unless spriteSheet # $('#spritesheets').empty()
for image in spriteSheet._images # return unless spriteSheet
$('#spritesheets').append(image) # for image in spriteSheet._images
# $('#spritesheets').append(image)
@showAnimation() @showAnimation()
@updatePortrait() @updatePortrait()
showRasterImage: -> showRasterImage: ->
sprite = new CocoSprite(@thangType, @getSpriteOptions()) sprite = new Lank(@thangType, @getLankOptions())
@currentSprite?.destroy() @currentLank?.destroy()
@currentSprite = sprite @currentLank = sprite
@showImageObject(sprite.imageObject) @showSprite(sprite.sprite)
@updateScale() @updateScale()
showAnimation: (animationName) -> showAnimation: (animationName) ->
@ -249,7 +254,7 @@ module.exports = class ThangTypeEditView extends RootView
animationName = animationName[4...] animationName = animationName[4...]
@showMovieClip(animationName) @showMovieClip(animationName)
else else
@showSprite(animationName) @showAction(animationName)
@updateRotation() @updateRotation()
@updateScale() # must happen after update rotation, because updateRotation calls the sprite update() method. @updateScale() # must happen after update rotation, because updateRotation calls the sprite update() method.
@ -261,37 +266,52 @@ module.exports = class ThangTypeEditView extends RootView
if reg if reg
movieClip.regX = -reg.x movieClip.regX = -reg.x
movieClip.regY = -reg.y movieClip.regY = -reg.y
@showImageObject(movieClip) @showSprite(movieClip)
getSpriteOptions: -> {resolutionFactor: @resolution, thang: @mockThang} getLankOptions: -> {resolutionFactor: @resolution, thang: @mockThang}
showSprite: (actionName) -> showAction: (actionName) ->
options = @getSpriteOptions() options = @getLankOptions()
sprite = new CocoSprite(@thangType, options) lank = new Lank(@thangType, options)
sprite.queueAction(actionName) @showLank(lank)
@currentSprite?.destroy() lank.queueAction(actionName)
@currentSprite = sprite
@showImageObject(sprite.imageObject)
updatePortrait: -> updatePortrait: ->
options = @getSpriteOptions() options = @getLankOptions()
portrait = @thangType.getPortraitImage(options) portrait = @thangType.getPortraitImage(options)
return unless portrait return unless portrait
portrait?.attr('id', 'portrait').addClass('img-thumbnail') portrait?.attr('id', 'portrait').addClass('img-thumbnail')
portrait.addClass 'img-thumbnail' portrait.addClass 'img-thumbnail'
$('#portrait').replaceWith(portrait) $('#portrait').replaceWith(portrait)
showImageObject: (imageObject) -> showLank: (lank) ->
@clearDisplayObject() @clearDisplayObject()
imageObject.x = CENTER.x @clearLank()
imageObject.y = CENTER.y @layerAdapter.resetSpriteSheet()
@stage.addChildAt(imageObject, 1) @layerAdapter.addLank(lank)
@currentObject = imageObject @currentLank = lank
lank.sprite.x = CENTER.x
lank.sprite.y = CENTER.y
lank.on 'new-sprite', ->
lank.sprite.x = CENTER.x
lank.sprite.y = CENTER.y
showSprite: (sprite) ->
@clearDisplayObject()
@clearLank()
sprite.x = CENTER.x
sprite.y = CENTER.y
@stage.addChildAt(sprite, 1)
@currentObject = sprite
@updateDots() @updateDots()
clearDisplayObject: -> clearDisplayObject: ->
@stage.removeChild(@currentObject) if @currentObject? @stage.removeChild(@currentObject) if @currentObject?
clearLank: ->
@layerAdapter.removeLank(@currentLank) if @currentLank
@currentLank?.destroy()
# sliders # sliders
initSliders: -> initSliders: ->
@ -303,9 +323,9 @@ module.exports = class ThangTypeEditView extends RootView
updateRotation: => updateRotation: =>
value = parseInt(180 * (@rotationSlider.slider('value') - 50) / 50) value = parseInt(180 * (@rotationSlider.slider('value') - 50) / 50)
@$el.find('.rotation-label').text " #{value}° " @$el.find('.rotation-label').text " #{value}° "
if @currentSprite if @currentLank
@currentSprite.rotation = value @currentLank.rotation = value
@currentSprite.update(true) @currentLank.update(true)
updateScale: => updateScale: =>
resValue = (@resolutionSlider.slider('value') + 1) / 10 resValue = (@resolutionSlider.slider('value') + 1) / 10
@ -313,9 +333,9 @@ module.exports = class ThangTypeEditView extends RootView
fixed = scaleValue.toFixed(1) fixed = scaleValue.toFixed(1)
@scale = scaleValue @scale = scaleValue
@$el.find('.scale-label').text " #{fixed}x " @$el.find('.scale-label').text " #{fixed}x "
if @currentSprite if @currentLank
@currentSprite.scaleFactorX = @currentSprite.scaleFactorY = scaleValue @currentLank.scaleFactorX = @currentLank.scaleFactorY = scaleValue
@currentSprite.updateScale() @currentLank.updateScale()
else if @currentObject? else if @currentObject?
@currentObject.scaleX = @currentObject.scaleY = scaleValue / resValue @currentObject.scaleX = @currentObject.scaleY = scaleValue / resValue
@updateGrid() @updateGrid()
@ -332,7 +352,7 @@ module.exports = class ThangTypeEditView extends RootView
value = parseInt((@healthSlider.slider('value')) / 10) value = parseInt((@healthSlider.slider('value')) / 10)
@$el.find('.health-label').text " #{value}hp " @$el.find('.health-label').text " #{value}hp "
@mockThang.health = value @mockThang.health = value
@currentSprite?.update() @currentLank?.update()
# save # save
@ -352,7 +372,7 @@ module.exports = class ThangTypeEditView extends RootView
url = "/editor/thang/#{newThangType.get('slug') or newThangType.id}" url = "/editor/thang/#{newThangType.get('slug') or newThangType.id}"
portraitSource = null portraitSource = null
if @thangType.get('raster') if @thangType.get('raster')
image = @currentSprite.imageObject.image image = @currentLank.sprite.image
portraitSource = imageToPortrait image portraitSource = imageToPortrait image
# bit of a hacky way to get that portrait # bit of a hacky way to get that portrait
success = => success = =>
@ -422,11 +442,11 @@ module.exports = class ThangTypeEditView extends RootView
bounds = obj.frameBounds[0] bounds = obj.frameBounds[0]
obj.regX = bounds.x + bounds.width / 2 obj.regX = bounds.x + bounds.width / 2
obj.regY = bounds.y + bounds.height / 2 obj.regY = bounds.y + bounds.height / 2
@showImageObject(obj) if obj @showSprite(obj) if obj
obj.y = 200 if obj # truly center the container obj.y = 200 if obj # truly center the container
@showingSelectedNode = true @showingSelectedNode = true
@currentSprite?.destroy() @currentLank?.destroy()
@currentSprite = null @currentLank = null
@updateScale() @updateScale()
@grid.alpha = 0.0 @grid.alpha = 0.0

View file

@ -1,6 +1,6 @@
ModalView = require 'views/kinds/ModalView' ModalView = require 'views/kinds/ModalView'
template = require 'templates/modal/wizard_settings' template = require 'templates/modal/wizard_settings'
WizardSprite = require 'lib/surface/WizardSprite' WizardLank = require 'lib/surface/WizardLank'
ThangType = require 'models/ThangType' ThangType = require 'models/ThangType'
{me} = require 'lib/auth' {me} = require 'lib/auth'
forms = require 'lib/forms' forms = require 'lib/forms'

View file

@ -118,7 +118,7 @@ module.exports = class LevelHUDView extends CocoView
@listeningToCreateAvatar = true @listeningToCreateAvatar = true
return return
@listeningToCreateAvatar = false @listeningToCreateAvatar = false
options = thang.getSpriteOptions() or {} options = thang.getLankOptions() or {}
options.async = false options.async = false
options.colorConfig = colorConfig if colorConfig options.colorConfig = colorConfig if colorConfig
wrapper = @$el.find '.thang-canvas-wrapper' wrapper = @$el.find '.thang-canvas-wrapper'

View file

@ -36,7 +36,7 @@ module.exports = class ThangAvatarView extends CocoView
getRenderData: (context={}) -> getRenderData: (context={}) ->
context = super context context = super context
context.thang = @thang context.thang = @thang
options = @thang?.getSpriteOptions() or {} options = @thang?.getLankOptions() or {}
options.async = true options.async = true
context.avatarURL = @thangType.getPortraitSource(options) unless @thangType.loading context.avatarURL = @thangType.getPortraitSource(options) unless @thangType.loading
context.includeName = @includeName context.includeName = @includeName

View file

@ -1,4 +1,4 @@
SpriteBoss = require 'lib/surface/SpriteBoss' LankBoss = require 'lib/surface/LankBoss'
Camera = require 'lib/surface/Camera' Camera = require 'lib/surface/Camera'
World = require 'lib/world/world' World = require 'lib/world/world'
ThangType = require 'models/ThangType' ThangType = require 'models/ThangType'
@ -8,8 +8,8 @@ munchkinData = require 'test/app/fixtures/ogre-munchkin-m.thang.type'
fangriderData = require 'test/app/fixtures/ogre-fangrider.thang.type' fangriderData = require 'test/app/fixtures/ogre-fangrider.thang.type'
curseData = require 'test/app/fixtures/curse.thang.type' curseData = require 'test/app/fixtures/curse.thang.type'
describe 'SpriteBoss', -> describe 'LankBoss', ->
spriteBoss = null lankBoss = null
canvas = null canvas = null
stage = null stage = null
midRenderExpectations = [] # bit of a hack to move tests which happen mid-initialization into a separate test midRenderExpectations = [] # bit of a hack to move tests which happen mid-initialization into a separate test
@ -18,7 +18,7 @@ describe 'SpriteBoss', ->
# check the resulting data for the whole thing, without changing anything. # check the resulting data for the whole thing, without changing anything.
init = (done) -> init = (done) ->
return done() if spriteBoss return done() if lankBoss
t = new Date() t = new Date()
canvas = $('<canvas width="800" height="600"></canvas>') canvas = $('<canvas width="800" height="600"></canvas>')
camera = new Camera(canvas) camera = new Camera(canvas)
@ -45,7 +45,7 @@ describe 'SpriteBoss', ->
thangTypes = [fangrider, segmentedMunchkin, singularMunchkin, segmentedTree, singularTree] thangTypes = [fangrider, segmentedMunchkin, singularMunchkin, segmentedTree, singularTree]
# Build the Stage and SpriteBoss. # Build the Stage and LankBoss.
window.stage = stage = new createjs.SpriteStage(canvas[0]) window.stage = stage = new createjs.SpriteStage(canvas[0])
options = { options = {
camera: camera camera: camera
@ -55,9 +55,9 @@ describe 'SpriteBoss', ->
thangTypes: thangTypes thangTypes: thangTypes
} }
window.spriteBoss = spriteBoss = new SpriteBoss(options) window.lankBoss = lankBoss = new LankBoss(options)
defaultLayer = spriteBoss.spriteLayers.Default defaultLayer = lankBoss.layerAdapters.Default
defaultLayer.buildAsync = false # cause faster defaultLayer.buildAsync = false # cause faster
# Sort of an implicit test. By default, all the default actions are always rendered, # Sort of an implicit test. By default, all the default actions are always rendered,
@ -66,14 +66,14 @@ describe 'SpriteBoss', ->
defaultLayer.defaultActions = ['idle'] defaultLayer.defaultActions = ['idle']
# Render the simple world with just trees # Render the simple world with just trees
spriteBoss.update(true) lankBoss.update(true)
# Test that the unrendered, static sprites aren't showing anything # Test that the unrendered, static sprites aren't showing anything
midRenderExpectations.push([spriteBoss.sprites['Segmented Tree'].imageObject.children.length,1,'static segmented action']) midRenderExpectations.push([lankBoss.lanks['Segmented Tree'].imageObject.children.length,1,'static segmented action'])
midRenderExpectations.push([spriteBoss.sprites['Segmented Tree'].imageObject.children[0].currentFrame,0,'static segmented action']) midRenderExpectations.push([lankBoss.lanks['Segmented Tree'].imageObject.children[0].currentFrame,0,'static segmented action'])
midRenderExpectations.push([spriteBoss.sprites['Segmented Tree'].imageObject.children[0].paused,true,'static segmented action']) midRenderExpectations.push([lankBoss.lanks['Segmented Tree'].imageObject.children[0].paused,true,'static segmented action'])
midRenderExpectations.push([spriteBoss.sprites['Singular Tree'].imageObject.currentFrame,0,'static singular action']) midRenderExpectations.push([lankBoss.lanks['Singular Tree'].imageObject.currentFrame,0,'static singular action'])
midRenderExpectations.push([spriteBoss.sprites['Singular Tree'].imageObject.paused,true,'static singular action']) midRenderExpectations.push([lankBoss.lanks['Singular Tree'].imageObject.paused,true,'static singular action'])
defaultLayer.once 'new-spritesheet', -> defaultLayer.once 'new-spritesheet', ->
@ -101,15 +101,15 @@ describe 'SpriteBoss', ->
_.find(world.thangs, {id: 'Disappearing Tree'}).exists = false _.find(world.thangs, {id: 'Disappearing Tree'}).exists = false
world.thangMap[thang.id] = thang for thang in world.thangs world.thangMap[thang.id] = thang for thang in world.thangs
spriteBoss.update(true) lankBoss.update(true)
jasmine.Ajax.requests.sendResponses({'/db/thang.type/curse': curseData}) jasmine.Ajax.requests.sendResponses({'/db/thang.type/curse': curseData})
# Test that the unrendered, animated sprites aren't showing anything # Test that the unrendered, animated sprites aren't showing anything
midRenderExpectations.push([spriteBoss.sprites['Segmented Ogre'].imageObject.children.length,10,'animated segmented action']) midRenderExpectations.push([lankBoss.lanks['Segmented Ogre'].imageObject.children.length,10,'animated segmented action'])
for child in spriteBoss.sprites['Segmented Ogre'].imageObject.children for child in lankBoss.lanks['Segmented Ogre'].imageObject.children
midRenderExpectations.push([child.children[0].currentFrame, 0, 'animated segmented action']) midRenderExpectations.push([child.children[0].currentFrame, 0, 'animated segmented action'])
midRenderExpectations.push([spriteBoss.sprites['Singular Ogre'].imageObject.currentFrame,0,'animated singular action']) midRenderExpectations.push([lankBoss.lanks['Singular Ogre'].imageObject.currentFrame,0,'animated singular action'])
midRenderExpectations.push([spriteBoss.sprites['Singular Ogre'].imageObject.paused,true,'animated singular action']) midRenderExpectations.push([lankBoss.lanks['Singular Ogre'].imageObject.paused,true,'animated singular action'])
defaultLayer.once 'new-spritesheet', -> defaultLayer.once 'new-spritesheet', ->
# showMe() # Uncomment to display this world when you run any of these tests. # showMe() # Uncomment to display this world when you run any of these tests.
@ -127,7 +127,7 @@ describe 'SpriteBoss', ->
return if ticks >= 100 return if ticks >= 100
ticks += 1 ticks += 1
if ticks % 20 is 0 if ticks % 20 is 0
spriteBoss.update(true) lankBoss.update(true)
stage.update() stage.update()
} }
createjs.Ticker.addEventListener "tick", listener createjs.Ticker.addEventListener "tick", listener
@ -140,43 +140,43 @@ describe 'SpriteBoss', ->
expect(expectation[0]).toBe(expectation[1]) expect(expectation[0]).toBe(expectation[1])
it 'rotates and animates sprites according to thang rotation', -> it 'rotates and animates sprites according to thang rotation', ->
expect(spriteBoss.sprites['Ogre N'].imageObject.currentAnimation).toBe('move_fore') expect(lankBoss.lanks['Ogre N'].imageObject.currentAnimation).toBe('move_fore')
expect(spriteBoss.sprites['Ogre E'].imageObject.currentAnimation).toBe('move_side') expect(lankBoss.lanks['Ogre E'].imageObject.currentAnimation).toBe('move_side')
expect(spriteBoss.sprites['Ogre W'].imageObject.currentAnimation).toBe('move_side') expect(lankBoss.lanks['Ogre W'].imageObject.currentAnimation).toBe('move_side')
expect(spriteBoss.sprites['Ogre S'].imageObject.currentAnimation).toBe('move_back') expect(lankBoss.lanks['Ogre S'].imageObject.currentAnimation).toBe('move_back')
expect(spriteBoss.sprites['Ogre E'].imageObject.scaleX).toBeLessThan(0) expect(lankBoss.lanks['Ogre E'].imageObject.scaleX).toBeLessThan(0)
expect(spriteBoss.sprites['Ogre W'].imageObject.scaleX).toBeGreaterThan(0) expect(lankBoss.lanks['Ogre W'].imageObject.scaleX).toBeGreaterThan(0)
it 'positions sprites according to thang pos', -> it 'positions sprites according to thang pos', ->
expect(spriteBoss.sprites['Ogre N'].imageObject.x).toBe(0) expect(lankBoss.lanks['Ogre N'].imageObject.x).toBe(0)
expect(spriteBoss.sprites['Ogre N'].imageObject.y).toBeCloseTo(-60) expect(lankBoss.lanks['Ogre N'].imageObject.y).toBeCloseTo(-60)
expect(spriteBoss.sprites['Ogre E'].imageObject.x).toBeCloseTo(80) expect(lankBoss.lanks['Ogre E'].imageObject.x).toBeCloseTo(80)
expect(spriteBoss.sprites['Ogre E'].imageObject.y).toBe(0) expect(lankBoss.lanks['Ogre E'].imageObject.y).toBe(0)
expect(spriteBoss.sprites['Ogre W'].imageObject.x).toBe(-80) expect(lankBoss.lanks['Ogre W'].imageObject.x).toBe(-80)
expect(spriteBoss.sprites['Ogre W'].imageObject.y).toBeCloseTo(0) expect(lankBoss.lanks['Ogre W'].imageObject.y).toBeCloseTo(0)
expect(spriteBoss.sprites['Ogre S'].imageObject.x).toBe(0) expect(lankBoss.lanks['Ogre S'].imageObject.x).toBe(0)
expect(spriteBoss.sprites['Ogre S'].imageObject.y).toBeCloseTo(60) expect(lankBoss.lanks['Ogre S'].imageObject.y).toBeCloseTo(60)
it 'scales sprites according to thang scaleFactorX and scaleFactorY', -> it 'scales sprites according to thang scaleFactorX and scaleFactorY', ->
expect(spriteBoss.sprites['Ogre N'].imageObject.scaleX).toBe(spriteBoss.sprites['Ogre N'].imageObject.baseScaleX * 1.5) expect(lankBoss.lanks['Ogre N'].imageObject.scaleX).toBe(lankBoss.lanks['Ogre N'].imageObject.baseScaleX * 1.5)
expect(spriteBoss.sprites['Ogre W'].imageObject.scaleY).toBe(spriteBoss.sprites['Ogre N'].imageObject.baseScaleY * 1.5) expect(lankBoss.lanks['Ogre W'].imageObject.scaleY).toBe(lankBoss.lanks['Ogre N'].imageObject.baseScaleY * 1.5)
it 'sets alpha based on thang alpha', -> it 'sets alpha based on thang alpha', ->
expect(spriteBoss.sprites['Ogre E'].imageObject.alpha).toBe(0.5) expect(lankBoss.lanks['Ogre E'].imageObject.alpha).toBe(0.5)
it 'orders sprites in the layer based on thang pos.y\'s', -> it 'orders sprites in the layer based on thang pos.y\'s', ->
container = spriteBoss.spriteLayers.Default.container container = lankBoss.layerAdapters.Default.container
l = container.children l = container.children
i1 = container.getChildIndex(_.find(container.children, (c) -> c.sprite.thang.id is 'Dying Ogre 1')) i1 = container.getChildIndex(_.find(container.children, (c) -> c.lank.thang.id is 'Dying Ogre 1'))
i2 = container.getChildIndex(_.find(container.children, (c) -> c.sprite.thang.id is 'Dying Ogre 2')) i2 = container.getChildIndex(_.find(container.children, (c) -> c.lank.thang.id is 'Dying Ogre 2'))
i3 = container.getChildIndex(_.find(container.children, (c) -> c.sprite.thang.id is 'Dying Ogre 3')) i3 = container.getChildIndex(_.find(container.children, (c) -> c.lank.thang.id is 'Dying Ogre 3'))
i4 = container.getChildIndex(_.find(container.children, (c) -> c.sprite.thang.id is 'Dying Ogre 4')) i4 = container.getChildIndex(_.find(container.children, (c) -> c.lank.thang.id is 'Dying Ogre 4'))
expect(i1).toBeGreaterThan(i2) expect(i1).toBeGreaterThan(i2)
expect(i2).toBeGreaterThan(i3) expect(i2).toBeGreaterThan(i3)
expect(i3).toBeGreaterThan(i4) expect(i3).toBeGreaterThan(i4)
it 'only contains children Sprites and SpriteContainers whose spritesheet matches the Layer', -> it 'only contains children Sprites and SpriteContainers whose spritesheet matches the Layer', ->
defaultLayerContainer = spriteBoss.spriteLayers.Default.container defaultLayerContainer = lankBoss.layerAdapters.Default.container
for c in defaultLayerContainer.children for c in defaultLayerContainer.children
expect(c.spriteSheet).toBe(defaultLayerContainer.spriteSheet) expect(c.spriteSheet).toBe(defaultLayerContainer.spriteSheet)