2014-11-28 20:49:41 -05:00
RootView = require ' views/core/RootView '
2016-05-26 20:46:49 -04:00
template = require ' templates/play/play-level-view '
2014-11-28 20:49:41 -05:00
{ me } = require ' core/auth '
2014-01-03 13:32:13 -05:00
ThangType = require ' models/ThangType '
2014-11-28 20:49:41 -05:00
utils = require ' core/utils '
storage = require ' core/storage '
2014-11-22 23:48:04 -05:00
{ createAetherOptions } = require ' lib/aether_utils '
2014-01-03 13:32:13 -05:00
# tools
Surface = require ' lib/surface/Surface '
God = require ' lib/God '
GoalManager = require ' lib/world/GoalManager '
ScriptManager = require ' lib/scripts/ScriptManager '
2014-05-05 20:37:14 -04:00
LevelBus = require ' lib/LevelBus '
2014-01-03 13:32:13 -05:00
LevelLoader = require ' lib/LevelLoader '
LevelSession = require ' models/LevelSession '
Level = require ' models/Level '
LevelComponent = require ' models/LevelComponent '
2014-03-07 18:18:56 -05:00
Article = require ' models/Article '
2014-01-03 13:32:13 -05:00
Camera = require ' lib/surface/Camera '
2014-02-19 14:42:33 -05:00
AudioPlayer = require ' lib/AudioPlayer '
2015-11-29 15:30:19 -05:00
Simulator = require ' lib/simulator/Simulator '
2016-07-08 17:17:07 -04:00
GameUIState = require ' models/GameUIState '
2014-01-03 13:32:13 -05:00
# subviews
2014-07-23 10:02:45 -04:00
LevelLoadingView = require ' ./LevelLoadingView '
2014-11-07 00:43:39 -05:00
ProblemAlertView = require ' ./tome/ProblemAlertView '
2014-07-23 10:02:45 -04:00
TomeView = require ' ./tome/TomeView '
ChatView = require ' ./LevelChatView '
HUDView = require ' ./LevelHUDView '
2014-11-08 14:35:25 -05:00
LevelDialogueView = require ' ./LevelDialogueView '
2014-07-23 10:02:45 -04:00
ControlBarView = require ' ./ControlBarView '
LevelPlaybackView = require ' ./LevelPlaybackView '
GoalsView = require ' ./LevelGoalsView '
2014-08-23 22:00:35 -04:00
LevelFlagsView = require ' ./LevelFlagsView '
2014-07-23 10:02:45 -04:00
GoldView = require ' ./LevelGoldView '
2015-08-20 14:57:47 -04:00
DuelStatsView = require ' ./DuelStatsView '
2014-07-23 10:02:45 -04:00
VictoryModal = require ' ./modal/VictoryModal '
2014-09-30 19:14:47 -04:00
HeroVictoryModal = require ' ./modal/HeroVictoryModal '
2016-01-19 18:42:20 -05:00
CourseVictoryModal = require ' ./modal/CourseVictoryModal '
2016-02-17 14:33:50 -05:00
PicoCTFVictoryModal = require ' ./modal/PicoCTFVictoryModal '
2014-07-23 10:02:45 -04:00
InfiniteLoopModal = require ' ./modal/InfiniteLoopModal '
2014-11-06 19:23:23 -05:00
LevelSetupManager = require ' lib/LevelSetupManager '
2014-12-19 21:37:42 -05:00
ContactModal = require ' views/core/ContactModal '
2016-05-26 20:46:49 -04:00
HintsView = require ' ./HintsView '
HintsState = require ' ./HintsState '
2016-07-14 21:07:36 -04:00
WebSurfaceView = require ' ./WebSurfaceView '
2014-01-03 13:32:13 -05:00
PROFILE_ME = false
2014-07-17 20:16:32 -04:00
module.exports = class PlayLevelView extends RootView
2014-01-03 13:32:13 -05:00
id: ' level-view '
template: template
cache: false
shortcutsEnabled: true
isEditorPreview: false
subscriptions:
2015-11-29 15:32:04 -05:00
' level:set-volume ' : ' onSetVolume '
2014-08-27 15:24:03 -04:00
' level:show-victory ' : ' onShowVictory '
' level:restart ' : ' onRestartLevel '
2014-10-08 13:46:10 -04:00
' level:highlight-dom ' : ' onHighlightDOM '
2014-08-27 15:24:03 -04:00
' level:end-highlight-dom ' : ' onEndHighlight '
' level:focus-dom ' : ' onFocusDom '
' level:disable-controls ' : ' onDisableControls '
' level:enable-controls ' : ' onEnableControls '
2014-08-30 12:19:41 -04:00
' god:world-load-progress-changed ' : ' onWorldLoadProgressChanged '
2014-02-06 17:00:27 -05:00
' god:new-world-created ' : ' onNewWorld '
2014-08-21 19:27:52 -04:00
' god:streaming-world-updated ' : ' onNewWorld '
2014-01-03 13:32:13 -05:00
' god:infinite-loop ' : ' onInfiniteLoop '
2014-08-27 15:24:03 -04:00
' level:reload-from-data ' : ' onLevelReloadFromData '
' level:reload-thang-type ' : ' onLevelReloadThangType '
2014-03-14 20:06:08 -04:00
' level:started ' : ' onLevelStarted '
2014-09-21 18:52:49 -04:00
' level:loading-view-unveiling ' : ' onLoadingViewUnveiling '
2014-03-14 20:06:08 -04:00
' level:loading-view-unveiled ' : ' onLoadingViewUnveiled '
2016-07-15 16:24:54 -04:00
' level:loaded ' : ' onLevelLoaded '
2014-09-21 23:19:27 -04:00
' level:session-loaded ' : ' onSessionLoaded '
2014-08-23 16:54:52 -04:00
' playback:real-time-playback-started ' : ' onRealTimePlaybackStarted '
' playback:real-time-playback-ended ' : ' onRealTimePlaybackEnded '
2014-11-18 14:21:29 -05:00
' ipad:memory-warning ' : ' onIPadMemoryWarning '
2014-11-21 19:23:26 -05:00
' store:item-purchased ' : ' onItemPurchased '
2014-01-03 13:32:13 -05:00
events:
' click # level-done-button ' : ' onDonePressed '
2014-09-22 18:34:25 -04:00
' click # stop-real-time-playback-button ' : -> Backbone . Mediator . publish ' playback:stop-real-time-playback ' , { }
2014-08-27 21:43:17 -04:00
' click # fullscreen-editor-background-screen ' : (e) -> Backbone . Mediator . publish ' tome:toggle-maximize ' , { }
2014-12-19 21:37:42 -05:00
' click .contact-link ' : ' onContactClicked '
2014-01-03 13:32:13 -05:00
2014-02-20 18:11:20 -05:00
shortcuts:
' ctrl+s ' : ' onCtrlS '
2014-11-23 18:24:59 -05:00
' esc ' : ' onEscapePressed '
2014-05-19 23:49:17 -04:00
2014-05-19 20:10:41 -04:00
# Initial Setup #############################################################
2014-02-20 18:11:20 -05:00
2014-01-03 13:32:13 -05:00
constructor: (options, @levelID) ->
console . profile ? ( ) if PROFILE_ME
super options
2015-11-12 12:57:34 -05:00
@courseID = options . courseID or @ getQueryVariable ' course '
@courseInstanceID = options . courseInstanceID or @ getQueryVariable ' course-instance '
2015-09-24 20:12:18 -04:00
2014-01-27 14:00:36 -05:00
@isEditorPreview = @ getQueryVariable ' dev '
@sessionID = @ getQueryVariable ' session '
2015-01-31 13:04:02 -05:00
@observing = @ getQueryVariable ' observing '
2014-11-18 00:30:44 -05:00
2014-11-17 18:07:10 -05:00
@opponentSessionID = @ getQueryVariable ( ' opponent ' )
@ opponentSessionID ? = @ options . opponent
2016-07-08 17:17:07 -04:00
@gameUIState = new GameUIState ( )
2014-01-03 13:32:13 -05:00
2014-08-29 20:52:47 -04:00
$ ( window ) . on ' resize ' , @ onWindowResize
2014-01-16 14:37:04 -05:00
2015-08-03 18:52:52 -04:00
application . tracker ? . enableInspectletJS ( @ levelID )
2014-01-27 14:00:36 -05:00
if @ isEditorPreview
2014-07-17 18:50:29 -04:00
@supermodel.shouldSaveBackups = (model) -> # Make sure to load possibly changed things from localStorage.
model . constructor . className in [ ' Level ' , ' LevelComponent ' , ' LevelSystem ' , ' ThangType ' ]
f = => @ load ( ) unless @ levelLoader # Wait to see if it's just given to us through setLevel.
2014-01-27 14:00:36 -05:00
setTimeout f , 100
else
@ load ( )
2015-02-27 19:07:41 -05:00
application . tracker ? . trackEvent ' Started Level Load ' , category: ' Play Level ' , level: @ levelID , label: @ levelID unless @ observing
2014-05-19 23:49:17 -04:00
2014-05-01 19:38:27 -04:00
setLevel: (@level, givenSupermodel) ->
@supermodel.models = givenSupermodel . models
@supermodel.collections = givenSupermodel . collections
@supermodel.shouldSaveBackups = givenSupermodel . shouldSaveBackups
2016-07-13 13:04:43 -04:00
serializedLevel = @ level . serialize { @ supermodel , @ session , @ otherSession , headless: false , sessionless: false }
2014-05-15 17:54:31 -04:00
@ god ? . setLevel serializedLevel
2014-01-21 02:14:34 -05:00
if @ world
@ world . loadFromLevel serializedLevel , false
else
@ load ( )
2014-01-16 14:37:04 -05:00
2014-01-15 21:37:47 -05:00
load: ->
2014-04-13 23:31:23 -04:00
@loadStartTime = new Date ( )
2016-04-08 15:59:10 -04:00
levelLoaderOptions = supermodel: @ supermodel , levelID: @ levelID , sessionID: @ sessionID , opponentSessionID: @ opponentSessionID , team: @ getQueryVariable ( ' team ' ) , observing: @ observing , courseID: @ courseID
2016-04-15 13:58:52 -04:00
if me . isSessionless ( )
2016-04-08 15:59:10 -04:00
levelLoaderOptions.fakeSessionConfig = { }
@levelLoader = new LevelLoader levelLoaderOptions
2014-05-19 20:10:41 -04:00
@ listenToOnce @ levelLoader , ' world-necessities-loaded ' , @ onWorldNecessitiesLoaded
2016-06-06 22:42:57 -04:00
@ listenTo @ levelLoader , ' world-necessity-load-failed ' , @ onWorldNecessityLoadFailed
2014-05-19 20:10:41 -04:00
2016-07-15 16:24:54 -04:00
onLevelLoaded: (e) ->
2016-07-28 16:39:58 -04:00
return if @ destroyed
unless e . level . isType ( ' web-dev ' )
@god = new God ( {
@ gameUIState
indefiniteLength: e . level . isType ( ' game-dev ' )
} )
2016-07-26 13:38:15 -04:00
@ setupGod ( ) if @ waitingToSetUpGod
2016-07-15 16:24:54 -04:00
2014-10-06 20:46:13 -04:00
trackLevelLoadEnd: ->
return if @ isEditorPreview
@loadEndTime = new Date ( )
2015-11-29 15:30:19 -05:00
@loadDuration = @ loadEndTime - @ loadStartTime
console . debug " Level unveiled after #{ ( @ loadDuration / 1000 ) . toFixed ( 2 ) } s "
2015-01-31 13:04:02 -05:00
unless @ observing
2015-11-29 15:30:19 -05:00
application . tracker ? . trackEvent ' Finished Level Load ' , category: ' Play Level ' , label: @ levelID , level: @ levelID , loadDuration: @ loadDuration
application . tracker ? . trackTiming @ loadDuration , ' Level Load Time ' , @ levelID , @ levelID
2014-10-06 20:46:13 -04:00
2016-03-15 18:51:59 -04:00
isCourseMode: -> @ courseID and @ courseInstanceID
showAds: ->
2016-05-13 14:26:52 -04:00
return false # No ads for now.
2016-03-15 18:51:59 -04:00
if application . isProduction ( ) && ! me . isPremium ( ) && ! me . isTeacher ( ) && ! window . serverConfig . picoCTF && ! @ isCourseMode ( )
return me . getCampaignAdsGroup ( ) is ' leaderboard-ads '
false
2014-05-19 20:10:41 -04:00
# CocoView overridden methods ###############################################
2014-05-19 23:49:17 -04:00
2014-01-03 13:32:13 -05:00
getRenderData: ->
c = super ( )
c.world = @ world
c
afterRender: ->
2014-04-17 19:23:35 -04:00
super ( )
2014-01-03 13:32:13 -05:00
window . onPlayLevelViewLoaded ? @ # still a hack
2015-11-10 18:22:09 -05:00
@ insertSubView @loadingView = new LevelLoadingView autoUnveil: @ options . autoUnveil or @ observing , level: @ levelLoader ? . level ? @ level , session: @ levelLoader ? . session ? @ session # May not have @level loaded yet
2014-02-13 12:26:21 -05:00
@ $el . find ( ' # level-done-button ' ) . hide ( )
2014-04-11 19:15:26 -04:00
$ ( ' body ' ) . addClass ( ' is-playing ' )
2014-09-06 22:50:31 -04:00
$ ( ' body ' ) . bind ( ' touchmove ' , false ) if @ isIPadApp ( )
2014-01-03 13:32:13 -05:00
2014-05-19 20:10:41 -04:00
afterInsert: ->
super ( )
2014-03-07 18:18:56 -05:00
2014-05-19 20:10:41 -04:00
# Partially Loaded Setup ####################################################
2014-05-19 23:49:17 -04:00
2014-05-19 20:10:41 -04:00
onWorldNecessitiesLoaded: ->
# Called when we have enough to build the world, but not everything is loaded
@ grabLevelLoaderData ( )
2016-07-15 19:22:33 -04:00
team = @ getQueryVariable ( ' team ' ) ? @ session . get ( ' team ' ) ? @ world ? . teamForPlayer ( 0 ) ? ' humans '
2014-05-02 15:32:41 -04:00
@ loadOpponentTeam ( team )
2014-05-19 20:10:41 -04:00
@ setupGod ( )
2014-05-02 15:32:41 -04:00
@ setTeam team
@ initGoalManager ( )
2014-05-19 20:10:41 -04:00
@ insertSubviews ( )
2014-05-02 15:32:41 -04:00
@ initVolume ( )
@ register ( )
@ controlBar . setBus ( @ bus )
2014-01-03 13:32:13 -05:00
@ initScriptManager ( )
2014-03-13 12:02:19 -04:00
2016-06-06 22:42:57 -04:00
onWorldNecessityLoadFailed: (resource) ->
@ loadingView . onLoadError ( resource )
2014-02-27 19:44:11 -05:00
grabLevelLoaderData: ->
@session = @ levelLoader . session
@level = @ levelLoader . level
2016-07-14 15:34:22 -04:00
if @ level . isType ( ' web-dev ' )
@ $el . addClass ' web-dev ' # Hide some of the elements we won't be using
return
@world = @ levelLoader . world
2016-07-28 16:39:58 -04:00
@ $el . addClass ' game-dev ' if @ level . isType ( ' game-dev ' )
2016-07-14 15:34:22 -04:00
@ $el . addClass ' hero ' if @ level . isType ( ' hero ' , ' hero-ladder ' , ' hero-coop ' , ' course ' , ' course-ladder ' , ' game-dev ' ) # TODO: figure out what this does and comment it
2014-11-10 13:51:46 -05:00
@ $el . addClass ' flags ' if _ . any ( @ world . thangs , (t) -> ( t . programmableProperties and ' findFlags ' in t . programmableProperties ) or t . inventory ? . flag ) or @ level . get ( ' slug ' ) is ' sky-span '
2014-11-17 18:07:10 -05:00
# TODO: Update terminology to always be opponentSession or otherSession
# TODO: E.g. if it's always opponent right now, then variable names should be opponentSession until we have coop play
2014-02-27 19:44:11 -05:00
@otherSession = @ levelLoader . opponentSession
2016-08-02 15:31:36 -04:00
unless @ level . isType ( ' game-dev ' )
@worldLoadFakeResources = [ ] # first element (0) is 1%, last (99) is 100%
for percent in [ 1 . . 100 ]
@ worldLoadFakeResources . push @ supermodel . addSomethingResource 1
2016-07-28 16:39:58 -04:00
@ renderSelectors ' # stop-real-time-playback-button '
2014-08-30 12:19:41 -04:00
onWorldLoadProgressChanged: (e) ->
2015-11-29 15:30:19 -05:00
return unless e . god is @ god
2014-08-30 12:19:41 -04:00
return unless @ worldLoadFakeResources
@ lastWorldLoadPercent ? = 0
worldLoadPercent = Math . floor 100 * e . progress
for percent in [ @ lastWorldLoadPercent + 1 . . worldLoadPercent ] by 1
@ worldLoadFakeResources [ percent - 1 ] . markLoaded ( )
@lastWorldLoadPercent = worldLoadPercent
@worldFakeLoadResources = null if worldLoadPercent is 100 # Done, don't need to watch progress any more.
2014-03-13 12:02:19 -04:00
2014-02-27 19:44:11 -05:00
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-16 20:38:33 -04:00
if ( not @ session . get ( ' teamSpells ' ) ) and @ otherSession ? . get ( ' teamSpells ' )
2014-06-30 22:16:26 -04:00
@ session . set ( ' teamSpells ' , @ otherSession . get ( ' teamSpells ' ) )
2016-05-24 15:00:04 -04:00
opponentCode = @ otherSession ? . get ( ' code ' ) or { }
2014-02-27 19:44:11 -05:00
myCode = @ session . get ( ' code ' ) or { }
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 ]
@ session . set ( ' code ' , myCode )
2014-01-03 13:32:13 -05:00
2014-05-19 20:10:41 -04:00
setupGod: ->
2016-07-14 15:34:22 -04:00
return if @ level . isType ( ' web-dev ' )
2016-07-16 02:26:43 -04:00
return @waitingToSetUpGod = true unless @ god
@waitingToSetUpGod = undefined
2016-07-13 13:04:43 -04:00
@ god . setLevel @ level . serialize { @ supermodel , @ session , @ otherSession , headless: false , sessionless: false }
2014-05-19 20:10:41 -04:00
@ god . setLevelSessionIDs if @ otherSession then [ @ session . id , @ otherSession . id ] else [ @ session . id ]
@ god . setWorldClassMap @ world . classMap
setTeam: (team) ->
team = team ? . team unless _ . isString team
team ? = ' humans '
me.team = team
2014-11-17 18:07:10 -05:00
@ session . set ' team ' , team
2014-08-27 15:24:03 -04:00
Backbone . Mediator . publish ' level:team-set ' , team: team # Needed for scripts
2014-05-19 23:50:05 -04:00
@team = team
2014-05-19 20:10:41 -04:00
initGoalManager: ->
2014-05-19 23:50:05 -04:00
@goalManager = new GoalManager ( @ world , @ level . get ( ' goals ' ) , @ team )
2016-07-15 16:24:54 -04:00
@ god ? . setGoalManager @ goalManager
2014-05-19 20:10:41 -04:00
insertSubviews: ->
2016-05-26 20:46:49 -04:00
@hintsState = new HintsState ( { hidden: true } , { @ session , @ level } )
2016-07-14 15:34:22 -04:00
@ insertSubView @tome = new TomeView { @ levelID , @ session , @ otherSession , thangs: @ world ? . thangs ? [ ] , @ supermodel , @ level , @ observing , @ courseID , @ courseInstanceID , @ god , @ hintsState }
@ insertSubView new LevelPlaybackView session: @ session , level: @ level unless @ level . isType ( ' web-dev ' )
2016-06-24 11:32:50 -04:00
@ insertSubView new GoalsView { level: @ level }
2014-11-23 20:15:59 -05:00
@ insertSubView new LevelFlagsView levelID: @ levelID , world: @ world if @ $el . hasClass ' flags '
2016-07-14 15:34:22 -04:00
@ insertSubView new GoldView { } unless @ level . get ( ' slug ' ) in [ ' wakka-maul ' ] unless @ level . isType ( ' web-dev ' )
@ insertSubView new HUDView { level: @ level } unless @ level . isType ( ' web-dev ' )
2015-01-15 14:04:48 -05:00
@ insertSubView new LevelDialogueView { level: @ level , sessionID: @ session . id }
2014-05-19 20:10:41 -04:00
@ insertSubView new ChatView levelID: @ levelID , sessionID: @ session . id , session: @ session
2014-12-15 18:11:27 -05:00
@ insertSubView new ProblemAlertView session: @ session , level: @ level , supermodel: @ supermodel
2016-07-14 11:58:43 -04:00
@ insertSubView new DuelStatsView level: @ level , session: @ session , otherSession: @ otherSession , supermodel: @ supermodel , thangs: @ world . thangs if @ level . isType ( ' hero-ladder ' , ' course-ladder ' )
2015-09-24 20:12:18 -04:00
@ insertSubView @controlBar = new ControlBarView { worldName: utils . i18n ( @ level . attributes , ' name ' ) , session: @ session , level: @ level , supermodel: @ supermodel , courseID: @ courseID , courseInstanceID: @ courseInstanceID }
2016-05-26 20:46:49 -04:00
@ insertSubView @hintsView = new HintsView ( { @ session , @ level , @ hintsState } ) , @ $ ( ' .hints-view ' )
2016-07-15 03:40:32 -04:00
@ insertSubView @webSurface = new WebSurfaceView { level: @ level , @ goalManager } if @ level . isType ( ' web-dev ' )
2014-10-02 01:02:52 -04:00
#_.delay (=> Backbone.Mediator.publish('level:set-debug', debug: true)), 5000 if @isIPadApp() # if me.displayName() is 'Nick'
2014-05-19 20:10:41 -04:00
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-05-19 20:10:41 -04:00
initScriptManager: ->
2016-07-14 15:34:22 -04:00
return if @ level . isType ( ' web-dev ' )
2014-11-01 19:59:12 -04:00
@scriptManager = new ScriptManager ( { scripts: @ world . scripts or [ ] , view: @ , session: @ session , levelID: @ level . get ( ' slug ' ) } )
2014-05-19 20:10:41 -04:00
@ scriptManager . loadFromSession ( )
register: ->
@bus = LevelBus . get ( @ levelID , @ session . id )
@ bus . setSession ( @ session )
@ bus . setSpells @ tome . spells
2016-07-14 13:26:09 -04:00
#@bus.connect() if @session.get('multiplayer') # TODO: session's multiplayer flag removed; connect bus another way if we care about it
2014-05-19 23:49:17 -04:00
2014-05-19 20:10:41 -04:00
# Load Completed Setup ######################################################
2014-09-21 23:19:27 -04:00
onSessionLoaded: (e) ->
2015-11-29 15:30:19 -05:00
return if @ session
2014-11-30 16:19:00 -05:00
Backbone . Mediator . publish " ipad:language-chosen " , language: e . session . get ( ' codeLanguage ' ) ? " python "
2014-09-21 23:19:27 -04:00
# Just the level and session have been loaded by the level loader
2015-03-14 12:39:43 -04:00
if e . level . get ( ' slug ' ) is ' zero-sum '
2015-04-03 15:19:00 -04:00
sorcerer = ' 52fd1524c7e6cf99160e7bc9 '
if e . session . get ( ' creator ' ) is ' 532dbc73a622924444b68ed9 ' # Wizard Dude gets his own avatar
sorcerer = ' 53e126a4e06b897606d38bef '
e . session . set ' heroConfig ' , { " thangType " : sorcerer , " inventory " : { " misc-0 " : " 53e2396a53457600003e3f0f " , " programming-book " : " 546e266e9df4a17d0d449be5 " , " minion " : " 54eb5dbc49fa2d5c905ddf56 " , " feet " : " 53e214f153457600003e3eab " , " right-hand " : " 54eab7f52b7506e891ca7202 " , " left-hand " : " 5463758f3839c6e02811d30f " , " wrists " : " 54693797a2b1f53ce79443e9 " , " gloves " : " 5469425ca2b1f53ce7944421 " , " torso " : " 546d4a549df4a17d0d449a97 " , " neck " : " 54693274a2b1f53ce79443c9 " , " eyes " : " 546941fda2b1f53ce794441d " , " head " : " 546d4ca19df4a17d0d449abf " } }
2016-01-26 16:20:23 -05:00
else if e . level . get ( ' slug ' ) in [ ' ace-of-coders ' , ' elemental-wars ' ]
2015-08-29 11:02:20 -04:00
goliath = ' 55e1a6e876cb0948c96af9f8 '
2015-08-30 10:28:35 -04:00
e . session . set ' heroConfig ' , { " thangType " : goliath , " inventory " : { " eyes " : " 53eb99f41a100989a40ce46e " , " neck " : " 54693274a2b1f53ce79443c9 " , " wrists " : " 54693797a2b1f53ce79443e9 " , " feet " : " 546d4d8e9df4a17d0d449acd " , " minion " : " 54eb5bf649fa2d5c905ddf4a " , " programming-book " : " 557871261ff17fef5abee3ee " } }
2015-11-27 15:12:58 -05:00
else if e . level . get ( ' slug ' ) is ' assembly-speed '
raider = ' 55527eb0b8abf4ba1fe9a107 '
e . session . set ' heroConfig ' , { " thangType " : raider , " inventory " : { } }
2016-07-14 11:58:43 -04:00
else if e . level . isType ( ' hero ' , ' hero-ladder ' , ' hero-coop ' ) and not _ . size e . session . get ( ' heroConfig ' ) ? . inventory ? { }
2014-11-09 19:19:18 -05:00
@ setupManager ? . destroy ( )
2015-11-29 15:30:19 -05:00
@setupManager = new LevelSetupManager ( { supermodel: @ supermodel , level: e . level , levelID: @ levelID , parent: @ , session: e . session , courseID: @ courseID , courseInstanceID: @ courseInstanceID } )
2014-11-09 19:19:18 -05:00
@ setupManager . open ( )
2014-11-06 19:23:23 -05:00
2014-05-19 20:10:41 -04:00
onLoaded: ->
2014-09-21 23:19:27 -04:00
_ . defer => @ onLevelLoaderLoaded ( )
2014-05-19 23:49:17 -04:00
2014-09-21 23:19:27 -04:00
onLevelLoaderLoaded: ->
2014-05-19 20:10:41 -04:00
# Everything is now loaded
2014-05-19 23:49:17 -04:00
return unless @ levelLoader . progress ( ) is 1 # double check, since closing the guide may trigger this early
2014-05-19 20:10:41 -04:00
2014-08-30 17:30:53 -04:00
# Save latest level played.
2016-07-14 11:58:43 -04:00
if not @ observing and not ( @ levelLoader . level . isType ( ' ladder ' , ' ladder-tutorial ' ) )
2014-05-19 20:10:41 -04:00
me . set ( ' lastLevel ' , @ levelID )
me . save ( )
2015-01-28 20:58:56 -05:00
application . tracker ? . identify ( )
2014-08-30 17:30:53 -04:00
@ saveRecentMatch ( ) if @ otherSession
2014-05-19 20:10:41 -04:00
@ levelLoader . destroy ( )
@levelLoader = null
2016-07-14 15:34:22 -04:00
if @ level . isType ( ' web-dev ' )
2016-07-14 21:07:36 -04:00
Backbone . Mediator . publish ' level:started ' , { }
2016-07-14 15:34:22 -04:00
else
@ initSurface ( )
2014-05-19 20:10:41 -04:00
2014-08-30 17:30:53 -04:00
saveRecentMatch: ->
allRecentlyPlayedMatches = storage . load ( ' recently-played-matches ' ) ? { }
recentlyPlayedMatches = allRecentlyPlayedMatches [ @ levelID ] ? [ ]
allRecentlyPlayedMatches [ @ levelID ] = recentlyPlayedMatches
recentlyPlayedMatches . unshift yourTeam: me . team , otherSessionID: @ otherSession . id , opponentName: @ otherSession . get ( ' creatorName ' ) unless _ . find recentlyPlayedMatches , otherSessionID: @ otherSession . id
recentlyPlayedMatches . splice ( 8 )
storage . save ' recently-played-matches ' , allRecentlyPlayedMatches
2014-05-19 20:10:41 -04:00
initSurface: ->
2014-09-25 13:47:53 -04:00
webGLSurface = $ ( ' canvas # webgl-surface ' , @ $el )
normalSurface = $ ( ' canvas # normal-surface ' , @ $el )
2016-07-08 17:17:07 -04:00
surfaceOptions = {
2016-03-15 18:51:59 -04:00
thangTypes: @ supermodel . getModels ( ThangType )
2016-07-08 17:17:07 -04:00
@ observing
2016-03-15 18:51:59 -04:00
playerNames: @ findPlayerNames ( )
levelType: @ level . get ( ' type ' , true )
stayVisible: @ showAds ( )
2016-07-08 17:17:07 -04:00
@ gameUIState
2016-07-28 16:39:58 -04:00
@ level # TODO: change from levelType to level
2016-07-08 17:17:07 -04:00
}
2016-03-15 18:51:59 -04:00
@surface = new Surface ( @ world , normalSurface , webGLSurface , surfaceOptions )
2014-05-19 20:10:41 -04:00
worldBounds = @ world . getBounds ( )
2014-06-30 22:16:26 -04:00
bounds = [ { x: worldBounds . left , y: worldBounds . top } , { x: worldBounds . right , y: worldBounds . bottom } ]
2014-05-19 20:10:41 -04:00
@ surface . camera . setBounds ( bounds )
2014-06-30 22:16:26 -04:00
@ surface . camera . zoomTo ( { x: 0 , y: 0 } , 0.1 , 0 )
2014-05-19 20:10:41 -04:00
2015-02-12 22:47:57 -05:00
findPlayerNames: ->
2016-07-14 11:58:43 -04:00
return { } unless @ level . isType ( ' ladder ' , ' hero-ladder ' , ' course-ladder ' )
2015-02-12 22:47:57 -05:00
playerNames = { }
for session in [ @ session , @ otherSession ] when session ? . get ( ' team ' )
2016-05-26 19:46:03 -04:00
playerNames [ session . get ( ' team ' ) ] = session . get ( ' creatorName ' ) or ' Anonymous '
2015-02-12 22:47:57 -05:00
playerNames
2014-05-19 20:10:41 -04:00
# Once Surface is Loaded ####################################################
onLevelStarted: ->
2016-07-14 15:34:22 -04:00
return unless @ surface ? or @ webSurface ?
2014-05-19 23:49:17 -04:00
@ loadingView . showReady ( )
2014-10-06 20:46:13 -04:00
@ trackLevelLoadEnd ( )
2014-09-23 14:39:56 -04:00
if window . currentModal and not window . currentModal . destroyed and window . currentModal . constructor isnt VictoryModal
2014-08-27 15:24:03 -04:00
return Backbone . Mediator . subscribeOnce ' modal:closed ' , @ onLevelStarted , @
2016-07-14 15:34:22 -04:00
@ surface ? . showLevel ( )
2015-11-10 18:22:09 -05:00
Backbone . Mediator . publish ' level:set-time ' , time: 0
2015-11-10 19:09:21 -05:00
if ( @ isEditorPreview or @ observing ) and not @ getQueryVariable ( ' intro ' )
2014-09-21 23:19:27 -04:00
@ loadingView . startUnveiling ( )
2015-11-10 18:22:09 -05:00
@ loadingView . unveil true
2015-11-11 09:42:12 -05:00
else
2016-07-15 12:53:08 -04:00
@ scriptManager ? . initializeCamera ( )
2014-03-14 20:06:08 -04:00
2014-09-21 18:52:49 -04:00
onLoadingViewUnveiling: (e) ->
2015-11-10 18:22:09 -05:00
@ selectHero ( )
2014-03-14 20:06:08 -04:00
onLoadingViewUnveiled: (e) ->
2016-07-14 11:58:43 -04:00
if @ level . isType ( ' course-ladder ' , ' hero-ladder ' ) or @ observing
2015-11-30 17:01:39 -05:00
# We used to autoplay by default, but now we only do it if the level says to in the introduction script.
Backbone . Mediator . publish ' level:set-playing ' , playing: true
2014-04-28 17:01:33 -04:00
@ loadingView . $el . remove ( )
2014-03-14 20:06:08 -04:00
@ removeSubView @ loadingView
@loadingView = null
2014-09-22 01:10:52 -04:00
@ playAmbientSound ( )
2015-01-15 14:04:48 -05:00
# TODO: Is it possible to create a Mongoose ObjectId for 'ls', instead of the string returned from get()?
2015-01-31 13:04:02 -05:00
application . tracker ? . trackEvent ' Started Level ' , category : ' Play Level ' , levelID: @ levelID , ls: @ session ? . get ( ' _id ' ) unless @ observing
2015-04-18 19:33:40 -04:00
$ ( window ) . trigger ' resize '
2015-11-29 15:30:19 -05:00
_ . delay ( => @ perhapsStartSimulating ? ( ) ) , 10 * 1000
2014-09-22 01:10:52 -04:00
2015-11-29 15:32:04 -05:00
onSetVolume: (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.
if e . volume and not @ ambientSound
@ playAmbientSound ( )
2014-09-22 01:10:52 -04:00
playAmbientSound: ->
2014-11-29 19:46:36 -05:00
return if @ destroyed
2014-09-22 01:10:52 -04:00
return if @ ambientSound
2015-11-29 15:32:04 -05:00
return unless me . get ' volume '
2014-09-22 01:10:52 -04:00
return unless file = { Dungeon: ' ambient-dungeon ' , Grass: ' ambient-grass ' } [ @ level . get ( ' terrain ' ) ]
src = " /file/interface/ #{ file } #{ AudioPlayer . ext } "
unless AudioPlayer . getStatus ( src ) ? . loaded
AudioPlayer . preloadSound src
Backbone . Mediator . subscribeOnce ' audio-player:loaded ' , @ playAmbientSound , @
return
2014-09-22 23:15:51 -04:00
@ambientSound = createjs . Sound . play src , loop : - 1 , volume: 0.1
createjs . Tween . get ( @ ambientSound ) . to ( { volume: 1.0 } , 10000 )
2014-03-13 12:02:19 -04:00
2015-11-10 18:22:09 -05:00
selectHero: ->
Backbone . Mediator . publish ' level:suppress-selection-sounds ' , suppress: true
Backbone . Mediator . publish ' tome:select-primary-sprite ' , { }
Backbone . Mediator . publish ' level:suppress-selection-sounds ' , suppress: false
2016-07-14 15:34:22 -04:00
@ surface ? . focusOnHero ( )
2014-01-03 13:32:13 -05:00
2015-11-29 15:30:19 -05:00
perhapsStartSimulating: ->
return unless @ shouldSimulate ( )
2016-05-24 15:00:04 -04:00
return console . error " Should not auto-simulate until we fix how these languages are loaded "
2015-11-30 10:23:33 -05:00
# TODO: how can we not require these as part of /play bundle?
2016-05-24 15:00:04 -04:00
##require "vendor/aether-#{codeLanguage}" for codeLanguage in ['javascript', 'python', 'coffeescript', 'lua', 'java']
#require 'vendor/aether-javascript'
#require 'vendor/aether-python'
#require 'vendor/aether-coffeescript'
#require 'vendor/aether-lua'
#require 'vendor/aether-java'
2015-11-29 15:30:19 -05:00
@ simulateNextGame ( )
simulateNextGame: ->
return @ simulator . fetchAndSimulateOneGame ( ) if @ simulator
2015-12-06 12:20:30 -05:00
simulatorOptions = background: true , leagueID: @ courseInstanceID
2016-07-14 11:58:43 -04:00
simulatorOptions.levelID = @ level . get ( ' slug ' ) if @ level . isType ( ' course-ladder ' , ' hero-ladder ' )
2015-12-06 12:20:30 -05:00
@simulator = new Simulator simulatorOptions
2015-11-29 15:30:19 -05:00
# Crude method of mitigating Simulator memory leak issues
fetchAndSimulateOneGameOriginal = @ simulator . fetchAndSimulateOneGame
@simulator.fetchAndSimulateOneGame = =>
if @ simulator . simulatedByYou >= 10
console . log ' ------------------- Destroying Simulator and making a new one ----------------- '
@ simulator . destroy ( )
@simulator = null
@ simulateNextGame ( )
else
fetchAndSimulateOneGameOriginal . apply @ simulator
@ simulator . fetchAndSimulateOneGame ( )
shouldSimulate: ->
2015-12-06 12:20:30 -05:00
return true if @ getQueryVariable ( ' simulate ' ) is true
2015-12-15 19:37:53 -05:00
return false if @ getQueryVariable ( ' simulate ' ) is false
2015-12-06 12:20:30 -05:00
stillBuggy = true # Keep this true while we still haven't fixed the zombie worker problem when simulating the more difficult levels on Chrome
2015-11-29 15:30:19 -05:00
defaultCores = 2
cores = window . navigator . hardwareConcurrency or defaultCores # Available on Chrome/Opera, soon Safari
defaultHeapLimit = 793000000
heapLimit = window . performance ? . memory ? . jsHeapSizeLimit or defaultHeapLimit # Only available on Chrome, basically just says 32- vs. 64-bit
gamesSimulated = me . get ( ' simulatedBy ' )
console . debug " Should we start simulating? Cores: " , window . navigator . hardwareConcurrency , " Heap limit: " , window . performance ? . memory ? . jsHeapSizeLimit , " Load duration: " , @ loadDuration
return false unless $ . browser ? . desktop
return false if $ . browser ? . msie or $ . browser ? . msedge
return false if $ . browser . linux
return false if me . level ( ) < 8
2016-07-14 12:38:45 -04:00
if @ level . isType ( ' course ' , ' game-dev ' , ' web-dev ' )
2015-11-29 15:30:19 -05:00
return false
2016-07-14 11:58:43 -04:00
else if @ level . isType ( ' hero ' ) and gamesSimulated
2015-12-06 12:20:30 -05:00
return false if stillBuggy
2015-11-29 15:30:19 -05:00
return false if cores < 8
return false if heapLimit < defaultHeapLimit
return false if @ loadDuration > 10000
2016-07-14 11:58:43 -04:00
else if @ level . isType ( ' hero-ladder ' ) and gamesSimulated
2015-12-06 12:20:30 -05:00
return false if stillBuggy
2015-11-29 15:30:19 -05:00
return false if cores < 4
return false if heapLimit < defaultHeapLimit
return false if @ loadDuration > 15000
2016-07-14 11:58:43 -04:00
else if @ level . isType ( ' hero-ladder ' ) and not gamesSimulated
2015-12-06 12:20:30 -05:00
return false if stillBuggy
2015-11-29 15:30:19 -05:00
return false if cores < 8
return false if heapLimit <= defaultHeapLimit
return false if @ loadDuration > 20000
2016-07-14 11:58:43 -04:00
else if @ level . isType ( ' course-ladder ' )
2015-11-29 15:30:19 -05:00
return false if cores <= defaultCores
return false if heapLimit < defaultHeapLimit
return false if @ loadDuration > 18000
else
2016-07-21 14:18:21 -04:00
console . warn " Unwritten level type simulation heuristics; fill these in for new level type #{ @ level . get ( ' type ' ) } ? "
2015-12-06 12:20:30 -05:00
return false if stillBuggy
2015-11-29 15:30:19 -05:00
return false if cores < 8
return false if heapLimit < defaultHeapLimit
return false if @ loadDuration > 10000
console . debug " We should have the power. Begin background ladder simulation. "
true
2014-01-03 13:32:13 -05:00
# callbacks
2014-02-20 18:11:20 -05:00
onCtrlS: (e) ->
e . preventDefault ( )
2014-11-23 18:24:59 -05:00
onEscapePressed: (e) ->
return unless @ $el . hasClass ' real-time '
Backbone . Mediator . publish ' playback:stop-real-time-playback ' , { }
2014-01-03 13:32:13 -05:00
onLevelReloadFromData: (e) ->
isReload = Boolean @ world
2014-08-12 15:56:55 -04:00
@ setLevel e . level , e . supermodel
2014-01-03 13:32:13 -05:00
if isReload
@ scriptManager . setScripts ( e . level . get ( ' scripts ' ) )
2014-08-31 18:08:52 -04:00
Backbone . Mediator . publish ' tome:cast-spell ' , { } # a bit hacky
2014-01-03 13:32:13 -05:00
2014-08-12 15:50:41 -04:00
onLevelReloadThangType: (e) ->
tt = e . thangType
for url , model of @ supermodel . models
if model . id is tt . id
for key , val of tt . attributes
model . attributes [ key ] = val
break
2014-08-31 18:08:52 -04:00
Backbone . Mediator . publish ' tome:cast-spell ' , { }
2016-04-07 22:06:57 -04:00
onWindowResize: (e) =>
2016-03-15 18:51:59 -04:00
@ endHighlight ( )
2014-01-03 13:32:13 -05:00
2014-02-12 15:41:41 -05:00
onDisableControls: (e) ->
2014-01-03 13:32:13 -05:00
return if e . controls and not ( ' level ' in e . controls )
@shortcutsEnabled = false
@wasFocusedOn = document . activeElement
$ ( ' body ' ) . focus ( )
2014-02-12 15:41:41 -05:00
onEnableControls: (e) ->
2014-01-03 13:32:13 -05:00
return if e . controls ? and not ( ' level ' in e . controls )
@shortcutsEnabled = true
$ ( @ wasFocusedOn ) . focus ( ) if @ wasFocusedOn
@wasFocusedOn = null
2014-02-12 15:41:41 -05:00
onDonePressed: -> @ showVictory ( )
2014-01-03 13:32:13 -05:00
2014-02-12 15:41:41 -05:00
onShowVictory: (e) ->
2016-07-14 12:38:45 -04:00
$ ( ' # level-done-button ' ) . show ( ) unless @ level . isType ( ' hero ' , ' hero-ladder ' , ' hero-coop ' , ' course ' , ' course-ladder ' , ' game-dev ' , ' web-dev ' )
2014-01-03 13:32:13 -05:00
@ showVictory ( ) if e . showModal
2014-04-13 23:31:23 -04:00
return if @ victorySeen
@victorySeen = true
victoryTime = ( new Date ( ) ) - @ loadEndTime
2015-01-31 13:04:02 -05:00
if not @ observing and victoryTime > 10 * 1000 # Don't track it if we're reloading an already-beaten level
2014-11-10 12:36:40 -05:00
application . tracker ? . trackEvent ' Saw Victory ' ,
2014-11-28 15:05:34 -05:00
category: ' Play Level '
2014-11-10 00:33:47 -05:00
level: @ level . get ( ' name ' )
label: @ level . get ( ' name ' )
2015-01-08 19:09:39 -05:00
levelID: @ levelID
2015-01-15 14:04:48 -05:00
ls: @ session ? . get ( ' _id ' )
2015-02-27 19:07:41 -05:00
application . tracker ? . trackTiming victoryTime , ' Level Victory Time ' , @ levelID , @ levelID
2014-01-03 13:32:13 -05:00
showVictory: ->
2015-07-24 11:56:20 -04:00
return if @ level . hasLocalChanges ( ) # Don't award achievements when beating level changed in level editor
2014-11-12 18:28:08 -05:00
@ endHighlight ( )
2016-03-03 20:18:17 -05:00
options = { level: @ level , supermodel: @ supermodel , session: @ session , hasReceivedMemoryWarning: @ hasReceivedMemoryWarning , courseID: @ courseID , courseInstanceID: @ courseInstanceID , world: @ world }
2016-07-14 12:38:45 -04:00
ModalClass = if @ level . isType ( ' hero ' , ' hero-ladder ' , ' hero-coop ' , ' course ' , ' course-ladder ' , ' game-dev ' , ' web-dev ' ) then HeroVictoryModal else VictoryModal
2016-04-15 13:58:52 -04:00
ModalClass = CourseVictoryModal if @ isCourseMode ( ) or me . isSessionless ( )
2016-07-15 19:01:57 -04:00
if @ level . isType ( ' course-ladder ' )
2016-07-14 19:50:17 -04:00
ModalClass = CourseVictoryModal
2016-08-02 16:43:13 -04:00
options.courseInstanceID = @ getQueryVariable ' course-instance ' or @ getQueryVariable ' league '
2016-02-17 14:33:50 -05:00
ModalClass = PicoCTFVictoryModal if window . serverConfig . picoCTF
2014-10-13 17:18:33 -04:00
victoryModal = new ModalClass ( options )
2014-09-30 19:14:47 -04:00
@ openModalView ( victoryModal )
2014-03-31 16:56:13 -04:00
if me . get ( ' anonymous ' )
2014-12-28 16:25:20 -05:00
window . nextURL = ' /play/ ' + ( @ level . get ( ' campaign ' ) ? ' ' ) # Signup will go here on completion instead of reloading.
2014-01-03 13:32:13 -05:00
onRestartLevel: ->
@ tome . reloadAllCode ( )
2014-08-27 15:24:03 -04:00
Backbone . Mediator . publish ' level:restarted ' , { }
2014-01-03 13:32:13 -05:00
$ ( ' # level-done-button ' , @ $el ) . hide ( )
2015-01-31 13:04:02 -05:00
application . tracker ? . trackEvent ' Confirmed Restart ' , category: ' Play Level ' , level: @ level . get ( ' name ' ) , label: @ level . get ( ' name ' ) unless @ observing
2014-01-03 13:32:13 -05:00
onInfiniteLoop: (e) ->
2015-11-29 15:30:19 -05:00
return unless e . firstWorld and e . god is @ god
2015-04-25 20:29:02 -04:00
@ openModalView new InfiniteLoopModal nonUserCodeProblem: e . nonUserCodeProblem
2015-01-31 13:04:02 -05:00
application . tracker ? . trackEvent ' Saw Initial Infinite Loop ' , category: ' Play Level ' , level: @ level . get ( ' name ' ) , label: @ level . get ( ' name ' ) unless @ observing
2014-01-03 13:32:13 -05:00
2014-10-08 13:46:10 -04:00
onHighlightDOM: (e) -> @ highlightElement e . selector , delay: e . delay , sides: e . sides , offset: e . offset , rotation: e . rotation
2014-01-03 13:32:13 -05:00
2014-10-09 22:54:39 -04:00
onEndHighlight: -> @ endHighlight ( )
2014-01-03 13:32:13 -05:00
2014-10-08 13:46:10 -04:00
onFocusDom: (e) -> $ ( e . selector ) . focus ( )
2014-01-03 13:32:13 -05:00
2014-12-19 21:37:42 -05:00
onContactClicked: (e) ->
2016-02-08 17:24:08 -05:00
Backbone . Mediator . publish ' level:contact-button-pressed ' , { }
2015-12-28 11:15:48 -05:00
@ openModalView contactModal = new ContactModal levelID: @ level . get ( ' slug ' ) or @ level . id , courseID: @ courseID , courseInstanceID: @ courseInstanceID
2014-12-19 21:37:42 -05:00
screenshot = @ surface . screenshot ( 1 , ' image/png ' , 1.0 , 1 )
body =
b64png: screenshot . replace ' data:image/png;base64, ' , ' '
filename: " screenshot- #{ @ levelID } - #{ _ . string . slugify ( ( new Date ( ) ) . toString ( ) ) } .png "
path: " db/user/ #{ me . id } "
mimetype: ' image/png '
contactModal.screenshotURL = " http://codecombat.com/file/ #{ body . path } / #{ body . filename } "
window . screenshot = screenshot
window . screenshotURL = contactModal . screenshotURL
$ . ajax ' /file ' , type: ' POST ' , data: body , success: (e) ->
contactModal . updateScreenshot ? ( )
2014-02-19 14:42:33 -05:00
# Dynamic sound loading
2014-05-14 13:35:16 -04:00
onNewWorld: (e) ->
2014-02-19 14:42:33 -05:00
return if @ headless
2014-05-16 20:38:33 -04:00
scripts = @ world . scripts # Since these worlds don't have scripts, preserve them.
2014-05-14 13:35:16 -04:00
@world = e . world
2014-05-16 20:38:33 -04:00
@world.scripts = scripts
2014-02-19 14:42:33 -05:00
thangTypes = @ supermodel . getModels ( ThangType )
2014-08-25 00:39:34 -04:00
startFrame = @ lastWorldFramesLoaded ? 0
2014-09-22 17:05:13 -04:00
finishedLoading = @ world . frames . length is @ world . totalFrames
if finishedLoading
2014-08-25 00:39:34 -04:00
@lastWorldFramesLoaded = 0
2014-09-22 17:05:13 -04:00
if @ waitingForSubmissionComplete
2014-10-02 20:49:14 -04:00
_ . defer @ onSubmissionComplete # Give it a frame to make sure we have the latest goals
2014-09-22 17:05:13 -04:00
@waitingForSubmissionComplete = false
2014-08-25 00:39:34 -04:00
else
@lastWorldFramesLoaded = @ world . frames . length
for [ spriteName , message ] in @ world . thangDialogueSounds startFrame
2014-02-19 14:42:33 -05: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-08-23 16:54:52 -04:00
# Real-time playback
onRealTimePlaybackStarted: (e) ->
2014-08-23 20:26:56 -04:00
@ $el . addClass ( ' real-time ' ) . focus ( )
2016-07-28 16:39:58 -04:00
@ $ ( ' # how-to-play-game-dev-panel ' ) . removeClass ( ' hide ' ) if @ level . isType ( ' game-dev ' )
2014-08-23 16:54:52 -04:00
@ onWindowResize ( )
onRealTimePlaybackEnded: (e) ->
2014-09-23 13:56:52 -04:00
return unless @ $el . hasClass ' real-time '
2016-07-28 16:39:58 -04:00
@ $ ( ' # how-to-play-game-dev-panel ' ) . addClass ( ' hide ' ) if @ level . isType ( ' game-dev ' )
2014-08-23 16:54:52 -04:00
@ $el . removeClass ' real-time '
@ onWindowResize ( )
2016-05-31 11:50:17 -04:00
if @ world . frames . length is @ world . totalFrames and not @ surface . countdownScreen ? . showing
2014-09-22 17:05:13 -04:00
_ . delay @ onSubmissionComplete , 750 # Wait for transition to end.
else
@waitingForSubmissionComplete = true
2014-08-23 16:54:52 -04:00
2014-09-22 17:05:13 -04:00
onSubmissionComplete: =>
return if @ destroyed
2016-05-31 11:50:17 -04:00
Backbone . Mediator . publish ' level:set-time ' , ratio: 1
2015-07-24 11:56:20 -04:00
return if @ level . hasLocalChanges ( ) # Don't award achievements when beating level changed in level editor
2016-07-14 13:26:09 -04:00
if @ goalManager . checkOverallStatus ( ) is ' success '
2015-01-22 15:26:37 -05:00
showModalFn = -> Backbone . Mediator . publish ' level:show-victory ' , showModal: true
2015-01-31 00:36:36 -05:00
@ session . recordScores @ world . scores , @ level
2015-01-22 15:26:37 -05:00
if @ level . get ' replayable '
@ session . increaseDifficulty showModalFn
else
showModalFn ( )
2014-09-22 17:05:13 -04:00
2014-01-03 13:32:13 -05:00
destroy: ->
2014-02-06 17:00:27 -05:00
@ levelLoader ? . destroy ( )
2014-01-03 13:32:13 -05:00
@ surface ? . destroy ( )
@ god ? . destroy ( )
@ goalManager ? . destroy ( )
@ scriptManager ? . destroy ( )
2014-11-09 19:19:18 -05:00
@ setupManager ? . destroy ( )
2015-11-29 15:30:19 -05:00
@ simulator ? . destroy ( )
2014-09-22 23:15:51 -04:00
if ambientSound = @ ambientSound
# Doesn't seem to work; stops immediately.
createjs . Tween . get ( ambientSound ) . to ( { volume: 0.0 } , 1500 ) . call -> ambientSound . stop ( )
2014-08-29 20:52:47 -04:00
$ ( window ) . off ' resize ' , @ onWindowResize
2014-02-11 17:24:06 -05:00
delete window . world # not sure where this is set, but this is one way to clean it up
2014-01-03 13:32:13 -05:00
@ bus ? . destroy ( )
#@instance.save() unless @instance.loading
2014-12-28 16:25:20 -05:00
delete window . nextURL
2014-01-03 13:32:13 -05:00
console . profileEnd ? ( ) if PROFILE_ME
2015-08-03 18:52:52 -04:00
application . tracker ? . disableInspectletJS ( )
2014-02-14 13:57:47 -05:00
super ( )
Real-time multiplayer initial commit
Simple matchmaking, synchronous multiplayer PVP, flags!
Rough matchmaking is under the game menu multiplayer tab, for ladder
games only. After creating a 2-person game there, you can exit that
modal and real-time cast to play against each other.
If you’re the first person to cast, you’ll sit at the real-time level
playback view waiting until the other player casts. When they do, you
both should start the real-time playback (and start placing flags like
crazy people).
If in a multiplayer session, the real-time simulation runs the players’
code against each other. Your multiplayer opponent’s name should be up
near the level name.
Multiplayer sessions are stored completely in Firebase for now, and
removed if both players leave the game. There’s plenty of bugs,
synchronization issues, and minimal polish to add before we push it to
master.
2014-08-29 02:34:07 -04:00
2014-11-22 23:48:04 -05:00
onIPadMemoryWarning: (e) ->
@hasReceivedMemoryWarning = true
onItemPurchased: (e) ->
heroConfig = @ session . get ( ' heroConfig ' ) ? { }
inventory = heroConfig . inventory ? { }
slot = e . item . getAllowedSlots ( ) [ 0 ]
if slot and not inventory [ slot ]
# Open up the inventory modal so they can equip the new item
@ setupManager ? . destroy ( )
2015-08-13 08:58:37 -04:00
@setupManager = new LevelSetupManager ( { supermodel: @ supermodel , level: @ level , levelID: @ levelID , parent: @ , session: @ session , hadEverChosenHero: true } )
2014-11-22 23:48:04 -05:00
@ setupManager . open ( )