diff --git a/app/styles/play/level/tome/cast_button.sass b/app/styles/play/level/tome/cast_button.sass index 3a8a8c45c..44392ab9e 100644 --- a/app/styles/play/level/tome/cast_button.sass +++ b/app/styles/play/level/tome/cast_button.sass @@ -54,12 +54,23 @@ border-image: url(/images/level/code_toolbar_run_button_active_pressed.png) 14 20 20 20 fill round padding: 2px 0 0 2px - &.submit-button, &.done-button + &.submit-button, &.done-button, &.rank-button margin-left: 10px border-image: url(/images/level/code_toolbar_submit_button_active.png) 14 20 20 20 fill round &:active border-image: url(/images/level/code_toolbar_submit_button_active_pressed.png) 14 20 20 20 fill round + + .ladder-submission-view + width: 45% + width: -webkit-calc(50% - 10px) + width: calc(50% - 10px) + display: inline-block + + .btn.btn-illustrated + width: 100% + font-size: 18px + .cast-button @include opacity(0.77) diff --git a/app/templates/play/ladder/ladder-tab-view.jade b/app/templates/play/ladder/ladder-tab-view.jade index 16991fa49..c8b5f8855 100644 --- a/app/templates/play/ladder/ladder-tab-view.jade +++ b/app/templates/play/ladder/ladder-tab-view.jade @@ -32,7 +32,7 @@ div#columns.row td.score-cell= Math.round(sessionStats.totalScore * 100) td.name-col-cell= session.get('creatorName') || "Anonymous" td.fight-cell - a(href="/play/level/#{level.get('slug') || level.id}?team=#{team.otherTeam}&opponent=#{session.id}") + a(href="/play/level/#{level.get('slug') || level.id}?team=#{team.otherTeam}&opponent=#{session.id}" + (league ? "&league=" + league.id : "")) span(data-i18n="ladder.fight") Fight! td.spectate-cell.iconic-cell .glyphicon.glyphicon-eye-open @@ -51,7 +51,7 @@ div#columns.row td.score-cell= Math.round(sessionStats.totalScore * 100) td.name-col-cell= session.get('creatorName') || "Anonymous" td.fight-cell - a(href="/play/level/#{level.get('slug') || level.id}?team=#{team.otherTeam}&opponent=#{session.id}") + a(href="/play/level/#{level.get('slug') || level.id}?team=#{team.otherTeam}&opponent=#{session.id}" + (league ? "&league=" + league.id : "")) span(data-i18n="ladder.fight") Fight! td.spectate-cell.iconic-cell .glyphicon.glyphicon-eye-open @@ -84,7 +84,7 @@ div#columns.row span : span= friend.team br - a(href="/play/level/#{level.get('slug') || level.id}/?team=#{friend.otherTeam}&opponent=#{friend._id}") + a(href="/play/level/#{level.get('slug') || level.id}/?team=#{friend.otherTeam}&opponent=#{friend._id}" + (league ? "&league=" + league.id : "")) span(data-i18n="ladder.fight") Fight! else if onFacebook || onGPlus diff --git a/app/templates/play/ladder/ladder.jade b/app/templates/play/ladder/ladder.jade index 8d7c6f98f..af30ff3e7 100644 --- a/app/templates/play/ladder/ladder.jade +++ b/app/templates/play/ladder/ladder.jade @@ -109,7 +109,7 @@ block content div.column.col-md-2 .spectate-button-container - a(href="/play/spectate/#{level.get('slug')}").spectate-button.btn.btn-illustrated.btn-info.center + a(href="/play/spectate/#{level.get('slug')}" + (league ? "?league=" + league.id : "")).spectate-button.btn.btn-illustrated.btn-info.center span(data-i18n="play.spectate") Spectate ul.nav.nav-pills diff --git a/app/templates/play/ladder/my_matches_tab.jade b/app/templates/play/ladder/my_matches_tab.jade index 8506604a8..fabbb7117 100644 --- a/app/templates/play/ladder/my_matches_tab.jade +++ b/app/templates/play/ladder/my_matches_tab.jade @@ -43,7 +43,7 @@ div#columns.row td.name-cell= match.opponentName || "Anonymous" td.time-cell= match.when td.battle-cell - a(href="/play/level/#{levelID}?team=#{team.id}&opponent=#{match.sessionID}") + a(href="/play/level/#{levelID}?team=#{team.id}&opponent=#{match.sessionID}" + (league ? "&league=" + league.id : "")) if (match.state === 'win') span(data-i18n="ladder.watch_victory") Watch your victory else diff --git a/app/templates/play/ladder/play_modal.jade b/app/templates/play/ladder/play_modal.jade index 86631c841..175ccc658 100644 --- a/app/templates/play/ladder/play_modal.jade +++ b/app/templates/play/ladder/play_modal.jade @@ -12,7 +12,7 @@ block modal-body-content option(value=option.id selected=(language === option.id))= option.name div#noob-view.secret - a(href="/play/level/#{levelID}-tutorial").btn.btn-success.btn-block.btn-lg + a(href="/play/level/#{levelID}-tutorial" + (league ? "?league=" + league.id : "")).btn.btn-success.btn-block.btn-lg p strong(data-i18n="ladder.tutorial_play") Play Tutorial span(data-i18n="ladder.tutorial_recommended") Recommended if you've never played before @@ -23,8 +23,8 @@ block modal-body-content p.tutorial-suggestion strong(data-i18n="ladder.tutorial_not_sure") Not sure what's going on? | - a(href="/play/level/#{levelID}-tutorial", data-i18n="ladder.tutorial_play_first") Play the tutorial first. - a(href="/play/level/#{levelID}?team=#{teamID}") + a(href="/play/level/#{levelID}-tutorial" + (league ? "?league=" + league.id : ""), data-i18n="ladder.tutorial_play_first") Play the tutorial first. + a(href="/play/level/#{levelID}?team=#{teamID}" + (league ? "&league=" + league.id : "")) div.play-option img(src=myPortrait).my-icon.only-one img(src="/images/pages/play/ladder/"+teamID+"_ladder_tutorial.png", style="border: 1px solid #{teamColor}; background: #{teamBackgroundColor}").my-team-icon.img-circle.only-one @@ -39,7 +39,7 @@ block modal-body-content span(data-i18n="ladder.warmup") Warmup if challengers.easy - a(href="/play/level/#{levelID}?team=#{teamID}&opponent=#{challengers.easy.sessionID}") + a(href="/play/level/#{levelID}?team=#{teamID}&opponent=#{challengers.easy.sessionID}" + (league ? "&league=" + league.id : "")) div.play-option.easy-option img(src=myPortrait).my-icon.only-one img(src="/images/pages/play/ladder/"+teamID+"_ladder_easy.png", style="border: 1px solid #{teamColor}; background: #{teamBackgroundColor}").my-team-icon.img-circle.only-one @@ -55,7 +55,7 @@ block modal-body-content span(data-i18n="general.easy") Easy if challengers.medium - a(href="/play/level/#{levelID}?team=#{teamID}&opponent=#{challengers.medium.sessionID}") + a(href="/play/level/#{levelID}?team=#{teamID}&opponent=#{challengers.medium.sessionID}" + (league ? "&league=" + league.id : "")) div.play-option.medium-option img(src=myPortrait).my-icon.only-one img(src="/images/pages/play/ladder/"+teamID+"_ladder_medium.png", style="border: 1px solid #{teamColor}; background: #{teamBackgroundColor}").my-team-icon.img-circle.only-one @@ -71,7 +71,7 @@ block modal-body-content span(data-i18n="general.medium") Medium if challengers.hard - a(href="/play/level/#{levelID}?team=#{teamID}&opponent=#{challengers.hard.sessionID}") + a(href="/play/level/#{levelID}?team=#{teamID}&opponent=#{challengers.hard.sessionID}" + (league ? "&league=" + league.id : "")) div.play-option.hard-option img(src=myPortrait).my-icon.only-one img(src="/images/pages/play/ladder/"+teamID+"_ladder_hard.png", style="border: 1px solid #{teamColor}; background: #{teamBackgroundColor}").my-team-icon.img-circle.only-one diff --git a/app/templates/play/level/control_bar.jade b/app/templates/play/level/control_bar.jade index 2aec41d20..c122681f1 100644 --- a/app/templates/play/level/control_bar.jade +++ b/app/templates/play/level/control_bar.jade @@ -7,7 +7,7 @@ .levels-link-area a.levels-link(href=homeLink || "/") .glyphicon.glyphicon-play - span(data-i18n="nav.play").home-text Levels + span(data-i18n=ladderGame ? "general.ladder" : "nav.play").home-text Levels if isMultiplayerLevel && !observing .multiplayer-area-container diff --git a/app/templates/play/level/tome/cast_button.jade b/app/templates/play/level/tome/cast_button.jade index a8f2470cd..013ee73d9 100644 --- a/app/templates/play/level/tome/cast_button.jade +++ b/app/templates/play/level/tome/cast_button.jade @@ -2,9 +2,12 @@ button.btn.btn-lg.btn-illustrated.cast-button(title=castVerbose) span(data-i18n="play_level.tome_run_button_ran") Ran if !observing - button.btn.btn-lg.btn-illustrated.submit-button(title=castRealTimeVerbose) - span(data-i18n="play_level.tome_submit_button") Submit - span.spl.secret.submit-again-time - - button.btn.btn-lg.btn-illustrated.done-button.secret - span(data-i18n="play_level.done") Done + if mirror + .ladder-submission-view + else + button.btn.btn-lg.btn-illustrated.submit-button(title=castRealTimeVerbose) + span(data-i18n="play_level.tome_submit_button") Submit + span.spl.secret.submit-again-time + + button.btn.btn-lg.btn-illustrated.done-button.secret + span(data-i18n="play_level.done") Done diff --git a/app/views/ladder/LadderPlayModal.coffee b/app/views/ladder/LadderPlayModal.coffee index 6fa076ea6..3a729cb9f 100644 --- a/app/views/ladder/LadderPlayModal.coffee +++ b/app/views/ladder/LadderPlayModal.coffee @@ -103,6 +103,7 @@ module.exports = class LadderPlayModal extends ModalView {id: 'lua', name: 'Lua'} {id: 'io', name: 'Io (Experimental)'} ] + ctx.league = @options.league teamsList = teamDataFromLevel @level teams = {} teams[team.id] = team for team in teamsList diff --git a/app/views/ladder/LadderView.coffee b/app/views/ladder/LadderView.coffee index dfd1c09d7..cb756c7c9 100644 --- a/app/views/ladder/LadderView.coffee +++ b/app/views/ladder/LadderView.coffee @@ -108,6 +108,8 @@ module.exports = class LadderView extends RootView e.preventDefault() e.stopImmediatePropagation() url = "/play/spectate/#{@level.get('slug')}?session-one=#{humanSession}&session-two=#{ogreSession}" + url += '&league=' + @league.id if @league + url += '&autoplay=false' if key.command window.open url, if key.command then '_blank' else 'spectate' # New tab for spectating specific matches #Backbone.Mediator.publish 'router:navigate', route: url diff --git a/app/views/ladder/MyMatchesTabView.coffee b/app/views/ladder/MyMatchesTabView.coffee index e6d3c34e7..43ed6d5c8 100644 --- a/app/views/ladder/MyMatchesTabView.coffee +++ b/app/views/ladder/MyMatchesTabView.coffee @@ -67,6 +67,7 @@ module.exports = class MyMatchesTabView extends CocoView ctx.level = @level ctx.levelID = @level.get('slug') or @level.id ctx.teams = @teams + ctx.league = @options.league convertMatch = (match, submitDate) => opponent = match.opponents[0] @@ -116,7 +117,9 @@ module.exports = class MyMatchesTabView extends CocoView placeholder = $(el) sessionID = placeholder.data('session-id') session = _.find @sessions.models, {id: sessionID} - ladderSubmissionView = new LadderSubmissionView session: session, level: @level + if @level.get('slug') in ['ace-of-coders'] + mirrorSession = (s for s in @sessions.models when s.get('team') isnt session.get('team'))[0] + ladderSubmissionView = new LadderSubmissionView session: session, level: @level, mirrorSession: mirrorSession @insertSubView ladderSubmissionView, placeholder @$el.find('.score-chart-wrapper').each (i, el) => diff --git a/app/views/play/SpectateView.coffee b/app/views/play/SpectateView.coffee index d32b94ae5..ea7fe1639 100644 --- a/app/views/play/SpectateView.coffee +++ b/app/views/play/SpectateView.coffee @@ -257,7 +257,8 @@ module.exports = class SpectateLevelView extends RootView startFrame = @lastWorldFramesLoaded ? 0 if @world.frames.length is @world.totalFrames # Finished loading @lastWorldFramesLoaded = 0 - Backbone.Mediator.publish 'level:set-playing', playing: true # Since we paused at first, now we autostart playback. + unless @getQueryVariable('autoplay') is false + Backbone.Mediator.publish 'level:set-playing', playing: true # Since we paused at first, now we autostart playback. else @lastWorldFramesLoaded = @world.frames.length for [spriteName, message] in @world.thangDialogueSounds startFrame @@ -271,6 +272,8 @@ module.exports = class SpectateLevelView extends RootView @sessionOne = data[0]._id @sessionTwo = data[1]._id url = "/play/spectate/#{@levelID}?session-one=#{@sessionOne}&session-two=#{@sessionTwo}" + if leagueID = @getQueryVariable 'league' + url += "&league=" + leagueID Backbone.Mediator.publish 'router:navigate', { route: url, viewClass: SpectateLevelView, diff --git a/app/views/play/common/LadderSubmissionView.coffee b/app/views/play/common/LadderSubmissionView.coffee index 8c029ef4c..c47825a16 100644 --- a/app/views/play/common/LadderSubmissionView.coffee +++ b/app/views/play/common/LadderSubmissionView.coffee @@ -13,6 +13,7 @@ module.exports = class LadderSubmissionView extends CocoView constructor: (options) -> super options @session = options.session + @mirrorSession = options.mirrorSession @level = options.level getRenderData: -> @@ -62,20 +63,31 @@ module.exports = class LadderSubmissionView extends CocoView console.log jqxhr.responseText @setRankingButtonText 'failed' unless @destroyed @transpileSession (transpiledCode) => - ajaxData = session: @session.id levelID: @level.id originalLevelID: @level.get('original') levelMajorVersion: @level.get('version').major transpiledCode: transpiledCode - - $.ajax '/queue/scoring', { + ajaxOptions = type: 'POST' data: ajaxData success: success error: failure - } + if @mirrorSession + # Also submit the mirrorSession after the main session submits successfully. + mirrorAjaxData = _.clone ajaxData + mirrorAjaxData.session = @mirrorSession.id + if @session.get('team') is 'humans' + mirrorAjaxData.transpiledCode = 'hero-placeholder-1': transpiledCode['hero-placeholder'] + else + mirrorAjaxData.transpiledCode = 'hero-placeholder': transpiledCode['hero-placeholder-1'] + mirrorAjaxOptions = _.clone ajaxOptions + mirrorAjaxOptions.data = mirrorAjaxData + ajaxOptions.success = -> + $.ajax '/queue/scoring', mirrorAjaxOptions + + $.ajax '/queue/scoring', ajaxOptions transpileSession: (callback) -> submittedCode = @session.get('code') diff --git a/app/views/play/level/ControlBarView.coffee b/app/views/play/level/ControlBarView.coffee index 3ce4307c3..d0a100bad 100644 --- a/app/views/play/level/ControlBarView.coffee +++ b/app/views/play/level/ControlBarView.coffee @@ -76,13 +76,18 @@ module.exports = class ControlBarView extends CocoView @homeLink = '/play/ladder/' + levelID @homeViewClass = 'views/ladder/LadderView' @homeViewArgs.push levelID + if leagueID = @getQueryVariable 'league' + leagueType = if @level.get('type') is 'course-ladder' then 'course' else 'clan' + @homeViewArgs.push leagueType + @homeViewArgs.push leagueID + @homeLink += "/#{leagueType}/#{leagueID}" else if @level.get('type', true) in ['hero', 'hero-coop'] @homeLink = '/play' @homeViewClass = 'views/play/CampaignView' campaign = @level.get 'campaign' @homeLink += '/' + campaign @homeViewArgs.push campaign - else if @level.get('type', true) in ['course', 'course-ladder'] + else if @level.get('type', true) in ['course'] @homeLink = '/courses/mock1' @homeViewClass = 'views/courses/mock1/CourseDetailsView' #campaign = @level.get 'campaign' diff --git a/app/views/play/level/modal/HeroVictoryModal.coffee b/app/views/play/level/modal/HeroVictoryModal.coffee index aa4e89b87..8a7f7b15f 100644 --- a/app/views/play/level/modal/HeroVictoryModal.coffee +++ b/app/views/play/level/modal/HeroVictoryModal.coffee @@ -369,9 +369,18 @@ module.exports = class HeroVictoryModal extends ModalView @$el.find('.sign-up-poke').toggleClass('hide', not @readyToContinue) onGameSubmitted: (e) -> - ladderURL = "/play/ladder/#{@level.get('slug')}#my-matches" + @returnToLadder() + + returnToLadder: -> # Preserve the supermodel as we navigate back to the ladder. - Backbone.Mediator.publish 'router:navigate', route: ladderURL, viewClass: 'views/ladder/LadderView', viewArgs: [{supermodel: @supermodel}, @level.get('slug')] + viewArgs = [{supermodel: if @options.hasReceivedMemoryWarning then null else @supermodel}, @level.get('slug')] + ladderURL = "/play/ladder/#{@level.get('slug') || @level.id}#my-matches" + if leagueID = @getQueryVariable 'league' + leagueType = if @level.get('type') is 'course-ladder' then 'course' else 'clan' + viewArgs.push leagueType + viewArgs.push leagueID + ladderURL += "/#{leagueType}/#{leagueID}" + Backbone.Mediator.publish 'router:navigate', route: ladderURL, viewClass: 'views/ladder/LadderView', viewArgs: viewArgs playSelectionSound: (hero, preload=false) -> return unless sounds = hero.get('soundTriggers')?.selected @@ -433,9 +442,7 @@ module.exports = class HeroVictoryModal extends ModalView onClickReturnToLadder: (e) -> @playSound 'menu-button-click' e.preventDefault() - route = $(e.target).data('href') - # Preserve the supermodel as we navigate back to the ladder. - Backbone.Mediator.publish 'router:navigate', route: route, viewClass: 'views/ladder/LadderView', viewArgs: [{supermodel: if @options.hasReceivedMemoryWarning then null else @supermodel}, @level.get('slug')] + @returnToLadder() onClickSignupButton: (e) -> e.preventDefault() diff --git a/app/views/play/level/tome/CastButtonView.coffee b/app/views/play/level/tome/CastButtonView.coffee index ea114f6e2..59343ff0e 100644 --- a/app/views/play/level/tome/CastButtonView.coffee +++ b/app/views/play/level/tome/CastButtonView.coffee @@ -1,6 +1,8 @@ CocoView = require 'views/core/CocoView' template = require 'templates/play/level/tome/cast_button' {me} = require 'core/auth' +LadderSubmissionView = require 'views/play/common/LadderSubmissionView' +LevelSession = require 'models/LevelSession' module.exports = class CastButtonView extends CocoView id: 'cast-button-view' @@ -28,6 +30,7 @@ module.exports = class CastButtonView extends CocoView @castShortcut = '⇧↵' @updateReplayabilityInterval = setInterval @updateReplayability, 1000 @observing = options.session.get('creator') isnt me.id + @loadMirrorSession() if @options.level.get('slug') in ['ace-of-coders'] destroy: -> clearInterval @updateReplayabilityInterval @@ -42,6 +45,7 @@ module.exports = class CastButtonView extends CocoView context.castVerbose = castShortcutVerbose + ': ' + $.i18n.t('keyboard_shortcuts.run_code') context.castRealTimeVerbose = castRealTimeShortcutVerbose + ': ' + $.i18n.t('keyboard_shortcuts.run_real_time') context.observing = @observing + context.mirror = @mirrorSession? context afterRender: -> @@ -55,6 +59,7 @@ module.exports = class CastButtonView extends CocoView if @options.level.get('slug') is 'thornbush-farm'# and not @options.session.get('state')?.complete @$el.find('.submit-button').hide() # Hide submit until first win so that script can explain it. @updateReplayability() + @updateLadderSubmissionViews() attachTo: (spellView) -> @$el.detach().prependTo(spellView.toolbarView.$el).show() @@ -96,6 +101,7 @@ module.exports = class CastButtonView extends CocoView @casting = false if @hasCastOnce # Don't play this sound the first time @playSound 'cast-end', 0.5 + _.delay (=> @ladderSubmissionView?.rankSession()), 1000 if @ladderSubmissionView @hasCastOnce = true @updateCastButton() @world = e.world @@ -136,6 +142,7 @@ module.exports = class CastButtonView extends CocoView castText = $.i18n.t('play_level.tome_cast_button_ran') @castButton.text castText #@castButton.prop 'disabled', not castable + @ladderSubmissionView?.updateButton() updateReplayability: => return if @destroyed @@ -148,6 +155,18 @@ module.exports = class CastButtonView extends CocoView waitTime = moment().add(timeUntilResubmit, 'ms').fromNow() submitAgainLabel.text waitTime + loadMirrorSession: -> + url = "/db/level/#{@options.level.get('slug') or @options.level.id}/session" + url += "?team=#{if me.team is 'humans' then 'ogres' else 'humans'}" + mirrorSession = new LevelSession().setURL url + @mirrorSession = @supermodel.loadModel(mirrorSession, 'level_session', {cache: false}).model + + updateLadderSubmissionViews: -> + @removeSubView subview for key, subview of @subviews when subview instanceof LadderSubmissionView + placeholder = @$el.find('.ladder-submission-view') + @ladderSubmissionView = new LadderSubmissionView session: @options.session, level: @options.level, mirrorSession: @mirrorSession + @insertSubView @ladderSubmissionView, placeholder + onJoinedRealTimeMultiplayerGame: (e) -> @inRealTimeMultiplayerSession = true diff --git a/app/views/play/menu/GameMenuModal.coffee b/app/views/play/menu/GameMenuModal.coffee index f618cf4c7..bcde144b5 100644 --- a/app/views/play/menu/GameMenuModal.coffee +++ b/app/views/play/menu/GameMenuModal.coffee @@ -33,7 +33,7 @@ module.exports = class GameMenuModal extends ModalView submenus = ['guide', 'options', 'save-load', 'multiplayer'] submenus = _.without submenus, 'guide' unless docs.specificArticles?.length or docs.generalArticles?.length submenus = _.without submenus, 'save-load' unless me.isAdmin() or /https?:\/\/localhost/.test(window.location.href) - submenus = _.without submenus, 'multiplayer' unless me.isAdmin() or @level?.get('type') in ['ladder', 'hero-ladder', 'course-ladder'] + submenus = _.without submenus, 'multiplayer' unless me.isAdmin() or (@level?.get('type') in ['ladder', 'hero-ladder', 'course-ladder'] and @level.get('slug') not in ['ace-of-coders']) @includedSubmenus = submenus context.showTab = @options.showTab ? submenus[0] context.submenus = submenus diff --git a/app/views/play/menu/MultiplayerView.coffee b/app/views/play/menu/MultiplayerView.coffee index 435dbccd1..9e03757d1 100644 --- a/app/views/play/menu/MultiplayerView.coffee +++ b/app/views/play/menu/MultiplayerView.coffee @@ -72,8 +72,15 @@ module.exports = class MultiplayerView extends CocoView 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}#my-matches" - Backbone.Mediator.publish 'router:navigate', route: ladderURL + if leagueID = @getQueryVariable 'league' + leagueType = if @level?.get('type') is 'course-ladder' then 'course' else 'clan' + viewArgs.push leagueType + viewArgs.push leagueID + ladderURL += "/#{leagueType}/#{leagueID}" + Backbone.Mediator.publish 'router:navigate', route: ladderURL, viewClass: 'views/ladder/LadderView', viewArgs: viewArgs updateLinkSection: -> multiplayer = @$el.find('#multiplayer').prop('checked')