mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2025-03-14 07:00:01 -04:00
Improved branching choices and set up an A/B/C/D test around branching.
This commit is contained in:
parent
83516f562b
commit
13fe1bbece
10 changed files with 84 additions and 21 deletions
|
@ -202,10 +202,13 @@
|
|||
victory_sign_up_poke: "Want to save your code? Create a free account!"
|
||||
victory_rate_the_level: "Rate the level: " # Only in old-style levels.
|
||||
victory_return_to_ladder: "Return to Ladder"
|
||||
victory_play_next_level: "Play Next Level" # Only in old-style levels.
|
||||
victory_play_continue: "Continue"
|
||||
victory_play_more_practice: "More Practice"
|
||||
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.
|
||||
|
|
|
@ -76,3 +76,15 @@ module.exports = class User extends CocoModel
|
|||
earnedHero: (heroOriginal) -> heroOriginal in (me.get('earned')?.heroes ? [])
|
||||
earnedItem: (itemOriginal) -> itemOriginal in (me.get('earned')?.items ? [])
|
||||
earnedLevel: (levelOriginal) -> levelOriginal in (me.get('earned')?.levels ? [])
|
||||
|
||||
getBranchingGroup: ->
|
||||
return @branchingGroup if @branchingGroup
|
||||
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
|
||||
@branchingGroup
|
||||
|
|
|
@ -202,6 +202,16 @@
|
|||
.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
|
||||
|
||||
|
||||
html.no-borderimage
|
||||
#hero-victory-modal
|
||||
|
|
|
@ -41,6 +41,10 @@ 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');
|
||||
a.btn.btn-success.btn-lg.world-map-button.next-level-branch-button(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')
|
||||
|
@ -63,8 +67,4 @@ block modal-footer-content
|
|||
else if level.get('type') === 'hero-ladder'
|
||||
a.btn.btn-primary(href="/play/ladder/#{level.get('slug')}#my-matches", data-dismiss="modal", data-i18n="play_level.victory_return_to_ladder") Return to Ladder
|
||||
else
|
||||
if morePracticeLevel
|
||||
a.btn.btn-primary.world-map-button.next-level-button.hide#more-practice-button(href="/play?next=" + morePracticeLevel, data-dismiss="modal", data-i18n="play_level.victory_play_more_practice") More Practice
|
||||
a.btn.btn-success.world-map-button.next-level-button.hide#continue-button(href="/play" + (continueLevel ? '?next=' + continueLevel : ''), data-dismiss="modal", data-i18n="play_level.victory_play_continue") Continue
|
||||
if skipAheadLevel
|
||||
a.btn.btn-primary.world-map-button.next-level-button.hide#skip-ahead-button(href="/play?next=" + skipAheadLevel, data-dismiss="modal", data-i18n="play_level.victory_skip_ahead") Skip Ahead
|
||||
button.btn.btn-success.world-map-button.next-level-button.hide#continue-button(data-i18n="play_level.victory_play_continue") Continue
|
||||
|
|
|
@ -8,7 +8,9 @@
|
|||
- var seenNext = nextLevel;
|
||||
each campaign in campaigns
|
||||
each level in campaign.levels
|
||||
- var next = level.id == nextLevel || (!seenNext && levelStatusMap[level.id] != "complete");
|
||||
if level.hidden
|
||||
continue;
|
||||
- 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)
|
||||
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)
|
||||
|
|
|
@ -86,7 +86,10 @@ module.exports = class WorldMapView extends RootView
|
|||
level.locked = index > 0 and not me.earnedLevel level.original
|
||||
window.levelUnlocksNotWorking = true if level.locked and level.id is @nextLevel # Temporary
|
||||
level.locked = false if window.levelUnlocksNotWorking # Temporary; also possible in HeroVictoryModal
|
||||
level.color = if level.practice then 'rgb(80, 130, 200)' else campaign.color
|
||||
level.color = campaign.color
|
||||
if level.practice
|
||||
level.color = 'rgb(80, 130, 200)' unless me.getBranchingGroup() is 'all-practice'
|
||||
level.hidden = true if me.getBranchingGroup() is 'no-practice'
|
||||
context.levelStatusMap = @levelStatusMap
|
||||
context.levelPlayCountMap = @levelPlayCountMap
|
||||
context.isIPadApp = application.isIPadApp
|
||||
|
@ -716,7 +719,7 @@ hero = [
|
|||
skip_ahead: 'new-sight'
|
||||
}
|
||||
{
|
||||
name: 'The One-Point_Fifth Kithmaze'
|
||||
name: 'The One-Point-Fifth Kithmaze'
|
||||
type: 'hero'
|
||||
difficulty: 1
|
||||
id: 'the-one-point-fifth-kithmaze'
|
||||
|
|
|
@ -18,6 +18,10 @@ module.exports = class HeroVictoryModal extends ModalView
|
|||
subscriptions:
|
||||
'ladder:game-submitted': 'onGameSubmitted'
|
||||
|
||||
events:
|
||||
'click #continue-button': 'onClickContinue'
|
||||
'click .next-level-branch-button': 'onClickNextLevelBranch'
|
||||
|
||||
constructor: (options) ->
|
||||
super(options)
|
||||
@session = options.session
|
||||
|
@ -33,6 +37,10 @@ module.exports = class HeroVictoryModal extends ModalView
|
|||
@waitingToContinueSince = new Date()
|
||||
Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'victory'
|
||||
|
||||
destroy: ->
|
||||
clearInterval @sequentialAnimationInterval
|
||||
super()
|
||||
|
||||
onAchievementsLoaded: ->
|
||||
thangTypeOriginals = []
|
||||
achievementIDs = []
|
||||
|
@ -102,9 +110,14 @@ module.exports = class HeroVictoryModal extends ModalView
|
|||
c.me = me
|
||||
c.readyToRank = @level.get('type', true) is 'hero-ladder' and @session.readyToRank()
|
||||
c.level = @level
|
||||
c.continueLevel = @getNextLevel 'continue'
|
||||
c.morePracticeLevel = me.isAdmin() and @getNextLevel 'more_practice'
|
||||
c.skipAheadLevel = me.isAdmin() and @getNextLevel 'skip_ahead'
|
||||
@continueLevelLink = @getNextLevelLink 'continue'
|
||||
@morePracticeLevelLink = me.isAdmin() and @getNextLevelLink 'more_practice'
|
||||
@skipAheadLevelLink = me.isAdmin() and @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'}
|
||||
]
|
||||
return c
|
||||
|
||||
afterRender: ->
|
||||
|
@ -227,7 +240,8 @@ module.exports = class HeroVictoryModal extends ModalView
|
|||
|
||||
onGameSubmitted: (e) ->
|
||||
ladderURL = "/play/ladder/#{@level.get('slug')}#my-matches"
|
||||
Backbone.Mediator.publish 'router:navigate', route: ladderURL
|
||||
# Preserve the supermodel as we navigate back to the ladder.
|
||||
Backbone.Mediator.publish 'router:navigate', route: ladderURL, viewClass: require('views/play/ladder/LadderView'), viewArgs: [{supermodel: @supermodel}]
|
||||
|
||||
playSelectionSound: (hero, preload=false) ->
|
||||
return unless sounds = hero.get('soundTriggers')?.selected
|
||||
|
@ -238,6 +252,8 @@ module.exports = class HeroVictoryModal extends ModalView
|
|||
else
|
||||
AudioPlayer.playSound name, 1
|
||||
|
||||
# Branching group testing
|
||||
|
||||
getNextLevel: (type) ->
|
||||
for campaign in require('views/play/WorldMapView').campaigns
|
||||
break if levelInfo
|
||||
|
@ -247,8 +263,25 @@ module.exports = class HeroVictoryModal extends ModalView
|
|||
break
|
||||
levelInfo?.nextLevels?[type] # 'more_practice', 'skip_ahead', 'continue'
|
||||
|
||||
# TODO: award heroes/items and play an awesome sound when you get one
|
||||
getNextLevelLink: (type) ->
|
||||
return '/play' unless nextLevel = @getNextLevel type
|
||||
"play?next=#{nextLevel}"
|
||||
|
||||
destroy: ->
|
||||
clearInterval @sequentialAnimationInterval
|
||||
super()
|
||||
onClickContinue: (e) ->
|
||||
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: @supermodel}]
|
||||
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) ->
|
||||
application.tracker?.trackEvent 'Branch Selected', level: @level.get('slug'), label: @level.get('slug'), branch: $(e.target).data('branch-key'), branchingGroup: me.getBranchingGroup()
|
||||
# Preserve the supermodel as we navigate back to world map.
|
||||
Backbone.Mediator.publish 'router:navigate', route: '/play', viewClass: require('views/play/WorldMapView'), viewArgs: [{supermodel: @supermodel}]
|
||||
|
|
|
@ -75,7 +75,7 @@ module.exports = class SpellPaletteView extends CocoView
|
|||
columns = ({items: [], nEntries: 0} for i in [0 ... nColumns])
|
||||
nRows = 0
|
||||
for group, entries of @entryGroups
|
||||
shortestColumn = _.sortBy(columns, (column) -> column.nEntries)[0]
|
||||
continue unless shortestColumn = _.sortBy(columns, (column) -> column.nEntries)[0]
|
||||
shortestColumn.nEntries += Math.max 2, entries.length
|
||||
shortestColumn.items.push @entryGroupElements[group]
|
||||
nRows = Math.max nRows, shortestColumn.nEntries
|
||||
|
|
|
@ -641,7 +641,7 @@ module.exports = class SpellView extends CocoView
|
|||
# TODO: move this whole thing into SpellDebugView or somewhere?
|
||||
@highlightComments() unless @destroyed
|
||||
flow ?= @spellThang?.castAether?.flow
|
||||
return unless flow
|
||||
return unless flow and @thang
|
||||
executed = []
|
||||
executedRows = {}
|
||||
matched = false
|
||||
|
|
|
@ -32,7 +32,7 @@ setupExpressMiddleware = (app) ->
|
|||
express.logger.format('prod', productionLogging)
|
||||
app.use(express.logger('prod'))
|
||||
app.use express.compress filter: (req, res) ->
|
||||
return false if req.headers.host is 'codecombat.com' # Cloudflare will gzip it for us on codecombat.com
|
||||
#return false if req.headers.host is 'codecombat.com' # CloudFlare will gzip it for us on codecombat.com # But now it's disabled.
|
||||
compressible res.getHeader('Content-Type')
|
||||
else
|
||||
app.use(express.logger('dev'))
|
||||
|
|
Loading…
Reference in a new issue