Unlocked core levels even for non-subscribers, moving extra levels to subscribers-only.

This commit is contained in:
Nick Winter 2014-12-05 15:44:49 -08:00
parent 8932cef46d
commit 6344b0c1a3
12 changed files with 109 additions and 154 deletions

View file

@ -346,7 +346,7 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription:
parents: "Für Eltern"
parents_title: "Dein Kind lernt zu programmieren."
parents_blurb1: "Mit CodeCombat, lernt dein Kind richtige Programme zu schreiben. Es fängt mit einfachen Befehlen an, und schreitet ganz unmerklich zu schwierigeren Themen fort."
parents_blurb2: "Für 9.99 im Monat, bekommt es jede Woche neue Herausforderungen sowie persönlichen email support von professionellen Programmierern."
parents_blurb2: "Für 9.99 im Monat, bekommt es jede Woche neue Herausforderungen sowie persönlichen email support von professionellen Programmierern." # Please update based on new wording from en.coffee
parents_blurb3: "Kein Risiko: 100% Geld zurück Garantie, und 1-Klick Abokündigung."
subscribe_button: "Abonniere jetzt"
stripe_description: "Monatsabo"

View file

@ -227,12 +227,6 @@
victory_rate_the_level: "Rate the level: " # Only in old-style levels.
victory_return_to_ladder: "Return to Ladder"
victory_play_continue: "Continue"
victory_play_skip: "Skip Ahead"
victory_play_next_level: "Play Next Level"
victory_play_more_practice: "More Practice"
victory_play_too_easy: "Too Easy"
victory_play_just_right: "Just Right"
victory_play_too_hard: "Too Hard"
victory_saving_progress: "Saving Progress"
victory_go_home: "Go Home" # Only in old-style levels.
victory_review: "Tell us more!" # Only in old-style levels.
@ -339,7 +333,7 @@
subscribe:
subscribe_title: "Subscribe"
levels: "Unlock 25 levels! With 5 new ones every week!"
levels: "Unlock 17 extra levels! With 5 new ones every week!"
heroes: "More powerful heroes!"
gems: "3500 bonus gems every month!"
items: "Over 250 bonus items!"
@ -925,7 +919,7 @@
email_settings_url: "your email settings"
email_description_suffix: "or through links in the emails we send, you can change your preferences and easily unsubscribe at any time."
cost_title: "Cost"
cost_description: "CodeCombat is free to play in the dungeon campaign, with a $9.99 USD/mo subscription for access to later campaigns and 3500 bonus gems per month. You can cancel with a click, and we offer a 100% money-back guarantee."
cost_description: "CodeCombat is free to play for all of its core levels, with a $9.99 USD/mo subscription for access to extra level branches and 3500 bonus gems per month. You can cancel with a click, and we offer a 100% money-back guarantee."
copyrights_title: "Copyrights and Licenses"
contributor_title: "Contributor License Agreement"
contributor_description_prefix: "All contributions, both on the site and on our GitHub repository, are subject to our"

View file

@ -346,7 +346,7 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription:
parents: "Para Educadores"
parents_title: "O teu educando vai aprender a programar."
parents_blurb1: "Com o CodeCombat, o teu educando aprende ao escrever código real. Começa por aprender comandos simples e progride para tópicos mais avançados."
parents_blurb2: "Por $9.99 USD/mês, recebe novos desafios todas as semanas e suporte pessoal, via e-mail, de programadores profissionais."
parents_blurb2: "Por $9.99 USD/mês, recebe novos desafios todas as semanas e suporte pessoal, via e-mail, de programadores profissionais." # Please update based on new wording from en.coffee
parents_blurb3: "Sem Risco: 100% de garantia de devolução do dinheiro, com anulação fácil de 1 clique."
subscribe_button: "Subscrever Agora"
stripe_description: "Subscrição Mensal"

View file

@ -346,7 +346,7 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi
parents: "Для Родителей"
parents_title: "Ваш ребенок научиться программировать."
parents_blurb1: "С CodeCombat ваш ребенок учится через написание реального кода. Начиная с изучения простых команд, продолжая более продвинутыми темами."
parents_blurb2: "За $9.99/месяц они получат новые испытания каждую неделю и персональную поддержку профессиональных программистов через электронную почту."
parents_blurb2: "За $9.99/месяц они получат новые испытания каждую неделю и персональную поддержку профессиональных программистов через электронную почту." # Please update based on new wording from en.coffee
parents_blurb3: "Без риска: 100% гарантия возрата денег, возможность отписаться в 1 клик."
subscribe_button: "Подпишись сейчас"
stripe_description: "Месячная подписка"
@ -925,7 +925,7 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi
email_settings_url: "ваши email настройки"
email_description_suffix: "или через ссылки в email-ах, которые мы отправляем, вы можете изменить предпочтения и легко отписаться в любой момент."
cost_title: "Стоимость"
cost_description: "В настоящее время, CodeCombat 100% бесплатен! Одной из наших главных целей является сохранить его таким, чтобы как можно больше людей могли играть, независимо от места в жизни. Если небо потемнеет, мы, возможно, введём подписки, возможно, только на некоторый контент, но нам не хотелось бы. Если повезёт, мы сможем поддерживать компанию, используя"
#cost_description: "В настоящее время, CodeCombat 100% бесплатен! Одной из наших главных целей является сохранить его таким, чтобы как можно больше людей могли играть, независимо от места в жизни. Если небо потемнеет, мы, возможно, введём подписки, возможно, только на некоторый контент, но нам не хотелось бы. Если повезёт, мы сможем поддерживать компанию, используя" # Please update based on new wording from en.coffee
copyrights_title: "Авторские права и лицензии"
contributor_title: "Лицензионное соглашение соавторов"
contributor_description_prefix: "Все вклады, как на сайте, так и на нашем репозитории GitHub, подпадают под наше"

View file

@ -102,19 +102,6 @@ module.exports = class User extends CocoModel
myHeroClasses.push heroClass for heroClass, heroSlugs of ThangType.heroClasses when _.intersection(myHeroSlugs, heroSlugs).length
myHeroClasses
getBranchingGroup: ->
return @branchingGroup if @branchingGroup
return 'all-practice' # A/B test paused for Hour of Code
group = me.get('testGroupNumber') % 4
@branchingGroup = switch group
when 0 then 'no-practice'
when 1 then 'all-practice'
when 2 then 'choice-explicit'
when 3 then 'choice-implicit'
@branchingGroup = 'choice-explicit' if me.isAdmin()
application.tracker.identify branchingGroup: @branchingGroup unless me.isAdmin()
@branchingGroup
getGemPromptGroup: ->
return @gemPromptGroup if @gemPromptGroup
group = me.get('testGroupNumber') % 8

View file

@ -221,16 +221,6 @@
.last-submitted
float: none
.next-levels-prompt
display: none
margin: 30px -21px
.btn
width: 30%
width: -webkit-calc(33.333333% - 10px)
width: calc(33.333333% - 10px)
margin: 5px
.hour-of-code-done
clear: both
padding-top: 10px

View file

@ -231,7 +231,7 @@ $gameControlMargin: 30px
font-size: 2vw
text-shadow: 0 0 0.3vw white, 0 0 0.3vw white
&:hover
&:hover
text-decoration: none
&#forest-link
@ -240,9 +240,10 @@ $gameControlMargin: 30px
transform: rotate(-35deg)
&#dungeon-link
left: 13.01%
top: 58%
left: 9%
top: 54.5%
transform: rotate(180deg)
color: fuchsia
.game-controls
position: absolute

View file

@ -42,11 +42,6 @@ block modal-body-content
img(src=item.getPortraitURL())
.reward-text= animate ? 'New Item' : item.get('name')
.next-levels-prompt
for button in continueButtons
- var enabled = Boolean(button.link != '/play' || me.getBranchingGroup() == 'choice-implicit' || button.key == 'continue');
button.btn.btn-success.btn-lg.world-map-button.next-level-branch-button(data-href=button.link, disabled=!enabled, data-dismiss="modal", data-i18n="play_level.victory_play_" + button[me.getBranchingGroup()], data-branch-key=button.key)
block modal-footer-content
if me.get('anonymous')
p.sign-up-poke.hide

View file

@ -16,18 +16,17 @@
#selling-points
#point-levels.point
.blurb(data-i18n="subscribe.levels") 25 more levels, with 5 new levels every week!
.blurb(data-i18n="subscribe.levels")
#point-heroes.point
.blurb(data-i18n="subscribe.heroes") Unlock more heroes, including wizards and rangers!
.blurb(data-i18n="subscribe.heroes")
#point-gems.point
.blurb(data-i18n="subscribe.gems") Subscribers get 3500 bonus gems per month!
.blurb(data-i18n="subscribe.gems")
#point-items.point
.blurb(data-i18n="subscribe.items") Unlock the coding power of 275 new items!
.blurb(data-i18n="subscribe.items")
#parents-info(data-i18n="subscribe.parents")
button.btn.btn-lg.btn-illustrated.purchase-button(data-i18n="subscribe.subscribe_button")
span $9.99/mo - Subscribe
if state === 'declined'
#declined-alert.alert.alert-danger.alert-dismissible

View file

@ -8,7 +8,7 @@
- var seenNext = nextLevel;
each level in campaign.levels
if !level.hidden
- var next = level.id == nextLevel || (!seenNext && levelStatusMap[level.id] != "complete" && !level.locked && !level.disabled && (!level.practice || me.getBranchingGroup() == 'all-practice'));
- var next = level.id == nextLevel || (!seenNext && levelStatusMap[level.id] != "complete" && !level.locked && !level.disabled);
- seenNext = seenNext || next;
div(style="left: #{level.x}%; bottom: #{level.y}%; background-color: #{level.color}", class="level" + (next ? " next" : "") + (level.disabled ? " disabled" : "") + (level.locked ? " locked" : "") + " " + levelStatusMap[level.id] || "", data-level-id=level.id, title=level.name + (level.disabled ? ' (Coming Soon to Adventurers)' : ''))
a(href=level.type == 'hero' ? '#' : level.disabled ? "/play" : "/play/#{level.levelPath || 'level'}/#{level.id}", disabled=level.disabled, data-level-id=level.id, data-level-path=level.levelPath || 'level', data-level-name=level.name)
@ -73,15 +73,15 @@ button.btn.btn-lg.btn-inverse#volume-button(title="Adjust volume")
.glyphicon.glyphicon-volume-down
.glyphicon.glyphicon-volume-up
h1#campaign-status
if mapType == 'dungeon'
span.spr(data-i18n="play.campaign_dungeon")
else if mapType == 'forest'
span.spr(data-i18n="play.campaign_forest")
| -
if requiresSubscription
span.spl(data-i18n="play.subscription_required")
else if mapType == 'dungeon'
span.spl(data-i18n="play.free")
else
span.spl(data-i18n="play.subscribed")
//h1#campaign-status
// if mapType == 'dungeon'
// span.spr(data-i18n="play.campaign_dungeon")
// else if mapType == 'forest'
// span.spr(data-i18n="play.campaign_forest")
// | -
// if requiresSubscription
// span.spl(data-i18n="play.subscription_required")
// else if mapType == 'dungeon'
// span.spl(data-i18n="play.free")
// else
// span.spl(data-i18n="play.subscribed")

View file

@ -82,6 +82,7 @@ module.exports = class WorldMapView extends RootView
trackedHourOfCode = true
@requiresSubscription = @terrain isnt 'dungeon' and not me.get('stripe')?.subscriptionID
@requiresSubscription = true
destroy: ->
@setupManager?.destroy()
@ -118,14 +119,6 @@ module.exports = class WorldMapView extends RootView
@fullyRendered = true
@render()
@preloadTopHeroes() unless me.get('heroConfig')?.thangType
if @requiresSubscription
modal = if me.get('anonymous') then AuthModal else SubscribeModal
_.delay =>
@openModalView? new modal() unless window.currentModal
if modal is SubscribeModal
window.tracker?.trackEvent 'Show subscription modal', category: 'Subscription', label: 'world map loadded'
window.tracker?.trackPageView "subscription/show-modal", ['Google Analytics']
, 2000
onSubscribed: ->
@requiresSubscription = false
@ -144,9 +137,8 @@ module.exports = class WorldMapView extends RootView
level.locked = false if me.get('slug') is 'nick'
level.disabled = false if @levelStatusMap[level.id] in ['started', 'complete']
level.color = 'rgb(255, 80, 60)'
if level.practice
level.color = 'rgb(80, 130, 200)' unless me.getBranchingGroup() is 'all-practice'
level.hidden = true if me.getBranchingGroup() is 'no-practice'
if level.requiresSubscription
level.color = 'rgb(80, 130, 200)'
context.levelStatusMap = @levelStatusMap
context.levelPlayCountMap = @levelPlayCountMap
context.isIPadApp = application.isIPadApp
@ -161,10 +153,16 @@ module.exports = class WorldMapView extends RootView
@onWindowResize()
unless application.isIPadApp
_.defer => @$el?.find('.game-controls .btn').tooltip() # Have to defer or i18n doesn't take effect.
@$el.find('.level').tooltip()
@$el.find('.level').tooltip().each ->
return unless me.isAdmin()
$(@).draggable().on 'dragstop', ->
bg = $('.map-background')
x = ($(@).offset().left - bg.offset().left + $(@).outerWidth() / 2) / bg.width()
y = 1 - ($(@).offset().top - bg.offset().top + $(@).outerHeight() / 2) / bg.height()
console.log "#{$(@).data('level-id')}\n x: #{(100 * x).toFixed(2)}\n y: #{(100 * y).toFixed(2)}\n"
@$el.addClass _.string.slugify @terrain
@updateVolume()
unless window.currentModal or not @fullyRendered or @requiresSubscription
unless window.currentModal or not @fullyRendered
@highlightElement '.level.next', delay: 500, duration: 60000, rotation: 0, sides: ['top']
if levelID = @$el.find('.level.next').data('level-id')
@$levelInfo = @$el.find(".level-info-container[data-level-id=#{levelID}]").show()
@ -210,7 +208,7 @@ module.exports = class WorldMapView extends RootView
@adjustLevelInfoPosition e
@endHighlight()
else
if @requiresSubscription and not @levelStatusMap[level.id] and not level.adventurer
if level.requiresSubscription and @requiresSubscription and not @levelStatusMap[level.id] and not level.adventurer
modal = if me.get('anonymous') then AuthModal else SubscribeModal
@openModalView new modal()
if modal is SubscribeModal
@ -376,8 +374,8 @@ dungeon = [
id: 'shadow-guard'
original: '54174347844506ae0195a0b8'
description: 'Evade the Kithgard minion.'
x: 44
y: 11
x: 40.54
y: 11.03
nextLevels:
more_practice: 'kounter-kithwise'
continue: 'forgetful-gemsmith'
@ -388,33 +386,34 @@ dungeon = [
id: 'kounter-kithwise'
original: '54527a6257e83800009730c7'
description: 'Practice your evasion skills with more guards.'
x: 55
y: 11
x: 35.37
y: 20.61
nextLevels:
continue: 'crawlways-of-kithgard'
practice: true
requiresSubscription: true
}
{
name: 'Crawlways of Kithgard'
type: 'hero'
id: 'crawlways-of-kithgard'
original: '545287ef57e83800009730d5'
description: 'Dart in and grab the gemat the right moment.'
x: 36.48
y: 29.03
nextLevels:
#more_practice: 'crawlways-of-kithgard'
continue: 'forgetful-gemsmith'
practice: true
requiresSubscription: true
}
#{
# name: 'Crawlways of Kithgard'
# type: 'hero'
# # id: 'crawlways-of-kithgard'
# original: '545287ef57e83800009730d5'
# description: 'Dart in and grab the gemat the right moment.'
# x: 57
# y: 12
# nextLevels:
# continue: 'true-names'
# practice: true
#}
{
name: 'Forgetful Gemsmith'
type: 'hero'
id: 'forgetful-gemsmith'
original: '544a98f62d002f0000fe331a'
description: 'Grab even more gems as you practice moving.'
x: 66
y: 11
x: 54.98
y: 10.53
nextLevels:
continue: 'true-names'
}
@ -424,8 +423,8 @@ dungeon = [
id: 'true-names'
original: '541875da4c16460000ab990f'
description: 'Learn an enemy\'s true name to defeat it.'
x: 76
y: 13
x: 68.44
y: 10.70
nextLevels:
more_practice: 'favorable-odds'
continue: 'the-raised-sword'
@ -436,11 +435,12 @@ dungeon = [
id: 'favorable-odds'
original: '5452972f57e83800009730de'
description: 'Test out your battle skills by defeating more munchkins.'
x: 80.85
y: 16
x: 88.25
y: 14.92
nextLevels:
continue: 'the-raised-sword'
practice: true
requiresSubscription: true
}
{
name: 'The Raised Sword'
@ -448,23 +448,11 @@ dungeon = [
id: 'the-raised-sword'
original: '5418aec24c16460000ab9aa6'
description: 'Learn to equip yourself for combat.'
x: 85
y: 20
x: 81.51
y: 17.92
nextLevels:
continue: 'haunted-kithmaze'
}
#{
# name: 'The First Kithmaze'
# type: 'hero'
# id: 'the-first-kithmaze'
# original: '5418b9d64c16460000ab9ab4'
# description: 'The builders of Kithgard constructed many mazes to confuse travelers.'
# x: 78
# y: 29
# nextLevels:
# more_practice: 'descending-further'
# continue: 'the-second-kithmaze'
#}
{
name: 'Haunted Kithmaze'
type: 'hero'
@ -477,17 +465,32 @@ dungeon = [
more_practice: 'descending-further'
continue: 'the-second-kithmaze'
}
{
name: 'Riddling Kithmaze'
type: 'hero'
id: 'riddling-kithmaze'
original: '5418b9d64c16460000ab9ab4'
description: 'If at first you go astray, change your loop to find the way.'
x: 69.97
y: 28.03
nextLevels:
more_practice: 'descending-further'
continue: 'the-second-kithmaze'
practice: true
requiresSubscription: true
}
{
name: 'Descending Further'
type: 'hero'
id: 'descending-further'
original: '5452a84d57e83800009730e4'
description: 'Another day, another maze.'
x: 70
y: 28
x: 61.68
y: 22.80
nextLevels:
continue: 'the-second-kithmaze'
practice: true
requiresSubscription: true
}
{
name: 'The Second Kithmaze'
@ -495,8 +498,8 @@ dungeon = [
id: 'the-second-kithmaze'
original: '5418cf256bae62f707c7e1c3'
description: 'Many have tried, few have found their way through this maze.'
x: 58
y: 23
x: 54.49
y: 26.49
nextLevels:
continue: 'dread-door'
}
@ -506,8 +509,8 @@ dungeon = [
id: 'dread-door'
original: '5418d40f4c16460000ab9ac2'
description: 'Behind a dread door lies a chest full of riches.'
x: 59
y: 32
x: 60.52
y: 33.70
nextLevels:
continue: 'known-enemy'
}
@ -562,11 +565,12 @@ dungeon = [
id: 'tactical-strike'
original: '5452cfa706a59e000067e4f5'
description: 'They\'re, uh, coming right for us! Sneak up behind them.'
x: 88.65
y: 63.06
x: 83.23
y: 52.73
nextLevels:
continue: 'the-final-kithmaze'
practice: true
requiresSubscription: true
}
{
name: 'The Final Kithmaze'
@ -574,8 +578,8 @@ dungeon = [
id: 'the-final-kithmaze'
original: '541b434e1ccc8eaae19f3c33'
description: 'To escape you must find your way through an Elder Kithman\'s maze.'
x: 83
y: 68
x: 86.95
y: 64.70
nextLevels:
more_practice: 'the-gauntlet'
continue: 'kithgard-gates'
@ -586,11 +590,12 @@ dungeon = [
id: 'the-gauntlet'
original: '5452d8b906a59e000067e4fa'
description: 'Rush for the stairs, battling foes at every turn.'
x: 84.89
y: 73.88
x: 76.50
y: 72.69
nextLevels:
continue: 'kithgard-gates'
practice: true
requiresSubscription: true
}
{
name: 'Kithgard Gates'
@ -658,6 +663,8 @@ forest = [
continue: 'thornbush-farm'
x: 33
y: 37
practice: true
requiresSubscription: true
}
{
name: 'Thornbush Farm'
@ -750,6 +757,7 @@ forest = [
continue: 'swift-dagger'
x: 38
y: 72
requiresSubscription: true
}
{
name: 'Swift Dagger'
@ -761,6 +769,7 @@ forest = [
continue: 'shrapnel'
x: 33
y: 72
requiresSubscription: true
}
{
name: 'Shrapnel'
@ -772,6 +781,7 @@ forest = [
continue: 'coinucopia'
x: 28
y: 73
requiresSubscription: true
}
# Wizard branch
@ -786,6 +796,7 @@ forest = [
x: 47
y: 71
adventurer: true
requiresSubscription: true
}
{
name: 'Touch of Death'
@ -798,6 +809,7 @@ forest = [
x: 52
y: 70
adventurer: true
requiresSubscription: true
}
{
name: 'Bonemender'
@ -810,6 +822,7 @@ forest = [
x: 58
y: 67
adventurer: true
requiresSubscription: true
}
{
@ -855,6 +868,7 @@ forest = [
continue: 'rich-forager'
x: 74.5
y: 92
requiresSubscription: true
}
{
name: 'Rich Forager'
@ -867,6 +881,7 @@ forest = [
x: 80
y: 88
adventurer: true
requiresSubscription: true
}
{
name: 'Siege of Stonehold'
@ -880,6 +895,7 @@ forest = [
x: 85.5
y: 83.5
adventurer: true
requiresSubscription: true
}
{
name: 'Multiplayer Treasure Grove'

View file

@ -21,7 +21,6 @@ module.exports = class HeroVictoryModal extends ModalView
events:
'click #continue-button': 'onClickContinue'
'click .next-level-branch-button': 'onClickNextLevelBranch'
'click .return-to-ladder-button': 'onClickReturnToLadder'
constructor: (options) ->
@ -110,13 +109,6 @@ module.exports = class HeroVictoryModal extends ModalView
c.readyToRank = @level.get('type', true) is 'hero-ladder' and @session.readyToRank()
c.level = @level
@continueLevelLink = @getNextLevelLink 'continue'
@morePracticeLevelLink = @getNextLevelLink 'more_practice'
@skipAheadLevelLink = @getNextLevelLink 'skip_ahead'
c.continueButtons = [
{key: 'skip_ahead', link: @skipAheadLevelLink, 'choice-explicit': 'skip', 'choice-implicit': 'too_easy'}
{key: 'continue', link: @continueLevelLink, 'choice-explicit': 'next_level', 'choice-implicit': 'just_right'}
{key: 'more_practice', link: @morePracticeLevelLink, 'choice-explicit': 'more_practice', 'choice-implicit': 'too_hard'}
]
elapsed = (new Date() - new Date(me.get('dateCreated')))
isHourOfCode = me.get('hourOfCode') or elapsed < 120 * 60 * 1000
@ -290,34 +282,15 @@ module.exports = class HeroVictoryModal extends ModalView
return link unless nextLevel = @getNextLevel type
"#{link}?next=#{nextLevel}"
# Branching group testing
getNextLevel: (type) ->
levelInfo = @getLevelInfoForSlug @level.get 'slug'
levelInfo?.nextLevels?[type] # 'more_practice', 'skip_ahead', 'continue'
levelInfo?.nextLevels?[type] # 'continue'; TODO: refactor to not have the object and just use single nextLevel property
onClickContinue: (e) ->
@playSound 'menu-button-click'
nextLevelLink = @continueLevelLink
if me.getBranchingGroup() is 'all-practice' and @morePracticeLevelLink
nextLevelLink = @morePracticeLevelLink
skipPrompt = me.getBranchingGroup() in ['no-practice', 'all-practice']
skipPrompt ||= not (@skipAheadLevelLink or @morePractiveLevelLink) and me.getBranchingGroup() is 'choice-explicit'
if skipPrompt
# Preserve the supermodel as we navigate back to the world map.
Backbone.Mediator.publish 'router:navigate', route: nextLevelLink, viewClass: require('views/play/WorldMapView'), viewArgs: [{supermodel: if @options.hasReceivedMemoryWarning then null else @supermodel}, @getNextLevelCampaign()]
else
# Hide everything except the buttons prompting them for which kind of next level to do
@$el.find('.modal-footer, .modal-body > *').hide()
@$el.find('.next-levels-prompt').show()
onClickNextLevelBranch: (e) ->
@playSound 'menu-button-click'
e.preventDefault()
route = $(e.target).data('href') or "/play/#{@getNextLevelCampaign()}"
application.tracker?.trackEvent 'Branch Selected', level: @level.get('slug'), label: @level.get('slug'), branch: $(e.target).data('branch-key'), branchingGroup: me.getBranchingGroup(), route: route
# Preserve the supermodel as we navigate back to world map.
Backbone.Mediator.publish 'router:navigate', route: route, viewClass: require('views/play/WorldMapView'), viewArgs: [{supermodel: if @options.hasReceivedMemoryWarning then null else @supermodel}, @getNextLevelCampaign()]
# Preserve the supermodel as we navigate back to the world map.
Backbone.Mediator.publish 'router:navigate', route: nextLevelLink, viewClass: require('views/play/WorldMapView'), viewArgs: [{supermodel: if @options.hasReceivedMemoryWarning then null else @supermodel}, @getNextLevelCampaign()]
onClickReturnToLadder: (e) ->
@playSound 'menu-button-click'