mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2024-11-23 15:48:11 -05:00
Remove real-time multiplayer prototype code
This commit is contained in:
parent
349ab24da7
commit
c5c831c211
29 changed files with 36 additions and 1128 deletions
|
@ -1,6 +0,0 @@
|
|||
module.exports = class RealTimeCollection extends Backbone.Firebase.Collection
|
||||
constructor: (savePath) ->
|
||||
# TODO: Don't hard code this here
|
||||
# TODO: Use prod path in prod
|
||||
@firebase = 'https://codecombat.firebaseio.com/test/db/' + savePath
|
||||
super()
|
|
@ -125,8 +125,6 @@ module.exports = class CocoRouter extends Backbone.Router
|
|||
|
||||
'legal': go('LegalView')
|
||||
|
||||
'multiplayer': go('MultiplayerView')
|
||||
|
||||
'play(/)': go('play/CampaignView') # extra slash is to get Facebook app to work
|
||||
'play/ladder/:levelID/:leagueType/:leagueID': go('ladder/LadderView')
|
||||
'play/ladder/:levelID': go('ladder/LadderView')
|
||||
|
|
|
@ -8,7 +8,6 @@ channelSchemas =
|
|||
'errors': require 'schemas/subscriptions/errors'
|
||||
'ipad': require 'schemas/subscriptions/ipad'
|
||||
'misc': require 'schemas/subscriptions/misc'
|
||||
'multiplayer': require 'schemas/subscriptions/multiplayer'
|
||||
'play': require 'schemas/subscriptions/play'
|
||||
'surface': require 'schemas/subscriptions/surface'
|
||||
'tome': require 'schemas/subscriptions/tome'
|
||||
|
@ -165,5 +164,5 @@ window.onbeforeunload = (e) ->
|
|||
return leavingMessage
|
||||
else
|
||||
return
|
||||
|
||||
|
||||
$ -> init()
|
||||
|
|
|
@ -41,7 +41,6 @@ module.exports = class LevelBus extends Bus
|
|||
@fireScriptsRef = @fireRef?.child('scripts')
|
||||
|
||||
setSession: (@session) ->
|
||||
@listenTo(@session, 'change:multiplayer', @onMultiplayerChanged)
|
||||
@timerIntervalID = setInterval(@incrementSessionPlaytime, 1000)
|
||||
|
||||
onIdleChanged: (e) ->
|
||||
|
@ -53,8 +52,7 @@ module.exports = class LevelBus extends Bus
|
|||
@session.set('playtime', (@session.get('playtime') ? 0) + 1)
|
||||
|
||||
onPoint: ->
|
||||
return true unless @session?.get('multiplayer')
|
||||
super()
|
||||
return true
|
||||
|
||||
onMeSynced: =>
|
||||
super()
|
||||
|
@ -236,17 +234,11 @@ module.exports = class LevelBus extends Bus
|
|||
@changedSessionProperties.chat = true
|
||||
@saveSession()
|
||||
|
||||
onMultiplayerChanged: ->
|
||||
@changedSessionProperties.multiplayer = true
|
||||
@session.updatePermissions()
|
||||
@changedSessionProperties.permissions = true
|
||||
@saveSession()
|
||||
|
||||
# Debounced as saveSession
|
||||
reallySaveSession: ->
|
||||
return if _.isEmpty @changedSessionProperties
|
||||
# don't let peeking admins mess with the session accidentally
|
||||
return unless @session.get('multiplayer') or @session.get('creator') is me.id
|
||||
return unless @session.get('creator') is me.id
|
||||
return if @session.fake
|
||||
Backbone.Mediator.publish 'level:session-will-save', session: @session
|
||||
patch = {}
|
||||
|
|
|
@ -10,7 +10,6 @@ Letterbox = require './Letterbox'
|
|||
Dimmer = require './Dimmer'
|
||||
CountdownScreen = require './CountdownScreen'
|
||||
PlaybackOverScreen = require './PlaybackOverScreen'
|
||||
WaitingScreen = require './WaitingScreen'
|
||||
DebugDisplay = require './DebugDisplay'
|
||||
CoordinateDisplay = require './CoordinateDisplay'
|
||||
CoordinateGrid = require './CoordinateGrid'
|
||||
|
@ -70,7 +69,6 @@ module.exports = Surface = class Surface extends CocoClass
|
|||
'level:set-letterbox': 'onSetLetterbox'
|
||||
'application:idle-changed': 'onIdleChanged'
|
||||
'camera:zoom-updated': 'onZoomUpdated'
|
||||
'playback:real-time-playback-waiting': 'onRealTimePlaybackWaiting'
|
||||
'playback:real-time-playback-started': 'onRealTimePlaybackStarted'
|
||||
'playback:real-time-playback-ended': 'onRealTimePlaybackEnded'
|
||||
'level:flag-color-selected': 'onFlagColorSelected'
|
||||
|
@ -135,7 +133,6 @@ module.exports = Surface = class Surface extends CocoClass
|
|||
@countdownScreen = new CountdownScreen camera: @camera, layer: @screenLayer, showsCountdown: @world.showsCountdown
|
||||
@playbackOverScreen = new PlaybackOverScreen camera: @camera, layer: @screenLayer, playerNames: @options.playerNames
|
||||
@normalStage.addChildAt @playbackOverScreen.dimLayer, 0 # Put this below the other layers, actually, so we can more easily read text on the screen.
|
||||
@waitingScreen = new WaitingScreen camera: @camera, layer: @screenLayer
|
||||
@initCoordinates()
|
||||
@webGLStage.enableMouseOver(10)
|
||||
@webGLStage.addEventListener 'stagemousemove', @onMouseMove
|
||||
|
@ -570,7 +567,7 @@ module.exports = Surface = class Surface extends CocoClass
|
|||
scaleFactor = 1
|
||||
if @options.stayVisible
|
||||
availableHeight = window.innerHeight
|
||||
availableHeight -= $('.ad-container').outerHeight()
|
||||
availableHeight -= $('.ad-container').outerHeight()
|
||||
availableHeight -= $('#game-area').outerHeight() - $('#canvas-wrapper').outerHeight()
|
||||
scaleFactor = availableHeight / newHeight if availableHeight < newHeight
|
||||
newWidth *= scaleFactor
|
||||
|
@ -602,9 +599,6 @@ module.exports = Surface = class Surface extends CocoClass
|
|||
|
||||
#- Real-time playback
|
||||
|
||||
onRealTimePlaybackWaiting: (e) ->
|
||||
@onRealTimePlaybackStarted e
|
||||
|
||||
onRealTimePlaybackStarted: (e) ->
|
||||
return if @realTime
|
||||
@realTimeInputEvents.reset()
|
||||
|
@ -741,7 +735,6 @@ module.exports = Surface = class Surface extends CocoClass
|
|||
@dimmer?.destroy()
|
||||
@countdownScreen?.destroy()
|
||||
@playbackOverScreen?.destroy()
|
||||
@waitingScreen?.destroy()
|
||||
@coordinateDisplay?.destroy()
|
||||
@coordinateGrid?.destroy()
|
||||
@normalStage.clear()
|
||||
|
|
|
@ -1,65 +0,0 @@
|
|||
CocoClass = require 'core/CocoClass'
|
||||
RealTimeCollection = require 'collections/RealTimeCollection'
|
||||
|
||||
module.exports = class WaitingScreen extends CocoClass
|
||||
subscriptions:
|
||||
'playback:real-time-playback-waiting': 'onRealTimePlaybackWaiting'
|
||||
'playback:real-time-playback-started': 'onRealTimePlaybackStarted'
|
||||
'playback:real-time-playback-ended': 'onRealTimePlaybackEnded'
|
||||
'real-time-multiplayer:player-status': 'onRealTimeMultiplayerPlayerStatus'
|
||||
|
||||
constructor: (options) ->
|
||||
super()
|
||||
options ?= {}
|
||||
@camera = options.camera
|
||||
@layer = options.layer
|
||||
@waitingText = options.text or 'Waiting...'
|
||||
console.error @toString(), 'needs a camera.' unless @camera
|
||||
console.error @toString(), 'needs a layer.' unless @layer
|
||||
@build()
|
||||
|
||||
onCastingBegins: (e) -> @show() unless e.preload
|
||||
onCastingEnds: (e) -> @hide()
|
||||
|
||||
toString: -> '<WaitingScreen>'
|
||||
|
||||
build: ->
|
||||
@dimLayer = new createjs.Container()
|
||||
@dimLayer.mouseEnabled = @dimLayer.mouseChildren = false
|
||||
@dimLayer.addChild @dimScreen = new createjs.Shape()
|
||||
@dimScreen.graphics.beginFill('rgba(0,0,0,0.5)').rect 0, 0, @camera.canvasWidth, @camera.canvasHeight
|
||||
@dimLayer.alpha = 0
|
||||
@dimLayer.addChild @makeWaitingText()
|
||||
|
||||
makeWaitingText: ->
|
||||
size = Math.ceil @camera.canvasHeight / 8
|
||||
text = new createjs.Text @waitingText, "#{size}px Open Sans Condensed", '#F7B42C'
|
||||
text.shadow = new createjs.Shadow '#000', Math.ceil(@camera.canvasHeight / 300), Math.ceil(@camera.canvasHeight / 300), Math.ceil(@camera.canvasHeight / 120)
|
||||
text.textAlign = 'center'
|
||||
text.textBaseline = 'middle'
|
||||
text.x = @camera.canvasWidth / 2
|
||||
text.y = @camera.canvasHeight / 2
|
||||
@text = text
|
||||
return text
|
||||
|
||||
show: ->
|
||||
return if @showing
|
||||
@showing = true
|
||||
@dimLayer.alpha = 0
|
||||
createjs.Tween.removeTweens @dimLayer
|
||||
createjs.Tween.get(@dimLayer).to({alpha: 1}, 500)
|
||||
@layer.addChild @dimLayer
|
||||
|
||||
hide: ->
|
||||
return unless @showing
|
||||
@showing = false
|
||||
createjs.Tween.removeTweens @dimLayer
|
||||
createjs.Tween.get(@dimLayer).to({alpha: 0}, 500).call => @layer.removeChild @dimLayer unless @destroyed
|
||||
|
||||
onRealTimeMultiplayerPlayerStatus: (e) -> @text.text = e.status
|
||||
|
||||
onRealTimePlaybackWaiting: (e) -> @show()
|
||||
|
||||
onRealTimePlaybackStarted: (e) -> @hide()
|
||||
|
||||
onRealTimePlaybackEnded: (e) -> @hide()
|
|
@ -318,7 +318,7 @@
|
|||
write_this_down: "Write this down:"
|
||||
start_playing: "Start Playing!"
|
||||
sso_connected: "Successfully connected with:"
|
||||
|
||||
|
||||
recover:
|
||||
recover_account_title: "Recover Account"
|
||||
send_password: "Send Recovery Password"
|
||||
|
@ -1890,16 +1890,6 @@
|
|||
merge_conflict_with: "MERGE CONFLICT WITH"
|
||||
no_changes: "No Changes"
|
||||
|
||||
multiplayer:
|
||||
multiplayer_title: "Multiplayer Settings" # We'll be changing this around significantly soon. Until then, it's not important to translate.
|
||||
multiplayer_toggle: "Enable multiplayer"
|
||||
multiplayer_toggle_description: "Allow others to join your game."
|
||||
multiplayer_link_description: "Give this link to anyone to have them join you."
|
||||
multiplayer_hint_label: "Hint:"
|
||||
multiplayer_hint: " Click the link to select all, then press ⌘-C or Ctrl-C to copy the link."
|
||||
multiplayer_coming_soon: "More multiplayer features to come!"
|
||||
multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
|
||||
|
||||
legal:
|
||||
page_title: "Legal"
|
||||
opensource_intro: "CodeCombat is completely open source."
|
||||
|
|
|
@ -15,8 +15,6 @@ module.exports = class LevelSession extends CocoModel
|
|||
updatePermissions: ->
|
||||
permissions = @get 'permissions', true
|
||||
permissions = (p for p in permissions when p.target isnt 'public')
|
||||
if @get('multiplayer')
|
||||
permissions.push {target: 'public', access: 'write'}
|
||||
@set 'permissions', permissions
|
||||
|
||||
getSourceFor: (spellKey) ->
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
module.exports = class RealTimeModel extends Backbone.Firebase.Model
|
||||
constructor: (savePath) ->
|
||||
# TODO: Don't hard code this here
|
||||
# TODO: Use prod path in prod
|
||||
@firebase = 'https://codecombat.firebaseio.com/test/db/' + savePath
|
||||
super()
|
|
@ -37,8 +37,6 @@ _.extend LevelSessionSchema.properties,
|
|||
type: 'string'
|
||||
levelID:
|
||||
type: 'string'
|
||||
multiplayer:
|
||||
type: 'boolean'
|
||||
creator: c.objectId
|
||||
links:
|
||||
[
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
c = require 'schemas/schemas'
|
||||
|
||||
module.exports =
|
||||
'real-time-multiplayer:created-game': c.object {title: 'Multiplayer created game', required: ['realTimeSessionID']},
|
||||
realTimeSessionID: {type: 'string'}
|
||||
|
||||
'real-time-multiplayer:joined-game': c.object {title: 'Multiplayer joined game', required: ['realTimeSessionID']},
|
||||
realTimeSessionID: {type: 'string'}
|
||||
|
||||
'real-time-multiplayer:left-game': c.object {title: 'Multiplayer left game'},
|
||||
userID: {type: 'string'}
|
||||
|
||||
'real-time-multiplayer:manual-cast': c.object {title: 'Multiplayer manual cast'}
|
||||
|
||||
'real-time-multiplayer:new-opponent-code': c.object {title: 'Multiplayer new opponent code', required: ['code', 'codeLanguage']},
|
||||
code: {type: 'object'}
|
||||
codeLanguage: {type: 'string'}
|
||||
team: {type: 'string'}
|
||||
|
||||
'real-time-multiplayer:player-status': c.object {title: 'Multiplayer player status', required: ['status']},
|
||||
status: {type: 'string'}
|
|
@ -96,8 +96,6 @@ module.exports =
|
|||
|
||||
'playback:stop-real-time-playback': c.object {}
|
||||
|
||||
'playback:real-time-playback-waiting': c.object {}
|
||||
|
||||
'playback:real-time-playback-started': c.object {}
|
||||
|
||||
'playback:real-time-playback-ended': c.object {}
|
||||
|
|
|
@ -124,37 +124,6 @@
|
|||
@include rotate(-15deg)
|
||||
vertical-align: middle
|
||||
|
||||
.multiplayer-area-container
|
||||
position: relative
|
||||
width: 100%
|
||||
height: 50px
|
||||
pointer-events: none
|
||||
|
||||
.multiplayer-area
|
||||
min-width: 200px
|
||||
max-width: 293px
|
||||
height: 60px
|
||||
margin: 0 auto
|
||||
padding: 8px
|
||||
border-style: solid
|
||||
border-image: url(/images/level/control_bar_level_name_background.png) 30 fill round
|
||||
border-width: 0 15px 15px 15px
|
||||
text-align: center
|
||||
position: absolute
|
||||
left: 50%
|
||||
cursor: pointer
|
||||
pointer-events: all
|
||||
@include translate(-50%, 0)
|
||||
|
||||
.multiplayer-label
|
||||
font-size: 12px
|
||||
color: $control-yellow-highlight
|
||||
margin-bottom: -5px
|
||||
|
||||
.multiplayer-status
|
||||
color: white
|
||||
font-size: 18px
|
||||
|
||||
.buttons-area
|
||||
position: absolute
|
||||
right: 35px
|
||||
|
@ -210,11 +179,6 @@ html.no-borderimage
|
|||
background: transparent url(/images/level/control_bar_level_name_background.png)
|
||||
background-size: contain
|
||||
background-repeat: no-repeat
|
||||
#control-bar-view .multiplayer-area
|
||||
border: 0
|
||||
background: transparent url(/images/level/control_bar_level_name_background.png)
|
||||
background-size: contain
|
||||
background-repeat: no-repeat
|
||||
|
||||
|
||||
body:not(.ipad)
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
#multiplayer-view
|
||||
textarea
|
||||
width: 100%
|
||||
box-sizing: border-box
|
||||
padding: 5px
|
||||
text-align: center
|
||||
height: 30px
|
||||
font-size: 11px
|
|
@ -9,22 +9,13 @@
|
|||
.glyphicon.glyphicon-play
|
||||
span(data-i18n=me.isSessionless() ? "nav.courses" : (ladderGame ? "general.ladder" : "nav.play")).home-text Levels
|
||||
|
||||
if isMultiplayerLevel && !observing
|
||||
.multiplayer-area-container
|
||||
.multiplayer-area
|
||||
.multiplayer-label(data-i18n="play_level.control_bar_multiplayer")
|
||||
if multiplayerStatus
|
||||
.multiplayer-status= multiplayerStatus
|
||||
else
|
||||
.multiplayer-status(data-i18n="play_level.control_bar_join_game")
|
||||
else
|
||||
.level-name-area-container
|
||||
.level-name-area
|
||||
.level-label(data-i18n="play_level.level")
|
||||
.level-name(title=difficultyTitle || "")
|
||||
span #{view.levelNumber ? view.levelNumber + '. ' : ''}#{worldName.replace('Course: ', '')}
|
||||
if levelDifficulty
|
||||
sup.level-difficulty= levelDifficulty
|
||||
.level-name-area-container
|
||||
.level-name-area
|
||||
.level-label(data-i18n="play_level.level")
|
||||
.level-name(title=difficultyTitle || "")
|
||||
span #{view.levelNumber ? view.levelNumber + '. ' : ''}#{worldName.replace('Course: ', '')}
|
||||
if levelDifficulty
|
||||
sup.level-difficulty= levelDifficulty
|
||||
|
||||
.buttons-area
|
||||
|
||||
|
|
|
@ -1,90 +0,0 @@
|
|||
if !ladderGame
|
||||
.form
|
||||
.form-group.checkbox
|
||||
label(for="multiplayer")
|
||||
input#multiplayer(name="multiplayer", type="checkbox", checked=multiplayer)
|
||||
span(data-i18n="multiplayer.multiplayer_toggle") Enable multiplayer
|
||||
span.help-block(data-i18n="multiplayer.multiplayer_toggle_description") Allow others to join your game.
|
||||
|
||||
hr
|
||||
|
||||
div#link-area
|
||||
p(data-i18n="multiplayer.multiplayer_link_description") Give this link to anyone to have them join you.
|
||||
|
||||
textarea.well#multiplayer-join-link(readonly=true)= joinLink
|
||||
|
||||
p
|
||||
strong(data-i18n="multiplayer.multiplayer_hint_label") Hint:
|
||||
span(data-i18n="multiplayer.multiplayer_hint") Click the link to select all, then press ⌘-C or Ctrl-C to copy the link.
|
||||
|
||||
p(data-i18n="multiplayer.multiplayer_coming_soon") More multiplayer features to come!
|
||||
|
||||
if ladderGame
|
||||
if me.get('anonymous')
|
||||
p(data-i18n="multiplayer.multiplayer_sign_in_leaderboard") Sign in or create an account and get your solution on the leaderboard.
|
||||
else if realTimeSessions && realTimeSessionsPlayers
|
||||
button#create-game-button Create Game
|
||||
|
||||
hr
|
||||
|
||||
div#created-multiplayer-session
|
||||
h3 Your Game
|
||||
if currentRealTimeSession
|
||||
div
|
||||
span(style="margin:10px")= currentRealTimeSession.get('levelID')
|
||||
span(style="margin:10px")= currentRealTimeSession.get('creatorName')
|
||||
span(style="margin:10px")= currentRealTimeSession.get('state')
|
||||
span(style="margin:10px")= currentRealTimeSession.id
|
||||
button#leave-game-button(data-item=item) Leave Game
|
||||
div
|
||||
- var players = realTimeSessionsPlayers[currentRealTimeSession.id]
|
||||
if players
|
||||
span(style="margin:10px") Players:
|
||||
- for (var i=0; i < players.length; i++) {
|
||||
span(style="margin:10px")= players.at(i).get('name')
|
||||
span(style="margin:10px")= players.at(i).get('team')
|
||||
span(style="margin:10px")= players.at(i).get('state')
|
||||
- }
|
||||
else
|
||||
span No Players?
|
||||
else
|
||||
div Click something above to create a game.
|
||||
|
||||
hr
|
||||
|
||||
div#open-games
|
||||
h3 Open Games
|
||||
//- TODO: do not let you join ones with same-team opponent
|
||||
- var noOpenGames = true
|
||||
- for (var i=0; i < realTimeSessions.length; i++) {
|
||||
if (currentRealTimeSession && realTimeSessions.at(i).id == currentRealTimeSession.id)
|
||||
- continue
|
||||
if levelID === realTimeSessions.at(i).get('levelID') && realTimeSessions.at(i).get('state') === 'creating'
|
||||
- var id = realTimeSessions.at(i).get('id')
|
||||
- var players = realTimeSessionsPlayers[id]
|
||||
if players && players.length === 1
|
||||
- noOpenGames = false
|
||||
- var creatorName = realTimeSessions.at(i).get('creatorName')
|
||||
- var creator = realTimeSessions.at(i).get('creator')
|
||||
- var state = realTimeSessions.at(i).get('state')
|
||||
- var item = realTimeSessions.at(i)
|
||||
div
|
||||
button#join-game-button(data-item=item) Join Game
|
||||
span(style="margin:10px")= levelID
|
||||
span(style="margin:10px")= creatorName
|
||||
span(style="margin:10px")= state
|
||||
span(style="margin:10px")= id
|
||||
div
|
||||
span(style="margin:10px") Players:
|
||||
span(style="margin:10px")= players.at(0).get('name')
|
||||
span(style="margin:10px")= players.at(0).get('team')
|
||||
span(style="margin:10px")= players.at(0).get('state')
|
||||
- }
|
||||
if noOpenGames
|
||||
div No games available.
|
||||
|
||||
hr
|
||||
|
||||
.ladder-submission-view
|
||||
else
|
||||
a.btn.btn-primary(href="/play/ladder/#{levelSlug}#my-matches", data-i18n="multiplayer.victory_go_ladder") Return to Ladder
|
|
@ -21,7 +21,7 @@ module.exports = class MainLadderView extends RootView
|
|||
@campaigns = campaigns
|
||||
|
||||
@sessions = @supermodel.loadCollection(new LevelSessionsCollection(), 'your_sessions', {cache: false}, 0).model
|
||||
@listenToOnce @sessions, 'sync', @onSessionsLoaded
|
||||
@listenToOnce @sessions, 'sync', @onSessionsLoaded
|
||||
|
||||
@getLevelPlayCounts()
|
||||
|
||||
|
@ -94,52 +94,6 @@ heroArenas = [
|
|||
}
|
||||
]
|
||||
|
||||
oldArenas = [
|
||||
{
|
||||
name: 'Criss-Cross'
|
||||
difficulty: 5
|
||||
id: 'criss-cross'
|
||||
image: '/file/db/level/5391f3d519dc22b8082159b2/banner2.png'
|
||||
description: 'Participate in a bidding war with opponents to reach the other side!'
|
||||
}
|
||||
{
|
||||
name: 'Greed'
|
||||
difficulty: 4
|
||||
id: 'greed'
|
||||
image: '/file/db/level/53558b5a9914f5a90d7ccddb/greed_banner.jpg'
|
||||
description: 'Liked Dungeon Arena and Gold Rush? Put them together in this economic arena!'
|
||||
}
|
||||
{
|
||||
name: 'Sky Span (Testing)'
|
||||
difficulty: 3
|
||||
id: 'sky-span'
|
||||
image: '/file/db/level/53c80fce0ddbef000084c667/sky-Span-banner.jpg'
|
||||
description: 'Preview version of an upgraded Dungeon Arena. Help us with hero balance before release!'
|
||||
}
|
||||
{
|
||||
name: 'Dungeon Arena'
|
||||
difficulty: 3
|
||||
id: 'dungeon-arena'
|
||||
image: '/file/db/level/53173f76c269d400000543c2/Level%20Banner%20Dungeon%20Arena.jpg'
|
||||
description: 'Play head-to-head against fellow Wizards in a dungeon melee!'
|
||||
}
|
||||
{
|
||||
name: 'Gold Rush'
|
||||
difficulty: 3
|
||||
id: 'gold-rush'
|
||||
image: '/file/db/level/533353722a61b7ca6832840c/Gold-Rush.png'
|
||||
description: 'Prove you are better at collecting gold than your opponent!'
|
||||
}
|
||||
{
|
||||
name: 'Brawlwood'
|
||||
difficulty: 4
|
||||
id: 'brawlwood'
|
||||
image: '/file/db/level/52d97ecd32362bc86e004e87/Level%20Banner%20Brawlwood.jpg'
|
||||
description: 'Combat the armies of other Wizards in a strategic forest arena! (Fast computer required.)'
|
||||
}
|
||||
]
|
||||
|
||||
campaigns = [
|
||||
{id: 'multiplayer', name: 'Multiplayer Arenas', description: '... in which you code head-to-head against other players.', levels: heroArenas}
|
||||
#{id: 'old_multiplayer', name: '(Deprecated) Old Multiplayer Arenas', description: 'Relics of a more civilized age. No simulations are run for these older, hero-less multiplayer arenas.', levels: oldArenas}
|
||||
]
|
||||
|
|
|
@ -144,9 +144,6 @@ module.exports = class SpectateLevelView extends RootView
|
|||
if c then myCode[thang][spell] = c else delete myCode[thang][spell]
|
||||
|
||||
@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
|
||||
|
||||
onLevelStarted: (e) ->
|
||||
go = =>
|
||||
|
|
|
@ -7,17 +7,13 @@ Classroom = require 'models/Classroom'
|
|||
Course = require 'models/Course'
|
||||
CourseInstance = require 'models/CourseInstance'
|
||||
GameMenuModal = require 'views/play/menu/GameMenuModal'
|
||||
RealTimeModel = require 'models/RealTimeModel'
|
||||
RealTimeCollection = require 'collections/RealTimeCollection'
|
||||
LevelSetupManager = require 'lib/LevelSetupManager'
|
||||
GameMenuModal = require 'views/play/menu/GameMenuModal'
|
||||
|
||||
module.exports = class ControlBarView extends CocoView
|
||||
id: 'control-bar-view'
|
||||
template: template
|
||||
|
||||
subscriptions:
|
||||
'bus:player-states-changed': 'onPlayerStatesChanged'
|
||||
'level:disable-controls': 'onDisableControls'
|
||||
'level:enable-controls': 'onEnableControls'
|
||||
'ipad:memory-warning': 'onIPadMemoryWarning'
|
||||
|
@ -28,7 +24,6 @@ module.exports = class ControlBarView extends CocoView
|
|||
'click': -> Backbone.Mediator.publish 'tome:focus-editor', {}
|
||||
'click .levels-link-area': 'onClickHome'
|
||||
'click .home a': 'onClickHome'
|
||||
'click .multiplayer-area': 'onClickMultiplayer'
|
||||
'click #control-bar-sign-up-button': 'onClickSignupButton'
|
||||
|
||||
constructor: (options) ->
|
||||
|
@ -64,9 +59,6 @@ module.exports = class ControlBarView extends CocoView
|
|||
@supermodel.trackRequest(@campaign.fetch())
|
||||
)
|
||||
super options
|
||||
if @level.isType('hero-ladder', 'course-ladder') and me.isAdmin()
|
||||
@isMultiplayerLevel = true
|
||||
@multiplayerStatusManager = new MultiplayerStatusManager @levelID, @onMultiplayerStateChanged
|
||||
if @level.get 'replayable'
|
||||
@listenTo @session, 'change-difficulty', @onSessionDifficultyChanged
|
||||
|
||||
|
@ -79,25 +71,10 @@ module.exports = class ControlBarView extends CocoView
|
|||
|
||||
setBus: (@bus) ->
|
||||
|
||||
onPlayerStatesChanged: (e) ->
|
||||
# TODO: this doesn't fire any more. Replacement?
|
||||
return unless @bus is e.bus
|
||||
numPlayers = _.keys(e.players).length
|
||||
return if numPlayers is @numPlayers
|
||||
@numPlayers = numPlayers
|
||||
text = 'Multiplayer'
|
||||
text += " (#{numPlayers})" if numPlayers > 1
|
||||
$('#multiplayer-button', @$el).text(text)
|
||||
|
||||
onMultiplayerStateChanged: => @render?()
|
||||
|
||||
getRenderData: (c={}) ->
|
||||
super c
|
||||
c.worldName = @worldName
|
||||
c.multiplayerEnabled = @session.get('multiplayer')
|
||||
c.ladderGame = @level.isType('ladder', 'hero-ladder', 'course-ladder')
|
||||
if c.isMultiplayerLevel = @isMultiplayerLevel
|
||||
c.multiplayerStatus = @multiplayerStatusManager?.status
|
||||
if @level.get 'replayable'
|
||||
c.levelDifficulty = @session.get('state')?.difficulty ? 0
|
||||
if @observing
|
||||
|
@ -161,9 +138,6 @@ module.exports = class ControlBarView extends CocoView
|
|||
e.stopImmediatePropagation()
|
||||
Backbone.Mediator.publish 'router:navigate', route: @homeLink, viewClass: @homeViewClass, viewArgs: @homeViewArgs
|
||||
|
||||
onClickMultiplayer: (e) ->
|
||||
@showGameMenuModal e, 'multiplayer'
|
||||
|
||||
onClickSignupButton: (e) ->
|
||||
window.tracker?.trackEvent 'Started Signup', category: 'Play Level', label: 'Control Bar', level: @levelID
|
||||
|
||||
|
@ -184,62 +158,4 @@ module.exports = class ControlBarView extends CocoView
|
|||
|
||||
destroy: ->
|
||||
@setupManager?.destroy()
|
||||
@multiplayerStatusManager?.destroy()
|
||||
super()
|
||||
|
||||
# MultiplayerStatusManager ######################################################
|
||||
#
|
||||
# Manages the multiplayer status, and calls @statusChangedCallback when it changes.
|
||||
#
|
||||
# It monitors these:
|
||||
# Real-time multiplayer players
|
||||
# Internal multiplayer status
|
||||
#
|
||||
# Real-time state variables:
|
||||
# @playersCollection - Real-time multiplayer players
|
||||
#
|
||||
# TODO: Not currently using player counts. Should remove if we keep simple design.
|
||||
#
|
||||
class MultiplayerStatusManager
|
||||
|
||||
constructor: (@levelID, @statusChangedCallback) ->
|
||||
@status = ''
|
||||
# @players = {}
|
||||
# @playersCollection = new RealTimeCollection('multiplayer_players/' + @levelID)
|
||||
# @playersCollection.on 'add', @onPlayerAdded
|
||||
# @playersCollection.each (player) => @onPlayerAdded player
|
||||
Backbone.Mediator.subscribe 'real-time-multiplayer:player-status', @onMultiplayerPlayerStatus
|
||||
|
||||
destroy: ->
|
||||
Backbone.Mediator.unsubscribe 'real-time-multiplayer:player-status', @onMultiplayerPlayerStatus
|
||||
# @playersCollection?.off 'add', @onPlayerAdded
|
||||
# player.off 'change', @onPlayerChanged for id, player of @players
|
||||
|
||||
onMultiplayerPlayerStatus: (e) =>
|
||||
@status = e.status
|
||||
@statusChangedCallback()
|
||||
|
||||
# onPlayerAdded: (player) =>
|
||||
# unless player.id is me.id
|
||||
# @players[player.id] = new RealTimeModel('multiplayer_players/' + @levelID + '/' + player.id)
|
||||
# @players[player.id].on 'change', @onPlayerChanged
|
||||
# @countPlayers player
|
||||
#
|
||||
# onPlayerChanged: (player) =>
|
||||
# @countPlayers player
|
||||
#
|
||||
# countPlayers: (changedPlayer) =>
|
||||
# # TODO: save this stale hearbeat threshold setting somewhere
|
||||
# staleHeartbeat = new Date()
|
||||
# staleHeartbeat.setMinutes staleHeartbeat.getMinutes() - 3
|
||||
# @playerCount = 0
|
||||
# @playersCollectionAvailable = 0
|
||||
# @playersCollectionUnavailable = 0
|
||||
# @playersCollection.each (player) =>
|
||||
# # Assume changedPlayer is fresher than entry in @playersCollection collection
|
||||
# player = changedPlayer if changedPlayer? and player.id is changedPlayer.id
|
||||
# unless staleHeartbeat >= new Date(player.get('heartbeat'))
|
||||
# @playerCount++
|
||||
# @playersCollectionAvailable++ if player.get('state') is 'available'
|
||||
# @playersCollectionUnavailable++ if player.get('state') is 'unavailable'
|
||||
# @statusChangedCallback()
|
||||
|
|
|
@ -18,6 +18,7 @@ module.exports = class LevelChatView extends CocoView
|
|||
constructor: (options) ->
|
||||
@levelID = options.levelID
|
||||
@session = options.session
|
||||
# TODO: we took out session.multiplayer, so this will not fire. If we want to resurrect it, we'll of course need a new way of activating chat.
|
||||
@listenTo(@session, 'change:multiplayer', @updateMultiplayerVisibility)
|
||||
@sessionID = options.sessionID
|
||||
@bus = LevelBus.get(@levelID, @sessionID)
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
CocoView = require 'views/core/CocoView'
|
||||
template = require 'templates/play/level/level-flags-view'
|
||||
{me} = require 'core/auth'
|
||||
RealTimeCollection = require 'collections/RealTimeCollection'
|
||||
|
||||
multiplayerFlagDelay = 0.5 # Long, static second delay for now; should be more than enough.
|
||||
|
||||
module.exports = class LevelFlagsView extends CocoView
|
||||
id: 'level-flags-view'
|
||||
|
@ -17,7 +14,6 @@ module.exports = class LevelFlagsView extends CocoView
|
|||
'god:new-world-created': 'onNewWorld'
|
||||
'god:streaming-world-updated': 'onNewWorld'
|
||||
'surface:remove-flag': 'onRemoveFlag'
|
||||
'real-time-multiplayer:joined-game': 'onJoinedMultiplayerGame'
|
||||
|
||||
events:
|
||||
'click .green-flag': -> @onFlagSelected color: 'green', source: 'button'
|
||||
|
@ -60,9 +56,8 @@ module.exports = class LevelFlagsView extends CocoView
|
|||
return unless @flagColor and @realTime
|
||||
@playSound 'menu-button-click' # TODO: different flag placement sound?
|
||||
pos = x: e.worldPos.x, y: e.worldPos.y
|
||||
delay = if @realTimeFlags then multiplayerFlagDelay else 0
|
||||
now = @world.dt * @world.frames.length
|
||||
flag = player: me.id, team: me.team, color: @flagColor, pos: pos, time: now + delay, active: true, source: 'click'
|
||||
flag = player: me.id, team: me.team, color: @flagColor, pos: pos, time: now, active: true, source: 'click'
|
||||
@flags[@flagColor] = flag
|
||||
@flagHistory.push flag
|
||||
@realTimeFlags?.create flag
|
||||
|
@ -75,9 +70,8 @@ module.exports = class LevelFlagsView extends CocoView
|
|||
|
||||
onRemoveFlag: (e) ->
|
||||
delete @flags[e.color]
|
||||
delay = if @realTimeFlags then multiplayerFlagDelay else 0
|
||||
now = @world.dt * @world.frames.length
|
||||
flag = player: me.id, team: me.team, color: e.color, time: now + delay, active: false, source: 'click'
|
||||
flag = player: me.id, team: me.team, color: e.color, time: now, active: false, source: 'click'
|
||||
@flagHistory.push flag
|
||||
Backbone.Mediator.publish 'level:flag-updated', flag
|
||||
#console.log e.color, 'deleted at time', flag.time
|
||||
|
@ -85,31 +79,3 @@ module.exports = class LevelFlagsView extends CocoView
|
|||
onNewWorld: (event) ->
|
||||
return unless event.world.name is @world.name
|
||||
@world = @options.world = event.world
|
||||
|
||||
onJoinedMultiplayerGame: (e) ->
|
||||
@realTimeFlags = new RealTimeCollection("multiplayer_level_sessions/#{@levelID}/#{e.realTimeSessionID}/flagHistory")
|
||||
@realTimeFlags.on 'add', @onRealTimeMultiplayerFlagAdded
|
||||
@realTimeFlags.on 'remove', @onRealTimeMultiplayerFlagRemoved
|
||||
|
||||
onLeftMultiplayerGame: (e) ->
|
||||
if @realTimeFlags
|
||||
@realTimeFlags.off 'add', @onRealTimeMultiplayerFlagAdded
|
||||
@realTimeFlags.off 'remove', @onRealTimeMultiplayerFlagRemoved
|
||||
@realTimeFlags = null
|
||||
|
||||
onRealTimeMultiplayerFlagAdded: (e) =>
|
||||
if e.get('player') != me.id
|
||||
# TODO: what is @flags used for?
|
||||
# Build local flag from Backbone.Model flag
|
||||
flag =
|
||||
player: e.get('player')
|
||||
team: e.get('team')
|
||||
color: e.get('color')
|
||||
pos: e.get('pos')
|
||||
time: e.get('time')
|
||||
active: e.get('active')
|
||||
#source: 'click'? e.get('source')? nothing?
|
||||
@flagHistory.push flag
|
||||
Backbone.Mediator.publish 'level:flag-updated', flag
|
||||
|
||||
onRealTimeMultiplayerFlagRemoved: (e) =>
|
||||
|
|
|
@ -21,7 +21,6 @@ module.exports = class LevelPlaybackView extends CocoView
|
|||
'tome:cast-spells': 'onTomeCast'
|
||||
'playback:real-time-playback-ended': 'onRealTimePlaybackEnded'
|
||||
'playback:stop-real-time-playback': 'onStopRealTimePlayback'
|
||||
'real-time-multiplayer:manual-cast': 'onRealTimeMultiplayerCast'
|
||||
|
||||
events:
|
||||
'click #music-button': 'onToggleMusic'
|
||||
|
@ -110,11 +109,6 @@ module.exports = class LevelPlaybackView extends CocoView
|
|||
Backbone.Mediator.publish 'playback:real-time-playback-started', {}
|
||||
@playSound 'real-time-playback-start'
|
||||
|
||||
onRealTimeMultiplayerCast: (e) ->
|
||||
@realTime = true
|
||||
@togglePlaybackControls false
|
||||
Backbone.Mediator.publish 'playback:real-time-playback-waiting', {}
|
||||
|
||||
onWindowResize: (s...) =>
|
||||
@barWidth = $('.progress', @$el).width()
|
||||
|
||||
|
|
|
@ -73,13 +73,8 @@ module.exports = class PlayLevelView extends RootView
|
|||
'level:loading-view-unveiling': 'onLoadingViewUnveiling'
|
||||
'level:loading-view-unveiled': 'onLoadingViewUnveiled'
|
||||
'level:session-loaded': 'onSessionLoaded'
|
||||
'playback:real-time-playback-waiting': 'onRealTimePlaybackWaiting'
|
||||
'playback:real-time-playback-started': 'onRealTimePlaybackStarted'
|
||||
'playback:real-time-playback-ended': 'onRealTimePlaybackEnded'
|
||||
'real-time-multiplayer:created-game': 'onRealTimeMultiplayerCreatedGame'
|
||||
'real-time-multiplayer:joined-game': 'onRealTimeMultiplayerJoinedGame'
|
||||
'real-time-multiplayer:left-game': 'onRealTimeMultiplayerLeftGame'
|
||||
'real-time-multiplayer:manual-cast': 'onRealTimeMultiplayerCast'
|
||||
'ipad:memory-warning': 'onIPadMemoryWarning'
|
||||
'store:item-purchased': 'onItemPurchased'
|
||||
|
||||
|
@ -192,8 +187,6 @@ module.exports = class PlayLevelView extends RootView
|
|||
@initGoalManager()
|
||||
@insertSubviews()
|
||||
@initVolume()
|
||||
@listenTo(@session, 'change:multiplayer', @onMultiplayerChanged)
|
||||
|
||||
@register()
|
||||
@controlBar.setBus(@bus)
|
||||
@initScriptManager()
|
||||
|
@ -239,9 +232,6 @@ module.exports = class PlayLevelView extends RootView
|
|||
myCode[thang] ?= {}
|
||||
if c then myCode[thang][spell] = c else delete myCode[thang][spell]
|
||||
@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
|
||||
|
||||
setupGod: ->
|
||||
@god.setLevel @level.serialize {@supermodel, @session, @otherSession, headless: false, sessionless: false}
|
||||
|
@ -289,9 +279,7 @@ module.exports = class PlayLevelView extends RootView
|
|||
@bus = LevelBus.get(@levelID, @session.id)
|
||||
@bus.setSession(@session)
|
||||
@bus.setSpells @tome.spells
|
||||
if @session.get('multiplayer') and not me.isAdmin()
|
||||
@session.set 'multiplayer', false # Temp: multiplayer has bugged out some sessions, so ignoring it.
|
||||
@bus.connect() if @session.get('multiplayer')
|
||||
#@bus.connect() if @session.get('multiplayer') # TODO: session's multiplayer flag removed; connect bus another way if we care about it
|
||||
|
||||
# Load Completed Setup ######################################################
|
||||
|
||||
|
@ -315,8 +303,6 @@ module.exports = class PlayLevelView extends RootView
|
|||
@setupManager = new LevelSetupManager({supermodel: @supermodel, level: e.level, levelID: @levelID, parent: @, session: e.session, courseID: @courseID, courseInstanceID: @courseInstanceID})
|
||||
@setupManager.open()
|
||||
|
||||
@onRealTimeMultiplayerLevelLoaded e.session if e.level.isType('hero-ladder', 'course-ladder')
|
||||
|
||||
onLoaded: ->
|
||||
_.defer => @onLevelLoaderLoaded()
|
||||
|
||||
|
@ -393,9 +379,6 @@ module.exports = class PlayLevelView extends RootView
|
|||
@removeSubView @loadingView
|
||||
@loadingView = null
|
||||
@playAmbientSound()
|
||||
if @options.realTimeMultiplayerSessionID?
|
||||
Backbone.Mediator.publish 'playback:real-time-playback-waiting', {}
|
||||
@realTimeMultiplayerContinueGame @options.realTimeMultiplayerSessionID
|
||||
# TODO: Is it possible to create a Mongoose ObjectId for 'ls', instead of the string returned from get()?
|
||||
application.tracker?.trackEvent 'Started Level', category:'Play Level', levelID: @levelID, ls: @session?.get('_id') unless @observing
|
||||
$(window).trigger 'resize'
|
||||
|
@ -584,13 +567,6 @@ module.exports = class PlayLevelView extends RootView
|
|||
|
||||
onFocusDom: (e) -> $(e.selector).focus()
|
||||
|
||||
onMultiplayerChanged: (e) ->
|
||||
if @session.get('multiplayer')
|
||||
@bus.connect()
|
||||
else
|
||||
@bus.removeFirebaseData =>
|
||||
@bus.disconnect()
|
||||
|
||||
onContactClicked: (e) ->
|
||||
Backbone.Mediator.publish 'level:contact-button-pressed', {}
|
||||
@openModalView contactModal = new ContactModal levelID: @level.get('slug') or @level.id, courseID: @courseID, courseInstanceID: @courseInstanceID
|
||||
|
@ -629,10 +605,6 @@ module.exports = class PlayLevelView extends RootView
|
|||
AudioPlayer.preloadSoundReference sound
|
||||
|
||||
# Real-time playback
|
||||
onRealTimePlaybackWaiting: (e) ->
|
||||
@$el.addClass('real-time').focus()
|
||||
@onWindowResize()
|
||||
|
||||
onRealTimePlaybackStarted: (e) ->
|
||||
@$el.addClass('real-time').focus()
|
||||
@onWindowResize()
|
||||
|
@ -645,14 +617,12 @@ module.exports = class PlayLevelView extends RootView
|
|||
_.delay @onSubmissionComplete, 750 # Wait for transition to end.
|
||||
else
|
||||
@waitingForSubmissionComplete = true
|
||||
@onRealTimeMultiplayerPlaybackEnded()
|
||||
|
||||
onSubmissionComplete: =>
|
||||
return if @destroyed
|
||||
Backbone.Mediator.publish 'level:set-time', ratio: 1
|
||||
return if @level.hasLocalChanges() # Don't award achievements when beating level changed in level editor
|
||||
# TODO: Show a victory dialog specific to hero-ladder level
|
||||
if @goalManager.checkOverallStatus() is 'success' and not @options.realTimeMultiplayerSessionID?
|
||||
if @goalManager.checkOverallStatus() is 'success'
|
||||
showModalFn = -> Backbone.Mediator.publish 'level:show-victory', showModal: true
|
||||
@session.recordScores @world.scores, @level
|
||||
if @level.get 'replayable'
|
||||
|
@ -677,7 +647,6 @@ module.exports = class PlayLevelView extends RootView
|
|||
#@instance.save() unless @instance.loading
|
||||
delete window.nextURL
|
||||
console.profileEnd?() if PROFILE_ME
|
||||
@onRealTimeMultiplayerLevelUnloaded()
|
||||
application.tracker?.disableInspectletJS()
|
||||
super()
|
||||
|
||||
|
@ -693,358 +662,3 @@ module.exports = class PlayLevelView extends RootView
|
|||
@setupManager?.destroy()
|
||||
@setupManager = new LevelSetupManager({supermodel: @supermodel, level: @level, levelID: @levelID, parent: @, session: @session, hadEverChosenHero: true})
|
||||
@setupManager.open()
|
||||
|
||||
# Start Real-time Multiplayer ######################################################
|
||||
#
|
||||
# This view acts as a hub for the real-time multiplayer session for the current level.
|
||||
#
|
||||
# It performs these actions:
|
||||
# Player heartbeat
|
||||
# Publishes player status
|
||||
# Updates real-time multiplayer session state
|
||||
# Updates real-time multiplayer player state
|
||||
# Cleans up old sessions (sets state to 'finished')
|
||||
# Real-time multiplayer cast handshake
|
||||
# Swap teams on game joined, if necessary
|
||||
# Reload PlayLevelView on real-time submit, automatically continue game and real-time playback
|
||||
#
|
||||
# It monitors these:
|
||||
# Real-time multiplayer sessions
|
||||
# Current real-time multiplayer session
|
||||
# Internal multiplayer create/joined/left events
|
||||
#
|
||||
# Real-time state variables.
|
||||
# Each Ref is Firebase reference, and may have a matching Data suffixed variable with the latest data received.
|
||||
# @realTimePlayerRef - User's real-time multiplayer player for this level
|
||||
# @realTimePlayerGameRef - User's current real-time multiplayer player game session
|
||||
# @realTimeSessionRef - Current real-time multiplayer game session
|
||||
# @realTimeOpponentRef - Current real-time multiplayer opponent
|
||||
# @realTimePlayersRef - Real-time players for current real-time multiplayer game session
|
||||
# @options.realTimeMultiplayerSessionID - Need to continue an existing real-time multiplayer session
|
||||
#
|
||||
# TODO: Move this code to it's own file, or possibly the LevelBus
|
||||
# TODO: Save settings somewhere reasonable
|
||||
multiplayerFireHost: 'https://codecombat.firebaseio.com/test/db/'
|
||||
|
||||
onRealTimeMultiplayerLevelLoaded: (session) ->
|
||||
# console.log 'PlayLevelView onRealTimeMultiplayerLevelLoaded'
|
||||
return if @realTimePlayerRef?
|
||||
return if me.get('anonymous')
|
||||
@realTimePlayerRef = new Firebase "#{@multiplayerFireHost}multiplayer_players/#{@levelID}/#{me.id}"
|
||||
unless @options.realTimeMultiplayerSessionID?
|
||||
# TODO: Wait for name instead of using 'Anon', or try and update it later?
|
||||
name = me.get('name') ? session.get('creatorName') ? 'Anon'
|
||||
@realTimePlayerRef.set
|
||||
id: me.id # TODO: is this redundant info necessary?
|
||||
name: name
|
||||
state: 'playing'
|
||||
created: new Date().toISOString()
|
||||
heartbeat: new Date().toISOString()
|
||||
@timerMultiplayerHeartbeatID = setInterval @onRealTimeMultiplayerHeartbeat, 60 * 1000
|
||||
@cleanupRealTimeSessions()
|
||||
|
||||
cleanupRealTimeSessions: ->
|
||||
# console.log 'PlayLevelView cleanupRealTimeSessions'
|
||||
# TODO: Reduce this call, possibly by username and dates
|
||||
realTimeSessionCollection = new Firebase "#{@multiplayerFireHost}multiplayer_level_sessions/#{@levelID}"
|
||||
realTimeSessionCollection.once 'value', (collectionSnapshot) =>
|
||||
for multiplayerSessionID, multiplayerSession of collectionSnapshot.val()
|
||||
continue if @options.realTimeMultiplayerSessionID? and @options.realTimeMultiplayerSessionID is multiplayerSessionID
|
||||
continue unless multiplayerSession.state isnt 'finished'
|
||||
player = realTimeSessionCollection.child "#{multiplayerSession.id}/players/#{me.id}"
|
||||
player.once 'value', (playerSnapshot) =>
|
||||
if playerSnapshot.val()
|
||||
console.info 'Cleaning up previous real-time multiplayer session', multiplayerSessionID
|
||||
player.update 'state': 'left'
|
||||
multiplayerSessionRef = realTimeSessionCollection.child "#{multiplayerSessionID}"
|
||||
multiplayerSessionRef.update 'state': 'finished'
|
||||
|
||||
onRealTimeMultiplayerLevelUnloaded: ->
|
||||
# console.log 'PlayLevelView onRealTimeMultiplayerLevelUnloaded'
|
||||
if @timerMultiplayerHeartbeatID?
|
||||
clearInterval @timerMultiplayerHeartbeatID
|
||||
@timerMultiplayerHeartbeatID = null
|
||||
|
||||
# TODO: similar to game ending cleanup
|
||||
if @realTimeOpponentRef?
|
||||
@realTimeOpponentRef.off 'value', @onRealTimeOpponentChanged
|
||||
@realTimeOpponentRef = null
|
||||
if @realTimePlayersRef?
|
||||
@realTimePlayersRef.off 'child_added', @onRealTimePlayerAdded
|
||||
@realTimePlayersRef = null
|
||||
if @realTimeSessionRef?
|
||||
@realTimeSessionRef.off 'value', @onRealTimeSessionChanged
|
||||
@realTimeSessionRef = null
|
||||
if @realTimePlayerGameRef?
|
||||
@realTimePlayerGameRef = null
|
||||
if @realTimePlayerRef?
|
||||
@realTimePlayerRef = null
|
||||
|
||||
onRealTimeMultiplayerHeartbeat: =>
|
||||
# console.log 'PlayLevelView onRealTimeMultiplayerHeartbeat', @realTimePlayerRef
|
||||
@realTimePlayerRef.update 'heartbeat': new Date().toISOString() if @realTimePlayerRef?
|
||||
|
||||
onRealTimeMultiplayerCreatedGame: (e) ->
|
||||
# console.log 'PlayLevelView onRealTimeMultiplayerCreatedGame'
|
||||
@joinRealTimeMultiplayerGame e
|
||||
@realTimePlayerGameRef.update 'state': 'coding'
|
||||
@realTimePlayerRef.update 'state': 'available'
|
||||
Backbone.Mediator.publish 'real-time-multiplayer:player-status', status: 'Waiting for opponent..'
|
||||
|
||||
onRealTimeSessionChanged: (snapshot) =>
|
||||
# console.log 'PlayLevelView onRealTimeSessionChanged', snapshot.val()
|
||||
@realTimeSessionData = snapshot.val()
|
||||
if @realTimeSessionData?.state is 'finished'
|
||||
@realTimeGameEnded()
|
||||
Backbone.Mediator.publish 'real-time-multiplayer:left-game', {}
|
||||
|
||||
onRealTimePlayerAdded: (snapshot) =>
|
||||
# console.log 'PlayLevelView onRealTimePlayerAdded', snapshot.val()
|
||||
# Assume game is full, game on
|
||||
data = snapshot.val()
|
||||
if data? and data.id isnt me.id
|
||||
@realTimeOpponentData = data
|
||||
# console.log 'PlayLevelView onRealTimePlayerAdded opponent', @realTimeOpponentData, @realTimePlayersData
|
||||
@realTimePlayersData[@realTimeOpponentData.id] = @realTimeOpponentData
|
||||
if @realTimeSessionData?.state is 'creating'
|
||||
@realTimeSessionRef.update 'state': 'coding'
|
||||
@realTimePlayerRef.update 'state': 'unavailable'
|
||||
@realTimeOpponentRef = @realTimeSessionRef.child "players/#{@realTimeOpponentData.id}"
|
||||
@realTimeOpponentRef.on 'value', @onRealTimeOpponentChanged
|
||||
Backbone.Mediator.publish 'real-time-multiplayer:player-status', status: "Playing against #{@realTimeOpponentData.name}"
|
||||
|
||||
onRealTimeOpponentChanged: (snapshot) =>
|
||||
# console.log 'PlayLevelView onRealTimeOpponentChanged', snapshot.val()
|
||||
@realTimeOpponentData = snapshot.val()
|
||||
switch @realTimeOpponentData?.state
|
||||
when 'left'
|
||||
console.info 'Real-time multiplayer opponent left the game'
|
||||
opponentID = @realTimeOpponentData.id
|
||||
@realTimeGameEnded()
|
||||
Backbone.Mediator.publish 'real-time-multiplayer:left-game', userID: opponentID
|
||||
when 'submitted'
|
||||
# TODO: What should this message say?
|
||||
Backbone.Mediator.publish 'real-time-multiplayer:player-status', status: "#{@realTimeOpponentData.name} waiting for your code"
|
||||
|
||||
joinRealTimeMultiplayerGame: (e) ->
|
||||
# console.log 'PlayLevelView joinRealTimeMultiplayerGame', e
|
||||
unless @realTimeSessionRef?
|
||||
@session.set('submittedCodeLanguage', @session.get('codeLanguage'))
|
||||
@session.save()
|
||||
|
||||
@realTimeSessionRef = new Firebase "#{@multiplayerFireHost}multiplayer_level_sessions/#{@levelID}/#{e.realTimeSessionID}"
|
||||
@realTimePlayersRef = @realTimeSessionRef.child 'players'
|
||||
|
||||
# Look for opponent
|
||||
@realTimeSessionRef.once 'value', (multiplayerSessionSnapshot) =>
|
||||
if @realTimeSessionData = multiplayerSessionSnapshot.val()
|
||||
@realTimePlayersRef.once 'value', (playsSnapshot) =>
|
||||
if @realTimePlayersData = playsSnapshot.val()
|
||||
for id, player of @realTimePlayersData
|
||||
if id isnt me.id
|
||||
@realTimeOpponentRef = @realTimeSessionRef.child "players/#{id}"
|
||||
@realTimeOpponentRef.once 'value', (opponentSnapshot) =>
|
||||
if @realTimeOpponentData = opponentSnapshot.val()
|
||||
@updateTeam()
|
||||
else
|
||||
console.error 'Could not lookup multiplayer opponent data.'
|
||||
@realTimeOpponentRef.on 'value', @onRealTimeOpponentChanged
|
||||
Backbone.Mediator.publish 'real-time-multiplayer:player-status', status: 'Playing against ' + player.name
|
||||
else
|
||||
console.error 'Could not lookup multiplayer session players data.'
|
||||
# TODO: need child_removed too?
|
||||
@realTimePlayersRef.on 'child_added', @onRealTimePlayerAdded
|
||||
else
|
||||
console.error 'Could not lookup multiplayer session data.'
|
||||
@realTimeSessionRef.on 'value', @onRealTimeSessionChanged
|
||||
|
||||
@realTimePlayerGameRef = @realTimeSessionRef.child "players/#{me.id}"
|
||||
|
||||
# TODO: Follow up in MultiplayerView to see if double joins can be avoided
|
||||
# else
|
||||
# console.error 'Joining real-time multiplayer game with an existing @realTimeSessionRef.'
|
||||
|
||||
onRealTimeMultiplayerJoinedGame: (e) ->
|
||||
# console.log 'PlayLevelView onRealTimeMultiplayerJoinedGame', e
|
||||
@joinRealTimeMultiplayerGame e
|
||||
@realTimePlayerGameRef.update 'state': 'coding'
|
||||
@realTimePlayerRef.update 'state': 'unavailable'
|
||||
|
||||
onRealTimeMultiplayerLeftGame: (e) ->
|
||||
# console.log 'PlayLevelView onRealTimeMultiplayerLeftGame', e
|
||||
if e.userID? and e.userID is me.id
|
||||
@realTimePlayerGameRef.update 'state': 'left'
|
||||
@realTimeGameEnded()
|
||||
|
||||
realTimeMultiplayerContinueGame: (realTimeSessionID) ->
|
||||
# console.log 'PlayLevelView realTimeMultiplayerContinueGame', realTimeSessionID, me.id
|
||||
Backbone.Mediator.publish 'real-time-multiplayer:joined-game', realTimeSessionID: realTimeSessionID
|
||||
|
||||
console.info 'Setting my game status to ready'
|
||||
@realTimePlayerGameRef.update 'state': 'ready'
|
||||
|
||||
if @realTimeOpponentData.state is 'ready'
|
||||
@realTimeOpponentIsReady()
|
||||
else
|
||||
console.info 'Waiting for opponent to be ready'
|
||||
@realTimeOpponentRef.on 'value', @realTimeOpponentMaybeReady
|
||||
|
||||
realTimeOpponentMaybeReady: (snapshot) =>
|
||||
# console.log 'PlayLevelView realTimeOpponentMaybeReady'
|
||||
if @realTimeOpponentData = snapshot.val()
|
||||
if @realTimeOpponentData.state is 'ready'
|
||||
@realTimeOpponentRef.off 'value', @realTimeOpponentMaybeReady
|
||||
@realTimeOpponentIsReady()
|
||||
|
||||
realTimeOpponentIsReady: =>
|
||||
console.info 'All real-time multiplayer players are ready!'
|
||||
@realTimeSessionRef.update 'state': 'running'
|
||||
Backbone.Mediator.publish 'real-time-multiplayer:player-status', status: 'Battling ' + @realTimeOpponentData.name
|
||||
Backbone.Mediator.publish 'tome:manual-cast', {realTime: true}
|
||||
|
||||
realTimeGameEnded: ->
|
||||
if @realTimeOpponentRef?
|
||||
@realTimeOpponentRef.off 'value', @onRealTimeOpponentChanged
|
||||
@realTimeOpponentRef = null
|
||||
if @realTimePlayersRef?
|
||||
@realTimePlayersRef.off 'child_added', @onRealTimePlayerAdded
|
||||
@realTimePlayersRef = null
|
||||
if @realTimeSessionRef?
|
||||
@realTimeSessionRef.off 'value', @onRealTimeSessionChanged
|
||||
@realTimeSessionRef.update 'state': 'finished'
|
||||
@realTimeSessionRef = null
|
||||
if @realTimePlayerGameRef?
|
||||
@realTimePlayerGameRef = null
|
||||
if @realTimePlayerRef?
|
||||
@realTimePlayerRef.update 'state': 'playing'
|
||||
Backbone.Mediator.publish 'real-time-multiplayer:player-status', status: ''
|
||||
|
||||
onRealTimeMultiplayerCast: (e) ->
|
||||
# console.log 'PlayLevelView onRealTimeMultiplayerCast', @realTimeSessionData, @realTimePlayersData
|
||||
unless @realTimeSessionRef?
|
||||
console.error 'Real-time multiplayer cast without multiplayer session.'
|
||||
return
|
||||
unless @realTimeSessionData?
|
||||
console.error 'Real-time multiplayer cast without multiplayer data.'
|
||||
return
|
||||
unless @realTimePlayersData?
|
||||
console.error 'Real-time multiplayer cast without multiplayer players data.'
|
||||
return
|
||||
|
||||
# Set submissionCount for created real-time multiplayer session
|
||||
if me.id is @realTimeSessionData.creator
|
||||
sessionState = @session.get('state')
|
||||
if sessionState?
|
||||
submissionCount = sessionState.submissionCount ? 0
|
||||
console.info 'Setting multiplayer submissionCount to', submissionCount
|
||||
@realTimeSessionRef.update 'submissionCount': submissionCount
|
||||
else
|
||||
console.error 'Failed to read sessionState in onRealTimeMultiplayerCast'
|
||||
|
||||
console.info 'Submitting my code'
|
||||
permissions = @session.get 'permissions' ? []
|
||||
unless _.find(permissions, (p) -> p.target is 'public' and p.access is 'read')
|
||||
permissions.push target:'public', access:'read'
|
||||
@session.set 'permissions', permissions
|
||||
@session.patch()
|
||||
@realTimePlayerGameRef.update 'state': 'submitted'
|
||||
|
||||
console.info 'Other player is', @realTimeOpponentData.state
|
||||
if @realTimeOpponentData.state in ['submitted', 'ready']
|
||||
@realTimeOpponentSubmittedCode @realTimeOpponentData, @realTimePlayerGameData
|
||||
else
|
||||
# Wait for opponent to submit their code
|
||||
Backbone.Mediator.publish 'real-time-multiplayer:player-status', status: "Waiting for code from #{@realTimeOpponentData.name}"
|
||||
@realTimeOpponentRef.on 'value', @realTimeOpponentMaybeSubmitted
|
||||
|
||||
realTimeOpponentMaybeSubmitted: (snapshot) =>
|
||||
if @realTimeOpponentData = snapshot.val()
|
||||
if @realTimeOpponentData.state in ['submitted', 'ready']
|
||||
@realTimeOpponentRef.off 'value', @realTimeOpponentMaybeSubmitted
|
||||
@realTimeOpponentSubmittedCode @realTimeOpponentData, @realTimePlayerGameData
|
||||
|
||||
onRealTimeMultiplayerPlaybackEnded: ->
|
||||
# console.log 'PlayLevelView onRealTimeMultiplayerPlaybackEnded'
|
||||
if @realTimeSessionRef?
|
||||
@realTimeSessionRef.update 'state': 'coding'
|
||||
@realTimePlayerGameRef.update 'state': 'coding'
|
||||
if @realTimeOpponentData?
|
||||
Backbone.Mediator.publish 'real-time-multiplayer:player-status', status: "Playing against #{@realTimeOpponentData.name}"
|
||||
|
||||
realTimeOpponentSubmittedCode: (opponentPlayer, myPlayer) =>
|
||||
# console.log 'PlayLevelView realTimeOpponentSubmittedCode', @realTimeSessionData.id, opponentPlayer.level_session
|
||||
# Read submissionCount for joined real-time multiplayer session
|
||||
if me.id isnt @realTimeSessionData.creator
|
||||
sessionState = @session.get('state') ? {}
|
||||
newSubmissionCount = @realTimeSessionData.submissionCount
|
||||
if newSubmissionCount?
|
||||
# TODO: This isn't always getting updated where the random seed generation uses it.
|
||||
sessionState.submissionCount = parseInt newSubmissionCount
|
||||
console.info 'Got multiplayer submissionCount', sessionState.submissionCount
|
||||
@session.set 'state', sessionState
|
||||
@session.patch()
|
||||
|
||||
# Reload this level so the opponent session can easily be wired up
|
||||
Backbone.Mediator.publish 'router:navigate',
|
||||
route: "/play/level/#{@levelID}"
|
||||
viewClass: PlayLevelView
|
||||
viewArgs: [{supermodel: @supermodel, autoUnveil: true, realTimeMultiplayerSessionID: @realTimeSessionData.id, opponent: opponentPlayer.level_session, team: @team}, @levelID]
|
||||
|
||||
updateTeam: ->
|
||||
# If not creator, and same team as creator, then switch teams
|
||||
# TODO: Assumes there are only 'humans' and 'ogres'
|
||||
|
||||
unless @realTimeOpponentData?
|
||||
console.error 'Tried to switch teams without real-time multiplayer opponent data.'
|
||||
return
|
||||
unless @realTimeSessionData?
|
||||
console.error 'Tried to switch teams without real-time multiplayer session data.'
|
||||
return
|
||||
return if me.id is @realTimeSessionData.creator
|
||||
|
||||
oldTeam = @realTimeOpponentData.team
|
||||
return unless oldTeam is @session.get('team')
|
||||
|
||||
# Need to switch to other team
|
||||
newTeam = if oldTeam is 'humans' then 'ogres' else 'humans'
|
||||
console.info "Switching from team #{oldTeam} to #{newTeam}"
|
||||
|
||||
# Move code from old team to new team
|
||||
# Assumes teamSpells has matching spells for each team
|
||||
# TODO: Similar to code in loadOpponentTeam, consolidate?
|
||||
code = @session.get 'code'
|
||||
teamSpells = @session.get 'teamSpells'
|
||||
for oldSpellKey in teamSpells[oldTeam]
|
||||
[oldThang, oldSpell] = oldSpellKey.split '/'
|
||||
oldCode = code[oldThang]?[oldSpell]
|
||||
continue unless oldCode?
|
||||
# Move oldCode to new team under same spell
|
||||
for newSpellKey in teamSpells[newTeam]
|
||||
[newThang, newSpell] = newSpellKey.split '/'
|
||||
if newSpell is oldSpell
|
||||
# Found spell location under new team
|
||||
# console.log "Swapping spell=#{oldSpell} from #{oldThang} to #{newThang}"
|
||||
if code[newThang]?[oldSpell]?
|
||||
# Option 1: have a new spell to swap
|
||||
code[oldThang][oldSpell] = code[newThang][oldSpell]
|
||||
else
|
||||
# Option 2: no new spell to swap
|
||||
delete code[oldThang][oldSpell]
|
||||
code[newThang] = {} unless code[newThang]?
|
||||
code[newThang][oldSpell] = oldCode
|
||||
break
|
||||
|
||||
@setTeam newTeam # Sets @session 'team'
|
||||
sessionState = @session.get('state')
|
||||
if sessionState?
|
||||
# TODO: Don't hard code thangID
|
||||
sessionState.selected = if newTeam is 'humans' then 'Hero Placeholder' else 'Hero Placeholder 1'
|
||||
@session.set 'state', sessionState
|
||||
@session.set 'code', code
|
||||
@session.patch()
|
||||
|
||||
if sessionState?
|
||||
# TODO: Don't hardcode spellName
|
||||
Backbone.Mediator.publish 'level:select-sprite', thangID: sessionState.selected, spellName: 'plan'
|
||||
|
||||
# End Real-time Multiplayer ######################################################
|
||||
|
|
|
@ -18,9 +18,6 @@ module.exports = class CastButtonView extends CocoView
|
|||
'tome:cast-spells': 'onCastSpells'
|
||||
'tome:manual-cast-denied': 'onManualCastDenied'
|
||||
'god:new-world-created': 'onNewWorld'
|
||||
'real-time-multiplayer:created-game': 'onJoinedRealTimeMultiplayerGame'
|
||||
'real-time-multiplayer:joined-game': 'onJoinedRealTimeMultiplayerGame'
|
||||
'real-time-multiplayer:left-game': 'onLeftRealTimeMultiplayerGame'
|
||||
'goal-manager:new-goal-states': 'onNewGoalStates'
|
||||
'god:goals-calculated': 'onGoalsCalculated'
|
||||
'playback:ended-changed': 'onPlaybackEndedChanged'
|
||||
|
@ -71,9 +68,7 @@ module.exports = class CastButtonView extends CocoView
|
|||
Backbone.Mediator.publish 'tome:manual-cast', {}
|
||||
|
||||
onCastRealTimeButtonClick: (e) ->
|
||||
if @inRealTimeMultiplayerSession
|
||||
Backbone.Mediator.publish 'real-time-multiplayer:manual-cast', {}
|
||||
else if @options.level.get('replayable') and (timeUntilResubmit = @options.session.timeUntilResubmit()) > 0
|
||||
if @options.level.get('replayable') and (timeUntilResubmit = @options.session.timeUntilResubmit()) > 0
|
||||
Backbone.Mediator.publish 'tome:manual-cast-denied', timeUntilResubmit: timeUntilResubmit
|
||||
else
|
||||
Backbone.Mediator.publish 'tome:manual-cast', {realTime: true}
|
||||
|
@ -178,9 +173,3 @@ module.exports = class CastButtonView extends CocoView
|
|||
return unless placeholder.length
|
||||
@ladderSubmissionView = new LadderSubmissionView session: @options.session, level: @options.level, mirrorSession: @mirrorSession
|
||||
@insertSubView @ladderSubmissionView, placeholder
|
||||
|
||||
onJoinedRealTimeMultiplayerGame: (e) ->
|
||||
@inRealTimeMultiplayerSession = true
|
||||
|
||||
onLeftRealTimeMultiplayerGame: (e) ->
|
||||
@inRealTimeMultiplayerSession = false
|
||||
|
|
|
@ -14,7 +14,6 @@ module.exports = class ProblemAlertView extends CocoView
|
|||
'level:restart': 'onHideProblemAlert'
|
||||
'tome:jiggle-problem-alert': 'onJiggleProblemAlert'
|
||||
'tome:manual-cast': 'onHideProblemAlert'
|
||||
'real-time-multiplayer:manual-cast': 'onHideProblemAlert'
|
||||
|
||||
events:
|
||||
'click .close': 'onRemoveClicked'
|
||||
|
|
|
@ -61,7 +61,6 @@ module.exports = class SpellView extends CocoView
|
|||
@supermodel = options.supermodel
|
||||
@worker = options.worker
|
||||
@session = options.session
|
||||
@listenTo(@session, 'change:multiplayer', @onMultiplayerChanged)
|
||||
@spell = options.spell
|
||||
@problems = []
|
||||
@savedProblems = {} # Cache saved user code problems to prevent duplicates
|
||||
|
@ -77,11 +76,7 @@ module.exports = class SpellView extends CocoView
|
|||
@fillACE()
|
||||
@createOnCodeChangeHandlers()
|
||||
@lockDefaultCode()
|
||||
if @session.get('multiplayer')
|
||||
@createFirepad()
|
||||
else
|
||||
# needs to happen after the code generating this view is complete
|
||||
_.defer @onAllLoaded
|
||||
_.defer @onAllLoaded # Needs to happen after the code generating this view is complete
|
||||
|
||||
createACE: ->
|
||||
# Test themes and settings here: http://ace.ajax.org/build/kitchen-sink.html
|
||||
|
@ -402,7 +397,6 @@ module.exports = class SpellView extends CocoView
|
|||
wrapper => orig.apply obj, args
|
||||
obj[method]
|
||||
|
||||
|
||||
finishRange = (row, startRow, startColumn) =>
|
||||
range = new Range startRow, startColumn, row, @aceSession.getLine(row).length - 1
|
||||
range.start = @aceDoc.createAnchor range.start
|
||||
|
@ -502,8 +496,6 @@ module.exports = class SpellView extends CocoView
|
|||
return unless @zatanna and @autocomplete
|
||||
@zatanna.addCodeCombatSnippets @options.level, @, e
|
||||
|
||||
|
||||
|
||||
translateFindNearest: ->
|
||||
# If they have advanced glasses but are playing a level which assumes earlier glasses, we'll adjust the sample code to use the more advanced APIs instead.
|
||||
oldSource = @getSource()
|
||||
|
@ -514,14 +506,9 @@ module.exports = class SpellView extends CocoView
|
|||
@updateACEText newSource
|
||||
_.delay (=> @recompile?()), 1000
|
||||
|
||||
onMultiplayerChanged: ->
|
||||
if @session.get('multiplayer')
|
||||
@createFirepad()
|
||||
else
|
||||
@firepad?.dispose()
|
||||
|
||||
createFirepad: ->
|
||||
# load from firebase or the original source if there's nothing there
|
||||
# Currently not called; could be brought back for future multiplayer modes.
|
||||
# Load from firebase or the original source if there's nothing there.
|
||||
return if @firepadLoading
|
||||
@eventsSuppressed = true
|
||||
@loaded = false
|
||||
|
@ -532,19 +519,18 @@ module.exports = class SpellView extends CocoView
|
|||
@fireRef = new Firebase fireURL
|
||||
firepadOptions = userId: me.id
|
||||
@firepad = Firepad.fromACE @fireRef, @ace, firepadOptions
|
||||
@firepad.on 'ready', @onFirepadLoaded
|
||||
@firepadLoading = true
|
||||
|
||||
onFirepadLoaded: =>
|
||||
@firepadLoading = false
|
||||
firepadSource = @ace.getValue()
|
||||
if firepadSource
|
||||
@spell.source = firepadSource
|
||||
else
|
||||
@ace.setValue @previousSource
|
||||
@aceSession.setUndoManager(new UndoManager())
|
||||
@ace.clearSelection()
|
||||
@onAllLoaded()
|
||||
@firepad.on 'ready', =>
|
||||
return if @destroyed
|
||||
@firepadLoading = false
|
||||
firepadSource = @ace.getValue()
|
||||
if firepadSource
|
||||
@spell.source = firepadSource
|
||||
else
|
||||
@ace.setValue @previousSource
|
||||
@aceSession.setUndoManager(new UndoManager())
|
||||
@ace.clearSelection()
|
||||
@onAllLoaded()
|
||||
|
||||
onAllLoaded: =>
|
||||
@spell.transpile @spell.source
|
||||
|
@ -573,7 +559,7 @@ module.exports = class SpellView extends CocoView
|
|||
@saveSpade()
|
||||
|
||||
getSource: ->
|
||||
@ace.getValue() # could also do @firepad.getText()
|
||||
@ace.getValue()
|
||||
|
||||
setThang: (thang) ->
|
||||
@focus()
|
||||
|
|
|
@ -5,7 +5,6 @@ submenuViews = [
|
|||
require 'views/play/menu/SaveLoadView'
|
||||
require 'views/play/menu/OptionsView'
|
||||
require 'views/play/menu/GuideView'
|
||||
require 'views/play/menu/MultiplayerView'
|
||||
]
|
||||
|
||||
module.exports = class GameMenuModal extends ModalView
|
||||
|
@ -31,11 +30,10 @@ module.exports = class GameMenuModal extends ModalView
|
|||
getRenderData: (context={}) ->
|
||||
context = super(context)
|
||||
docs = @options.level.get('documentation') ? {}
|
||||
submenus = ['guide', 'options', 'save-load', 'multiplayer']
|
||||
submenus = ['guide', 'options', 'save-load']
|
||||
submenus = _.without submenus, 'options' if window.serverConfig.picoCTF
|
||||
submenus = _.without submenus, 'guide' unless docs.specificArticles?.length or docs.generalArticles?.length or window.serverConfig.picoCTF
|
||||
submenus = _.without submenus, 'save-load' unless me.isAdmin() or /https?:\/\/localhost/.test(window.location.href)
|
||||
submenus = _.without submenus, 'multiplayer' unless me.isAdmin() or (@level?.isType('ladder', 'hero-ladder', 'course-ladder') and @level.get('slug') not in ['ace-of-coders', 'elemental-wars'])
|
||||
@includedSubmenus = submenus
|
||||
context.showTab = @options.showTab ? submenus[0]
|
||||
context.submenus = submenus
|
||||
|
@ -43,7 +41,6 @@ module.exports = class GameMenuModal extends ModalView
|
|||
'options': 'cog'
|
||||
'guide': 'list'
|
||||
'save-load': 'floppy-disk'
|
||||
'multiplayer': 'globe'
|
||||
context
|
||||
|
||||
showsChooseHero: ->
|
||||
|
@ -55,7 +52,6 @@ module.exports = class GameMenuModal extends ModalView
|
|||
super()
|
||||
@insertSubView new submenuView @options for submenuView in submenuViews
|
||||
firstView = switch @options.showTab
|
||||
when 'multiplayer' then @subviews.multiplayer_view
|
||||
when 'guide' then @subviews.guide_view
|
||||
else
|
||||
if 'guide' in @includedSubmenus then @subviews.guide_view else @subviews.options_view
|
||||
|
|
|
@ -1,229 +0,0 @@
|
|||
CocoView = require 'views/core/CocoView'
|
||||
template = require 'templates/play/menu/multiplayer-view'
|
||||
{me} = require 'core/auth'
|
||||
ThangType = require 'models/ThangType'
|
||||
LadderSubmissionView = require 'views/play/common/LadderSubmissionView'
|
||||
RealTimeModel = require 'models/RealTimeModel'
|
||||
RealTimeCollection = require 'collections/RealTimeCollection'
|
||||
|
||||
module.exports = class MultiplayerView extends CocoView
|
||||
id: 'multiplayer-view'
|
||||
className: 'tab-pane'
|
||||
template: template
|
||||
|
||||
subscriptions:
|
||||
'ladder:game-submitted': 'onGameSubmitted'
|
||||
|
||||
events:
|
||||
'click textarea': 'onClickLink'
|
||||
'change #multiplayer': 'updateLinkSection'
|
||||
'click #create-game-button': 'onCreateRealTimeGame'
|
||||
'click #join-game-button': 'onJoinRealTimeGame'
|
||||
'click #leave-game-button': 'onLeaveRealTimeGame'
|
||||
|
||||
constructor: (options) ->
|
||||
super(options)
|
||||
@level = options.level
|
||||
@levelID = @level?.get 'slug'
|
||||
@session = options.session
|
||||
@listenTo @session, 'change:multiplayer', @updateLinkSection
|
||||
@watchRealTimeSessions() if @level?.isType('hero-ladder', 'course-ladder') and me.isAdmin()
|
||||
|
||||
destroy: ->
|
||||
@realTimeSessions?.off 'add', @onRealTimeSessionAdded
|
||||
@currentRealTimeSession?.off 'change', @onCurrentRealTimeSessionChanged
|
||||
collection.off() for id, collection of @realTimeSessionsPlayers
|
||||
super()
|
||||
|
||||
getRenderData: ->
|
||||
c = super()
|
||||
c.joinLink = "#{document.location.href.replace(/\?.*/, '').replace('#', '')}?session=#{@session.id}"
|
||||
c.multiplayer = @session.get 'multiplayer'
|
||||
c.team = @session.get 'team'
|
||||
c.levelSlug = @levelID
|
||||
# For now, ladderGame will disallow multiplayer, because session code combining doesn't play nice yet.
|
||||
if @level?.isType('ladder', 'hero-ladder', 'course-ladder')
|
||||
c.ladderGame = true
|
||||
c.readyToRank = @session?.readyToRank()
|
||||
|
||||
# Real-time multiplayer stuff
|
||||
if @level?.isType('hero-ladder', 'course-ladder') and me.isAdmin()
|
||||
c.levelID = @session.get('levelID')
|
||||
c.realTimeSessions = @realTimeSessions
|
||||
c.currentRealTimeSession = @currentRealTimeSession if @currentRealTimeSession
|
||||
c.realTimeSessionsPlayers = @realTimeSessionsPlayers if @realTimeSessionsPlayers
|
||||
# console.log 'MultiplayerView getRenderData', c.levelID
|
||||
# console.log 'realTimeSessions', c.realTimeSessions
|
||||
# console.log c.realTimeSessions.at(c.realTimeSessions.length - 1).get('state') if c.realTimeSessions.length > 0
|
||||
# console.log 'currentRealTimeSession', c.currentRealTimeSession
|
||||
# console.log 'realTimeSessionPlayers', c.realTimeSessionsPlayers
|
||||
|
||||
c
|
||||
|
||||
afterRender: ->
|
||||
super()
|
||||
@updateLinkSection()
|
||||
@ladderSubmissionView = new LadderSubmissionView session: @session, level: @level
|
||||
@insertSubView @ladderSubmissionView, @$el.find('.ladder-submission-view')
|
||||
@$el.find('#created-multiplayer-session').toggle Boolean(@currentRealTimeSession?)
|
||||
@$el.find('#create-game-button').toggle Boolean(not (@currentRealTimeSession?))
|
||||
|
||||
onClickLink: (e) ->
|
||||
e.target.select()
|
||||
|
||||
onGameSubmitted: (e) ->
|
||||
# Preserve the supermodel as we navigate back to the ladder.
|
||||
viewArgs = [{supermodel: if @options.hasReceivedMemoryWarning then null else @supermodel}, @levelID]
|
||||
ladderURL = "/play/ladder/#{@levelID}"
|
||||
if leagueID = @getQueryVariable 'league'
|
||||
leagueType = if @level?.isType('course-ladder') then 'course' else 'clan'
|
||||
viewArgs.push leagueType
|
||||
viewArgs.push leagueID
|
||||
ladderURL += "/#{leagueType}/#{leagueID}"
|
||||
ladderURL += '#my-matches'
|
||||
Backbone.Mediator.publish 'router:navigate', route: ladderURL, viewClass: 'views/ladder/LadderView', viewArgs: viewArgs
|
||||
|
||||
updateLinkSection: ->
|
||||
multiplayer = @$el.find('#multiplayer').prop('checked')
|
||||
la = @$el.find('#link-area')
|
||||
la.toggle if @level?.isType('ladder', 'hero-ladder', 'course-ladder') then false else Boolean(multiplayer)
|
||||
true
|
||||
|
||||
onHidden: ->
|
||||
multiplayer = Boolean(@$el.find('#multiplayer').prop('checked'))
|
||||
@session.set('multiplayer', multiplayer)
|
||||
|
||||
# Real-time Multiplayer ######################################################
|
||||
#
|
||||
# This view is responsible for joining and leaving real-time multiplayer games.
|
||||
#
|
||||
# It performs these actions:
|
||||
# Display your current game (level, players)
|
||||
# Display open games
|
||||
# Create game button, if not in a game
|
||||
# Join game button
|
||||
# Leave game button, if in a game
|
||||
#
|
||||
# It monitors these:
|
||||
# Real-time multiplayer sessions (for open games, player states)
|
||||
# Current real-time multiplayer game session for changes
|
||||
# Players for real-time multiplayer game session
|
||||
#
|
||||
# Real-time state variables:
|
||||
# @realTimeSessionsPlayers - Collection of player lists for active real-time multiplayer sessions
|
||||
# @realTimeSessions - Active real-time multiplayer sessions
|
||||
# @currentRealTimeSession - Our current real-time multiplayer session
|
||||
#
|
||||
# TODO: Ditch backfire and just use Firebase directly. Easier to debug, richer APIs (E.g. presence stuff).
|
||||
|
||||
watchRealTimeSessions: ->
|
||||
# Setup monitoring of real-time multiplayer level sessions
|
||||
@realTimeSessionsPlayers = {}
|
||||
# TODO: only request sessions for this level, !team, etc.
|
||||
@realTimeSessions = new RealTimeCollection("multiplayer_level_sessions/#{@levelID}")
|
||||
@realTimeSessions.on 'add', @onRealTimeSessionAdded
|
||||
@realTimeSessions.each (rts) => @watchRealTimeSession rts
|
||||
|
||||
watchRealTimeSession: (rts) ->
|
||||
return if rts.get('state') is 'finished'
|
||||
return if rts.get('levelID') isnt @session.get('levelID')
|
||||
# console.log 'MultiplayerView watchRealTimeSession', rts
|
||||
# Setup monitoring of players for given session
|
||||
# TODO: verify we need this
|
||||
realTimeSession = new RealTimeModel("multiplayer_level_sessions/#{@levelID}/#{rts.id}")
|
||||
realTimeSession.on 'change', @onRealTimeSessionChanged
|
||||
@realTimeSessionsPlayers[rts.id] = new RealTimeCollection("multiplayer_level_sessions/#{@levelID}/#{rts.id}/players")
|
||||
@realTimeSessionsPlayers[rts.id].on 'add', @onRealTimePlayerAdded
|
||||
@findCurrentRealTimeSession rts
|
||||
|
||||
findCurrentRealTimeSession: (rts) ->
|
||||
# Look for our current real-time session (level, level state, member player)
|
||||
return if @currentRealTimeSession or not @realTimeSessionsPlayers?
|
||||
if rts.get('levelID') is @session.get('levelID') and rts.get('state') isnt 'finished'
|
||||
@realTimeSessionsPlayers[rts.id].each (player) =>
|
||||
if player.id is me.id and player.get('state') isnt 'left'
|
||||
# console.log 'MultiplayerView found current real-time session', rts
|
||||
@currentRealTimeSession = new RealTimeModel("multiplayer_level_sessions/#{@levelID}/#{rts.id}")
|
||||
@currentRealTimeSession.on 'change', @onCurrentRealTimeSessionChanged
|
||||
|
||||
# TODO: Is this necessary? Shouldn't everyone already know we joined a game at this point?
|
||||
Backbone.Mediator.publish 'real-time-multiplayer:joined-game', realTimeSessionID: @currentRealTimeSession.id
|
||||
|
||||
onRealTimeSessionAdded: (rts) =>
|
||||
@watchRealTimeSession rts
|
||||
@render()
|
||||
|
||||
onRealTimeSessionChanged: (rts) =>
|
||||
# console.log 'MultiplayerView onRealTimeSessionChanged', rts.get('state')
|
||||
# TODO: @realTimeSessions isn't updated before we call render() here
|
||||
# TODO: so this game isn't updated in open games list
|
||||
@render?()
|
||||
|
||||
onCurrentRealTimeSessionChanged: (rts) =>
|
||||
# console.log 'MultiplayerView onCurrentRealTimeSessionChanged', rts
|
||||
if rts.get('state') is 'finished'
|
||||
@currentRealTimeSession.off 'change', @onCurrentRealTimeSessionChanged
|
||||
@currentRealTimeSession = null
|
||||
@render?()
|
||||
|
||||
onRealTimePlayerAdded: (e) =>
|
||||
@render?()
|
||||
|
||||
onCreateRealTimeGame: ->
|
||||
@playSound 'menu-button-click'
|
||||
s = @realTimeSessions.create {
|
||||
creator: @session.get('creator')
|
||||
creatorName: @session.get('creatorName')
|
||||
levelID: @session.get('levelID')
|
||||
created: (new Date()).toISOString()
|
||||
state: 'creating'
|
||||
}
|
||||
@currentRealTimeSession = @realTimeSessions.get(s.id)
|
||||
@currentRealTimeSession.on 'change', @onCurrentRealTimeSessionChanged
|
||||
# TODO: s.id === @currentRealTimeSession.id ?
|
||||
players = new RealTimeCollection("multiplayer_level_sessions/#{@levelID}/#{@currentRealTimeSession.id}/players")
|
||||
players.create
|
||||
id: me.id
|
||||
state: 'coding'
|
||||
name: @session.get('creatorName')
|
||||
team: @session.get('team')
|
||||
level_session: @session.id
|
||||
Backbone.Mediator.publish 'real-time-multiplayer:created-game', realTimeSessionID: @currentRealTimeSession.id
|
||||
@render()
|
||||
|
||||
onJoinRealTimeGame: (e) ->
|
||||
return if @currentRealTimeSession
|
||||
@playSound 'menu-button-click'
|
||||
item = @$el.find(e.target).data('item')
|
||||
@currentRealTimeSession = @realTimeSessions.get(item.id)
|
||||
@currentRealTimeSession.on 'change', @onCurrentRealTimeSessionChanged
|
||||
if @realTimeSessionsPlayers[item.id]
|
||||
|
||||
# TODO: SpellView updateTeam() should take care of this team swap update in the real-time multiplayer session
|
||||
creatorID = @currentRealTimeSession.get('creator')
|
||||
creator = @realTimeSessionsPlayers[item.id].get(creatorID)
|
||||
creatorTeam = creator.get('team')
|
||||
myTeam = @session.get('team')
|
||||
if myTeam is creatorTeam
|
||||
myTeam = if creatorTeam is 'humans' then 'ogres' else 'humans'
|
||||
|
||||
@realTimeSessionsPlayers[item.id].create
|
||||
id: me.id
|
||||
state: 'coding'
|
||||
name: me.get('name')
|
||||
team: myTeam
|
||||
level_session: @session.id
|
||||
else
|
||||
console.error 'MultiplayerView onJoinRealTimeGame did not have a players collection', @currentRealTimeSession
|
||||
Backbone.Mediator.publish 'real-time-multiplayer:joined-game', realTimeSessionID: @currentRealTimeSession.id
|
||||
@render()
|
||||
|
||||
onLeaveRealTimeGame: (e) ->
|
||||
@playSound 'menu-button-click'
|
||||
if @currentRealTimeSession
|
||||
@currentRealTimeSession.off 'change', @onCurrentRealTimeSessionChanged
|
||||
@currentRealTimeSession = null
|
||||
Backbone.Mediator.publish 'real-time-multiplayer:left-game', userID: me.id
|
||||
else
|
||||
console.error "Tried to leave a game with no currentMultiplayerSession"
|
||||
@render()
|
|
@ -83,7 +83,7 @@ LevelSessionSchema.pre 'save', (next) ->
|
|||
next()
|
||||
|
||||
LevelSessionSchema.statics.privateProperties = ['code', 'submittedCode', 'unsubscribed']
|
||||
LevelSessionSchema.statics.editableProperties = ['multiplayer', 'players', 'code', 'codeLanguage', 'completed', 'state',
|
||||
LevelSessionSchema.statics.editableProperties = ['players', 'code', 'codeLanguage', 'completed', 'state',
|
||||
'levelName', 'creatorName', 'levelID',
|
||||
'chat', 'teamSpells', 'submitted', 'submittedCodeLanguage',
|
||||
'unsubscribed', 'playtime', 'heroConfig', 'team',
|
||||
|
|
Loading…
Reference in a new issue