2014-11-28 20:49:41 -05:00
RootView = require ' views/core/RootView '
2014-02-13 13:37:41 -05:00
template = require ' templates/play/spectate '
2014-11-28 20:49:41 -05:00
{ me } = require ' core/auth '
2014-02-13 13:37:41 -05:00
ThangType = require ' models/ThangType '
2014-11-28 20:49:41 -05:00
utils = require ' core/utils '
2014-02-13 13:37:41 -05:00
World = require ' lib/world/world '
# tools
Surface = require ' lib/surface/Surface '
2014-05-06 14:02:53 -04:00
God = require ' lib/God ' # 'lib/Buddha'
2014-02-13 13:37:41 -05:00
GoalManager = require ' lib/world/GoalManager '
ScriptManager = require ' lib/scripts/ScriptManager '
LevelLoader = require ' lib/LevelLoader '
LevelSession = require ' models/LevelSession '
Level = require ' models/Level '
LevelComponent = require ' models/LevelComponent '
2014-03-11 16:12:40 -04:00
Article = require ' models/Article '
2014-02-13 13:37:41 -05:00
Camera = require ' lib/surface/Camera '
2014-03-11 16:12:40 -04:00
AudioPlayer = require ' lib/AudioPlayer '
2014-02-13 13:37:41 -05:00
# subviews
2014-07-23 10:02:45 -04:00
LoadingView = require ' ./level/LevelLoadingView '
TomeView = require ' ./level/tome/TomeView '
ChatView = require ' ./level/LevelChatView '
HUDView = require ' ./level/LevelHUDView '
ControlBarView = require ' ./level/ControlBarView '
PlaybackView = require ' ./level/LevelPlaybackView '
GoalsView = require ' ./level/LevelGoalsView '
GoldView = require ' ./level/LevelGoldView '
2015-08-20 14:57:47 -04:00
DuelStatsView = require ' ./level/DuelStatsView '
2014-07-23 10:02:45 -04:00
VictoryModal = require ' ./level/modal/VictoryModal '
InfiniteLoopModal = require ' ./level/modal/InfiniteLoopModal '
2014-02-13 13:37:41 -05:00
PROFILE_ME = false
2014-07-17 20:16:32 -04:00
module.exports = class SpectateLevelView extends RootView
2014-02-13 13:37:41 -05:00
id: ' spectate-level-view '
template: template
cache: false
isEditorPreview: false
subscriptions:
2014-09-24 18:06:22 -04:00
' level:set-volume ' : (e) -> createjs . Sound . setVolume ( if e . volume is 1 then 0.6 else e . volume ) # Quieter for now until individual sound FX controls work again.
2014-02-13 13:37:41 -05:00
' god:new-world-created ' : ' onNewWorld '
2014-08-27 15:24:03 -04:00
' god:streaming-world-updated ' : ' onNewWorld '
2014-02-13 13:37:41 -05:00
' god:infinite-loop ' : ' onInfiniteLoop '
2014-08-27 15:24:03 -04:00
' level:next-game-pressed ' : ' onNextGamePressed '
2014-03-14 20:06:08 -04:00
' level:started ' : ' onLevelStarted '
' level:loading-view-unveiled ' : ' onLoadingViewUnveiled '
2014-02-13 13:37:41 -05:00
constructor: (options, @levelID) ->
console . profile ? ( ) if PROFILE_ME
super options
2014-03-14 20:06:08 -04:00
2014-03-14 15:54:52 -04:00
@sessionOne = @ getQueryVariable ' session-one '
@sessionTwo = @ getQueryVariable ' session-two '
2014-03-14 19:14:35 -04:00
if options . spectateSessions
@sessionOne = options . spectateSessions . sessionOne
@sessionTwo = options . spectateSessions . sessionTwo
2014-03-16 23:32:24 -04:00
2014-03-14 15:54:52 -04:00
if not @ sessionOne or not @ sessionTwo
@ fetchRandomSessionPair (err, data) =>
if err ? then return console . log " There was an error fetching the random session pair: #{ data } "
@sessionOne = data [ 0 ] . _id
@sessionTwo = data [ 1 ] . _id
@ load ( )
else
@ load ( )
2014-02-13 13:37:41 -05:00
setLevel: (@level, @supermodel) ->
2014-10-18 17:51:43 -04:00
serializedLevel = @ level . serialize @ supermodel , @ session , @ otherSession
2014-05-15 17:54:31 -04:00
@ god ? . setLevel serializedLevel
2014-02-13 13:37:41 -05:00
if @ world
@ world . loadFromLevel serializedLevel , false
else
@ load ( )
load: ->
2014-03-13 12:02:19 -04:00
@levelLoader = new LevelLoader
supermodel: @ supermodel
2014-03-12 20:51:09 -04:00
levelID: @ levelID
sessionID: @ sessionOne
opponentSessionID: @ sessionTwo
spectateMode: true
2014-06-30 22:16:26 -04:00
team: @ getQueryVariable ( ' team ' )
2014-09-21 17:35:59 -04:00
@god = new God maxAngels: 1 , spectate: true
2014-02-13 13:37:41 -05:00
getRenderData: ->
c = super ( )
c.world = @ world
c
afterRender: ->
window . onPlayLevelViewLoaded ? @ # still a hack
2015-11-11 09:42:12 -05:00
@ insertSubView @loadingView = new LoadingView autoUnveil: true , level: @ levelLoader ? . level ? @ level
2014-03-11 16:12:40 -04:00
@ $el . find ( ' # level-done-button ' ) . hide ( )
2014-02-13 13:37:41 -05:00
super ( )
2014-04-11 19:15:26 -04:00
$ ( ' body ' ) . addClass ( ' is-playing ' )
2014-02-13 13:37:41 -05:00
2014-05-01 19:38:27 -04:00
onLoaded: ->
2014-09-21 23:19:27 -04:00
_ . defer => @ onLevelLoaderLoaded ( )
2014-05-01 19:38:27 -04:00
2014-09-21 23:19:27 -04:00
onLevelLoaderLoaded: ->
2014-03-11 16:12:40 -04:00
@ grabLevelLoaderData ( )
2014-03-12 20:51:09 -04:00
#at this point, all requisite data is loaded, and sessions are not denormalized
2014-03-12 10:32:27 -04:00
team = @ world . teamForPlayer ( 0 )
2014-03-11 16:12:40 -04:00
@ loadOpponentTeam ( team )
2014-10-18 17:51:43 -04:00
@ god . setLevel @ level . serialize @ supermodel , @ session , @ otherSession
2014-05-15 17:54:31 -04:00
@ god . setLevelSessionIDs if @ otherSession then [ @ session . id , @ otherSession . id ] else [ @ session . id ]
2014-05-05 20:37:14 -04:00
@ god . setWorldClassMap @ world . classMap
2014-03-11 16:12:40 -04:00
@ setTeam team
2014-02-13 13:37:41 -05:00
@ initSurface ( )
@ initGoalManager ( )
@ initScriptManager ( )
2014-05-05 18:33:08 -04:00
@ insertSubviews ( )
2014-02-13 13:37:41 -05:00
@ initVolume ( )
2014-03-14 20:06:08 -04:00
2014-03-18 16:08:26 -04:00
@originalSessionState = $ . extend ( true , { } , @ session . get ( ' state ' ) )
2014-02-13 13:37:41 -05:00
@ register ( )
@ controlBar . setBus ( @ bus )
@ surface . showLevel ( )
2014-03-13 12:02:19 -04:00
2014-03-11 16:12:40 -04:00
grabLevelLoaderData: ->
@session = @ levelLoader . session
@world = @ levelLoader . world
@level = @ levelLoader . level
@otherSession = @ levelLoader . opponentSession
@ levelLoader . destroy ( )
@levelLoader = null
loadOpponentTeam: (myTeam) ->
opponentSpells = [ ]
for spellTeam , spells of @ session . get ( ' teamSpells ' ) ? @ otherSession ? . get ( ' teamSpells ' ) ? { }
continue if spellTeam is myTeam or not myTeam
opponentSpells = opponentSpells . concat spells
2014-05-15 18:18:15 -04:00
opponentCode = @ otherSession ? . get ( ' transpiledCode ' ) or { }
myCode = @ session . get ( ' transpiledCode ' ) or { }
2014-03-11 16:12:40 -04:00
for spell in opponentSpells
[ thang , spell ] = spell . split ' / '
c = opponentCode [ thang ] ? [ spell ]
myCode [ thang ] ? = { }
if c then myCode [ thang ] [ spell ] = c else delete myCode [ thang ] [ spell ]
2014-06-30 22:16:26 -04:00
2014-03-11 16:12:40 -04:00
@ session . set ( ' code ' , myCode )
if @ session . get ( ' multiplayer ' ) and @ otherSession ?
# For now, ladderGame will disallow multiplayer, because session code combining doesn't play nice yet.
@ session . set ' multiplayer ' , false
2014-03-14 20:06:08 -04:00
onLevelStarted: (e) ->
2014-09-21 18:52:49 -04:00
go = =>
@ loadingView ? . startUnveiling ( )
2015-11-11 09:42:12 -05:00
@ loadingView ? . unveil true
2014-09-21 17:35:59 -04:00
_ . delay go , 1000
2014-03-14 20:06:08 -04:00
onLoadingViewUnveiled: (e) ->
2014-04-13 23:31:23 -04:00
# Don't remove it; we want its decoration around on large screens.
#@removeSubView @loadingView
#@loadingView = null
2015-09-03 18:22:25 -04:00
Backbone . Mediator . publish ' level:set-playing ' , playing: false
Backbone . Mediator . publish ' level:set-time ' , time: 1 # Helps to have perhaps built a few Thangs and gotten a good list of spritesheets we need to render for our initial paused frame
2014-02-13 13:37:41 -05:00
onSupermodelLoadedOne: =>
@ modelsLoaded ? = 0
@ modelsLoaded += 1
@ updateInitString ( )
updateInitString: ->
return if @ surface
@ modelsLoaded ? = 0
canvas = @ $el . find ( ' # surface ' ) [ 0 ]
ctx = canvas . getContext ( ' 2d ' )
2014-06-30 22:16:26 -04:00
ctx . font = ' 20px Georgia '
2014-02-13 13:37:41 -05:00
ctx . clearRect ( 0 , 0 , canvas . width , canvas . height )
ctx . fillText ( " Loaded #{ @ modelsLoaded } thingies " , 50 , 50 )
2014-05-05 18:33:08 -04:00
insertSubviews: ->
2015-02-12 22:47:57 -05:00
@ insertSubView @tome = new TomeView levelID: @ levelID , session: @ session , otherSession: @ otherSession , thangs: @ world . thangs , supermodel: @ supermodel , spectateView: true , spectateOpponentCodeLanguage: @ otherSession ? . get ( ' submittedCodeLanguage ' ) , level: @ level
@ insertSubView new PlaybackView session: @ session , level: @ level
2014-03-12 10:32:27 -04:00
2014-02-13 13:37:41 -05:00
@ insertSubView new GoldView { }
2015-02-12 22:47:57 -05:00
@ insertSubView new HUDView { level: @ level }
2015-08-20 14:57:47 -04:00
@ insertSubView new DuelStatsView level: @ level , session: @ session , otherSession: @ otherSession , supermodel: @ supermodel , thangs: @ world . thangs if @ level . get ( ' type ' ) in [ ' hero-ladder ' , ' course-ladder ' ]
@ insertSubView @controlBar = new ControlBarView { worldName: utils . i18n ( @ level . attributes , ' name ' ) , session: @ session , level: @ level , supermodel: @ supermodel , spectateGame: true }
2014-02-13 13:37:41 -05:00
2014-03-11 16:12:40 -04:00
# callbacks
2014-02-13 13:37:41 -05:00
onInfiniteLoop: (e) ->
return unless e . firstWorld
@ openModalView new InfiniteLoopModal ( )
window . tracker ? . trackEvent ' Saw Initial Infinite Loop ' , level: @ world . name , label: @ world . name
# initialization
initSurface: ->
2014-10-17 11:47:53 -04:00
webGLSurface = $ ( ' canvas # webgl-surface ' , @ $el )
normalSurface = $ ( ' canvas # normal-surface ' , @ $el )
2015-07-24 20:37:42 -04:00
@surface = new Surface @ world , normalSurface , webGLSurface , thangTypes: @ supermodel . getModels ( ThangType ) , playJingle: not @ isEditorPreview , spectateGame: true , playerNames: @ findPlayerNames ( )
2014-02-13 13:37:41 -05:00
worldBounds = @ world . getBounds ( )
bounds = [ { x : worldBounds . left , y : worldBounds . top } , { x : worldBounds . right , y : worldBounds . bottom } ]
@ surface . camera . setBounds ( bounds )
2014-03-16 23:32:24 -04:00
zoom = =>
@ surface . camera . zoomTo ( { x: ( worldBounds . right - worldBounds . left ) / 2 , y: ( worldBounds . top - worldBounds . bottom ) / 2 } , 0.1 , 0 )
_ . delay zoom , 4000 # call it later for some reason (TODO: figure this out)
2014-02-13 13:37:41 -05:00
2015-02-12 22:47:57 -05:00
findPlayerNames: ->
playerNames = { }
for session in [ @ session , @ otherSession ] when session ? . get ( ' team ' )
playerNames [ session . get ( ' team ' ) ] = session . get ( ' creatorName ' ) or ' Anoner '
playerNames
2014-02-13 13:37:41 -05:00
initGoalManager: ->
2014-03-11 16:12:40 -04:00
@goalManager = new GoalManager ( @ world , @ level . get ( ' goals ' ) )
2014-05-05 20:37:14 -04:00
@ god . setGoalManager @ goalManager
2014-02-13 13:37:41 -05:00
initScriptManager: ->
2014-03-13 18:22:35 -04:00
if @ world . scripts
nonVictoryPlaybackScripts = _ . reject @ world . scripts , (script) ->
2014-09-04 14:04:16 -04:00
script . id . indexOf ( ' Set Camera Boundaries ' ) is - 1
2014-03-13 18:22:35 -04:00
else
2014-06-30 22:16:26 -04:00
console . log ' World scripts don \' t exist! '
2014-03-13 18:22:35 -04:00
nonVictoryPlaybackScripts = [ ]
@scriptManager = new ScriptManager ( { scripts: nonVictoryPlaybackScripts , view : @ , session: @ session } )
2014-02-13 13:37:41 -05:00
@ scriptManager . loadFromSession ( )
initVolume: ->
volume = me . get ( ' volume ' )
volume = 1.0 unless volume ?
2014-08-27 15:24:03 -04:00
Backbone . Mediator . publish ' level:set-volume ' , volume: volume
2014-02-13 13:37:41 -05:00
2014-03-12 20:51:09 -04:00
register: -> return
2014-02-13 13:37:41 -05:00
onSessionWillSave: (e) ->
# Something interesting has happened, so (at a lower frequency), we'll save a screenshot.
2014-06-30 22:16:26 -04:00
console . log ' Session is saving but shouldn \' t save!!!!!!! '
2014-02-13 13:37:41 -05:00
# Throttled
saveScreenshot: (session) =>
return unless screenshot = @ surface ? . screenshot ( )
2014-10-27 19:09:52 -04:00
session . save { screenshot: screenshot } , { patch: true , type: ' PUT ' }
2014-02-13 13:37:41 -05:00
setTeam: (team) ->
team = team ? . team unless _ . isString team
team ? = ' humans '
me.team = team
Backbone . Mediator . publish ' level:team-set ' , team: team
2014-03-11 16:12:40 -04:00
# Dynamic sound loading
2014-08-27 15:24:03 -04:00
onNewWorld: (e) ->
2014-03-11 16:12:40 -04:00
return if @ headless
2014-08-27 15:24:03 -04:00
scripts = @ world . scripts # Since these worlds don't have scripts, preserve them.
@world = e . world
2014-09-21 17:35:59 -04:00
@world.scripts = scripts
2014-03-11 16:12:40 -04:00
thangTypes = @ supermodel . getModels ( ThangType )
2014-08-27 15:24:03 -04:00
startFrame = @ lastWorldFramesLoaded ? 0
if @ world . frames . length is @ world . totalFrames # Finished loading
@lastWorldFramesLoaded = 0
2015-09-04 19:21:35 -04:00
unless @ getQueryVariable ( ' autoplay ' ) is false
Backbone . Mediator . publish ' level:set-playing ' , playing: true # Since we paused at first, now we autostart playback.
2014-08-27 15:24:03 -04:00
else
@lastWorldFramesLoaded = @ world . frames . length
for [ spriteName , message ] in @ world . thangDialogueSounds startFrame
2014-03-11 16:12:40 -04:00
continue unless thangType = _ . find thangTypes , (m) -> m . get ( ' name ' ) is spriteName
continue unless sound = AudioPlayer . soundForDialogue message , thangType . get ( ' soundTriggers ' )
AudioPlayer . preloadSoundReference sound
2014-03-13 12:02:19 -04:00
2014-03-12 20:51:09 -04:00
onNextGamePressed: (e) ->
2014-03-14 14:30:04 -04:00
@ fetchRandomSessionPair (err, data) =>
if err ? then return console . log " There was an error fetching the random session pair: #{ data } "
@sessionOne = data [ 0 ] . _id
@sessionTwo = data [ 1 ] . _id
2014-03-14 19:14:35 -04:00
url = " /play/spectate/ #{ @ levelID } ?session-one= #{ @ sessionOne } &session-two= #{ @ sessionTwo } "
2015-09-04 19:21:35 -04:00
if leagueID = @ getQueryVariable ' league '
url += " &league= " + leagueID
2014-03-14 14:30:04 -04:00
Backbone . Mediator . publish ' router:navigate ' , {
route: url ,
viewClass: SpectateLevelView ,
2014-03-17 00:33:46 -04:00
viewArgs: [
{
spectateSessions: { sessionOne: @ sessionOne , sessionTwo: @ sessionTwo }
supermodel: @ supermodel
}
2014-06-30 22:16:26 -04:00
@ levelID
]
}
history ? . pushState ? { } , ' ' , url # Backbone won't update the URL if just query parameters change
2014-03-14 14:30:04 -04:00
fetchRandomSessionPair: (cb) ->
2014-06-30 22:16:26 -04:00
console . log ' Fetching random session pair! '
2014-03-14 15:54:52 -04:00
randomSessionPairURL = " /db/level/ #{ @ levelID } /random_session_pair "
2014-03-14 14:30:04 -04:00
$ . ajax
url: randomSessionPairURL
2014-06-30 22:16:26 -04:00
type: ' GET '
2015-02-11 16:12:42 -05:00
cache: false
2014-03-14 14:30:04 -04:00
complete: (jqxhr, textStatus) ->
2014-06-30 22:16:26 -04:00
if textStatus isnt ' success '
cb ( ' error ' , jqxhr . statusText )
2014-03-14 14:30:04 -04:00
else
cb ( null , $ . parseJSON ( jqxhr . responseText ) )
2014-03-14 20:06:08 -04:00
2014-03-13 18:22:35 -04:00
destroy: ()->
2014-02-13 13:37:41 -05:00
@ levelLoader ? . destroy ( )
@ surface ? . destroy ( )
@ god ? . destroy ( )
@ goalManager ? . destroy ( )
@ scriptManager ? . destroy ( )
delete window . world # not sure where this is set, but this is one way to clean it up
console . profileEnd ? ( ) if PROFILE_ME
2014-03-13 18:22:35 -04:00
super ( )