mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2025-04-27 14:33:59 -04:00
Added /play/ladder/levelID/clan/clanID clan-specific ladder pages.
This commit is contained in:
parent
bda2483738
commit
32ca453dec
7 changed files with 63 additions and 23 deletions
app
core
templates/play/ladder
views/ladder
server/levels
|
@ -104,6 +104,7 @@ module.exports = class CocoRouter extends Backbone.Router
|
|||
'multiplayer': go('MultiplayerView')
|
||||
|
||||
'play': go('play/CampaignView')
|
||||
'play/ladder/:levelID/:leagueType/:leagueID': go('ladder/LadderView')
|
||||
'play/ladder/:levelID': go('ladder/LadderView')
|
||||
'play/ladder': go('ladder/MainLadderView')
|
||||
'play/level/:levelID': go('play/level/PlayLevelView')
|
||||
|
|
|
@ -10,6 +10,9 @@ block content
|
|||
else
|
||||
h1= level.get('name')
|
||||
|
||||
if league
|
||||
h1 #{league.get('name')} League
|
||||
|
||||
if level.get('name') == 'Greed'
|
||||
.tournament-blurb
|
||||
h2
|
||||
|
|
|
@ -154,7 +154,7 @@ module.exports = class LadderTabView extends CocoView
|
|||
@supermodel.removeModelResource oldLeaderboard
|
||||
oldLeaderboard.destroy()
|
||||
teamSession = _.find @sessions.models, (session) -> session.get('team') is team.id
|
||||
@leaderboards[team.id] = new LeaderboardData(@level, team.id, teamSession, @ladderLimit)
|
||||
@leaderboards[team.id] = new LeaderboardData(@level, team.id, teamSession, @ladderLimit, @options.league)
|
||||
@leaderboardRes = @supermodel.addModelResource(@leaderboards[team.id], 'leaderboard', {cache: false}, 3)
|
||||
@leaderboardRes.load()
|
||||
|
||||
|
@ -166,7 +166,9 @@ module.exports = class LadderTabView extends CocoView
|
|||
team = _.find @teams, name: histogramWrapper.data('team-name')
|
||||
histogramData = null
|
||||
$.when(
|
||||
$.get "/db/level/#{@level.get('slug')}/histogram_data?team=#{team.name.toLowerCase()}", {cache: false}, (data) -> histogramData = data
|
||||
url = "/db/level/#{@level.get('slug')}/histogram_data?team=#{team.name.toLowerCase()}"
|
||||
url += '&leagues.leagueID=' + @options.league.id if @options.league
|
||||
$.get url, {cache: false}, (data) -> histogramData = data
|
||||
).then =>
|
||||
@generateHistogram(histogramWrapper, histogramData, team.name.toLowerCase()) unless @destroyed
|
||||
|
||||
|
@ -301,24 +303,32 @@ module.exports.LeaderboardData = LeaderboardData = class LeaderboardData extends
|
|||
Consolidates what you need to load for a leaderboard into a single Backbone Model-like object.
|
||||
###
|
||||
|
||||
constructor: (@level, @team, @session, @limit) ->
|
||||
constructor: (@level, @team, @session, @limit, @league) ->
|
||||
super()
|
||||
|
||||
collectionParameters: (parameters) ->
|
||||
parameters.team = @team
|
||||
parameters['leagues.leagueID'] = @league.id if @league
|
||||
parameters
|
||||
|
||||
fetch: ->
|
||||
console.warn 'Already have top players on', @ if @topPlayers
|
||||
@topPlayers = new LeaderboardCollection(@level, {order: -1, scoreOffset: HIGHEST_SCORE, team: @team, limit: @limit})
|
||||
|
||||
@topPlayers = new LeaderboardCollection(@level, @collectionParameters(order: -1, scoreOffset: HIGHEST_SCORE, limit: @limit))
|
||||
promises = []
|
||||
promises.push @topPlayers.fetch cache: false
|
||||
|
||||
if @session
|
||||
score = @session.get('totalScore') or 10
|
||||
@playersAbove = new LeaderboardCollection(@level, {order: 1, scoreOffset: score, limit: 4, team: @team})
|
||||
@playersAbove = new LeaderboardCollection(@level, @collectionParameters(order: 1, scoreOffset: score, limit: 4))
|
||||
promises.push @playersAbove.fetch cache: false
|
||||
@playersBelow = new LeaderboardCollection(@level, {order: -1, scoreOffset: score, limit: 4, team: @team})
|
||||
@playersBelow = new LeaderboardCollection(@level, @collectionParameters(order: -1, scoreOffset: score, limit: 4))
|
||||
promises.push @playersBelow.fetch cache: false
|
||||
level = "#{@level.get('original')}.#{@level.get('version').major}"
|
||||
success = (@myRank) =>
|
||||
promises.push $.ajax("/db/level/#{level}/leaderboard_rank?scoreOffset=#{@session.get('totalScore')}&team=#{@team}", cache: false, success: success)
|
||||
loadURL = "/db/level/#{level}/leaderboard_rank?scoreOffset=#{@session.get('totalScore')}&team=#{@team}"
|
||||
loadURL += '&leagues.leagueID=' + @league.id if @league
|
||||
promises.push $.ajax(loadURL, cache: false, success: success)
|
||||
@promise = $.when(promises...)
|
||||
@promise.then @onLoad
|
||||
@promise.fail @onFail
|
||||
|
|
|
@ -12,6 +12,9 @@ SimulateTabView = require './SimulateTabView'
|
|||
LadderPlayModal = require './LadderPlayModal'
|
||||
CocoClass = require 'core/CocoClass'
|
||||
|
||||
Clan = require 'models/Clan'
|
||||
#CourseInstance = require 'models/CourseInstance'
|
||||
|
||||
HIGHEST_SCORE = 1000000
|
||||
|
||||
class LevelSessionsCollection extends CocoCollection
|
||||
|
@ -35,12 +38,19 @@ module.exports = class LadderView extends RootView
|
|||
'click a:not([data-toggle])': 'onClickedLink'
|
||||
'click .spectate-button': 'onClickSpectateButton'
|
||||
|
||||
constructor: (options, @levelID) ->
|
||||
constructor: (options, @levelID, @leagueType, @leagueID) ->
|
||||
super(options)
|
||||
@level = @supermodel.loadModel(new Level(_id: @levelID), 'level').model
|
||||
@sessions = @supermodel.loadCollection(new LevelSessionsCollection(@levelID), 'your_sessions', {cache: false}).model
|
||||
|
||||
@teams = []
|
||||
@loadLeague()
|
||||
|
||||
loadLeague: ->
|
||||
@leagueID = @leagueType = null unless @leagueType in ['clan'] #, 'course']
|
||||
return unless @leagueID
|
||||
modelClass = if @leagueType is 'clan' then Clan else null# else CourseInstance
|
||||
resourceString = if @leagueType is 'clan' then 'clans.clan' else null# else 'courses.course'
|
||||
@league = @supermodel.loadModel(new modelClass(_id: @leagueID), resourceString).model
|
||||
|
||||
onLoaded: ->
|
||||
@teams = teamDataFromLevel @level
|
||||
|
@ -53,6 +63,8 @@ module.exports = class LadderView extends RootView
|
|||
ctx.teams = @teams
|
||||
ctx.levelID = @levelID
|
||||
ctx.levelDescription = marked(@level.get('description')) if @level.get('description')
|
||||
ctx.leagueType = @leagueType
|
||||
ctx.league = @league
|
||||
ctx._ = _
|
||||
if tournamentEndDate = {greed: 1402444800000, 'criss-cross': 1410912000000, 'zero-sum': 1428364800000}[@levelID]
|
||||
ctx.tournamentTimeLeft = moment(new Date(tournamentEndDate)).fromNow()
|
||||
|
@ -64,9 +76,9 @@ module.exports = class LadderView extends RootView
|
|||
afterRender: ->
|
||||
super()
|
||||
return unless @supermodel.finished()
|
||||
@insertSubView(@ladderTab = new LadderTabView({}, @level, @sessions))
|
||||
@insertSubView(@myMatchesTab = new MyMatchesTabView({}, @level, @sessions))
|
||||
@insertSubView(@simulateTab = new SimulateTabView())
|
||||
@insertSubView(@ladderTab = new LadderTabView({league: @league}, @level, @sessions))
|
||||
@insertSubView(@myMatchesTab = new MyMatchesTabView({league: @league}, @level, @sessions))
|
||||
@insertSubView(@simulateTab = new SimulateTabView(league: @league))
|
||||
@refreshInterval = setInterval(@fetchSessionsAndRefreshViews.bind(@), 60 * 1000)
|
||||
hash = document.location.hash[1..] if document.location.hash
|
||||
if hash and not (hash in ['my-matches', 'simulate', 'ladder', 'prizes', 'rules', 'winners'])
|
||||
|
|
|
@ -24,7 +24,8 @@ module.exports = class MyMatchesTabView extends CocoView
|
|||
# Only fetch the names for the userIDs we don't already have in @nameMap
|
||||
ids = []
|
||||
for session in @sessions.models
|
||||
for match in (session.get('matches') or [])
|
||||
matches = @statsFromSession(session).matches or []
|
||||
for match in matches
|
||||
id = match.opponents[0].userID
|
||||
unless id
|
||||
console.error 'Found bad opponent ID in malformed match:', match, 'from session', session
|
||||
|
@ -37,7 +38,8 @@ module.exports = class MyMatchesTabView extends CocoView
|
|||
success = (nameMap) =>
|
||||
return if @destroyed
|
||||
for session in @sessions.models
|
||||
for match in session.get('matches') or []
|
||||
matches = @statsFromSession(session).matches or []
|
||||
for match in matches
|
||||
opponent = match.opponents[0]
|
||||
continue if @nameMap[opponent.userID]
|
||||
opponentUser = nameMap[opponent.userID]
|
||||
|
@ -88,15 +90,16 @@ module.exports = class MyMatchesTabView extends CocoView
|
|||
|
||||
for team in @teams
|
||||
team.session = (s for s in @sessions.models when s.get('team') is team.id)[0]
|
||||
stats = @statsFromSession team.session
|
||||
team.readyToRank = team.session?.readyToRank()
|
||||
team.isRanking = team.session?.get('isRanking')
|
||||
team.matches = (convertMatch(match, team.session.get('submitDate')) for match in team.session?.get('matches') or [])
|
||||
team.matches = (convertMatch(match, team.session.get('submitDate')) for match in (stats?.matches or []))
|
||||
team.matches.reverse()
|
||||
team.score = (team.session?.get('totalScore') or 10).toFixed(2)
|
||||
team.score = (stats?.totalScore ? 10).toFixed(2)
|
||||
team.wins = _.filter(team.matches, {state: 'win', stale: false}).length
|
||||
team.ties = _.filter(team.matches, {state: 'tie', stale: false}).length
|
||||
team.losses = _.filter(team.matches, {state: 'loss', stale: false}).length
|
||||
scoreHistory = team.session?.get('scoreHistory')
|
||||
scoreHistory = stats?.scoreHistory
|
||||
if scoreHistory?.length > 1
|
||||
team.scoreHistory = scoreHistory
|
||||
|
||||
|
@ -123,6 +126,12 @@ module.exports = class MyMatchesTabView extends CocoView
|
|||
|
||||
@$el.find('tr.fresh').removeClass('fresh', 5000)
|
||||
|
||||
statsFromSession: (session) ->
|
||||
return null unless session
|
||||
if @options.league
|
||||
return _.find(session.get('leagues') or [], leagueID: @options.league.id)?.stats
|
||||
session.attributes
|
||||
|
||||
generateScoreLineChart: (wrapperID, scoreHistory, teamName) =>
|
||||
margin =
|
||||
top: 20
|
||||
|
|
|
@ -160,16 +160,18 @@ LevelHandler = class LevelHandler extends Handler
|
|||
creator: req.user._id+''
|
||||
|
||||
query = Session.find(sessionQuery).select('-screenshot -transpiledCode')
|
||||
# TODO: take out "code" as well, since that can get huge containing the transpiled code for the lat hero, and find another way of having the LadderSubmissionViews in the MyMatchesTab determine rankin readiness
|
||||
# TODO: take out "code" as well, since that can get huge containing the transpiled code for the lat hero, and find another way of having the LadderSubmissionViews in the MyMatchesTab determine ranking readiness
|
||||
query.exec (err, results) =>
|
||||
if err then @sendDatabaseError(res, err) else @sendSuccess res, results
|
||||
|
||||
getHistogramData: (req, res, slug) ->
|
||||
match = levelID: slug, submitted: true, team: req.query.team
|
||||
match['leagues.leagueID'] = league if league = req.query['leagues.leagueID']
|
||||
aggregate = Session.aggregate [
|
||||
{$match: {'levelID': slug, 'submitted': true, 'team': req.query.team}}
|
||||
{$match: match}
|
||||
{$project: {totalScore: 1, _id: 0}}
|
||||
]
|
||||
aggregate.cache()
|
||||
aggregate.cache() unless league
|
||||
|
||||
aggregate.exec (err, data) =>
|
||||
if err? then return @sendDatabaseError res, err
|
||||
|
@ -198,7 +200,7 @@ LevelHandler = class LevelHandler extends Handler
|
|||
|
||||
sortParameters =
|
||||
'totalScore': req.query.order
|
||||
selectProperties = ['totalScore', 'creatorName', 'creator', 'submittedCodeLanguage', 'heroConfig']
|
||||
selectProperties = ['totalScore', 'creatorName', 'creator', 'submittedCodeLanguage', 'heroConfig', 'leagues.leagueID']
|
||||
|
||||
query = Session
|
||||
.find(sessionsQueryParameters)
|
||||
|
@ -232,6 +234,7 @@ LevelHandler = class LevelHandler extends Handler
|
|||
team: req.query.team
|
||||
totalScore: scoreQuery
|
||||
submitted: true
|
||||
query['leagues.leagueID'] = league if league = req.query['leagues.leagueID']
|
||||
query
|
||||
|
||||
validateLeaderboardRequestParameters: (req) ->
|
||||
|
|
|
@ -24,8 +24,10 @@ LevelSessionSchema.index({team: 1}, {sparse: true})
|
|||
LevelSessionSchema.index({totalScore: 1}, {sparse: true})
|
||||
LevelSessionSchema.index({user: 1, changed: -1}, {name: 'last played index', sparse: true})
|
||||
LevelSessionSchema.index({'level.original': 1, 'state.topScores.type': 1, 'state.topScores.date': -1, 'state.topScores.score': -1}, {name: 'top scores index', sparse: true})
|
||||
LevelSessionSchema.index({submitted: 1, team: 1, level:1, totalScore: -1}, {name: 'rank counting index', sparse: true})
|
||||
LevelSessionSchema.index({levelID: 1, submitted:1, team: 1}, {name: 'get all scores index', sparse: true})
|
||||
LevelSessionSchema.index({submitted: 1, team: 1, level: 1, totalScore: -1}, {name: 'rank counting index', sparse: true})
|
||||
#LevelSessionSchema.index({level: 1, 'leagues.leagueID': 1, submitted: 1, team: 1, totalScore: -1}, {name: 'league rank counting index', sparse: true}) # needed for league leaderboards?
|
||||
LevelSessionSchema.index({levelID: 1, submitted: 1, team: 1}, {name: 'get all scores index', sparse: true})
|
||||
#LevelSessionSchema.index({levelID: 1, 'leagues.leagueID': 1, submitted: 1, team: 1}, {name: 'league get all scores index', sparse: true}) # needed for league histograms?
|
||||
LevelSessionSchema.index({submitted: 1, team: 1, levelID: 1, submitDate: -1}, {name: 'matchmaking index', sparse: true})
|
||||
LevelSessionSchema.index({submitted: 1, team: 1, levelID: 1, randomSimulationIndex: -1}, {name: 'matchmaking random index', sparse: true})
|
||||
LevelSessionSchema.index({'leagues.leagueID': 1, submitted: 1, levelID: 1, team: 1, randomSimulationIndex: -1}, {name: 'league-based matchmaking random index', sparse: true}) # Really need MongoDB 3.2 for partial indexes for this and several others: https://jira.mongodb.org/browse/SERVER-785
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue