diff --git a/app/styles/play/ladder.sass b/app/styles/play/ladder.sass new file mode 100644 index 000000000..2821a4098 --- /dev/null +++ b/app/styles/play/ladder.sass @@ -0,0 +1,2 @@ +#ladder-view + color: black \ No newline at end of file diff --git a/app/templates/play/ladder.jade b/app/templates/play/ladder.jade new file mode 100644 index 000000000..98ee0f316 --- /dev/null +++ b/app/templates/play/ladder.jade @@ -0,0 +1,31 @@ +extends /templates/base +block content + + div#level-column + h3= level.get('name') + div#level-description + !{description} + + div#leaderboard-column + ul.nav.nav-pills + for team in teams + li + a(href="#team-#{team.id}", data-toggle="tab")= team.name + + .tab-content + for team in teams + .tab-pane(id="team-#{team.id}") + .leaderboard + h4 Leaderboard + table.table + for session in team.leaderboard.topPlayers.models + tr= session.get('creatorName') + .challengers + h4 Challengers + + if team.easyChallenger + div.challenger-select + | Easy challenger: + button + a(href=link+'?team='+team.id+'&opponent='+team.easyChallenger.id) + //TODO: finish once the scoring system is finished \ No newline at end of file diff --git a/app/views/play/ladder_view.coffee b/app/views/play/ladder_view.coffee new file mode 100644 index 000000000..fe98c7b24 --- /dev/null +++ b/app/views/play/ladder_view.coffee @@ -0,0 +1,138 @@ +RootView = require 'views/kinds/RootView' +Level = require 'models/Level' +LevelSession = require 'models/LevelSession' +CocoCollection = require 'models/CocoCollection' + +HIGHEST_SCORE = 1000000 + +class LevelSessionsCollection extends CocoCollection + url: '' + model: LevelSession + + constructor: (levelID) -> + super() + @url = "/db/level/#{levelID}/all_sessions" + +class LeaderboardCollection extends CocoCollection + url: '' + model: LevelSession + + constructor: (level, options) -> + super() + options ?= {} + @url = "/db/level/#{level.get('original')}.#{level.get('version').major}/leaderboard?#{$.param(options)}" + +module.exports = class LadderView extends RootView + id: 'ladder-view' + template: require 'templates/play/ladder' + startsLoading: true + + constructor: (options, levelID) -> + super(options) + @level = new Level(_id:levelID) + @level.fetch() + @level.once 'sync', @onLevelLoaded, @ + + @sessions = new LevelSessionsCollection(levelID) + @sessions.fetch({}) + @sessions.once 'sync', @onMySessionsLoaded, @ + + onLevelLoaded: -> @startLoadingPhaseTwoMaybe() + onMySessionsLoaded: -> @startLoadingPhaseTwoMaybe() + + startLoadingPhaseTwoMaybe: -> + return unless @level.loaded and @sessions.loaded + @loadPhaseTwo() + + loadPhaseTwo: -> + alliedSystem = _.find @level.get('systems'), (value) -> value.config?.teams? + teams = [] + for teamName, teamConfig of alliedSystem.config.teams + continue unless teamConfig.playable + teams.push teamName + @teams = teams + + @leaderboards = {} + @challengers = {} + for team in teams + teamSession = _.find @sessions.models, (session) -> session.get('team') is team + @leaderboards[team] = new LeaderboardData(@level, team, teamSession) + @leaderboards[team].once 'sync', @onLeaderboardLoaded, @ + @challengers[team] = new ChallengersData(@level, team, teamSession) + @challengers[team].once 'sync', @onChallengersLoaded, @ + + onChallengersLoaded: -> @renderMaybe() + onLeaderboardLoaded: -> @renderMaybe() + + renderMaybe: -> + loaders = _.values(@leaderboards).concat(_.values(@challengers)) + return unless _.every loaders, (loader) -> loader.loaded + @startsLoading = false + @render() + + getRenderData: -> + ctx = super() + ctx.level = @level + description = @level.get('description') + ctx.description = if description then marked(description) else '' + ctx.link = "/play/level/#{@level.get('name')}" + ctx.teams = [] + for team in @teams or [] + ctx.teams.push({ + id: team + name: _.string.titleize(team) + leaderboard: @leaderboards[team] + easyChallenger: @challengers[team].easyPlayer.models[0] + mediumChallenger: @challengers[team].mediumPlayer.models[0] + hardChallenger: @challengers[team].hardPlayer.models[0] + }) + ctx + + afterRender: -> + super() + @$el.find('#leaderboard-column .nav a:first').tab('show') + +class LeaderboardData + constructor: (@level, @team, @session) -> + _.extend @, Backbone.Events + @topPlayers = new LeaderboardCollection(@level, {order:-1, scoreOffset: HIGHEST_SCORE, team: @team, limit: if @session then 10 else 20}) + @topPlayers.fetch() + @topPlayers.once 'sync', @leaderboardPartLoaded, @ + + if @session + score = @session.get('score') or 25 + @playersAbove = new LeaderboardCollection(@level, {order:1, scoreOffset: score, limit: 4, team: @team}) + @playersAbove.fetch() + @playersAbove.once 'sync', @leaderboardPartLoaded, @ + @playersBelow = new LeaderboardCollection(@level, {order:-1, scoreOffset: score, limit: 4, team: @team}) + @playersBelow.fetch() + @playersBelow.once 'sync', @leaderboardPartLoaded, @ + + leaderboardPartLoaded: -> + if @session + if @topPlayers.loaded and @playersAbove.loaded and @playersBelow.loaded + @loaded = true + @trigger 'sync' + else + @loaded = true + @trigger 'sync' + +class ChallengersData + constructor: (@level, @team, @session) -> + _.extend @, Backbone.Events + score = @session?.get('score') or 25 + @easyPlayer = new LeaderboardCollection(@level, {order:1, scoreOffset: score - 5, limit: 1, team: @team}) + @easyPlayer.fetch() + @easyPlayer.once 'sync', @challengerLoaded, @ + @mediumPlayer = new LeaderboardCollection(@level, {order:1, scoreOffset: score, limit: 1, team: @team}) + @mediumPlayer.fetch() + @mediumPlayer.once 'sync', @challengerLoaded, @ + @hardPlayer = new LeaderboardCollection(@level, {order:-1, scoreOffset: score + 5, limit: 1, team: @team}) + @hardPlayer.fetch() + @hardPlayer.once 'sync', @challengerLoaded, @ + + challengerLoaded: -> + if @easyPlayer.loaded and @mediumPlayer.loaded and @hardPlayer.loaded + @loaded = true + @trigger 'sync' + \ No newline at end of file diff --git a/server/levels/level_handler.coffee b/server/levels/level_handler.coffee index ea686a357..8f0bb1fbc 100644 --- a/server/levels/level_handler.coffee +++ b/server/levels/level_handler.coffee @@ -23,6 +23,7 @@ LevelHandler = class LevelHandler extends Handler getByRelationship: (req, res, args...) -> return @getSession(req, res, args[0]) if args[1] is 'session' + return @getLeaderboard(req, res, args[0]) if args[1] is 'leaderboard' return @getAllSessions(req, res, args[0]) if args[1] is 'all_sessions' return @getFeedback(req, res, args[0]) if args[1] is 'feedback' return @sendNotFoundError(res) @@ -72,7 +73,14 @@ LevelHandler = class LevelHandler extends Handler Session.find(sessionQuery).exec (err, results) => return @sendDatabaseError(res, err) if err res.send(results) - return res.end() + res.end() + + getLeaderboard: (req, res, id) -> + # stub handler +# [original, version] = id.split('.') +# version = parseInt version +# console.log 'get leaderboard for', original, version, req.query + return res.send([]) getFeedback: (req, res, id) -> @getDocumentForIdOrSlug id, (err, level) =>