2014-01-03 13:32:13 -05:00
CocoModel = require ' ./CocoModel '
LevelComponent = require ' ./LevelComponent '
LevelSystem = require ' ./LevelSystem '
ThangType = require ' ./ThangType '
module.exports = class Level extends CocoModel
2014-06-30 22:16:26 -04:00
@className: ' Level '
2014-04-22 14:11:08 -04:00
@schema: require ' schemas/models/level '
2014-06-30 22:16:26 -04:00
urlRoot: ' /db/level '
2014-08-14 19:34:55 -04:00
serialize: (supermodel, session) ->
o = @ denormalize supermodel , session
2014-01-03 13:32:13 -05:00
# Figure out Components
o.levelComponents = _ . cloneDeep ( lc . attributes for lc in supermodel . getModels LevelComponent )
@ sortThangComponents o . thangs , o . levelComponents
@ fillInDefaultComponentConfiguration o . thangs , o . levelComponents
# Figure out Systems
systemModels = _ . cloneDeep ( ls . attributes for ls in supermodel . getModels LevelSystem )
o.systems = @ sortSystems o . systems , systemModels
@ fillInDefaultSystemConfiguration o . systems
2014-08-11 19:15:46 -04:00
# Figure out ThangTypes' Components
o.thangTypes = ( original: tt . get ( ' original ' ) , name: tt . get ( ' name ' ) , components: $ . extend ( true , [ ] , tt . get ( ' components ' ) ) for tt in supermodel . getModels ThangType )
@ sortThangComponents o . thangTypes , o . levelComponents
@ fillInDefaultComponentConfiguration o . thangTypes , o . levelComponents
2014-01-03 13:32:13 -05:00
o
2014-08-11 01:09:13 -04:00
2014-08-14 19:34:55 -04:00
denormalize: (supermodel, session) ->
2014-08-06 18:18:22 -04:00
o = $ . extend true , { } , @ attributes
2014-08-14 18:09:10 -04:00
if @ get ( ' type ' ) is ' hero '
# TOOD: figure out if/when/how we are doing this for non-Hero levels that aren't expecting denormalization.
for levelThang in o . thangs
2014-08-14 19:34:55 -04:00
@ denormalizeThang ( levelThang , supermodel , session )
2014-08-06 18:18:22 -04:00
o
2014-08-14 19:34:55 -04:00
denormalizeThang: (levelThang, supermodel, session) ->
2014-08-07 21:27:47 -04:00
levelThang . components ? = [ ]
2014-08-06 18:18:22 -04:00
thangType = supermodel . getModelByOriginal ( ThangType , levelThang . thangType )
2014-08-14 19:34:55 -04:00
# Empty out placeholder Components and store their values if we're the hero placeholder.
placeholders = { }
if levelThang . id is ' Hero Placeholder '
for thangComponent in levelThang . components ? [ ]
placeholders [ thangComponent . original ] = thangComponent
levelThang.components = [ ]
2014-08-15 15:09:56 -04:00
heroThangType = session ? . get ( ' heroConfig ' ) ? . thangType
levelThang.thangType = heroThangType if heroThangType
2014-08-14 19:34:55 -04:00
2014-08-06 18:18:22 -04:00
configs = { }
for thangComponent in levelThang . components
configs [ thangComponent . original ] = thangComponent
2014-08-11 01:09:13 -04:00
2014-08-06 18:18:22 -04:00
for defaultThangComponent in thangType . get ( ' components ' )
if levelThangComponent = configs [ defaultThangComponent . original ]
2014-08-14 18:09:10 -04:00
# Take the ThangType default Components and merge level-specific Component config into it
2014-08-06 18:18:22 -04:00
copy = $ . extend true , { } , defaultThangComponent . config
levelThangComponent.config = _ . merge copy , levelThangComponent . config
2014-08-11 01:09:13 -04:00
2014-08-06 18:18:22 -04:00
else
2014-08-14 18:09:10 -04:00
# Just add the Component as is
levelThangComponent = $ . extend true , { } , defaultThangComponent
levelThang . components . push levelThangComponent
if placeholderComponent = placeholders [ defaultThangComponent . original ]
placeholderConfig = placeholderComponent . config ? { }
if placeholderConfig . pos # Pull in Physical pos x and y
levelThangComponent . config . pos ? = { }
levelThangComponent.config.pos.x = placeholderConfig . pos . x
levelThangComponent.config.pos.y = placeholderConfig . pos . y
else if placeholderConfig . team # Pull in Allied team
levelThangComponent.config.team = placeholderConfig . team
else if placeholderConfig . programmableMethods
# Take the ThangType default Programmable and merge level-specific Component config into it
copy = $ . extend true , { } , placeholderConfig
levelThangComponent.config = _ . merge copy , levelThangComponent . config
2014-08-14 19:34:55 -04:00
if levelThang . id is ' Hero Placeholder ' and equips = _ . find levelThang . components , { original: LevelComponent . EquipsID }
inventory = session ? . get ( ' heroConfig ' ) ? . inventory
equips.config.inventory = $ . extend true , { } , inventory if inventory
2014-08-14 18:09:10 -04:00
2014-01-03 13:32:13 -05:00
sortSystems: (levelSystems, systemModels) ->
[ sorted , originalsSeen ] = [ [ ] , { } ]
visit = (system) ->
return if system . original of originalsSeen
systemModel = _ . find systemModels , { original: system . original }
2014-07-13 12:31:31 -04:00
return console . error ' Couldn \' t find model for original ' , system . original , ' from ' , systemModels unless systemModel
2014-01-03 13:32:13 -05:00
for d in systemModel . dependencies or [ ]
system2 = _ . find levelSystems , { original: d . original }
visit system2
2014-06-30 22:16:26 -04:00
#console.log 'sorted systems adding', systemModel.name
2014-01-03 13:32:13 -05:00
sorted . push { model: systemModel , config: _ . cloneDeep system . config }
originalsSeen [ system . original ] = true
visit system for system in levelSystems
sorted
sortThangComponents: (thangs, levelComponents) ->
# Here we have to sort the Components by their dependencies.
# It's a bit tricky though, because we don't have either soft dependencies or priority levels.
# Example: Programmable must come last, since it has to override any Component-provided methods that any other Component might have created. Can't enumerate all soft dependencies.
# Example: Collides doesn't depend on Allied, but if both exist, Collides must come after Allied. Soft dependency example. Can't just figure out a proper priority to take care of it.
# Decision? Just special case the sort logic in here until we have more examples than these two and decide how best to handle most of the cases then, since we don't really know the whole of the problem yet.
# TODO: anything that depends on Programmable will break right now.
for thang in thangs
sorted = [ ]
visit = (c) ->
return if c in sorted
lc = _ . find levelComponents , { original: c . original }
2014-08-12 15:50:41 -04:00
console . error thang . id or thang . name , ' couldn \' t find lc for ' , c , ' of ' , levelComponents unless lc
2014-08-11 19:15:46 -04:00
return unless lc
2014-06-30 22:16:26 -04:00
if lc . name is ' Programmable '
2014-01-03 13:32:13 -05:00
# Programmable always comes last
visit c2 for c2 in _ . without thang . components , c
else
for d in lc . dependencies or [ ]
c2 = _ . find thang . components , { original: d . original }
2014-08-12 15:50:41 -04:00
console . error thang . id or thang . name , ' couldn \' t find dependent Component ' , d . original , ' from ' , lc . name unless c2
2014-07-13 12:31:31 -04:00
visit c2 if c2
2014-06-30 22:16:26 -04:00
if lc . name is ' Collides '
allied = _ . find levelComponents , { name: ' Allied ' }
2014-01-03 13:32:13 -05:00
if allied
collides = _ . find ( thang . components , { original: allied . original } )
visit collides if collides
2014-06-30 22:16:26 -04:00
#console.log thang.id, 'sorted comps adding', lc.name
2014-01-03 13:32:13 -05:00
sorted . push c
for comp in thang . components
visit comp
thang.components = sorted
fillInDefaultComponentConfiguration: (thangs, levelComponents) ->
for thang in thangs
for component in thang . components or [ ]
continue unless lc = _ . find levelComponents , { original: component . original }
component . config ? = { }
@ walkDefaults component . config , lc . configSchema . properties
fillInDefaultSystemConfiguration: (levelSystems) ->
for system in levelSystems ? [ ]
system . config ? = { }
@ walkDefaults system . config , system . model . configSchema . properties
walkDefaults: (config, properties) ->
return unless properties
for prop , schema of properties
if schema . default ? and config [ prop ] is undefined
2014-06-30 22:16:26 -04:00
#console.log 'Setting default of', config, 'for', prop, 'to', schema.default
2014-01-03 13:32:13 -05:00
config [ prop ] = schema . default
if schema . type is ' object ' and config [ prop ]
@ walkDefaults config [ prop ] , schema . properties
else if schema . type is ' array ' and config [ prop ]
for item in config [ prop ] or [ ]
@ walkDefaults item , schema . items
dimensions: ->
width = 0
height = 0
for thang in @ get ( ' thangs ' ) or [ ]
for component in thang . components
c = component . config
continue unless c ?
width = c . width if c . width ? and c . width > width
height = c . height if c . height ? and c . height > height
2014-06-30 22:16:26 -04:00
return { width: width , height: height }