2014-01-03 13:32:13 -05:00
|
|
|
Level = require 'models/Level'
|
2014-04-25 17:30:06 -04:00
|
|
|
LevelComponent = require 'models/LevelComponent'
|
|
|
|
LevelSystem = require 'models/LevelSystem'
|
|
|
|
Article = require 'models/Article'
|
2014-01-03 13:32:13 -05:00
|
|
|
LevelSession = require 'models/LevelSession'
|
|
|
|
ThangType = require 'models/ThangType'
|
2014-04-28 17:58:58 -04:00
|
|
|
ThangNamesCollection = require 'collections/ThangNamesCollection'
|
2014-04-25 17:30:06 -04:00
|
|
|
|
|
|
|
CocoClass = require 'lib/CocoClass'
|
|
|
|
AudioPlayer = require 'lib/AudioPlayer'
|
2014-01-03 13:32:13 -05:00
|
|
|
app = require 'application'
|
2014-01-14 16:16:30 -05:00
|
|
|
World = require 'lib/world/world'
|
2014-01-03 13:32:13 -05:00
|
|
|
|
|
|
|
# This is an initial stab at unifying loading and setup into a single place which can
|
|
|
|
# monitor everything and keep a LoadingScreen visible overall progress.
|
|
|
|
#
|
|
|
|
# Would also like to incorporate into here:
|
|
|
|
# * World Building
|
|
|
|
# * Sprite map generation
|
|
|
|
# * Connecting to Firebase
|
|
|
|
|
|
|
|
module.exports = class LevelLoader extends CocoClass
|
2014-01-07 02:45:33 -05:00
|
|
|
|
2014-02-15 18:44:45 -05:00
|
|
|
constructor: (options) ->
|
2014-04-28 17:58:58 -04:00
|
|
|
@t0 = new Date().getTime()
|
2014-01-03 13:32:13 -05:00
|
|
|
super()
|
2014-02-15 18:44:45 -05:00
|
|
|
@supermodel = options.supermodel
|
2014-05-14 12:24:52 -04:00
|
|
|
@supermodel.setMaxProgress 0.2
|
2014-02-15 18:44:45 -05:00
|
|
|
@levelID = options.levelID
|
|
|
|
@sessionID = options.sessionID
|
|
|
|
@opponentSessionID = options.opponentSessionID
|
|
|
|
@team = options.team
|
|
|
|
@headless = options.headless
|
2014-03-12 20:51:09 -04:00
|
|
|
@spectateMode = options.spectateMode ? false
|
2014-04-28 14:09:21 -04:00
|
|
|
@editorMode = options.editorMode # TODO: remove when the surface can load ThangTypes itself
|
2014-02-15 18:44:45 -05:00
|
|
|
|
2014-01-03 13:32:13 -05:00
|
|
|
@loadSession()
|
2014-04-25 17:30:06 -04:00
|
|
|
@loadLevel()
|
2014-01-03 13:32:13 -05:00
|
|
|
@loadAudio()
|
|
|
|
@playJingle()
|
2014-04-29 19:54:57 -04:00
|
|
|
if @supermodel.finished()
|
|
|
|
@onSupermodelLoaded()
|
|
|
|
else
|
|
|
|
@listenToOnce @supermodel, 'loaded-all', @onSupermodelLoaded
|
2014-01-07 02:45:33 -05:00
|
|
|
|
2014-01-03 13:32:13 -05:00
|
|
|
playJingle: ->
|
2014-02-15 18:44:45 -05:00
|
|
|
return if @headless
|
2014-02-26 23:23:13 -05:00
|
|
|
# Apparently the jingle, when it tries to play immediately during all this loading, you can't hear it.
|
|
|
|
# Add the timeout to fix this weird behavior.
|
|
|
|
f = ->
|
|
|
|
jingles = ["ident_1", "ident_2"]
|
|
|
|
AudioPlayer.playInterfaceSound jingles[Math.floor Math.random() * jingles.length]
|
|
|
|
setTimeout f, 500
|
2014-01-03 13:32:13 -05:00
|
|
|
|
|
|
|
# Session Loading
|
2014-01-07 02:45:33 -05:00
|
|
|
|
2014-01-03 13:32:13 -05:00
|
|
|
loadSession: ->
|
2014-03-18 14:52:23 -04:00
|
|
|
return if @headless
|
2014-02-13 18:51:41 -05:00
|
|
|
if @sessionID
|
|
|
|
url = "/db/level_session/#{@sessionID}"
|
|
|
|
else
|
|
|
|
url = "/db/level/#{@levelID}/session"
|
|
|
|
url += "?team=#{@team}" if @team
|
2014-02-15 18:44:45 -05:00
|
|
|
|
2014-05-07 16:28:42 -04:00
|
|
|
session = new LevelSession().setURL url
|
|
|
|
@session = @supermodel.loadModel(session, 'level_session', {cache:false}).model
|
2014-04-25 17:30:06 -04:00
|
|
|
@session.once 'sync', -> @url = -> '/db/level.session/' + @id
|
2014-02-15 18:44:45 -05:00
|
|
|
|
2014-02-13 19:42:35 -05:00
|
|
|
if @opponentSessionID
|
2014-05-07 16:28:42 -04:00
|
|
|
opponentSession = new LevelSession().setURL "/db/level_session/#{@opponentSessionID}"
|
|
|
|
@opponentSession = @supermodel.loadModel(opponentSession, 'opponent_session').model
|
2014-01-03 13:32:13 -05:00
|
|
|
|
|
|
|
# Supermodel (Level) Loading
|
|
|
|
|
2014-04-25 17:30:06 -04:00
|
|
|
loadLevel: ->
|
2014-01-03 13:32:13 -05:00
|
|
|
@level = @supermodel.getModel(Level, @levelID) or new Level _id: @levelID
|
2014-04-25 17:30:06 -04:00
|
|
|
if @level.loaded
|
|
|
|
@populateLevel()
|
|
|
|
else
|
2014-04-28 14:09:21 -04:00
|
|
|
@level = @supermodel.loadModel(@level, 'level').model
|
2014-04-25 17:30:06 -04:00
|
|
|
@listenToOnce @level, 'sync', @onLevelLoaded
|
2014-04-29 19:43:46 -04:00
|
|
|
|
2014-04-25 17:30:06 -04:00
|
|
|
onLevelLoaded: ->
|
|
|
|
@populateLevel()
|
2014-04-29 19:43:46 -04:00
|
|
|
|
2014-04-25 17:30:06 -04:00
|
|
|
populateLevel: ->
|
|
|
|
thangIDs = []
|
|
|
|
componentVersions = []
|
|
|
|
systemVersions = []
|
|
|
|
articleVersions = []
|
2014-04-29 19:43:46 -04:00
|
|
|
|
2014-04-25 17:30:06 -04:00
|
|
|
for thang in @level.get('thangs') or []
|
|
|
|
thangIDs.push thang.thangType
|
|
|
|
for comp in thang.components or []
|
|
|
|
componentVersions.push _.pick(comp, ['original', 'majorVersion'])
|
2014-04-29 19:43:46 -04:00
|
|
|
|
2014-04-25 17:30:06 -04:00
|
|
|
for system in @level.get('systems') or []
|
|
|
|
systemVersions.push _.pick(system, ['original', 'majorVersion'])
|
|
|
|
if indieSprites = system?.config?.indieSprites
|
|
|
|
for indieSprite in indieSprites
|
|
|
|
thangIDs.push indieSprite.thangType
|
2014-04-29 19:43:46 -04:00
|
|
|
|
|
|
|
unless @headless
|
|
|
|
for article in @level.get('documentation')?.generalArticles or []
|
|
|
|
articleVersions.push _.pick(article, ['original', 'majorVersion'])
|
2014-04-25 17:30:06 -04:00
|
|
|
|
|
|
|
objUniq = (array) -> _.uniq array, false, (arg) -> JSON.stringify(arg)
|
2014-04-28 17:58:58 -04:00
|
|
|
|
2014-05-02 13:31:20 -04:00
|
|
|
worldNecessities = []
|
|
|
|
|
|
|
|
@thangIDs = _.uniq thangIDs
|
2014-05-13 13:51:55 -04:00
|
|
|
@thangNames = new ThangNamesCollection(@thangIDs)
|
|
|
|
worldNecessities.push @supermodel.loadCollection(@thangNames, 'thang_names')
|
2014-05-02 15:32:41 -04:00
|
|
|
|
2014-04-25 17:30:06 -04:00
|
|
|
for obj in objUniq componentVersions
|
|
|
|
url = "/db/level.component/#{obj.original}/version/#{obj.majorVersion}"
|
2014-05-02 13:31:20 -04:00
|
|
|
worldNecessities.push @maybeLoadURL(url, LevelComponent, 'component')
|
2014-04-25 17:30:06 -04:00
|
|
|
for obj in objUniq systemVersions
|
|
|
|
url = "/db/level.system/#{obj.original}/version/#{obj.majorVersion}"
|
2014-05-02 13:31:20 -04:00
|
|
|
worldNecessities.push @maybeLoadURL(url, LevelSystem, 'system')
|
2014-04-25 17:30:06 -04:00
|
|
|
for obj in objUniq articleVersions
|
|
|
|
url = "/db/article/#{obj.original}/version/#{obj.majorVersion}"
|
|
|
|
@maybeLoadURL url, Article, 'article'
|
|
|
|
if obj = @level.get 'nextLevel'
|
|
|
|
url = "/db/level/#{obj.original}/version/#{obj.majorVersion}"
|
|
|
|
@maybeLoadURL url, Level, 'level'
|
|
|
|
|
2014-05-01 18:44:50 -04:00
|
|
|
unless @headless and not @editorMode
|
2014-04-29 19:43:46 -04:00
|
|
|
wizard = ThangType.loadUniversalWizard()
|
|
|
|
@supermodel.loadModel wizard, 'thang'
|
2014-05-02 15:32:41 -04:00
|
|
|
|
2014-05-02 13:31:20 -04:00
|
|
|
jqxhrs = (resource.jqxhr for resource in worldNecessities when resource?.jqxhr)
|
|
|
|
$.when(jqxhrs...).done(@onWorldNecessitiesLoaded)
|
|
|
|
|
|
|
|
onWorldNecessitiesLoaded: =>
|
|
|
|
@initWorld()
|
2014-05-14 12:24:52 -04:00
|
|
|
@supermodel.clearMaxProgress()
|
2014-05-13 13:51:55 -04:00
|
|
|
return if @headless and not @editorMode
|
|
|
|
thangsToLoad = _.uniq( (t.spriteName for t in @world.thangs) )
|
|
|
|
nameModelTuples = ([thangType.get('name'), thangType] for thangType in @thangNames.models)
|
|
|
|
nameModelMap = _.zipObject nameModelTuples
|
2014-05-13 17:39:45 -04:00
|
|
|
@spriteSheetsToBuild = []
|
2014-05-13 13:51:55 -04:00
|
|
|
|
|
|
|
for thangTypeName in thangsToLoad
|
2014-05-13 17:39:45 -04:00
|
|
|
thangType = nameModelMap[thangTypeName]
|
|
|
|
thangType.fetch()
|
|
|
|
thangType = @supermodel.loadModel(thangType, 'thang').model
|
|
|
|
res = @supermodel.addSomethingResource "sprite_sheet", 5
|
|
|
|
res.thangType = thangType
|
|
|
|
res.markLoading()
|
|
|
|
@spriteSheetsToBuild.push res
|
|
|
|
|
|
|
|
@buildLoopInterval = setInterval @buildLoop, 5
|
2014-04-25 17:30:06 -04:00
|
|
|
|
|
|
|
maybeLoadURL: (url, Model, resourceName) ->
|
|
|
|
return if @supermodel.getModel(url)
|
2014-04-28 14:09:21 -04:00
|
|
|
model = new Model().setURL url
|
|
|
|
@supermodel.loadModel(model, resourceName)
|
2014-04-29 19:43:46 -04:00
|
|
|
|
2014-04-25 17:30:06 -04:00
|
|
|
onSupermodelLoaded: ->
|
2014-05-13 17:39:45 -04:00
|
|
|
console.log 'SuperModel for Level loaded in', new Date().getTime() - @t0, 'ms'
|
2014-01-03 13:32:13 -05:00
|
|
|
@loadLevelSounds()
|
2014-04-25 17:30:06 -04:00
|
|
|
@denormalizeSession()
|
2014-03-18 14:52:23 -04:00
|
|
|
app.tracker.updatePlayState(@level, @session) unless @headless
|
2014-05-15 00:54:36 -04:00
|
|
|
|
2014-05-13 17:39:45 -04:00
|
|
|
buildLoop: =>
|
|
|
|
return if @lastBuilt and new Date().getTime() - @lastBuilt < 10
|
|
|
|
return clearInterval @buildLoopInterval unless @spriteSheetsToBuild.length
|
2014-05-15 00:54:36 -04:00
|
|
|
|
2014-05-13 17:39:45 -04:00
|
|
|
for spriteSheetResource, i in @spriteSheetsToBuild
|
|
|
|
if spriteSheetResource.thangType.loaded
|
|
|
|
@buildSpriteSheetsForThangType spriteSheetResource.thangType
|
|
|
|
@spriteSheetsToBuild.splice i, 1
|
|
|
|
@lastBuilt = new Date().getTime()
|
|
|
|
spriteSheetResource.markLoaded()
|
|
|
|
return
|
2014-01-07 02:45:33 -05:00
|
|
|
|
2014-01-03 13:32:13 -05:00
|
|
|
denormalizeSession: ->
|
2014-03-18 14:52:23 -04:00
|
|
|
return if @headless or @sessionDenormalized or @spectateMode
|
2014-01-03 13:32:13 -05:00
|
|
|
patch =
|
|
|
|
'levelName': @level.get('name')
|
|
|
|
'levelID': @level.get('slug') or @level.id
|
|
|
|
if me.id is @session.get 'creator'
|
|
|
|
patch.creatorName = me.get('name')
|
2014-02-26 20:06:21 -05:00
|
|
|
for key, value of patch
|
|
|
|
if @session.get(key) is value
|
|
|
|
delete patch[key]
|
|
|
|
unless _.isEmpty patch
|
|
|
|
@session.set key, value for key, value of patch
|
|
|
|
tempSession = new LevelSession _id: @session.id
|
|
|
|
tempSession.save(patch, {patch: true})
|
2014-01-03 13:32:13 -05:00
|
|
|
@sessionDenormalized = true
|
2014-01-16 14:37:04 -05:00
|
|
|
|
2014-02-28 01:14:52 -05:00
|
|
|
# Building sprite sheets
|
|
|
|
|
2014-04-25 17:30:06 -04:00
|
|
|
buildSpriteSheetsForThangType: (thangType) ->
|
2014-05-06 12:49:04 -04:00
|
|
|
return if @headless
|
2014-05-02 20:03:30 -04:00
|
|
|
# TODO: Finish making sure the supermodel loads the raster image before triggering load complete, and that the cocosprite has access to the asset.
|
|
|
|
# if f = thangType.get('raster')
|
|
|
|
# queue = new createjs.LoadQueue()
|
|
|
|
# queue.loadFile('/file/'+f)
|
2014-04-25 17:30:06 -04:00
|
|
|
@grabThangTypeTeams() unless @thangTypeTeams
|
|
|
|
for team in @thangTypeTeams[thangType.get('original')] ? [null]
|
|
|
|
spriteOptions = {resolutionFactor: 4, async: false}
|
|
|
|
if thangType.get('kind') is 'Floor'
|
|
|
|
spriteOptions.resolutionFactor = 2
|
|
|
|
if team and color = @teamConfigs[team]?.color
|
|
|
|
spriteOptions.colorConfig = team: color
|
|
|
|
@buildSpriteSheet thangType, spriteOptions
|
|
|
|
|
2014-02-28 01:14:52 -05:00
|
|
|
grabThangTypeTeams: ->
|
|
|
|
@grabTeamConfigs()
|
|
|
|
@thangTypeTeams = {}
|
|
|
|
for thang in @level.get('thangs')
|
|
|
|
for component in thang.components
|
|
|
|
if team = component.config?.team
|
|
|
|
@thangTypeTeams[thang.thangType] ?= []
|
|
|
|
@thangTypeTeams[thang.thangType].push team unless team in @thangTypeTeams[thang.thangType]
|
|
|
|
break
|
|
|
|
@thangTypeTeams
|
|
|
|
|
|
|
|
grabTeamConfigs: ->
|
|
|
|
for system in @level.get('systems')
|
|
|
|
if @teamConfigs = system.config?.teamConfigs
|
|
|
|
break
|
|
|
|
unless @teamConfigs
|
|
|
|
# Hack: pulled from Alliance System code. TODO: put in just one place.
|
|
|
|
@teamConfigs = {"humans":{"superteam":"humans","color":{"hue":0,"saturation":0.75,"lightness":0.5},"playable":true},"ogres":{"superteam":"ogres","color":{"hue":0.66,"saturation":0.75,"lightness":0.5},"playable":false},"neutral":{"superteam":"neutral","color":{"hue":0.33,"saturation":0.75,"lightness":0.5}}}
|
|
|
|
@teamConfigs
|
|
|
|
|
2014-01-14 16:16:30 -05:00
|
|
|
buildSpriteSheet: (thangType, options) ->
|
|
|
|
if thangType.get('name') is 'Wizard'
|
|
|
|
options.colorConfig = me.get('wizard')?.colorConfig or {}
|
2014-04-25 17:30:06 -04:00
|
|
|
thangType.buildSpriteSheet options
|
2014-01-16 14:37:04 -05:00
|
|
|
|
2014-02-28 01:14:52 -05:00
|
|
|
# World init
|
|
|
|
|
|
|
|
initWorld: ->
|
|
|
|
return if @initialized
|
|
|
|
@initialized = true
|
2014-05-10 21:24:50 -04:00
|
|
|
@world = new World()
|
2014-02-28 01:14:52 -05:00
|
|
|
serializedLevel = @level.serialize(@supermodel)
|
|
|
|
@world.loadFromLevel serializedLevel, false
|
2014-05-02 15:32:41 -04:00
|
|
|
console.log "World has been initialized from level loader."
|
2014-02-28 01:14:52 -05:00
|
|
|
|
2014-01-03 13:32:13 -05:00
|
|
|
# Initial Sound Loading
|
|
|
|
|
|
|
|
loadAudio: ->
|
2014-02-15 18:44:45 -05:00
|
|
|
return if @headless
|
2014-01-03 13:32:13 -05:00
|
|
|
AudioPlayer.preloadInterfaceSounds ["victory"]
|
2014-01-07 02:45:33 -05:00
|
|
|
|
2014-01-03 13:32:13 -05:00
|
|
|
loadLevelSounds: ->
|
2014-02-15 18:44:45 -05:00
|
|
|
return if @headless
|
2014-01-03 13:32:13 -05:00
|
|
|
scripts = @level.get 'scripts'
|
|
|
|
return unless scripts
|
|
|
|
|
|
|
|
for script in scripts when script.noteChain
|
|
|
|
for noteGroup in script.noteChain when noteGroup.sprites
|
|
|
|
for sprite in noteGroup.sprites when sprite.say?.sound
|
|
|
|
AudioPlayer.preloadSoundReference(sprite.say.sound)
|
|
|
|
|
|
|
|
thangTypes = @supermodel.getModels(ThangType)
|
|
|
|
for thangType in thangTypes
|
|
|
|
for trigger, sounds of thangType.get('soundTriggers') or {} when trigger isnt 'say'
|
|
|
|
AudioPlayer.preloadSoundReference sound for sound in sounds
|
2014-01-07 02:45:33 -05:00
|
|
|
|
2014-01-03 13:32:13 -05:00
|
|
|
# everything else sound wise is loaded as needed as worlds are generated
|
2014-01-07 02:45:33 -05:00
|
|
|
|
2014-04-29 19:43:46 -04:00
|
|
|
progress: -> @supermodel.progress
|
2014-05-15 00:54:36 -04:00
|
|
|
|
2014-05-13 17:39:45 -04:00
|
|
|
destroy: ->
|
|
|
|
clearInterval @buildLoopInterval if @buildLoopInterval
|
|
|
|
super()
|