2014-07-17 20:20:11 -04:00
CocoView = require ' views/kinds/CocoView '
2014-07-23 10:02:45 -04:00
AddThangsView = require ' ./AddThangsView '
2014-01-03 13:32:13 -05:00
thangs_template = require ' templates/editor/level/thangs_tab '
Level = require ' models/Level '
ThangType = require ' models/ThangType '
LevelComponent = require ' models/LevelComponent '
2014-04-22 15:42:26 -04:00
CocoCollection = require ' collections/CocoCollection '
2014-01-03 13:32:13 -05:00
{ isObjectID } = require ' models/CocoModel '
Surface = require ' lib/surface/Surface '
Thang = require ' lib/world/thang '
2014-07-23 10:02:45 -04:00
LevelThangEditView = require ' ./LevelThangEditView '
2014-03-15 15:31:39 -04:00
ComponentsCollection = require ' collections/ComponentsCollection '
2014-01-03 13:32:13 -05:00
# Moving the screen while dragging thangs constants
MOVE_MARGIN = 0.15
MOVE_SPEED = 13
# Essential component original ids
componentOriginals =
2014-06-30 22:16:26 -04:00
' existence.Exists ' : ' 524b4150ff92f1f4f8000024 '
' physics.Physical ' : ' 524b75ad7fc0f6d519000001 '
2014-01-03 13:32:13 -05:00
class ThangTypeSearchCollection extends CocoCollection
2014-06-09 10:18:26 -04:00
url: ' /db/thang.type?project=original,name,version,slug,kind,components '
2014-01-03 13:32:13 -05:00
model: ThangType
2014-07-17 20:20:11 -04:00
module.exports = class ThangsTabView extends CocoView
2014-06-30 22:16:26 -04:00
id: ' editor-level-thangs-tab-view '
2014-01-03 13:32:13 -05:00
className: ' tab-pane active '
template: thangs_template
startsLoading: true
subscriptions:
' surface:sprite-selected ' : ' onExtantThangSelected '
' surface:mouse-moved ' : ' onSurfaceMouseMoved '
' surface:mouse-over ' : ' onSurfaceMouseOver '
' surface:mouse-out ' : ' onSurfaceMouseOut '
' edit-level-thang ' : ' editThang '
' level-thang-edited ' : ' onLevelThangEdited '
' level-thang-done-editing ' : ' onLevelThangDoneEditing '
' level:view-switched ' : ' onViewSwitched '
' sprite:dragged ' : ' onSpriteDragged '
' sprite:mouse-up ' : ' onSpriteMouseUp '
' sprite:double-clicked ' : ' onSpriteDoubleClicked '
2014-05-20 19:20:24 -04:00
' surface:stage-mouse-up ' : ' onStageMouseUp '
2014-07-13 20:31:09 -04:00
' randomize:terrain-generated ' : ' onRandomizeTerrain '
2014-01-31 13:21:32 -05:00
2014-01-21 13:42:09 -05:00
events:
' click # extant-thangs-filter button ' : ' onFilterExtantThangs '
2014-03-30 13:38:54 -04:00
' click # delete ' : ' onDeleteClicked '
' click # duplicate ' : ' onDuplicateClicked '
2014-04-21 15:15:22 -04:00
' click # thangs-container-toggle ' : ' toggleThangsContainer '
2014-04-25 22:11:32 -04:00
# 'click #thangs-palette-toggle': 'toggleThangsPalette'
# 'click .add-thang-palette-icon': 'toggleThangsPalette'
2014-01-31 13:21:32 -05:00
2014-01-06 20:11:57 -05:00
shortcuts:
2014-02-24 12:58:12 -05:00
' esc ' : ' selectAddThang '
' delete, del, backspace ' : ' deleteSelectedExtantThang '
' left ' : -> @ moveAddThangSelection - 1
' right ' : -> @ moveAddThangSelection 1
2014-07-24 15:36:41 -04:00
' ctrl+z ' : ' undo '
' ctrl+shift+z ' : ' redo '
2014-01-21 13:42:09 -05:00
2014-01-03 13:32:13 -05:00
constructor: (options) ->
super options
@world = options . world
2014-04-16 02:28:59 -04:00
2014-04-28 14:52:04 -04:00
# should load depended-on Components, too
@thangTypes = @ supermodel . loadCollection ( new ThangTypeSearchCollection ( ) , ' thangs ' ) . model
2014-03-15 15:31:39 -04:00
# just loading all Components for now: https://github.com/codecombat/codecombat/issues/405
2014-04-28 14:52:04 -04:00
@componentCollection = @ supermodel . loadCollection ( new ComponentsCollection ( ) , ' components ' ) . load ( )
2014-05-01 18:44:50 -04:00
@level = options . level
2014-04-28 14:52:04 -04:00
2014-04-25 22:11:32 -04:00
$ ( document ) . bind ' contextmenu ' , @ preventDefaultContextMenu
2014-05-29 15:33:21 -04:00
2014-02-11 17:58:45 -05:00
getRenderData: (context={}) ->
2014-01-03 13:32:13 -05:00
context = super ( context )
2014-04-25 22:11:32 -04:00
return context unless @ supermodel . finished ( )
2014-01-21 02:02:23 -05:00
thangTypes = ( thangType . attributes for thangType in @ supermodel . getModels ( ThangType ) )
thangTypes = _ . uniq thangTypes , false , ' original '
2014-01-31 13:21:32 -05:00
thangTypes = _ . reject thangTypes , kind: ' Mark '
2014-01-21 02:02:23 -05:00
groupMap = { }
for thangType in thangTypes
groupMap [ thangType . kind ] ? = [ ]
groupMap [ thangType . kind ] . push thangType
2014-01-31 13:21:32 -05:00
2014-01-21 02:02:23 -05:00
groups = [ ]
for groupName in Object . keys ( groupMap ) . sort ( )
someThangTypes = groupMap [ groupName ]
someThangTypes = _ . sortBy someThangTypes , ' name '
group =
name: groupName
thangs: someThangTypes
groups . push group
2014-01-31 13:21:32 -05:00
2014-01-21 02:02:23 -05:00
context.thangTypes = thangTypes
context.groups = groups
2014-01-03 13:32:13 -05:00
context
2014-03-18 00:47:53 -04:00
onWindowResize: (e) ->
$ ( ' # thangs-list ' ) . height ( ' 100% ' )
thangsHeaderHeight = $ ( ' # thangs-header ' ) . height ( )
oldHeight = $ ( ' # thangs-list ' ) . height ( )
2014-04-21 15:15:22 -04:00
if $ ( document ) . width ( ) < 1050
2014-04-24 05:51:16 -04:00
$ ( ' # thangs-list ' ) . height ( oldHeight - thangsHeaderHeight - 40 )
2014-04-21 15:15:22 -04:00
else
$ ( ' # thangs-list ' ) . height ( oldHeight - thangsHeaderHeight - 80 )
2014-05-29 15:33:21 -04:00
2014-03-18 00:47:53 -04:00
2014-01-03 13:32:13 -05:00
afterRender: ->
super ( )
2014-04-25 22:11:32 -04:00
return unless @ supermodel . finished ( )
2014-05-20 19:20:24 -04:00
$ ( ' .tab-content ' ) . mousedown @ selectAddThang
2014-01-20 06:15:20 -05:00
$ ( ' # thangs-list ' ) . bind ' mousewheel ' , @ preventBodyScrollingInThangList
2014-01-21 13:42:09 -05:00
@ $el . find ( ' # extant-thangs-filter button:first ' ) . button ( ' toggle ' )
2014-03-18 00:47:53 -04:00
$ ( window ) . resize @ onWindowResize
2014-03-18 14:59:36 -04:00
@addThangsView = @ insertSubView new AddThangsView world: @ world , supermodel: @ supermodel
2014-05-01 18:44:50 -04:00
@ buildInterface ( ) # refactor to not have this trigger when this view re-renders?
2014-08-06 11:24:50 -04:00
if @ thangsTreema . data . length
2014-07-25 16:21:10 -04:00
@ $el . find ( ' # canvas-overlay ' ) . css ( ' display ' , ' none ' )
2014-01-31 13:21:32 -05:00
2014-02-24 12:58:12 -05:00
onFilterExtantThangs: (e) ->
2014-03-13 02:49:51 -04:00
@ $el . find ( ' # extant-thangs-filter button.active ' ) . button ( ' toggle ' )
2014-02-24 12:58:12 -05:00
button = $ ( e . target ) . closest ( ' button ' )
button . button ( ' toggle ' )
val = button . val ( )
@ thangsTreema . $el . removeClass ( @ lastHideClass ) if @ lastHideClass
@ thangsTreema . $el . addClass ( @lastHideClass = " hide-except- #{ val } " ) if val
2014-01-03 13:32:13 -05:00
2014-01-20 06:15:20 -05:00
preventBodyScrollingInThangList: (e) ->
@ scrollTop += ( if e . deltaY < 0 then 1 else - 1 ) * 30
e . preventDefault ( )
2014-05-01 18:44:50 -04:00
buildInterface: (e) ->
2014-04-25 22:11:32 -04:00
@level = e . level if e
2014-04-16 02:28:59 -04:00
2014-01-03 13:32:13 -05:00
data = $ . extend ( true , { } , @ level . attributes )
treemaOptions =
2014-04-12 04:35:56 -04:00
schema: Level . schema . properties . thangs
2014-01-03 13:32:13 -05:00
data: data . thangs
supermodel: @ supermodel
callbacks:
change: @ onThangsChanged
select: @ onTreemaThangSelected
dblclick: @ onTreemaThangDoubleClicked
readOnly: true
nodeClasses:
thang: ThangNode
array: ThangsNode
world: @ world
2014-04-16 02:28:59 -04:00
2014-01-03 13:32:13 -05:00
@thangsTreema = @ $el . find ( ' # thangs-treema ' ) . treema treemaOptions
@ thangsTreema . build ( )
@ thangsTreema . open ( )
@ onThangsChanged ( ) # Initialize the World with Thangs
@ initSurface ( )
2014-03-18 00:47:53 -04:00
thangsHeaderHeight = $ ( ' # thangs-header ' ) . height ( )
oldHeight = $ ( ' # thangs-list ' ) . height ( )
$ ( ' # thangs-list ' ) . height ( oldHeight - thangsHeaderHeight )
2014-08-07 14:12:07 -04:00
if data . thangs . length
2014-08-11 20:09:36 -04:00
@ $el . find ( ' # randomize-button ' ) . hide ( )
2014-01-03 13:32:13 -05:00
initSurface: ->
surfaceCanvas = $ ( ' canvas # surface ' , @ $el )
@surface = new Surface @ world , surfaceCanvas , {
wizards: false
paths: false
grid: true
navigateToSelection: false
thangTypes: @ supermodel . getModels ( ThangType )
showInvisible: true
2014-02-28 20:12:23 -05:00
frameRate: 15
2014-01-03 13:32:13 -05:00
}
@surface.playing = false
@ surface . setWorld @ world
2014-06-30 22:16:26 -04:00
@ surface . camera . zoomTo ( { x: 262 , y: - 164 } , 1.66 , 0 )
2014-01-03 13:32:13 -05:00
destroy: ->
@ selectAddThangType null
@ surface . destroy ( )
2014-04-15 15:31:35 -04:00
$ ( document ) . unbind ' contextmenu ' , @ preventDefaultContextMenu
2014-02-14 13:57:47 -05:00
super ( )
2014-01-03 13:32:13 -05:00
onViewSwitched: (e) ->
2014-01-28 14:54:16 -05:00
@ selectAddThang ( )
2014-01-03 13:32:13 -05:00
@ surface ? . spriteBoss ? . selectSprite null , null
onSpriteMouseDown: (e) ->
2014-01-06 17:53:21 -05:00
# Sprite clicks happen after stage clicks, but we need to know whether a sprite is being clicked.
2014-05-20 19:29:23 -04:00
# clearTimeout @backgroundAddClickTimeout
# if e.originalEvent.nativeEvent.button == 2
# @onSpriteContextMenu e
2014-01-06 17:53:21 -05:00
2014-05-20 19:20:24 -04:00
onStageMouseUp: (e) ->
2014-01-06 17:53:21 -05:00
if @ addThangSprite
2014-06-11 16:20:24 -04:00
@ surface . camera . lock ( )
2014-05-20 19:29:23 -04:00
# If we click on the background, we need to add @addThangSprite, but not if onSpriteMouseUp will fire.
2014-01-06 17:53:21 -05:00
@backgroundAddClickTimeout = _ . defer => @ onExtantThangSelected { }
2014-03-31 12:33:14 -04:00
$ ( ' # contextmenu ' ) . hide ( )
2014-01-03 13:32:13 -05:00
onSpriteDragged: (e) ->
return unless @ selectedExtantThang and e . thang ? . id is @ selectedExtantThang ? . id
2014-03-14 09:03:57 -04:00
@surface.camera.dragDisabled = true
2014-01-03 13:32:13 -05:00
{ stageX , stageY } = e . originalEvent
2014-05-30 18:06:33 -04:00
wop = @ surface . camera . screenToWorld x: stageX , y: stageY
2014-01-03 13:32:13 -05:00
wop.z = @ selectedExtantThang . depth / 2
@ adjustThangPos @ selectedExtantSprite , @ selectedExtantThang , wop
[ w , h ] = [ @ surface . camera . canvasWidth , @ surface . camera . canvasHeight ]
@ calculateMovement ( stageX / w , stageY / h , w / h )
onSpriteMouseUp: (e) ->
2014-05-20 19:29:23 -04:00
clearTimeout @ backgroundAddClickTimeout
2014-06-11 16:20:24 -04:00
@ surface . camera . unlock ( )
2014-06-10 18:09:56 -04:00
if e . originalEvent . nativeEvent . button == 2 and @ selectedExtantThang
2014-05-20 19:29:23 -04:00
@ onSpriteContextMenu e
2014-01-03 13:32:13 -05:00
clearInterval ( @ movementInterval ) if @ movementInterval ?
@movementInterval = null
2014-03-14 09:03:57 -04:00
@surface.camera.dragDisabled = false
2014-01-03 13:32:13 -05:00
return unless @ selectedExtantThang and e . thang ? . id is @ selectedExtantThang ? . id
pos = @ selectedExtantThang . pos
2014-06-30 22:16:26 -04:00
physicalOriginal = componentOriginals [ ' physics.Physical ' ]
2014-01-03 13:32:13 -05:00
path = " id= #{ @ selectedExtantThang . id } /components/original= #{ physicalOriginal } " # TODO: hack
physical = @ thangsTreema . get path
return if not physical or ( physical . config . pos . x is pos . x and physical . config . pos . y is pos . y )
@ thangsTreema . set path + ' /config/pos ' , x: pos . x , y: pos . y , z: pos . z
onSpriteDoubleClicked: (e) ->
return unless e . thang
@ editThang thangID: e . thang . id
2014-07-13 20:31:09 -04:00
onRandomizeTerrain: (e) ->
2014-07-13 15:58:21 -04:00
@thangsBatch = [ ]
2014-07-13 21:38:24 -04:00
nonRandomThangs = ( thang for thang in @ thangsTreema . get ( ' ' ) when not /Random/ . test thang . id )
@ thangsTreema . set ' ' , nonRandomThangs
2014-06-26 09:02:31 -04:00
for thang in e . thangs
2014-06-26 12:10:11 -04:00
@ selectAddThangType thang . id
2014-07-13 15:58:21 -04:00
@ addThang @ addThangType , thang . pos , true
@ batchInsert ( )
2014-07-10 15:37:03 -04:00
@ selectAddThangType null
2014-07-25 16:21:10 -04:00
2014-06-26 09:02:31 -04:00
2014-01-03 13:32:13 -05:00
# TODO: figure out a good way to have all Surface clicks and Treema clicks just proxy in one direction, so we can maintain only one way of handling selection and deletion
onExtantThangSelected: (e) ->
2014-03-31 12:33:14 -04:00
@ selectedExtantSprite ? . setNameLabel ? null unless @ selectedExtantSprite is e . sprite
2014-01-03 13:32:13 -05:00
@selectedExtantThang = e . thang
@selectedExtantSprite = e . sprite
if e . thang and ( key . alt or key . meta )
# We alt-clicked, so create a clone addThang
@ selectAddThangType e . thang . spriteName , @ selectedExtantThang
2014-06-30 22:16:26 -04:00
else if e . thang and not ( @ addThangSprite and @ addThangType is ' Blood Torch Test ' ) # TODO: figure out which Thangs can be placed on other Thangs
2014-01-03 13:32:13 -05:00
# We clicked on a Thang (or its Treema), so select the Thang
@ selectAddThang null
@selectedExtantThangClickTime = new Date ( )
treemaThang = _ . find @ thangsTreema . childrenTreemas , (treema) => treema . data . id is @ selectedExtantThang . id
if treemaThang
2014-03-22 15:17:15 -04:00
# Show the label above selected thang, notice that we may get here from thang-edit-view, so it will be selected but no label
# also covers selecting from Treema
@ selectedExtantSprite . setNameLabel @ selectedExtantSprite . thangType . get ( ' name ' ) + ' : ' + @ selectedExtantThang . id
2014-03-21 03:54:55 -04:00
if not treemaThang . isSelected ( )
treemaThang . select ( )
2014-03-31 12:33:14 -04:00
@ thangsTreema . $el . scrollTop ( @ thangsTreema . $el . find ( ' .treema-children .treema-selected ' ) [ 0 ] . offsetTop )
2014-01-03 13:32:13 -05:00
else if @ addThangSprite
# We clicked on the background when we had an add Thang selected, so add it
@ addThang @ addThangType , @ addThangSprite . thang . pos
2014-01-31 13:21:32 -05:00
2014-01-21 13:54:59 -05:00
# Commented out this bit so the extant thangs treema editor can select invisible thangs like arrows.
# Couldn't spot any bugs... But if there are any, better come up with a better solution.
# else
# # We clicked on the background, so deselect anything selected
# @thangsTreema.deselectAll()
2014-01-03 13:32:13 -05:00
selectAddThang: (e) =>
2014-05-30 06:38:43 -04:00
return if e ? and $ ( e . target ) . closest ( ' # thang-search ' ) . length # Ignore if you're trying to search thangs
2014-05-23 18:14:46 -04:00
return unless e ? and $ ( e . target ) . closest ( ' # editor-level-thangs-tab-view ' ) . length or key . isPressed ( ' esc ' )
2014-01-03 13:32:13 -05:00
if e then target = $ ( e . target ) else target = @ $el . find ( ' .add-thangs-palette ' ) # pretend to click on background if no event
return true if target . attr ( ' id ' ) is ' surface '
2014-01-06 20:11:57 -05:00
target = target . closest ( ' .add-thang-palette-icon ' )
2014-01-03 13:32:13 -05:00
wasSelected = target . hasClass ' selected '
@ $el . find ( ' .add-thangs-palette .add-thang-palette-icon.selected ' ) . removeClass ( ' selected ' )
2014-01-28 12:22:23 -05:00
@ selectAddThangType ( if wasSelected then null else target . attr ' data-thang-type ' ) unless key . alt or key . meta
2014-01-03 13:32:13 -05:00
target . addClass ( ' selected ' ) if @ addThangType
2014-05-30 19:43:55 -04:00
#false # was causing #1099, any reason to keep?
2014-01-03 13:32:13 -05:00
moveAddThangSelection: (direction) ->
return unless @ addThangType
icons = $ ( ' .add-thangs-palette .add-thang-palette-icon ' )
selectedIcon = icons . filter ( ' .selected ' )
selectedIndex = icons . index selectedIcon
nextSelectedIndex = ( selectedIndex + direction + icons . length ) % icons . length
@ selectAddThang { target: icons [ nextSelectedIndex ] }
selectAddThangType: (type, @cloneSourceThang) ->
if _ . isString type
2014-06-30 22:16:26 -04:00
type = _ . find @ supermodel . getModels ( ThangType ) , (m) -> m . get ( ' name ' ) is type
2014-01-03 13:32:13 -05:00
pos = @ addThangSprite ? . thang . pos # Maintain old sprite's pos if we have it
@ surface . spriteBoss . removeSprite @ addThangSprite if @ addThangSprite
@addThangType = type
if @ addThangType
thang = @ createAddThang ( )
2014-06-30 22:16:26 -04:00
@addThangSprite = @ surface . spriteBoss . addThangToSprites thang , @ surface . spriteBoss . spriteLayers [ ' Floating ' ]
2014-01-03 13:32:13 -05:00
@addThangSprite.notOfThisWorld = true
2014-05-14 18:59:56 -04:00
@addThangSprite.imageObject.alpha = 0.75
2014-01-03 13:32:13 -05:00
@ addThangSprite . playSound ? ' selected '
pos ? = x: Math . round ( @ world . width / 2 ) , y: Math . round ( @ world . height / 2 )
@ adjustThangPos @ addThangSprite , thang , pos
else
@addThangSprite = null
createEssentialComponents: ->
[
2014-06-30 22:16:26 -04:00
{ original: componentOriginals [ ' existence.Exists ' ] , majorVersion: 0 , config: { } }
{ original: componentOriginals [ ' physics.Physical ' ] , majorVersion: 0 , config: { pos: { x: 10 , y: 10 , z: 1 } , width: 2 , height: 2 , depth: 2 , shape: ' box ' } }
2014-01-03 13:32:13 -05:00
]
createAddThang: ->
allComponents = ( lc . attributes for lc in @ supermodel . getModels LevelComponent )
rawComponents = @ addThangType . get ( ' components ' ) ? [ ]
rawComponents = @ createEssentialComponents ( ) unless rawComponents . length
mockThang = { components: rawComponents }
@ level . sortThangComponents [ mockThang ] , allComponents
components = [ ]
for raw in mockThang . components
comp = _ . find allComponents , { original: raw . original }
continue if comp . name in [ ' Selectable ' , ' Attackable ' ] # Don't draw health bars or intercept clicks
2014-06-30 22:16:26 -04:00
componentClass = @ world . loadClassFromCode comp . js , comp . name , ' component '
2014-01-03 13:32:13 -05:00
components . push [ componentClass , raw . config ]
2014-06-30 22:16:26 -04:00
thang = new Thang @ world , @ addThangType . get ( ' name ' ) , ' Add Thang Phantom '
2014-01-03 13:32:13 -05:00
thang . addComponents components . . .
thang
adjustThangPos: (sprite, thang, pos) ->
snap = sprite ? . data ? . snap or sprite ? . thangType ? . get ( ' snap ' ) or { x: 0.01 , y: 0.01 } # Centimeter resolution by default
pos.x = Math . round ( ( pos . x - thang . width / 2 ) / snap . x ) * snap . x + thang . width / 2
pos.y = Math . round ( ( pos . y - thang . height / 2 ) / snap . y ) * snap . y + thang . height / 2
pos.z = thang . depth / 2
thang.pos = pos
@ surface . spriteBoss . update true # Make sure Obstacle layer resets cache
onSurfaceMouseMoved: (e) ->
return unless @ addThangSprite
2014-05-30 18:06:33 -04:00
wop = @ surface . camera . screenToWorld x: e . x , y: e . y
2014-01-03 13:32:13 -05:00
wop.z = 0.5
@ adjustThangPos @ addThangSprite , @ addThangSprite . thang , wop
null
onSurfaceMouseOver: (e) ->
return unless @ addThangSprite
2014-05-14 18:59:56 -04:00
@addThangSprite.imageObject.visible = true
2014-01-03 13:32:13 -05:00
onSurfaceMouseOut: (e) ->
return unless @ addThangSprite
2014-05-14 18:59:56 -04:00
@addThangSprite.imageObject.visible = false
2014-01-03 13:32:13 -05:00
calculateMovement: (pctX, pctY, widthHeightRatio) ->
MOVE_TOP_MARGIN = 1.0 - MOVE_MARGIN
if MOVE_TOP_MARGIN > pctX > MOVE_MARGIN and MOVE_TOP_MARGIN > pctY > MOVE_MARGIN
clearInterval ( @ movementInterval ) if @ movementInterval ?
@movementInterval = null
return @moveLatitude = @moveLongitude = @speed = 0
# calculating speed to be 0.0 to 1.0 within the movement buffer on the outer edge
diff = ( MOVE_MARGIN * 2 ) # comments are assuming MOVE_MARGIN is 0.1
@speed = Math . max ( Math . abs ( pctX - 0.5 ) , Math . abs ( pctY - 0.5 ) ) * 2 # pct is now 0.8 - 1.0
@ speed -= 1.0 - diff # 0.0 - 0.2
@ speed *= ( 1.0 / diff ) # 0.0 - 1.0
@ speed *= MOVE_SPEED
@moveLatitude = pctX * 2 - 1
@moveLongitude = pctY * 2 - 1
@ moveLongitude /= widthHeightRatio if widthHeightRatio > 1.0
@ moveLatitude *= widthHeightRatio if widthHeightRatio < 1.0
@movementInterval = setInterval ( @ moveSide , 16 ) unless @ movementInterval ?
moveSide: =>
return unless @ speed
c = @ surface . camera
2014-06-30 22:16:26 -04:00
p = { x: c . target . x + @ moveLatitude * @ speed / c . zoom , y: c . target . y + @ moveLongitude * @ speed / c . zoom }
2014-01-03 13:32:13 -05:00
c . zoomTo ( p , c . zoom , 0 )
deleteSelectedExtantThang: (e) =>
return if $ ( e . target ) . hasClass ' treema-node '
@ thangsTreema . onDeletePressed e
@ onTreemaThangSelected null , @ thangsTreema . getSelectedTreemas ( )
Thang . resetThangIDs ( ) # TODO: find some way to do this when we delete from treema, too
onThangsChanged: (e) =>
@ level . set ' thangs ' , @ thangsTreema . data
2014-03-14 14:58:29 -04:00
return if @ editThangView
2014-01-03 13:32:13 -05:00
serializedLevel = @ level . serialize @ supermodel
2014-02-03 17:01:20 -05:00
try
@ world . loadFromLevel serializedLevel , false
catch error
console . error ' Catastrophic error loading the level: ' , error
2014-01-03 13:32:13 -05:00
thang.isSelectable = not thang . isLand for thang in @ world . thangs # let us select walls and such
@ surface ? . setWorld @ world
2014-01-28 12:22:23 -05:00
@ selectAddThangType @ addThangType , @ cloneSourceThang if @ addThangType # make another addThang sprite, since the World just refreshed
2014-01-03 13:32:13 -05:00
Backbone . Mediator . publish ' level-thangs-changed ' , thangsData: @ thangsTreema . data
null
onTreemaThangSelected: (e, selectedTreemas) =>
selectedThangID = _ . last ( selectedTreemas ) ? . data . id
if selectedThangID isnt @ selectedExtantThang ? . id
2014-03-14 16:27:29 -04:00
@ surface . spriteBoss . selectThang selectedThangID , null , true
2014-01-03 13:32:13 -05:00
onTreemaThangDoubleClicked: (e, treema) =>
id = treema ? . data ? . id
@ editThang thangID: id if id
2014-07-13 15:58:21 -04:00
batchInsert: ->
2014-07-13 21:38:24 -04:00
@ thangsTreema . set ' ' , @ thangsTreema . get ( ' ' ) . concat ( @ thangsBatch )
2014-07-13 15:58:21 -04:00
@thangsBatch = [ ]
2014-07-13 21:38:24 -04:00
addThang: (thangType, pos, batchInsert=false) ->
2014-08-07 14:12:07 -04:00
@ $el . find ( ' # randomize-button ' ) . hide ( )
2014-07-13 21:38:24 -04:00
if batchInsert
thangID = " Random #{ thangType . get ( ' name ' ) } #{ @ thangsBatch . length } "
else
thangID = Thang . nextID ( thangType . get ( ' name ' ) , @ world ) until thangID and not @ thangsTreema . get " id= #{ thangID } "
2014-01-03 13:32:13 -05:00
if @ cloneSourceThang
components = _ . cloneDeep @ thangsTreema . get " id= #{ @ cloneSourceThang . id } /components "
@ selectAddThang null
else
components = _ . cloneDeep thangType . get ( ' components ' ) ? [ ]
components = @ createEssentialComponents ( ) unless components . length
physical = _ . find components , (c) -> c . config ? . pos ?
physical.config.pos = x: pos . x , y: pos . y , z: physical . config . pos . z if physical
thang = thangType: thangType . get ( ' original ' ) , id: thangID , components: components
2014-07-13 15:58:21 -04:00
if batchInsert
@ thangsBatch . push thang
2014-07-13 21:38:24 -04:00
else
2014-07-13 15:58:21 -04:00
@ thangsTreema . insert ' ' , thang
2014-01-03 13:32:13 -05:00
editThang: (e) ->
if e . target # click event
thangData = $ ( e . target ) . data ' thang-data '
else # Mediator event
window . thangsTreema = @ thangsTreema
thangData = @ thangsTreema . get " id= #{ e . thangID } "
@editThangView = new LevelThangEditView thangData: thangData , supermodel: @ supermodel , level: @ level , world: @ world
@ insertSubView @ editThangView
2014-02-13 12:26:21 -05:00
@ $el . find ( ' .thangs-column ' ) . hide ( )
2014-01-03 13:32:13 -05:00
Backbone . Mediator . publish ' level:view-switched ' , e
onLevelThangEdited: (e) ->
newThang = e . thangData
@ thangsTreema . set " id= #{ e . id } " , newThang
onLevelThangDoneEditing: ->
@ removeSubView @ editThangView
2014-03-14 14:58:29 -04:00
@editThangView = null
@ onThangsChanged ( )
2014-02-13 12:26:21 -05:00
@ $el . find ( ' .thangs-column ' ) . show ( )
2014-04-17 13:12:23 -04:00
2014-03-30 13:38:54 -04:00
preventDefaultContextMenu: (e) ->
2014-04-15 15:27:35 -04:00
return unless $ ( e . target ) . closest ( ' # canvas-wrapper ' ) . length
2014-03-30 13:38:54 -04:00
e . preventDefault ( )
2014-04-17 13:12:23 -04:00
2014-03-30 13:38:54 -04:00
onSpriteContextMenu: (e) ->
{ clientX , clientY } = e . originalEvent . nativeEvent
if @ addThangType
$ ( ' # duplicate a ' ) . html ' Stop Duplicate '
else
$ ( ' # duplicate a ' ) . html ' Duplicate '
$ ( ' # contextmenu ' ) . css { position: ' fixed ' , left: clientX , top: clientY }
$ ( ' # contextmenu ' ) . show ( )
2014-04-17 13:12:23 -04:00
2014-03-30 13:38:54 -04:00
onDeleteClicked: (e) ->
$ ( ' # contextmenu ' ) . hide ( )
@ deleteSelectedExtantThang e
2014-04-17 13:12:23 -04:00
2014-03-30 13:38:54 -04:00
onDuplicateClicked: (e) ->
$ ( ' # contextmenu ' ) . hide ( )
2014-05-29 15:33:21 -04:00
@ selectAddThangType @ selectedExtantThang . spriteName , @ selectedExtantThang
2014-04-21 15:15:22 -04:00
toggleThangsContainer: (e) ->
$ ( ' # all-thangs ' ) . toggle ( )
2014-05-29 15:33:21 -04:00
2014-04-21 15:15:22 -04:00
toggleThangsPalette: (e) ->
$ ( ' # add-thangs-column ' ) . toggle ( )
@ onWindowResize e
2014-05-29 15:33:21 -04:00
2014-07-24 15:36:41 -04:00
undo: (e) ->
if not @ editThangView then @ thangsTreema . undo ( ) else @ editThangView . undo ( )
2014-06-22 09:41:15 -04:00
2014-07-24 15:36:41 -04:00
redo: (e) ->
if not @ editThangView then @ thangsTreema . redo ( ) else @ editThangView . redo ( )
2014-06-22 09:41:15 -04:00
2014-08-10 20:03:39 -04:00
showUndoDescription: ->
if @ editThangView
@ editThangView . showUndoDescription ( )
else
undoDescription = @ thangsTreema . getUndoDescription ( )
titleText = $ ( ' # undo-button ' ) . attr ( ' title ' , ' Undo ' + undoDescription + ' (Ctrl+Z) ' )
showRedoDescription: ->
if @ editThangView
@ editThangView . showRedoDescription ( )
else
redoDescription = @ thangsTreema . getRedoDescription ( )
titleText = $ ( ' # redo-button ' ) . attr ( ' title ' , ' Redo ' + redoDescription + ' (Ctrl+Shift+Z) ' )
2014-01-03 13:32:13 -05:00
class ThangsNode extends TreemaNode . nodeMap . array
valueClass: ' treema-array-replacement '
getChildren: ->
children = super ( arguments . . . )
2014-01-21 00:19:54 -05:00
# TODO: add some filtering to only work with certain types of units at a time
2014-01-03 13:32:13 -05:00
return children
2014-08-10 20:03:39 -04:00
getUndoDescription: ->
trackedActions = @ getTrackedActions ( )
currentStateIndex = @ getCurrentStateIndex ( )
if currentStateIndex then return @ getTrackedActionDescription ( trackedActions [ currentStateIndex - 1 ] ) else return ' '
getRedoDescription: ->
trackedActions = @ getTrackedActions ( )
currentStateIndex = @ getCurrentStateIndex ( )
if currentStateIndex isnt trackedActions . length
return @ getTrackedActionDescription trackedActions [ currentStateIndex ]
else
return ' '
getTrackedActionDescription: (trackedAction) ->
switch trackedAction . action
when ' insert '
trackedActionDescription = ' Add New Thang '
when ' delete '
trackedActionDescription = ' Delete Thang '
when ' edit '
path = trackedAction . path . split ' / '
if path [ path . length - 1 ] is ' pos '
trackedActionDescription = ' Move Thang '
else
trackedActionDescription = ' Edit Thang '
else
trackedActionDescription = ' '
trackedActionDescription
2014-01-03 13:32:13 -05:00
class ThangNode extends TreemaObjectNode
valueClass: ' treema-thang '
collection: false
2014-01-21 00:19:54 -05:00
@thangNameMap: { }
2014-01-21 13:42:09 -05:00
@thangKindMap: { }
2014-01-03 13:32:13 -05:00
buildValueForDisplay: (valEl) ->
pos = _ . find ( @ data . components , (c) -> c . config ? . pos ? ) ? . config . pos # TODO: hack
s = " #{ @ data . thangType } "
if isObjectID s
2014-01-21 00:19:54 -05:00
unless name = ThangNode . thangNameMap [ s ]
2014-08-06 11:24:50 -04:00
thangType = _ . find @ settings . supermodel . getModels ( ThangType ) , (m) -> m . get ( ' original ' ) is s and m . get ( ' kind ' )
2014-01-21 00:19:54 -05:00
name = ThangNode . thangNameMap [ s ] = thangType . get ' name '
2014-01-21 13:42:09 -05:00
ThangNode . thangKindMap [ s ] = thangType . get ' kind '
kind = ThangNode . thangKindMap [ s ]
@ $el . addClass " treema- #{ kind } "
2014-01-21 00:19:54 -05:00
s = name
2014-06-30 22:16:26 -04:00
s += ' - ' + @ data . id if @ data . id isnt s
2014-01-03 13:32:13 -05:00
if pos
s += " ( #{ Math . round ( pos . x ) } , #{ Math . round ( pos . y ) } ) "
else
2014-06-30 22:16:26 -04:00
s += ' (non-physical) '
2014-01-03 13:32:13 -05:00
@ buildValueForDisplaySimply valEl , s
onEnterPressed: ->
2014-08-11 18:44:57 -04:00
Backbone . Mediator . publish ' edit-level-thang ' , thangID: @ data . id