Merge pull request from codecombat/master

update
This commit is contained in:
Eero Penttilä 2015-09-05 14:41:58 +03:00
commit 1dafdc88ad
52 changed files with 985 additions and 212 deletions

View file

@ -58,11 +58,12 @@ module.exports = class CocoRouter extends Backbone.Router
'contribute/diplomat': go('contribute/DiplomatView') 'contribute/diplomat': go('contribute/DiplomatView')
'contribute/scribe': go('contribute/ScribeView') 'contribute/scribe': go('contribute/ScribeView')
'courses': go('courses/CoursesView')
'courses/mock1': go('courses/mock1/CoursesView') 'courses/mock1': go('courses/mock1/CoursesView')
'courses/mock1/enroll/:courseID': go('courses/mock1/CourseEnrollView') 'courses/mock1/enroll/:courseID': go('courses/mock1/CourseEnrollView')
'courses/mock1/:courseID': go('courses/mock1/CourseDetailsView') 'courses/mock1/:courseID': go('courses/mock1/CourseDetailsView')
'courses/mock1/:courseID/info': go('courses/mock1/CourseInfoView') 'courses': go('courses/CoursesView')
'courses/enroll(/:courseID)': go('courses/CourseEnrollView')
'courses/:courseID': go('courses/CourseDetailsView')
'db/*path': 'routeToServer' 'db/*path': 'routeToServer'
'demo(/*subpath)': go('DemoView') 'demo(/*subpath)': go('DemoView')

View file

@ -198,3 +198,11 @@ module.exports.getSponsoredSubsAmount = getSponsoredSubsAmount = (price=999, sub
Math.round((1 - offset) * price + (subCount - 1 + offset) * price * 0.8) Math.round((1 - offset) * price + (subCount - 1 + offset) * price * 0.8)
else else
Math.round((1 - offset) * price + 10 * price * 0.8 + (subCount - 11 + offset) * price * 0.6) Math.round((1 - offset) * price + 10 * price * 0.8 + (subCount - 11 + offset) * price * 0.6)
module.exports.getCoursesPrice = getSponsoredSubsAmount = (courses, seats=20) ->
totalPricePerSeat = courses.reduce ((a, b) -> a + b.get('pricePerSeat')), 0
if courses.length > 2
pricePerSeat = Math.round(totalPricePerSeat / 2.0)
else
pricePerSeat = parseInt(totalPricePerSeat)
seats * pricePerSeat

View file

@ -9,6 +9,7 @@ module.exports = class PlaybackOverScreen extends CocoClass
options ?= {} options ?= {}
@camera = options.camera @camera = options.camera
@layer = options.layer @layer = options.layer
@playerNames = options.playerNames
console.error @toString(), 'needs a camera.' unless @camera console.error @toString(), 'needs a camera.' unless @camera
console.error @toString(), 'needs a layer.' unless @layer console.error @toString(), 'needs a layer.' unless @layer
@build() @build()
@ -22,6 +23,18 @@ module.exports = class PlaybackOverScreen extends CocoClass
@dimLayer.alpha = 0 @dimLayer.alpha = 0
@layer.addChild @dimLayer @layer.addChild @dimLayer
makeVictoryText: ->
s = ''
size = Math.ceil @camera.canvasHeight / 6
text = new createjs.Text s, "#{size}px Open Sans Condensed", '#F7B42C'
text.shadow = new createjs.Shadow '#000', Math.ceil(@camera.canvasHeight / 300), Math.ceil(@camera.canvasHeight / 300), Math.ceil(@camera.canvasHeight / 120)
text.textAlign = 'center'
text.textBaseline = 'middle'
text.x = 0.5 * @camera.canvasWidth
text.y = 0.8 * @camera.canvasHeight
@dimLayer.addChild text
@text = text
show: -> show: ->
return if @showing return if @showing
@showing = true @showing = true
@ -43,6 +56,7 @@ module.exports = class PlaybackOverScreen extends CocoClass
incomplete = not success and not failure and not timedOut incomplete = not success and not failure and not timedOut
color = if failure then 'rgba(255, 128, 128, 0.4)' else 'rgba(255, 255, 255, 0.4)' color = if failure then 'rgba(255, 128, 128, 0.4)' else 'rgba(255, 255, 255, 0.4)'
@updateColor color @updateColor color
@updateText e
updateColor: (color) -> updateColor: (color) ->
return if color is @color return if color is @color
@ -50,5 +64,33 @@ module.exports = class PlaybackOverScreen extends CocoClass
if @color if @color
@dimLayer.updateCache() @dimLayer.updateCache()
else else
@dimLayer.cache 0, 0, @camera.canvasWidth, @camera.canvasHeight # I wonder if caching is even worth it for just a rect fill. @dimLayer.cache 0, 0, @camera.canvasWidth, @camera.canvasHeight
@color = color @color = color
updateText: (goalEvent) ->
return unless _.size @playerNames # Only on multiplayer levels
teamOverallStatuses = {}
goals = if goalEvent.goalStates then _.values goalEvent.goalStates else []
goals = (g for g in goals when not g.optional)
for team in ['humans', 'ogres']
teamGoals = (g for g in goals when g.team in [undefined, team])
statuses = (goal.status for goal in teamGoals)
overallStatus = 'success' if statuses.length > 0 and _.every(statuses, (s) -> s is 'success')
overallStatus = 'failure' if statuses.length > 0 and 'failure' in statuses
teamOverallStatuses[team] = overallStatus
@makeVictoryText() unless @text
if teamOverallStatuses.humans is 'success'
@text.color = '#E62B1E'
@text.text = ((@playerNames.humans ? $.i18n.t('ladder.red_ai')) + ' ' + $.i18n.t('ladder.wins')).toLocaleUpperCase()
else if teamOverallStatuses.ogres is 'success'
@text.color = '#0597FF'
@text.text = ((@playerNames.ogres ? $.i18n.t('ladder.blue_ai')) + ' ' + $.i18n.t('ladder.wins')).toLocaleUpperCase()
else
@text.color = '#F7B42C'
if goalEvent.timedOut
@text.text = 'TIMED OUT'
else
@text.text = 'INCOMPLETE'
@dimLayer.updateCache()

View file

@ -114,7 +114,7 @@ module.exports = Surface = class Surface extends CocoClass
@lankBoss = new LankBoss camera: @camera, webGLStage: @webGLStage, surfaceTextLayer: @surfaceTextLayer, world: @world, thangTypes: @options.thangTypes, choosing: @options.choosing, navigateToSelection: @options.navigateToSelection, showInvisible: @options.showInvisible, playerNames: @options.playerNames @lankBoss = new LankBoss camera: @camera, webGLStage: @webGLStage, surfaceTextLayer: @surfaceTextLayer, world: @world, thangTypes: @options.thangTypes, choosing: @options.choosing, navigateToSelection: @options.navigateToSelection, showInvisible: @options.showInvisible, playerNames: @options.playerNames
@countdownScreen = new CountdownScreen camera: @camera, layer: @screenLayer, showsCountdown: @world.showsCountdown @countdownScreen = new CountdownScreen camera: @camera, layer: @screenLayer, showsCountdown: @world.showsCountdown
@playbackOverScreen = new PlaybackOverScreen camera: @camera, layer: @screenLayer @playbackOverScreen = new PlaybackOverScreen camera: @camera, layer: @screenLayer, playerNames: @options.playerNames
@normalStage.addChildAt @playbackOverScreen.dimLayer, 0 # Put this below the other layers, actually, so we can more easily read text on the screen. @normalStage.addChildAt @playbackOverScreen.dimLayer, 0 # Put this below the other layers, actually, so we can more easily read text on the screen.
@waitingScreen = new WaitingScreen camera: @camera, layer: @screenLayer @waitingScreen = new WaitingScreen camera: @camera, layer: @screenLayer
@initCoordinates() @initCoordinates()

View file

@ -112,7 +112,7 @@ module.exports = class GoalManager extends CocoClass
goalStates: @goalStates goalStates: @goalStates
goals: @goals goals: @goals
overallStatus: overallStatus overallStatus: overallStatus
timedOut: @world.totalFrames is @world.maxTotalFrames timedOut: @world.totalFrames is @world.maxTotalFrames and overallStatus not in ['success', 'failure']
Backbone.Mediator.publish('goal-manager:new-goal-states', event) Backbone.Mediator.publish('goal-manager:new-goal-states', event)
checkOverallStatus: (ignoreIncomplete=false) -> checkOverallStatus: (ignoreIncomplete=false) ->
@ -120,7 +120,7 @@ module.exports = class GoalManager extends CocoClass
goals = if @goalStates then _.values @goalStates else [] goals = if @goalStates then _.values @goalStates else []
goals = (g for g in goals when not g.optional) goals = (g for g in goals when not g.optional)
goals = (g for g in goals when g.team in [undefined, @team]) if @team goals = (g for g in goals when g.team in [undefined, @team]) if @team
statuses = if @goalStates then (goal.status for goal in goals) else [] statuses = (goal.status for goal in goals)
overallStatus = 'success' if statuses.length > 0 and _.every(statuses, (s) -> s is 'success' or (ignoreIncomplete and s is null)) overallStatus = 'success' if statuses.length > 0 and _.every(statuses, (s) -> s is 'success' or (ignoreIncomplete and s is null))
overallStatus = 'failure' if statuses.length > 0 and 'failure' in statuses overallStatus = 'failure' if statuses.length > 0 and 'failure' in statuses
#console.log 'got overallStatus', overallStatus, 'from goals', goals, 'goalStates', @goalStates, 'statuses', statuses #console.log 'got overallStatus', overallStatus, 'from goals', goals, 'goalStates', @goalStates, 'statuses', statuses

View file

@ -1081,6 +1081,11 @@
rules: "Rules" rules: "Rules"
winners: "Winners" winners: "Winners"
league: "League" league: "League"
red_ai: "Red AI" # "Red AI Wins", at end of multiplayer match playback
blue_ai: "Blue AI"
wins: "Wins" # At end of multiplayer match playback
humans: "Red" # Ladder page display team name
ogres: "Blue"
user: user:
stats: "Stats" stats: "Stats"

View file

@ -42,8 +42,8 @@ module.exports = nativeDescription: "Português do Brasil", englishDescription:
diplomat_suggestion: diplomat_suggestion:
title: "Ajude a traduzir o CodeCombat!" # This shows up when a player switches to a non-English language using the language selector. title: "Ajude a traduzir o CodeCombat!" # This shows up when a player switches to a non-English language using the language selector.
sub_heading: "Nós precisamos de suas habilidades linguísticas." sub_heading: "Nós precisamos de suas habilidades linguísticas."
pitch_body: "Desenvolvemos o CodeCombat em Inglês, mas já temos jogadores de todo o mundo. Muitos deles querem jogar em Português Brasileiro mas não falam Inglês, por isso, se você conhece os dois idiomas, por favor, considere inscrever-se para ser um Diplomata e ajudar a traduzir tanto o site do CodeCombat quanto todos os estágios para o Português Brasileiro." pitch_body: "Desenvolvemos o CodeCombat em Inglês, mas já temos jogadores de todo o mundo. Muitos deles querem jogar em Português Brasileiro por não falar Inglês, por isso, se você conhece os dois idiomas, por favor, considere inscrever-se no programa para Diplomata e assim ajudar a traduzir tanto o site do CodeCombat quanto todos os estágios para o Português Brasileiro."
missing_translations: "Até que possamos traduzir tudo para o Português Brasileiro, você lerá em Inglês quando a versão em Português Brasileiro ainda não estiver disponível." missing_translations: "Até que possamos traduzir todo o conteúdo para o Português Brasileiro, você lerá o texto em Inglês quando a versão em Português Brasileiro não estiver disponível."
learn_more: "Saiba mais sobre ser um Diplomata" learn_more: "Saiba mais sobre ser um Diplomata"
subscribe_as_diplomat: "Assinar como um Diplomata" subscribe_as_diplomat: "Assinar como um Diplomata"
@ -57,7 +57,7 @@ module.exports = nativeDescription: "Português do Brasil", englishDescription:
confirm: "Confirmar" confirm: "Confirmar"
owned: "Possui" # For items you own owned: "Possui" # For items you own
locked: "Bloqueado" locked: "Bloqueado"
purchasable: "Comprável" # For a hero you unlocked but haven't purchased purchasable: "Comprável" # For a hero you unlocked but have not purchased
available: "Disponível" available: "Disponível"
skills_granted: "Habilidades Concedidas" # Property documentation details skills_granted: "Habilidades Concedidas" # Property documentation details
heroes: "Heróis" # Tooltip on hero shop button from /play heroes: "Heróis" # Tooltip on hero shop button from /play
@ -68,7 +68,7 @@ module.exports = nativeDescription: "Português do Brasil", englishDescription:
next: "Próximo" # Go from choose hero to choose inventory before playing a level next: "Próximo" # Go from choose hero to choose inventory before playing a level
change_hero: "Alterar Herói" # Go back from choose inventory to choose hero change_hero: "Alterar Herói" # Go back from choose inventory to choose hero
choose_inventory: "Equipar Itens" choose_inventory: "Equipar Itens"
buy_gems: "Comprar Gems" buy_gems: "Comprar Gemas"
subscription_required: "Requer assinatura" subscription_required: "Requer assinatura"
anonymous: "Jogador Anônimo" anonymous: "Jogador Anônimo"
level_difficulty: "Dificuldade: " level_difficulty: "Dificuldade: "
@ -83,9 +83,9 @@ module.exports = nativeDescription: "Português do Brasil", englishDescription:
# campaign_old_multiplayer_description: "Relics of a more civilized age. No simulations are run for these older, hero-less multiplayer arenas." # campaign_old_multiplayer_description: "Relics of a more civilized age. No simulations are run for these older, hero-less multiplayer arenas."
share_progress_modal: share_progress_modal:
blurb: "Você está fazendo um grande progresso! Diga a alguém o quão você aprendeu com o CodeCombat." # {change} blurb: "Você está fazendo bastante progresso! Compartilhe com alguém o quanto você já aprendeu com o CodeCombat!" # {change}
email_invalid: "Endereço de email inválido." email_invalid: "Endereço de email inválido."
form_blurb: "Informe o e-mail deles abaixo e mostraremos a eles!" form_blurb: "Informe os e-mails abaixo e mostraremos a eles!"
form_label: "Endereço de Email" form_label: "Endereço de Email"
placeholder: "endereço de email" placeholder: "endereço de email"
title: "Excelente Trabalho, Aprendiz" title: "Excelente Trabalho, Aprendiz"

View file

@ -4,8 +4,8 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription:
no_ie: "O CodeCombat não funciona no Internet Explorer 8 ou anterior. Desculpa!" # Warning that only shows up in IE8 and older no_ie: "O CodeCombat não funciona no Internet Explorer 8 ou anterior. Desculpa!" # Warning that only shows up in IE8 and older
no_mobile: "O CodeCombat não foi feito para dispositivos móveis e pode não funcionar!" # Warning that shows up on mobile devices no_mobile: "O CodeCombat não foi feito para dispositivos móveis e pode não funcionar!" # Warning that shows up on mobile devices
play: "Jogar" # The big play button that opens up the campaign view. play: "Jogar" # The big play button that opens up the campaign view.
old_browser: "Ups, o teu navegador é demasiado antigo para que o CodeCombat funcione. Desculpa!" # Warning that shows up on really old Firefox/Chrome/Safari old_browser: "Uh oh, o teu navegador é demasiado antigo para que o CodeCombat funcione. Desculpa!" # Warning that shows up on really old Firefox/Chrome/Safari
old_browser_suffix: "Mesmo assim podes tentar, mas provavelmente não irá funcionar." old_browser_suffix: "Podes tentar na mesma, mas provavelmente não vai funcionar."
ipad_browser: "Más notícias: o CodeCombat não funciona no navegador do iPad. Boas notícias: a nossa aplicação nativa para iPad está à espera da aprovação da Apple." ipad_browser: "Más notícias: o CodeCombat não funciona no navegador do iPad. Boas notícias: a nossa aplicação nativa para iPad está à espera da aprovação da Apple."
campaign: "Campanha" campaign: "Campanha"
for_beginners: "Para Iniciantes" for_beginners: "Para Iniciantes"
@ -43,7 +43,7 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription:
title: "Ajuda a traduzir o CodeCombat!" # This shows up when a player switches to a non-English language using the language selector. title: "Ajuda a traduzir o CodeCombat!" # This shows up when a player switches to a non-English language using the language selector.
sub_heading: "Precisamos das tuas habilidades linguísticas." sub_heading: "Precisamos das tuas habilidades linguísticas."
pitch_body: "Desenvolvemos o CodeCombat em Inglês, mas já temos jogadores de todo o mundo. Muitos deles querem jogar em Português, mas não falam Inglês, por isso, se sabes falar ambas, por favor considera registar-te como Diplomata para ajudares a traduzir o sítio do CodeCombat e todos os níveis para Português." pitch_body: "Desenvolvemos o CodeCombat em Inglês, mas já temos jogadores de todo o mundo. Muitos deles querem jogar em Português, mas não falam Inglês, por isso, se sabes falar ambas, por favor considera registar-te como Diplomata para ajudares a traduzir o sítio do CodeCombat e todos os níveis para Português."
missing_translations: "Enquanto não conseguirmos traduzir tudo para Português, irás ver em Inglês o que não estiver disponível em Português." missing_translations: "Até conseguirmos traduzir tudo para Português, irás ver em Inglês o que não estiver disponível em Português."
learn_more: "Sabe mais sobre seres um Diplomata" learn_more: "Sabe mais sobre seres um Diplomata"
subscribe_as_diplomat: "Subscreve-te como Diplomata" subscribe_as_diplomat: "Subscreve-te como Diplomata"
@ -73,19 +73,19 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription:
anonymous: "Jogador Anónimo" anonymous: "Jogador Anónimo"
level_difficulty: "Dificuldade: " level_difficulty: "Dificuldade: "
campaign_beginner: "Campanha para Iniciantes" campaign_beginner: "Campanha para Iniciantes"
awaiting_levels_adventurer_prefix: "Nós lançamos novos níveis todas as semanas." awaiting_levels_adventurer_prefix: "Lançamos novos níveis todas as semanas."
awaiting_levels_adventurer: "Regista-te como Aventureiro" awaiting_levels_adventurer: "Regista-te como Aventureiro"
awaiting_levels_adventurer_suffix: "para seres o primeiro a jogar níveis novos." awaiting_levels_adventurer_suffix: "para seres o primeiro a jogar níveis novos."
adjust_volume: "Ajustar volume" adjust_volume: "Ajustar volume"
campaign_multiplayer: "Arenas Multijogador" campaign_multiplayer: "Arenas Multijogador"
campaign_multiplayer_description: "... onde programas frente-a-frente contra outros jogadores." campaign_multiplayer_description: "... nas quais programas contra outros jogadores."
campaign_old_multiplayer: "(Obsoletas) Arenas Multijogador Antigas" campaign_old_multiplayer: "(Obsoletas) Arenas Multijogador Antigas"
campaign_old_multiplayer_description: "Relíquias de uma idade mais civilizada. Não há simulações em curso para estas arenas multijogador, mais antigas e sem heróis." campaign_old_multiplayer_description: "Relíquias de uma era mais civilizada. Não há simulações em curso para estas arenas multijogador, mais antigas e sem heróis."
share_progress_modal: share_progress_modal:
blurb: "Estás a fazer grandes progressos! Conta ao teu educador o quanto aprendeste com o CodeCombat." blurb: "Estás a fazer grandes progressos! Conta ao teu educador o quanto aprendeste com o CodeCombat."
email_invalid: "Endereço de e-mail inválido." email_invalid: "Endereço de e-mail inválido."
form_blurb: "Introduz o e-mail dele abaixo e nós vamos mostrar-lhe!" form_blurb: "Introduz o e-mail do teu educador abaixo e nós vamos mostrar-lhe!"
form_label: "Endereço de E-mail" form_label: "Endereço de E-mail"
placeholder: "endereço de e-mail" placeholder: "endereço de e-mail"
title: "Excelente Trabalho, Aprendiz" title: "Excelente Trabalho, Aprendiz"
@ -98,7 +98,7 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription:
forgot_password: "Esqueceste a tua palavra-passe?" forgot_password: "Esqueceste a tua palavra-passe?"
authenticate_gplus: "Autenticar G+" authenticate_gplus: "Autenticar G+"
load_profile: "Carregar Perfil do G+" load_profile: "Carregar Perfil do G+"
finishing: "A terminar" finishing: "A Terminar"
sign_in_with_facebook: "Iniciar sessão com o FB" sign_in_with_facebook: "Iniciar sessão com o FB"
sign_in_with_gplus: "Iniciar sessão com o G+" sign_in_with_gplus: "Iniciar sessão com o G+"
signup_switch: "Queres criar uma conta?" signup_switch: "Queres criar uma conta?"
@ -108,8 +108,8 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription:
creating: "A Criar Conta..." creating: "A Criar Conta..."
sign_up: "Registar" sign_up: "Registar"
log_in: "iniciar sessão com palavra-passe" log_in: "iniciar sessão com palavra-passe"
social_signup: "Ou podes registar-te através do Facebook ou do Google+:" social_signup: "Ou podes registar-te através do FB ou do Google+:"
required: "Precisas de iniciar sessão antes de prosseguir dessa forma." required: "Precisas de iniciar sessão antes de prosseguires."
login_switch: "Já tens uma conta?" login_switch: "Já tens uma conta?"
recover: recover:
@ -128,9 +128,9 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription:
common: common:
back: "Voltar" # When used as an action verb, like "Navigate backward" back: "Voltar" # When used as an action verb, like "Navigate backward"
continue: "Continuar" # When used as an action verb, like "Continue forward" continue: "Continuar" # When used as an action verb, like "Continue forward"
loading: "A carregar..." loading: "A Carregar..."
saving: "A guardar..." saving: "A Guardar..."
sending: "A enviar..." sending: "A Enviar..."
send: "Enviar" send: "Enviar"
cancel: "Cancelar" cancel: "Cancelar"
save: "Guardar" save: "Guardar"
@ -145,7 +145,7 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription:
help: "Ajuda" help: "Ajuda"
watch: "Vigiar" watch: "Vigiar"
unwatch: "Desvigiar" unwatch: "Desvigiar"
submit_patch: "Submeter Versão" submit_patch: "Submeter Atualização"
submit_changes: "Submeter Alterações" submit_changes: "Submeter Alterações"
save_changes: "Guardar Alterações" save_changes: "Guardar Alterações"
@ -223,13 +223,13 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription:
restart: "Reiniciar" restart: "Reiniciar"
goals: "Objetivos" goals: "Objetivos"
goal: "Objetivo" goal: "Objetivo"
running: "A executar..." running: "A Executar..."
success: "Sucesso!" success: "Sucesso!"
incomplete: "Incompletos" incomplete: "Incompletos"
timed_out: "Ficaste sem tempo" timed_out: "Ficaste sem tempo"
failing: "A falhar" failing: "A falhar"
action_timeline: "Linha do Tempo de Ações" action_timeline: "Linha do Tempo de Ações"
click_to_select: "Clica numa unidade para selecioná-la." click_to_select: "Clica numa unidade para a selecionares."
control_bar_multiplayer: "Multijogador" control_bar_multiplayer: "Multijogador"
control_bar_join_game: "Entrar no Jogo" control_bar_join_game: "Entrar no Jogo"
reload: "Recarregar" reload: "Recarregar"
@ -241,10 +241,10 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription:
victory_title_suffix: " Concluído" victory_title_suffix: " Concluído"
victory_sign_up: "Criar Conta para Guardar Progresso" victory_sign_up: "Criar Conta para Guardar Progresso"
victory_sign_up_poke: "Queres guardar o teu código? Cria uma conta grátis!" victory_sign_up_poke: "Queres guardar o teu código? Cria uma conta grátis!"
victory_rate_the_level: "Classifica este nível: " # Only in old-style levels. victory_rate_the_level: "Classifica o nível: " # Only in old-style levels.
victory_return_to_ladder: "Voltar à Classificação" victory_return_to_ladder: "Voltar à Classificação"
victory_play_continue: "Continuar" victory_play_continue: "Continuar"
victory_saving_progress: "A Guardar o Progresso" victory_saving_progress: "A Guardar Progresso"
victory_go_home: "Ir para o Início" victory_go_home: "Ir para o Início"
victory_review: "Conta-nos mais!" victory_review: "Conta-nos mais!"
victory_review_placeholder: "Como foi o nível?" victory_review_placeholder: "Como foi o nível?"
@ -258,7 +258,7 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription:
# victory_bloc: "Great work! Your skills are improving, and someone's taking notice. If you've considered becoming a software developer, this may be your lucky day. Bloc is an online bootcamp that pairs you 1-on-1 with an expert mentor who will help train you into a professional developer! By beating A Mayhem of Munchkins, you're now eligible for a $500 price reduction with the code: CCRULES" # victory_bloc: "Great work! Your skills are improving, and someone's taking notice. If you've considered becoming a software developer, this may be your lucky day. Bloc is an online bootcamp that pairs you 1-on-1 with an expert mentor who will help train you into a professional developer! By beating A Mayhem of Munchkins, you're now eligible for a $500 price reduction with the code: CCRULES"
# victory_bloc_cta: "Meet your mentor learn about Bloc" # victory_bloc_cta: "Meet your mentor learn about Bloc"
guide_title: "Guia" guide_title: "Guia"
tome_minion_spells: "Feitiços dos Seus Minions" # Only in old-style levels. tome_minion_spells: "Feitiços dos Teus Minions" # Only in old-style levels.
tome_read_only_spells: "Feitiços Apenas de Leitura" # Only in old-style levels. tome_read_only_spells: "Feitiços Apenas de Leitura" # Only in old-style levels.
tome_other_units: "Outras Unidades" # Only in old-style levels. tome_other_units: "Outras Unidades" # Only in old-style levels.
tome_cast_button_run: "Executar" tome_cast_button_run: "Executar"
@ -266,9 +266,9 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription:
tome_cast_button_ran: "Executado" tome_cast_button_ran: "Executado"
tome_submit_button: "Submeter" tome_submit_button: "Submeter"
tome_reload_method: "Recarregar o código original para este método" # Title text for individual method reload button. tome_reload_method: "Recarregar o código original para este método" # Title text for individual method reload button.
tome_select_method: "Selecionar um método" tome_select_method: "Selecionar um Método"
tome_see_all_methods: "Ver todos os métodos editáveis" # Title text for method list selector (shown when there are multiple programmable methods). tome_see_all_methods: "Ver todos os métodos editáveis" # Title text for method list selector (shown when there are multiple programmable methods).
tome_select_a_thang: "Seleciona Alguém para " tome_select_a_thang: "Selecionar Alguém para "
tome_available_spells: "Feitiços Disponíveis" tome_available_spells: "Feitiços Disponíveis"
tome_your_skills: "As Tuas Habilidades" tome_your_skills: "As Tuas Habilidades"
tome_help: "Ajuda" tome_help: "Ajuda"
@ -276,7 +276,7 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription:
hud_continue_short: "Continuar" hud_continue_short: "Continuar"
code_saved: "Código Guardado" code_saved: "Código Guardado"
skip_tutorial: "Saltar (esc)" skip_tutorial: "Saltar (esc)"
keyboard_shortcuts: "Atalhos do Teclado" keyboard_shortcuts: "Atalhos de Teclado"
loading_ready: "Pronto!" loading_ready: "Pronto!"
loading_start: "Iniciar Nível" loading_start: "Iniciar Nível"
problem_alert_title: "Corrige o Teu Código" problem_alert_title: "Corrige o Teu Código"
@ -304,22 +304,22 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription:
tip_debugging_program: "Se depurar é o processo de remover erros, então programar deve ser o processo de os introduzir. - Edsger W. Dijkstra" tip_debugging_program: "Se depurar é o processo de remover erros, então programar deve ser o processo de os introduzir. - Edsger W. Dijkstra"
tip_forums: "Vai aos fóruns e diz-nos o que pensas!" tip_forums: "Vai aos fóruns e diz-nos o que pensas!"
tip_baby_coders: "No futuro, até os bebés serão Arcomagos." tip_baby_coders: "No futuro, até os bebés serão Arcomagos."
tip_morale_improves: "O carregamento irá continuar até que a moral melhore." tip_morale_improves: "O carregamento vai continuar até que a moral melhore."
tip_all_species: "Acreditamos em oportunidades iguais para todas as espécies, em relação a aprenderem a programar." tip_all_species: "Acreditamos em oportunidades iguais para todas as espécies, em relação a aprenderem a programar."
tip_reticulating: "A reticular espinhas." tip_reticulating: "A reticular espinhas."
tip_harry: "És um Feiticeiro, " tip_harry: "És um Feiticeiro, "
tip_great_responsibility: "Com uma grande habilidade de programação vem uma grande responsabilidade de depuração." tip_great_responsibility: "Com uma grande habilidade de programação vem uma grande responsabilidade de depuração."
tip_munchkin: "Se não comeres os teus vegetais, virá um ogre atrás de ti enquanto estiveres a dormir." tip_munchkin: "Se não comeres os teus vegetais, um ogre virá atrás de ti enquanto estiveres a dormir."
tip_binary: "Há apenas 10 tipos de pessoas no mundo: aquelas que percebem binário e aquelas que não." tip_binary: "Há apenas 10 tipos de pessoas no mundo: aquelas que percebem binário e aquelas que não."
tip_commitment_yoda: "Um programador deve ter o compromisso mais profundo, a mente mais séria. ~ Yoda" tip_commitment_yoda: "Um programador deve ter o compromisso mais profundo, a mente mais séria. ~ Yoda"
tip_no_try: "Fazer. Ou não fazer. Não há nenhum tentar. - Yoda" tip_no_try: "Fazer. Ou não fazer. Não há nenhum tentar. - Yoda"
tip_patience: "Paciência tu deves ter, jovem Padawan. - Yoda" tip_patience: "Paciência tu deves ter, jovem Padawan. - Yoda"
tip_documented_bug: "Um erro documentado não é um erro; é uma funcionalidade." tip_documented_bug: "Um erro documentado não é um erro; é uma funcionalidade."
tip_impossible: "Tudo parece sempre impossível até ser feito. - Nelson Mandela" tip_impossible: "Parece sempre impossível até ser feito. - Nelson Mandela"
tip_talk_is_cheap: "Falar é fácil. Mostra-me o código. - Linus Torvalds" tip_talk_is_cheap: "Falar é fácil. Mostra-me o código. - Linus Torvalds"
tip_first_language: "A coisa mais desastrosa que podes aprender é a tua primeira linguagem de programação. - Alan Kay" tip_first_language: "A coisa mais desastrosa que podes aprender é a tua primeira linguagem de programação. - Alan Kay"
tip_hardware_problem: "P: Quantos programadores são necessários para mudar uma lâmpada? R: Nenhum, é um problema de hardware." tip_hardware_problem: "P: Quantos programadores são necessários para mudar uma lâmpada? R: Nenhum, é um problema de 'hardware'."
tip_hofstadters_law: "Lei de Hofstadter: Tudo demora sempre mais do que pensas, mesmo quando levas em conta a Lei de Hofstadter." tip_hofstadters_law: "Lei de Hofstadter: Tudo demora sempre mais do que pensas, mesmo quando tens em conta a Lei de Hofstadter."
tip_premature_optimization: "Uma otimização permatura é a raíz de todo o mal. - Donald Knuth" tip_premature_optimization: "Uma otimização permatura é a raíz de todo o mal. - Donald Knuth"
tip_brute_force: "Quando em dúvida, usa a força bruta. - Ken Thompson" tip_brute_force: "Quando em dúvida, usa a força bruta. - Ken Thompson"
tip_extrapolation: "Há apenas dois tipos de pessoas: aquelas que conseguem tirar uma conclusão a partir de dados reduzidos..." tip_extrapolation: "Há apenas dois tipos de pessoas: aquelas que conseguem tirar uma conclusão a partir de dados reduzidos..."
@ -390,13 +390,13 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription:
few_gems: "Algumas gemas" few_gems: "Algumas gemas"
pile_gems: "Pilha de gemas" pile_gems: "Pilha de gemas"
chest_gems: "Arca de gemas" chest_gems: "Arca de gemas"
purchasing: "A adquirir..." purchasing: "A Adquirir..."
declined: "O teu cartão foi recusado." declined: "O teu cartão foi recusado."
retrying: "Erro do servidor, a tentar novamente." retrying: "Erro do servidor, a tentar novamente."
prompt_title: "Sem Gemas Suficientes" prompt_title: "Sem Gemas Suficientes"
prompt_body: "Queres obter mais?" prompt_body: "Queres obter mais?"
prompt_button: "Entra na Loja" prompt_button: "Entra na Loja"
recovered: "Recuperada a compra de gemas anterior. Por favor atualiza a página." recovered: "A compra de gemas anterior foi recuperada. Por favor atualiza a página."
price: "x3500 / mês" price: "x3500 / mês"
subscribe: subscribe:

View file

@ -73,7 +73,7 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese
anonymous: "匿名玩家" anonymous: "匿名玩家"
level_difficulty: "难度:" level_difficulty: "难度:"
campaign_beginner: "新手作战" campaign_beginner: "新手作战"
awaiting_levels_adventurer_prefix: "我们每周开放五个关卡" # {change} awaiting_levels_adventurer_prefix: "我们每周都会开放新关卡"
awaiting_levels_adventurer: "注册成为冒险家" awaiting_levels_adventurer: "注册成为冒险家"
awaiting_levels_adventurer_suffix: "来优先尝试新关卡" awaiting_levels_adventurer_suffix: "来优先尝试新关卡"
adjust_volume: "音量调节" adjust_volume: "音量调节"
@ -83,7 +83,7 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese
campaign_old_multiplayer_description: "多个文明时代的遗迹。但已没有模拟运行这些陈旧、英雄芜绝的多人竞技场。" campaign_old_multiplayer_description: "多个文明时代的遗迹。但已没有模拟运行这些陈旧、英雄芜绝的多人竞技场。"
share_progress_modal: share_progress_modal:
blurb: "您的进度真快!快告诉其他人您从CodeCombat学到了什么" # {change} blurb: "您的进度真快!快告诉您的家长, 您从CodeCombat学到了什么"
email_invalid: "邮件地址不可用。" email_invalid: "邮件地址不可用。"
form_blurb: "输入他们的邮件地址让他们知道CodeCombat的有趣" form_blurb: "输入他们的邮件地址让他们知道CodeCombat的有趣"
form_label: "您的邮件地址" form_label: "您的邮件地址"
@ -255,8 +255,8 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese
victory_new_item: "新的物品" victory_new_item: "新的物品"
victory_viking_code_school: "这关真的超难! 如果您想成为一个软件开发人员您就应该去试一下Viking Code School。在这里您可以把您的知识增长到另一个台阶。只需要14周您就能成为一个专业的网页开发人员。" victory_viking_code_school: "这关真的超难! 如果您想成为一个软件开发人员您就应该去试一下Viking Code School。在这里您可以把您的知识增长到另一个台阶。只需要14周您就能成为一个专业的网页开发人员。"
victory_become_a_viking: "成为一个维京人吧" victory_become_a_viking: "成为一个维京人吧"
# victory_bloc: "Great work! Your skills are improving, and someone's taking notice. If you've considered becoming a software developer, this may be your lucky day. Bloc is an online bootcamp that pairs you 1-on-1 with an expert mentor who will help train you into a professional developer! By beating A Mayhem of Munchkins, you're now eligible for a $500 price reduction with the code: CCRULES" victory_bloc: "干得好! 你的技能正在提升, 并且已经有人注意着你了. 如果你正考虑着当一名软件工程师, 现在可能就是你的幸运日. Bloc 是个能提供在线一对一专业指导的, 能锻炼你成为专业开发者的训练营! 通过 矮人骚乱 后, 你可以使用价值 $500 的优惠码: CCRULES"
# victory_bloc_cta: "Meet your mentor learn about Bloc" victory_bloc_cta: "会晤你的导师 了解 Bloc"
guide_title: "指南" guide_title: "指南"
tome_minion_spells: "助手的咒语" # Only in old-style levels. tome_minion_spells: "助手的咒语" # Only in old-style levels.
tome_read_only_spells: "只读的咒语" # Only in old-style levels. tome_read_only_spells: "只读的咒语" # Only in old-style levels.
@ -293,7 +293,7 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese
infinite_loop_reset_level: "重置关卡" infinite_loop_reset_level: "重置关卡"
infinite_loop_comment_out: "为我的代码添加注释" infinite_loop_comment_out: "为我的代码添加注释"
tip_toggle_play: "用 Ctrl+P 来暂停或继续" tip_toggle_play: "用 Ctrl+P 来暂停或继续"
tip_scrub_shortcut: "用 Ctrl+[ 和 Ctrl+] 来倒退和快进。" # {change} tip_scrub_shortcut: "用 Ctrl+[ 和 Ctrl+] 来倒退和快进。"
tip_guide_exists: "点击页面上方的指南, 可以获得更多有用信息。" tip_guide_exists: "点击页面上方的指南, 可以获得更多有用信息。"
tip_open_source: "「CodeCombat」是100%开源的!" tip_open_source: "「CodeCombat」是100%开源的!"
tip_tell_friends: "喜欢Codecombat那就赶快把它安利给朋友" tip_tell_friends: "喜欢Codecombat那就赶快把它安利给朋友"
@ -361,7 +361,7 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese
leaderboard: leaderboard:
leaderboard: "排行榜" leaderboard: "排行榜"
view_other_solutions: "查看其它解答" # {change} view_other_solutions: "查看排行榜"
scores: "分数" scores: "分数"
top_players: "顶尖玩家是" top_players: "顶尖玩家是"
day: "今天" day: "今天"
@ -401,9 +401,9 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese
subscribe: subscribe:
comparison_blurb: "订阅CodeCombat大力的提升您的技能" comparison_blurb: "订阅CodeCombat大力的提升您的技能"
feature1: "100+ 基本关卡4个世界" # {change} feature1: "110+ 基本关卡4个世界"
feature2: "10 个强大 <strong>英雄</strong>以及各式非凡技能!" # {change} feature2: "10 个强大 <strong>英雄</strong>以及各式非凡技能!"
feature3: "70+ 奖励关卡" # {change} feature3: "70+ 奖励关卡"
feature4: "每月享有3500额外宝石" feature4: "每月享有3500额外宝石"
feature5: "视频教学" feature5: "视频教学"
feature6: "专业邮件支援" feature6: "专业邮件支援"
@ -428,33 +428,33 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese
parent_email_sent: "邮件已发送!" parent_email_sent: "邮件已发送!"
parent_email_title: "什么是您父母的邮件地址?" parent_email_title: "什么是您父母的邮件地址?"
parents: "致家长" parents: "致家长"
parents_title: "您的孩子将要学习编写程序。" # {change} parents_title: "亲爱的家长: 您的孩子将要学习编程. 您会支持孩子发展吗?"
parents_blurb1: "通过使用CodeCombat您的孩子将学习编写真正的程序代码。他们将学到简单指令进而处理更复杂的问题" parents_blurb1: "您的孩子已经玩了 __nLevels__ 关,并且学习了编程基础。请您为了培养他们的兴趣而给他们订阅,他们能继续玩下去"
parents_blurb1a: "不用怀疑计算机编程能力将是您的孩子作为一个成年人的基本技能。到2020年77%的工作将会需要编码能力,并且软件工程师将在世界各地成为高需求职业。您知道吗,计算机科学是收入最高的大学学位?" parents_blurb1a: "不用怀疑计算机编程能力将是您的孩子作为一个成年人的基本技能。到2020年77%的工作将会需要编码能力,并且软件工程师将在世界各地成为高需求职业。您要知道计算机科学是收入最高的大学学位。"
parents_blurb2: "每月支付9.99美元,他们每周都会有新的挑战,并且通过电子邮件获得专业程序员的指导。" # {change} parents_blurb2: "每月支付9.99美元,他们每周都会有新的挑战,并且通过电子邮件获得专业程序员的指导。"
parents_blurb3: "无风险承诺100%退款,一键取消订阅。" parents_blurb3: "无风险承诺100%退款,一键取消订阅。"
payment_methods: "付费方式" payment_methods: "付费方式"
payment_methods_title: "可接受的付款方式" payment_methods_title: "可接受的付款方式"
payment_methods_blurb1: "我们现有的付费方式有信用卡和支付宝" payment_methods_blurb1: "我们现有的付费方式有信用卡和支付宝"
payment_methods_blurb2: "如果您想用其他付费方式,请联系我们" payment_methods_blurb2: "如果您想用其他付费方式,请联系我们"
# sale_already_subscribed: "You're already subscribed!" sale_already_subscribed: "您已经订阅!"
# sale_blurb1: "Save 35%" sale_blurb1: "和正常一年的订阅价格 $120 相比"
# sale_blurb2: "off regular subscription price of $120 for a whole year!" # {changed} sale_blurb2: "年费订阅能节省 35%"
# sale_button: "Sale!" sale_button: "促销"
# sale_button_title: "Save 35% when you purchase a 1 year subscription" sale_button_title: "年费订阅能节省 35% 的费用"
# sale_click_here: "Click Here" sale_click_here: "点击这里"
# sale_ends: "Ends" sale_ends: "结束"
# sale_extended: "*Existing subscriptions will be extended by 1 year." sale_extended: "*已订阅用户会续期一年。"
# sale_feature_here: "Here's what you'll get:" sale_feature_here: "你将会获得这些:"
# sale_feature2: "Access to 9 powerful <strong>new heroes</strong> with unique skills!" sale_feature2: "可以使用 10 个强大的 <strong>新英雄</strong> 和各种技能!"
# sale_feature4: "<strong>42,000 bonus gems</strong> awarded immediately!" sale_feature4: "立即取得 <strong>42,000 个额外的宝石</strong>!"
# sale_continue: "Ready to continue adventuring?" sale_continue: "准备好继续探险吗?"
# sale_limited_time: "Limited time offer!" sale_limited_time: "限时优惠!"
# sale_new_heroes: "New heroes!" sale_new_heroes: "新英雄!"
# sale_title: "Back to School Sale" sale_title: "开学促销"
# sale_view_button: "Buy 1 year subscription for" sale_view_button: "购买年费订阅:"
stripe_description: "每月订阅" stripe_description: "每月订阅"
# stripe_description_year_sale: "1 Year Subscription (35% discount)" stripe_description_year_sale: "年费订阅 (优惠 35%)"
subscription_required_to_play: "订阅后才可开始本关" subscription_required_to_play: "订阅后才可开始本关"
unlock_help_videos: "订阅后才可以解锁视频教学哦!" unlock_help_videos: "订阅后才可以解锁视频教学哦!"
personal_sub: "个人订阅" # Accounts Subscription View below personal_sub: "个人订阅" # Accounts Subscription View below
@ -462,7 +462,7 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese
managed_by: "管理" managed_by: "管理"
will_be_cancelled: "将被取消" will_be_cancelled: "将被取消"
currently_free: "您目前有一个免费订阅" currently_free: "您目前有一个免费订阅"
currently_free_until: "您目前有一个免费订阅,直到" # {changed} currently_free_until: "您目前有一个订阅,直到"
was_free_until: "您有过一个免费订阅,直到" was_free_until: "您有过一个免费订阅,直到"
managed_subs: "管理订阅" managed_subs: "管理订阅"
managed_subs_desc: "为其他玩家(学生、儿童等)添加订阅。" managed_subs_desc: "为其他玩家(学生、儿童等)添加订阅。"
@ -526,6 +526,8 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese
skill_docs: skill_docs:
writable: "可写" # Hover over "attack" in Your Skills while playing a level to see most of this writable: "可写" # Hover over "attack" in Your Skills while playing a level to see most of this
read_only: "只读" read_only: "只读"
action: "命令"
spell: "技能"
action_name: "名称" action_name: "名称"
action_cooldown: "释放时间" action_cooldown: "释放时间"
action_specific_cooldown: "冷却时间" action_specific_cooldown: "冷却时间"
@ -582,15 +584,15 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese
press_paragraph_1_link: "成套宣传包" press_paragraph_1_link: "成套宣传包"
press_paragraph_1_suffix: "里的所有材料。 所有商标和图像的使用都不必事先联系我们。" press_paragraph_1_suffix: "里的所有材料。 所有商标和图像的使用都不必事先联系我们。"
team: "团队" team: "团队"
george_title: "CEO" # {change} george_title: "共同创始人"
george_blurb: "商人" george_blurb: "商人"
scott_title: "程序员" # {change} scott_title: "共同创始人"
scott_blurb: "理性至上" scott_blurb: "理性至上"
nick_title: "程序员" # {change} nick_title: "共同创始人"
nick_blurb: "充满动力的大牛" nick_blurb: "充满动力的大牛"
michael_title: "程序员" michael_title: "程序员"
michael_blurb: "系统管理员" michael_blurb: "系统管理员"
matt_title: "程序员" # {change} matt_title: "共同创始人"
matt_blurb: "自行车爱好者" matt_blurb: "自行车爱好者"
cat_title: "首席关卡设计师" cat_title: "首席关卡设计师"
cat_blurb: "气宗" cat_blurb: "气宗"
@ -606,19 +608,19 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese
intro_1: "CodeCombat 是一个教编程的网上游戏。学生会用编程语言写代码。" intro_1: "CodeCombat 是一个教编程的网上游戏。学生会用编程语言写代码。"
intro_2: "无需经验!" intro_2: "无需经验!"
free_title: "要多少钱?" free_title: "要多少钱?"
cost_china: "CodeCombat的前5个关卡在中国是免费的在这之后需花费每月9.99美元来访问我们架设在中国专属服务器上的140多个关卡。" cost_china: "CodeCombat的前5个关卡在中国是免费的在这之后需花费每月9.99美元来访问我们架设在中国专属服务器上的180多个关卡。"
free_1: "有100多个覆盖了所有理论的免费关卡。" # {change} free_1: "有110多个覆盖了所有理论的免费关卡。"
free_2: "包月订阅可以访问视频教程和额外的练习关卡。" free_2: "包月订阅可以访问视频教程和额外的练习关卡。"
teacher_subs_title: "教师可免费订阅!" teacher_subs_title: "教师可免费订阅!"
teacher_subs_1: "联系" # {change} teacher_subs_1: "填写我们的"
teacher_subs_2: "教师调查" teacher_subs_2: "教师调查"
teacher_subs_3: "建立您的订阅。" teacher_subs_3: "建立您的订阅。"
sub_includes_title: "订阅里包含了什么内容?" sub_includes_title: "订阅里包含了什么内容?"
sub_includes_1: "除了100个基础关卡,学生包月订阅还可以使用这些附加功能:" # {change} sub_includes_1: "除了110+个基础关卡,学生包月订阅还可以使用这些附加功能:"
sub_includes_2: "超过70个练习关卡" # {change} sub_includes_2: "超过70个练习关卡"
sub_includes_3: "视频教学" sub_includes_3: "视频教学"
sub_includes_4: "优质的电子邮件支持" sub_includes_4: "优质的电子邮件支持"
sub_includes_5: "10个具有独特技能的新英雄" # {change} sub_includes_5: "10个具有独特技能的新英雄"
sub_includes_6: "每月享有3500额外宝石" sub_includes_6: "每月享有3500额外宝石"
sub_includes_7: "私人部落" sub_includes_7: "私人部落"
monitor_progress_title: "我要如何查看学生的进度?" monitor_progress_title: "我要如何查看学生的进度?"
@ -649,8 +651,8 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese
more_info_2: "教师论坛" more_info_2: "教师论坛"
more_info_3: "是个与其他使用CodeCombat的教育工作者联系的良好平台。" more_info_3: "是个与其他使用CodeCombat的教育工作者联系的良好平台。"
sys_requirements_title: "系统需求" sys_requirements_title: "系统需求"
sys_requirements_1: "因为CodeCombat是个游戏它对于电脑的要求很高来保证运行的顺畅。我们已经优化过以便让它能在每一个最新浏览器或是比较旧的电脑跑得顺畅所以每一个人都能享受CodeCombat带来的乐趣。如上为了使您顺利的完成Hour of Code的学习我们建议您" # {change} sys_requirements_1: "一个现代的浏览器. 需要更新的 Chrome, Firefox, Safari 或者 Internet Explorer 9 以上版本."
sys_requirements_2: "使用最新版本的Chrome或是Firefox." # {change} sys_requirements_2: "CodeCombat 暂时还不支持 iPad ."
teachers_survey: teachers_survey:
title: "教师调查" title: "教师调查"
@ -663,9 +665,9 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese
approved_3: "进一步的说明已被送往" approved_3: "进一步的说明已被送往"
denied_1: "您的免费订阅试用申请已被" denied_1: "您的免费订阅试用申请已被"
denied_2: "拒绝。" denied_2: "拒绝。"
contact_1: "请联系" contact_1: "如果你有进一步的问题, 请联系"
contact_2: "如果我们为教师提供免费订阅用评估的目的。您可以在我们的" contact_2: ""
# description_1: "We offer free subscriptions to teachers for evaluation purposes. You can find more information on our" description_1: "我们可为教师提供用于评估的免费订阅. 你可以在我们的"
description_2: "教师" description_2: "教师"
description_3: "页面找到更多的信息。" description_3: "页面找到更多的信息。"
description_4: "请填写此简单问卷,我们将会向您的电子邮件发送设置说明。" description_4: "请填写此简单问卷,我们将会向您的电子邮件发送设置说明。"
@ -673,12 +675,12 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese
school: "学校名字" school: "学校名字"
location: "城市" location: "城市"
age_students: "您的学生年龄为多少?" age_students: "您的学生年龄为多少?"
under: "几岁以下" under: "低于"
other: "其他:" other: "其他:"
amount_students: "请问您有多少学生需要教导?" amount_students: "请问您有多少学生需要教导?"
hear_about: "您是怎么知道CodeComabat的" hear_about: "您是怎么知道CodeComabat的"
fill_fields: "请填写所有问题。" fill_fields: "请填写所有问题。"
thanks: "非常感谢!我们会很快寄您设置说明。" thanks: "非常感谢!我们会很快寄您设置说明。"
versions: versions:
save_version_title: "保存新版本" save_version_title: "保存新版本"
@ -725,7 +727,7 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese
admin: "管理" admin: "管理"
new_password: "新密码" new_password: "新密码"
new_password_verify: "再次输入密码" new_password_verify: "再次输入密码"
type_in_email: "输入您的邮箱地址来确认删除" # {change} type_in_email: "输入您的邮箱地址来确认删除"
type_in_password: "同样的,输入您的密码。" type_in_password: "同样的,输入您的密码。"
email_subscriptions: "邮箱订阅" email_subscriptions: "邮箱订阅"
email_subscriptions_none: "取消订阅" email_subscriptions_none: "取消订阅"
@ -818,7 +820,7 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese
average_achievements: "平均成就" average_achievements: "平均成就"
delete_clan: "刪除部落" delete_clan: "刪除部落"
leave_clan: "离开部落" leave_clan: "离开部落"
join_clan: "部落" join_clan: "部落"
invite_1: "邀请:" invite_1: "邀请:"
invite_2: "*通过这个链接来邀请玩家加入部落。" invite_2: "*通过这个链接来邀请玩家加入部落。"
members: "成员" members: "成员"
@ -836,7 +838,7 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese
latest_achievement: "最新成就" latest_achievement: "最新成就"
playtime: "游戏时间" playtime: "游戏时间"
last_played: "最后玩了" last_played: "最后玩了"
# leagues_explanation: "Play in a league against other clan members in these multiplayer arena instances." leagues_explanation: "在部落里与其他成员组成联盟一起参加下面的多人竞技场."
classes: classes:
archmage_title: "大法师" archmage_title: "大法师"
@ -1026,7 +1028,7 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese
my_matches: "我的对手" my_matches: "我的对手"
simulate: "模拟" simulate: "模拟"
simulation_explanation: "通过模拟游戏,您可以让您的游戏更快的得到评分。" simulation_explanation: "通过模拟游戏,您可以让您的游戏更快的得到评分。"
# simulation_explanation_leagues: "You will mainly help simulate games for allied players in your clans and courses." simulation_explanation_leagues: "你会主要给在你的部落或者课程的同伴帮忙模拟游戏."
simulate_games: "模拟游戏!" simulate_games: "模拟游戏!"
simulate_all: "重置并模拟游戏!" simulate_all: "重置并模拟游戏!"
games_simulated_by: "由您模拟过的游戏数:" games_simulated_by: "由您模拟过的游戏数:"
@ -1036,8 +1038,8 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese
ratio: "比率" ratio: "比率"
leaderboard: "排行榜" leaderboard: "排行榜"
battle_as: "我要加入这一方 " battle_as: "我要加入这一方 "
summary_your: " " summary_your: " "
summary_matches: "对手 - " summary_matches: "的比赛 - "
summary_wins: " 胜利, " summary_wins: " 胜利, "
summary_losses: " 失败" summary_losses: " 失败"
rank_no_code: "没有新代码可供评分" rank_no_code: "没有新代码可供评分"
@ -1073,11 +1075,16 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese
tournament_rules: "锦标赛规则" tournament_rules: "锦标赛规则"
tournament_blurb: "写代码,收金币,建军队,碎敌军,赢奖品,以及在我们奖励多达$40,000的Greed tournament里升级您的事业 快去查阅详情!" tournament_blurb: "写代码,收金币,建军队,碎敌军,赢奖品,以及在我们奖励多达$40,000的Greed tournament里升级您的事业 快去查阅详情!"
tournament_blurb_criss_cross: "赢得竞拍,建造道路,智胜对手,夺取宝石,在纵横交错锦标赛中完成生涯晋级! 现在就查看详情!" tournament_blurb_criss_cross: "赢得竞拍,建造道路,智胜对手,夺取宝石,在纵横交错锦标赛中完成生涯晋级! 现在就查看详情!"
# tournament_blurb_zero_sum: "Unleash your coding creativity in both gold gathering and battle tactics in this alpine mirror match between red sorcerer and blue sorcerer. The tournament began on Friday, March 27 and will run until Monday, April 6 at 5PM PDT. Compete for fun and glory! Check out the details" tournament_blurb_zero_sum: "在红方法师和蓝方法师的山峰对决中, 同时在收集金币和战术上发挥你的代码创造力. 竞赛在3月27日开始, 在4月6日(星期一)的下午5点(PDT 太平洋时区)结束. 为乐趣和荣耀竞赛吧! 浏览了解更多"
tournament_blurb_blog: "关注我们的博客" tournament_blurb_blog: "关注我们的博客"
rules: "规则" rules: "规则"
winners: "胜利者" winners: "胜利者"
# league: "League" league: "联盟"
red_ai: "红方 AI" # "Red AI Wins", at end of multiplayer match playback
blue_ai: "蓝方 AI"
wins: "胜利" # At end of multiplayer match playback
humans: "红方" # Ladder page display team name
ogres: "蓝方"
user: user:
stats: "成就" stats: "成就"
@ -1118,7 +1125,7 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese
no_recent_games: "最近两个星期没有玩过游戏。" no_recent_games: "最近两个星期没有玩过游戏。"
payments: "支付方式" payments: "支付方式"
purchased: "已购买" purchased: "已购买"
# sale: "Sale" sale: "促销"
subscription: "订阅" subscription: "订阅"
invoices: "票据" invoices: "票据"
service_apple: "设备:苹果" service_apple: "设备:苹果"
@ -1221,15 +1228,15 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese
boolean_logic: "布尔逻辑" boolean_logic: "布尔逻辑"
break_statements: "Break语句" break_statements: "Break语句"
classes: "" classes: ""
# continue_statements: "Continue Statements" continue_statements: "Continue 语句"
for_loops: "For循环" for_loops: "For循环"
functions: "函数" functions: "函数"
# graphics: "Graphics" graphics: "图形"
if_statements: "If语句" if_statements: "If语句"
input_handling: "输入处理" input_handling: "输入处理"
math_operations: "数学运算" math_operations: "数学运算"
object_literals: "对象常量" object_literals: "对象常量"
# parameters: "Parameters" parameters: "参数"
strings: "字符串" strings: "字符串"
variables: "变量" variables: "变量"
vectors: "向量" vectors: "向量"
@ -1325,8 +1332,8 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese
prizes: "奖项" prizes: "奖项"
total_value: "总价值" total_value: "总价值"
in_cash: "现金" in_cash: "现金"
custom_wizard: "制CodeCombat巫师" custom_wizard: "制CodeCombat巫师"
custom_avatar: "制CodeCombat头像" custom_avatar: "制CodeCombat头像"
heap: "给六个月的\"Startup\"访问" heap: "给六个月的\"Startup\"访问"
credits: "信誉" credits: "信誉"
one_month_coupon: "优惠: 选择 Rails 或者 HTML" one_month_coupon: "优惠: 选择 Rails 或者 HTML"

View file

@ -73,7 +73,7 @@ module.exports = nativeDescription: "繁體中文", englishDescription: "Chinese
anonymous: "匿名玩家" anonymous: "匿名玩家"
level_difficulty: "難度" level_difficulty: "難度"
campaign_beginner: "新手指南" campaign_beginner: "新手指南"
awaiting_levels_adventurer_prefix: "我們每周將釋出五個關卡。" # {change} awaiting_levels_adventurer_prefix: "我們每周將釋出新的關卡。" # {change}
awaiting_levels_adventurer: "註冊成為冒險家" awaiting_levels_adventurer: "註冊成為冒險家"
awaiting_levels_adventurer_suffix: "成為第一個挑戰新關卡的冒險家吧!" awaiting_levels_adventurer_suffix: "成為第一個挑戰新關卡的冒險家吧!"
adjust_volume: "調整音量" adjust_volume: "調整音量"
@ -83,7 +83,7 @@ module.exports = nativeDescription: "繁體中文", englishDescription: "Chinese
campaign_old_multiplayer_description: "多個文明時代的遺跡。已沒有模擬運行這些陳舊、英雄蕪絕的多人競技場。" campaign_old_multiplayer_description: "多個文明時代的遺跡。已沒有模擬運行這些陳舊、英雄蕪絕的多人競技場。"
share_progress_modal: share_progress_modal:
blurb: "您正在建立優秀的進度! 告訴別人您已經從CodeCombat學習到多少東西." # {change} blurb: "您正在建立優秀的進度! 告訴您的家長, 您從CodeCombat學到了什麼" # {change}
email_invalid: "郵件地址無效." email_invalid: "郵件地址無效."
form_blurb: "在底下輸入他們的郵件並且我們將秀給他們!" form_blurb: "在底下輸入他們的郵件並且我們將秀給他們!"
form_label: "郵件地址" form_label: "郵件地址"
@ -255,8 +255,8 @@ module.exports = nativeDescription: "繁體中文", englishDescription: "Chinese
victory_new_item: "新的物品" victory_new_item: "新的物品"
victory_viking_code_school: "太厲害了, 您剛完成了非常困難的關卡! 如果您想成為一個軟件開發人員您就應該去試一下Viking Code School。在這裡您可以把您的知識增長到另一個台階。只需要14個星期您就能成為一個專業的網頁開發人員。" victory_viking_code_school: "太厲害了, 您剛完成了非常困難的關卡! 如果您想成為一個軟件開發人員您就應該去試一下Viking Code School。在這裡您可以把您的知識增長到另一個台階。只需要14個星期您就能成為一個專業的網頁開發人員。"
victory_become_a_viking: "成為一個維京人。" victory_become_a_viking: "成為一個維京人。"
# victory_bloc: "Great work! Your skills are improving, and someone's taking notice. If you've considered becoming a software developer, this may be your lucky day. Bloc is an online bootcamp that pairs you 1-on-1 with an expert mentor who will help train you into a professional developer! By beating A Mayhem of Munchkins, you're now eligible for a $500 price reduction with the code: CCRULES" victory_bloc: "做得好! 你的技能正在提升, 並且已經有人注意著你了. 如果你正考慮著當一名軟件工程師, 現在可能就是你的幸運日. Bloc 是個能提供在線一對一專業指導的,能鍛煉你成為專業開發者的訓練營! 通過矮人騷亂後, 你可以使用價值$500 的優惠碼: CCRULES"
# victory_bloc_cta: "Meet your mentor learn about Bloc" victory_bloc_cta: "會晤你的導師 了解 Bloc"
guide_title: "指南" guide_title: "指南"
tome_minion_spells: "助手的咒語" # Only in old-style levels. tome_minion_spells: "助手的咒語" # Only in old-style levels.
tome_read_only_spells: "唯讀的咒語" # Only in old-style levels. tome_read_only_spells: "唯讀的咒語" # Only in old-style levels.
@ -293,7 +293,7 @@ module.exports = nativeDescription: "繁體中文", englishDescription: "Chinese
infinite_loop_reset_level: "重置關卡" infinite_loop_reset_level: "重置關卡"
infinite_loop_comment_out: "在我的程式碼中加入注解" infinite_loop_comment_out: "在我的程式碼中加入注解"
tip_toggle_play: "使用 Ctrl+P 切換 播放/暫停." tip_toggle_play: "使用 Ctrl+P 切換 播放/暫停."
tip_scrub_shortcut: "Ctrl+[ 快退; Ctrl+] 快進." # {change} tip_scrub_shortcut: "Ctrl+[ 快退; Ctrl+] 快進."
tip_guide_exists: "點擊頁面上方的指南,可獲得更多有用的訊息." tip_guide_exists: "點擊頁面上方的指南,可獲得更多有用的訊息."
tip_open_source: "「CodeCombat」100% 開源!" tip_open_source: "「CodeCombat」100% 開源!"
tip_tell_friends: "喜歡Codecombat那就把它介紹給朋友!" tip_tell_friends: "喜歡Codecombat那就把它介紹給朋友!"
@ -339,7 +339,7 @@ module.exports = nativeDescription: "繁體中文", englishDescription: "Chinese
tip_open_source_contribute: "您可以幫助「CodeCombat」提高" tip_open_source_contribute: "您可以幫助「CodeCombat」提高"
tip_recurse: "迭代者人也,遞歸者神也 - L. Peter Deutsch" tip_recurse: "迭代者人也,遞歸者神也 - L. Peter Deutsch"
tip_free_your_mind: "放下一切私心雜念,丟棄害怕、疑問和拒信,解放您的思維。 - 莫菲斯《駭客任務》" tip_free_your_mind: "放下一切私心雜念,丟棄害怕、疑問和拒信,解放您的思維。 - 莫菲斯《駭客任務》"
tip_strong_opponents: "即使是最强大的對手也有弱点的. - 宇智波鼬《火影忍者》" tip_strong_opponents: "即使是最强大的對手也有弱点的 - 宇智波鼬《火影忍者》"
tip_paper_and_pen: "在您開始編碼之前,您可以隨時用一張紙和一支筆作計劃。" tip_paper_and_pen: "在您開始編碼之前,您可以隨時用一張紙和一支筆作計劃。"
game_menu: game_menu:
@ -361,7 +361,7 @@ module.exports = nativeDescription: "繁體中文", englishDescription: "Chinese
leaderboard: leaderboard:
leaderboard: "排行榜" leaderboard: "排行榜"
view_other_solutions: "查看其他解法" # {change} view_other_solutions: "查看其他解法"
scores: "分數" scores: "分數"
top_players: "頂級玩家由" top_players: "頂級玩家由"
day: "今天" day: "今天"
@ -401,9 +401,9 @@ module.exports = nativeDescription: "繁體中文", englishDescription: "Chinese
subscribe: subscribe:
comparison_blurb: "訂閱 CodeCombat 來磨練您的技巧!" comparison_blurb: "訂閱 CodeCombat 來磨練您的技巧!"
feature1: "100 個以上的基本關卡散佈在4張地圖中" # {change} feature1: "110 個以上的基本關卡散佈在4張地圖中"
feature2: "10 個強壯的<strong>新英雄</strong>並每隻都有不同技巧!" # {change} feature2: "10 個強壯的<strong>新英雄</strong>並每隻都有不同技巧!"
feature3: "70 個以上的額外關卡" # {change} feature3: "70 個以上的額外關卡"
feature4: "每個月<strong>3500顆額外寶石</strong>!" feature4: "每個月<strong>3500顆額外寶石</strong>!"
feature5: "視頻教學" feature5: "視頻教學"
feature6: "頂級信箱支援" feature6: "頂級信箱支援"
@ -428,33 +428,33 @@ module.exports = nativeDescription: "繁體中文", englishDescription: "Chinese
parent_email_sent: "已寄信!" parent_email_sent: "已寄信!"
parent_email_title: "您父母信箱是?" parent_email_title: "您父母信箱是?"
parents: "致家長" parents: "致家長"
parents_title: "您的孩子將學習編寫程式." # {change} parents_title: "親愛的家長: 您的孩子將要學習編程. 您會支持孩子發展嗎?"
parents_blurb1: "使用 CodeCombat , 您的孩子學習真正的編寫程式. 他們學習從簡單的指令,漸進到更加進階的課題." parents_blurb1: "您的孩子已經玩了 __nLevels__ 關,並且學習了編程基礎。請您為了培養他們的興趣而給他們訂閱,他們能繼續玩下去。"
parents_blurb1a: "不要懷疑計算機編程能力將是您的孩子作為一個成年人的基本技能。到2020年77%的工作將會需要編碼能力,並且軟件工程師將在世界各地成為高需求職業。您知道嗎,計算機科學是收入最高的大學學位。" parents_blurb1a: "不要懷疑計算機編程能力將是您的孩子作為一個成年人的基本技能。到2020年77%的工作將會需要編碼能力,並且軟件工程師將在世界各地成為高需求職業。您知道計算機科學是收入最高的大學學位。"
parents_blurb2: "每月支付 $9.99 美金, 他們每週獲得新挑戰以及使用信件取得專業程式員的幫助." # {change} parents_blurb2: "每月支付 $9.99 美金, 他們每週獲得新挑戰以及使用信件取得專業程式員的幫助."
parents_blurb3: "沒有風險: 保證 100% 退費, 一步取消訂閱." parents_blurb3: "沒有風險: 保證 100% 退費, 一步取消訂閱."
payment_methods: "付費方法" payment_methods: "付費方法"
payment_methods_title: "可接受的付款方式" payment_methods_title: "可接受的付款方式"
payment_methods_blurb1: "我們現有的付費方式有信用卡和支付寶" payment_methods_blurb1: "我們現有的付費方式有信用卡和支付寶"
payment_methods_blurb2: "如果您想用其他付費方式,請聯繫我們" payment_methods_blurb2: "如果您想用其他付費方式,請聯繫我們"
# sale_already_subscribed: "You're already subscribed!" sale_already_subscribed: "您已經訂閱!"
# sale_blurb1: "Save 35%" sale_blurb1: "和正常一年的訂閱價格 $120 相比"
# sale_blurb2: "off regular subscription price of $120 for a whole year!" # {changed} sale_blurb2: "年費訂閱能節省 35%!"
# sale_button: "Sale!" sale_button: "促銷!"
# sale_button_title: "Save 35% when you purchase a 1 year subscription" sale_button_title: "年費訂閱能節省 35% 的費用"
# sale_click_here: "Click Here" sale_click_here: "點擊這裡"
# sale_ends: "Ends" sale_ends: "結束"
# sale_extended: "*Existing subscriptions will be extended by 1 year." sale_extended: "*已訂閱用戶會續期一年。"
# sale_feature_here: "Here's what you'll get:" sale_feature_here: "你將會獲得這些:"
# sale_feature2: "Access to 9 powerful <strong>new heroes</strong> with unique skills!" sale_feature2: "可以使用10 個強大的<strong>新英雄</strong> 和各種技能!"
# sale_feature4: "<strong>42,000 bonus gems</strong> awarded immediately!" sale_feature4: "立即取得<strong>42,000 個額外的寶石</strong>"
# sale_continue: "Ready to continue adventuring?" sale_continue: "準備好繼續探險嗎?"
# sale_limited_time: "Limited time offer!" sale_limited_time: "限時優惠!"
# sale_new_heroes: "New heroes!" sale_new_heroes: "新英雄!"
# sale_title: "Back to School Sale" sale_title: "開學促銷"
# sale_view_button: "Buy 1 year subscription for" sale_view_button: "購買年費訂閱:"
stripe_description: "每月訂閱" stripe_description: "每月訂閱"
# stripe_description_year_sale: "1 Year Subscription (35% discount)" stripe_description_year_sale: "年費訂閱 (35% 優惠)"
subscription_required_to_play: "您將需要訂閱來開啟這關." subscription_required_to_play: "您將需要訂閱來開啟這關."
unlock_help_videos: "訂閱來解開所有鎖住得教學影片." unlock_help_videos: "訂閱來解開所有鎖住得教學影片."
personal_sub: "個人訂閱" # Accounts Subscription View below personal_sub: "個人訂閱" # Accounts Subscription View below
@ -462,7 +462,7 @@ module.exports = nativeDescription: "繁體中文", englishDescription: "Chinese
managed_by: "管理" managed_by: "管理"
will_be_cancelled: "將被取消" will_be_cancelled: "將被取消"
currently_free: "您目前有一個免費訂閱" currently_free: "您目前有一個免費訂閱"
currently_free_until: "您目前有一個免費訂閱,直到" # {changed} currently_free_until: "您目前有一個訂閱,直到" # {changed}
was_free_until: "您有過一個免費訂閱,直到" was_free_until: "您有過一個免費訂閱,直到"
managed_subs: "管理訂閱" managed_subs: "管理訂閱"
managed_subs_desc: "為其他玩家(學生、兒童等)添加訂閱。" managed_subs_desc: "為其他玩家(學生、兒童等)添加訂閱。"
@ -526,6 +526,8 @@ module.exports = nativeDescription: "繁體中文", englishDescription: "Chinese
skill_docs: skill_docs:
writable: "可編輯" # Hover over "attack" in Your Skills while playing a level to see most of this writable: "可編輯" # Hover over "attack" in Your Skills while playing a level to see most of this
read_only: "唯讀" read_only: "唯讀"
action: "命令"
spell: "技能"
action_name: "名稱" action_name: "名稱"
action_cooldown: "釋放時間" action_cooldown: "釋放時間"
action_specific_cooldown: "冷卻時間" action_specific_cooldown: "冷卻時間"
@ -582,15 +584,15 @@ module.exports = nativeDescription: "繁體中文", englishDescription: "Chinese
press_paragraph_1_link: "新聞稿懶人包" press_paragraph_1_link: "新聞稿懶人包"
press_paragraph_1_suffix: "裡面所有的LOGO和圖片都可以使用並且不必另外知會我們。" press_paragraph_1_suffix: "裡面所有的LOGO和圖片都可以使用並且不必另外知會我們。"
team: "製作團隊" team: "製作團隊"
george_title: "CEO" # {change} george_title: "共同創辦人"
george_blurb: "商人" george_blurb: "商人"
scott_title: "程式員" # {change} scott_title: "共同創辦人"
scott_blurb: "理性至上" scott_blurb: "理性至上"
nick_title: "程式員" # {change} nick_title: "程式員"
nick_blurb: "亢奮的Guru" nick_blurb: "亢奮的Guru"
michael_title: "程式員" michael_title: "共同創辦人"
michael_blurb: "系統管理員" michael_blurb: "系統管理員"
matt_title: "程式員" # {change} matt_title: "共同創辦人"
matt_blurb: "競速單車玩家" matt_blurb: "競速單車玩家"
cat_title: "首席開卡設計師" cat_title: "首席開卡設計師"
cat_blurb: "氣宗" cat_blurb: "氣宗"
@ -606,19 +608,19 @@ module.exports = nativeDescription: "繁體中文", englishDescription: "Chinese
intro_1: "CodeCombat 是一個教編程的網上游戲。學生會用編程語言寫代碼。" intro_1: "CodeCombat 是一個教編程的網上游戲。學生會用編程語言寫代碼。"
intro_2: "無需經驗!" intro_2: "無需經驗!"
free_title: "要多少錢?" free_title: "要多少錢?"
cost_china: "CodeCombat的前5個關卡在中國是免費的在這之後需花費每月9.99美元來訪問我們架設在中國專屬服務器上的140多個關卡。" cost_china: "CodeCombat的前5個關卡在中國是免費的在這之後需花費每月9.99美元來訪問我們架設在中國專屬服務器上的180多個關卡。"
free_1: "有100多個覆蓋了所有理論的免費關卡。" # {change} free_1: "有110多個覆蓋了所有理論的免費關卡。"
free_2: "包月訂閱可以訪問視頻教程和額外的練習關卡。" free_2: "包月訂閱可以訪問視頻教程和額外的練習關卡。"
teacher_subs_title: "教師可免費訂閱!" teacher_subs_title: "教師可免費訂閱!"
teacher_subs_1: "請聯繫" teacher_subs_1: "請聯繫"
teacher_subs_2: "教師調查" teacher_subs_2: "教師調查"
teacher_subs_3: "建立您的訂閱。" teacher_subs_3: "建立您的訂閱。"
sub_includes_title: "訂閱裡包含了什麼內容?" sub_includes_title: "訂閱裡包含了什麼內容?"
sub_includes_1: "除了100個基礎關卡學生包月訂閱還可以使用這些附加功能" # {change} sub_includes_1: "除了110個基礎關卡學生包月訂閱還可以使用這些附加功能"
sub_includes_2: "超過70個練習關卡" sub_includes_2: "超過70個練習關卡"
sub_includes_3: "視頻教學" sub_includes_3: "視頻教學"
sub_includes_4: "獨特的電子郵件支援" sub_includes_4: "獨特的電子郵件支援"
sub_includes_5: "10个具有独特技能的新英雄" # {change} sub_includes_5: "10个具有独特技能的新英雄"
sub_includes_6: "每月享有3500額外寶石" sub_includes_6: "每月享有3500額外寶石"
sub_includes_7: "私人部落" sub_includes_7: "私人部落"
monitor_progress_title: "我要如何查看學生的進度?" monitor_progress_title: "我要如何查看學生的進度?"
@ -649,8 +651,8 @@ module.exports = nativeDescription: "繁體中文", englishDescription: "Chinese
more_info_2: "教師論壇" more_info_2: "教師論壇"
more_info_3: "是個與其他使用CodeCombat的教育工作者聯繫的良好平台。" more_info_3: "是個與其他使用CodeCombat的教育工作者聯繫的良好平台。"
sys_requirements_title: "系統要求" sys_requirements_title: "系統要求"
sys_requirements_1: "因為CodeCombat是遊戲, 相比播放影片它讓電腦花費更多資源去順暢的執行. 為了讓所有人都可以接觸,我們已經讓它在現在的瀏覽器和老舊的電腦上執行最佳化. 以下是我們為了讓您順暢體驗Hour of Code所給的系統建議:" # {change} sys_requirements_1: "一個現代的瀏覽器。 需要更新的ChromeFirefox、Safari或者Internet Explorer 9 以上版本。" # {change}
sys_requirements_2: "使用較新的Chrome or Firefox版本." # {change} sys_requirements_2: "CodeCombat 暫時還不支持 iPad。" # {change}
teachers_survey: teachers_survey:
title: "教師調查" title: "教師調查"
@ -673,12 +675,12 @@ module.exports = nativeDescription: "繁體中文", englishDescription: "Chinese
school: "學校名字" school: "學校名字"
location: "城市" location: "城市"
age_students: "您的學生年齡為多少?" age_students: "您的學生年齡為多少?"
under: "幾歲以下" under: "低於"
other: "其他:" other: "其他:"
amount_students: "請問您有多少學生需要教導?" amount_students: "請問您有多少學生需要教導?"
hear_about: "您是怎麼知道CodeComabat的" hear_about: "您是怎麼知道CodeComabat的"
fill_fields: "請填寫所有問題。" fill_fields: "請填寫所有問題。"
thanks: "非常感謝!我們會很快寄設置說明。" thanks: "非常感謝!我們會很快寄給您設置說明。"
versions: versions:
save_version_title: "保存新版本" save_version_title: "保存新版本"
@ -725,7 +727,7 @@ module.exports = nativeDescription: "繁體中文", englishDescription: "Chinese
admin: "管理員" admin: "管理員"
new_password: "新密碼" new_password: "新密碼"
new_password_verify: "確認密碼" new_password_verify: "確認密碼"
type_in_email: "輸入您的Email來確認刪除" # {change} type_in_email: "輸入您的Email來確認刪除"
type_in_password: "還有輸入您的密碼。" type_in_password: "還有輸入您的密碼。"
email_subscriptions: "訂閱" email_subscriptions: "訂閱"
email_subscriptions_none: "無Email訂閱" email_subscriptions_none: "無Email訂閱"
@ -836,7 +838,7 @@ module.exports = nativeDescription: "繁體中文", englishDescription: "Chinese
latest_achievement: "最新成就" latest_achievement: "最新成就"
playtime: "遊戲時間" playtime: "遊戲時間"
last_played: "最後玩了" last_played: "最後玩了"
# leagues_explanation: "Play in a league against other clan members in these multiplayer arena instances." leagues_explanation: "在部落裡與其他成員組成聯盟一起參加下面的多人競技場。"
classes: classes:
archmage_title: "大法師" archmage_title: "大法師"
@ -1026,7 +1028,7 @@ module.exports = nativeDescription: "繁體中文", englishDescription: "Chinese
my_matches: "我的對手" my_matches: "我的對手"
simulate: "模擬" simulate: "模擬"
simulation_explanation: "通過模擬遊戲,您可以使您的遊戲更快得到評分!" simulation_explanation: "通過模擬遊戲,您可以使您的遊戲更快得到評分!"
# simulation_explanation_leagues: "You will mainly help simulate games for allied players in your clans and courses." simulation_explanation_leagues: "你會主要給在你的部落或者課程的同伴幫忙模擬遊戲。"
simulate_games: "模擬遊戲!" simulate_games: "模擬遊戲!"
simulate_all: "重置並模擬遊戲" simulate_all: "重置並模擬遊戲"
games_simulated_by: "您模擬過的次數:" games_simulated_by: "您模擬過的次數:"
@ -1040,7 +1042,7 @@ module.exports = nativeDescription: "繁體中文", englishDescription: "Chinese
summary_matches: "對手 - " summary_matches: "對手 - "
summary_wins: " 勝利, " summary_wins: " 勝利, "
summary_losses: " 失敗" summary_losses: " 失敗"
rank_no_code: "沒有新程式碼可評分No New Code to Rank" rank_no_code: "沒有新程式碼可評分"
rank_my_game: "對我的遊戲評分!" rank_my_game: "對我的遊戲評分!"
rank_submitting: "上傳中..." rank_submitting: "上傳中..."
rank_submitted: "已上傳以求評分" rank_submitted: "已上傳以求評分"
@ -1073,11 +1075,16 @@ module.exports = nativeDescription: "繁體中文", englishDescription: "Chinese
tournament_rules: "錦標賽規則" tournament_rules: "錦標賽規則"
tournament_blurb: "寫下程式碼, 收集金幣, 建立軍隊, 粉碎敵人, 贏得獎項,在我們價值$40,000的Greed錦標賽中升級您的職業! 查看" tournament_blurb: "寫下程式碼, 收集金幣, 建立軍隊, 粉碎敵人, 贏得獎項,在我們價值$40,000的Greed錦標賽中升級您的職業! 查看"
tournament_blurb_criss_cross: "贏得競賽, 建造道路, 智勝對手, 收集寶石, 在我們的Criss-Crossand錦標賽中升級您的職業! 查看" tournament_blurb_criss_cross: "贏得競賽, 建造道路, 智勝對手, 收集寶石, 在我們的Criss-Crossand錦標賽中升級您的職業! 查看"
# tournament_blurb_zero_sum: "Unleash your coding creativity in both gold gathering and battle tactics in this alpine mirror match between red sorcerer and blue sorcerer. The tournament began on Friday, March 27 and will run until Monday, April 6 at 5PM PDT. Compete for fun and glory! Check out the details" tournament_blurb_zero_sum: "在紅方法師和藍方法師的山峰對決中同時在收集金幣和戰術上發揮你的代碼創造力。競賽在3月27日開始在4月6日星期一的下午5點PDT太平洋時區結束。為樂趣和榮耀競賽吧瀏覽了解更多"
tournament_blurb_blog: "我們的部落格" tournament_blurb_blog: "我們的部落格"
rules: "規則" rules: "規則"
winners: "贏家" winners: "贏家"
# league: "League" league: "聯盟"
red_ai: "紅隊 AI" # "Red AI Wins", at end of multiplayer match playback
blue_ai: "藍隊 AI"
wins: "勝利" # At end of multiplayer match playback
humans: "紅隊" # Ladder page display team name
ogres: "藍隊"
user: user:
stats: "統計" stats: "統計"
@ -1118,7 +1125,7 @@ module.exports = nativeDescription: "繁體中文", englishDescription: "Chinese
no_recent_games: "在過去兩個星期沒有玩過遊戲。" no_recent_games: "在過去兩個星期沒有玩過遊戲。"
payments: "付款" payments: "付款"
purchased: "已購買" purchased: "已購買"
# sale: "Sale" sale: "促銷"
subscription: "訂閱" subscription: "訂閱"
invoices: "收據" invoices: "收據"
service_apple: "設備: Apple" service_apple: "設備: Apple"
@ -1221,15 +1228,15 @@ module.exports = nativeDescription: "繁體中文", englishDescription: "Chinese
boolean_logic: "布爾邏輯" boolean_logic: "布爾邏輯"
break_statements: "Break語句" break_statements: "Break語句"
classes: "" classes: ""
# continue_statements: "Continue Statements" continue_statements: "Continue 語句"
for_loops: "For循環" for_loops: "For循環"
functions: "函數" functions: "函數"
# graphics: "Graphics" graphics: "圖形"
if_statements: "If語句" if_statements: "If語句"
input_handling: "輸入處理" input_handling: "輸入處理"
math_operations: "數學運算" math_operations: "數學運算"
object_literals: "對象常量" object_literals: "對象常量"
# parameters: "Parameters" parameters: "參數"
strings: "字符串" strings: "字符串"
variables: "變量" variables: "變量"
vectors: "向量" vectors: "向量"
@ -1325,8 +1332,8 @@ module.exports = nativeDescription: "繁體中文", englishDescription: "Chinese
prizes: "獎項" prizes: "獎項"
total_value: "總價值" total_value: "總價值"
in_cash: "現金" in_cash: "現金"
custom_wizard: "制 CodeCombat 巫師" custom_wizard: "制 CodeCombat 巫師"
custom_avatar: "制 CodeCombat 頭像" custom_avatar: "制 CodeCombat 頭像"
heap: "給六個月的\"Startup\"訪問" heap: "給六個月的\"Startup\"訪問"
credits: "信譽" credits: "信譽"
one_month_coupon: "優惠: 選擇 Rails 或者 HTML" one_month_coupon: "優惠: 選擇 Rails 或者 HTML"

View file

@ -7,6 +7,7 @@ _.extend CourseSchema.properties,
campaignID: c.objectId() campaignID: c.objectId()
concepts: c.array {title: 'Programming Concepts', uniqueItems: true}, c.concept concepts: c.array {title: 'Programming Concepts', uniqueItems: true}, c.concept
description: {type: 'string'} description: {type: 'string'}
pricePerSeat: {type: 'number', description: 'Price per seat in USD cents.'}
screenshot: c.url {title: 'URL', description: 'Link to course screenshot.'} screenshot: c.url {title: 'URL', description: 'Link to course screenshot.'}
c.extendBasicProperties CourseSchema, 'Course' c.extendBasicProperties CourseSchema, 'Course'

View file

@ -1,11 +1,12 @@
c = require './../schemas' c = require './../schemas'
CourseInstanceSchema = c.object {title: 'Course Instance'} CourseInstanceSchema = c.object {title: 'Course Instance'}
c.extendNamedProperties CourseInstanceSchema # name first
_.extend CourseInstanceSchema.properties, _.extend CourseInstanceSchema.properties,
courseID: c.objectId()
description: {type: 'string'} description: {type: 'string'}
members: c.array {title: 'Members'}, c.objectId() members: c.array {title: 'Members'}, c.objectId()
name: {type: 'string'}
ownerID: c.objectId() ownerID: c.objectId()
prepaidID: c.objectId() prepaidID: c.objectId()

View file

@ -0,0 +1,7 @@
#course-details-view
.edit-description-input
width: 100%
.edit-name-input
width: 50%

View file

@ -0,0 +1,14 @@
#course-enroll-view
.btn-buy
margin: 20px 0px
.center
text-align: center
.enroll-container
margin: 5% 20%
width: 60%
.class-name
width: 300px

View file

@ -63,6 +63,9 @@
.health-bar-container .health-bar .health-bar-container .health-bar
background: hsla(205,100%,51%,1) background: hsla(205,100%,51%,1)
.thang-avatar-view
@include scaleX(-1)
.name-and-power .name-and-power
display: flex display: flex
flex-direction: row flex-direction: row

View file

@ -54,12 +54,23 @@
border-image: url(/images/level/code_toolbar_run_button_active_pressed.png) 14 20 20 20 fill round border-image: url(/images/level/code_toolbar_run_button_active_pressed.png) 14 20 20 20 fill round
padding: 2px 0 0 2px padding: 2px 0 0 2px
&.submit-button, &.done-button &.submit-button, &.done-button, &.rank-button
margin-left: 10px margin-left: 10px
border-image: url(/images/level/code_toolbar_submit_button_active.png) 14 20 20 20 fill round border-image: url(/images/level/code_toolbar_submit_button_active.png) 14 20 20 20 fill round
&:active &:active
border-image: url(/images/level/code_toolbar_submit_button_active_pressed.png) 14 20 20 20 fill round 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 .cast-button
@include opacity(0.77) @include opacity(0.77)

View file

@ -16,7 +16,7 @@ table.table
block tableBody block tableBody
for document in documents for document in documents
- var data = document.attributes; - var data = document.attributes;
- if(data.slug == 'ace-of-coders' && new Date() < new Date(1441954800000)) - if(data.slug == 'ace-of-coders' && new Date() < new Date(1441863900000))
- continue; - continue;
tr(class=document.getOwner() == me.id ? 'mine' : '') tr(class=document.getOwner() == me.id ? 'mine' : '')
td td

View file

@ -0,0 +1,31 @@
extends /templates/base
block content
//- DO NOT localize / i18n
div
span *UNDER CONSTRUCTION, send feedback to
a.spl(href='mailto:team@codecombat.com') team@codecombat.com
div(style='border-bottom: 1px solid black;')
h1(style='text-align: center;') Course
if course
div= course.get('name')
div= course.get('description')
div= course.get('campaignID')
div= course.get('concepts')
else
div No course found.
h1(style='text-align: center;') Class
if courseInstance
p
div= courseInstance.get('name') || 'Class Name'
div= courseInstance.get('description')
div= courseInstance.get('courseID')
div= courseInstance.get('ownerID')
div= courseInstance.get('members')
div= courseInstance.get('prepaidID')
else
p No classes found.

View file

@ -0,0 +1,77 @@
extends /templates/base
block content
//- DO NOT localize / i18n
div
span *UNDER CONSTRUCTION, send feedback to
a.spl(href='mailto:team@codecombat.com') team@codecombat.com
div(style='border-bottom: 1px solid black')
if state === 'declined' || state === 'unknown_error'
p
.alert.alert-danger ERROR #{stateMessage}
if state === 'creating'
p
.alert.alert-info Creating class...
else if state === 'purchasing'
p
.alert.alert-info Purchasing course...
else
.well.well-lg.enroll-container
if price > 0
h1.center Buy Course
else
h1.center Create Class
h3 1. Course
if courses.length > 2
p Select 'All Courses' for a 50% discount!
.form-group
select.form-control.course-select
each course in courses
option(value="#{course.id}")= course.get('name')
if courses.length > 1
option(value="All Courses") All Courses
h3 2. Number of students
p Enter the number of students you need for this class.
input.input-seats(type='text', value="#{seats}")
h3 3. Name your class
p This will be displayed on the course page for you and your students. It can be changed later.
input.class-name(type='text', placeholder="Mrs. Smith's 4th Period", value="#{className ? className : ''}")
if price > 0
h3 4. Buy
else
h3 4. Create Class
p
if price > 0
span.spr You are purchasing a license for
else
span.spr You are creating a class for
strong.spr #{selectedCourseTitle}
span.spr for
strong #{seats} students
| .
p Afterwards you will receive an unlock code to distribute to your students, which they can use to enroll in your class.
p.center
if price > 0
button.btn.btn-success.btn-lg.btn-buy $#{(price / 100.0).toFixed(2)}
else
button.btn.btn-success.btn-lg.btn-buy Create Class
+trial-and-questions
mixin trial-and-questions
h3 Free trial for teachers!
p
span.spr Please fill out our
a(href='/teachers/freetrial', data-i18n="teachers.teacher_subs_2")
span.spl to get individual access to all courses for evalutaion purposes.
h3 Questions?
p
span Please contact
a.spl(href='mailto:team@codecombat.com') team@codecombat.com

View file

@ -7,7 +7,7 @@ div#columns.row
tr tr
th(colspan=level.get('type', true) == 'hero-ladder' ? 3 : 2) th(colspan=level.get('type', true) == 'hero-ladder' ? 3 : 2)
th(colspan=4, style="color: #{team.primaryColor}") th(colspan=4, style="color: #{team.primaryColor}")
span= team.name span= team.displayName
span.spl(data-i18n="ladder.leaderboard") Leaderboard span.spl(data-i18n="ladder.leaderboard") Leaderboard
tr tr
th(colspan=level.get('type', true) == 'hero-ladder' ? 3 : 2) th(colspan=level.get('type', true) == 'hero-ladder' ? 3 : 2)
@ -32,7 +32,7 @@ div#columns.row
td.score-cell= Math.round(sessionStats.totalScore * 100) td.score-cell= Math.round(sessionStats.totalScore * 100)
td.name-col-cell= session.get('creatorName') || "Anonymous" td.name-col-cell= session.get('creatorName') || "Anonymous"
td.fight-cell 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! span(data-i18n="ladder.fight") Fight!
td.spectate-cell.iconic-cell td.spectate-cell.iconic-cell
.glyphicon.glyphicon-eye-open .glyphicon.glyphicon-eye-open
@ -51,7 +51,7 @@ div#columns.row
td.score-cell= Math.round(sessionStats.totalScore * 100) td.score-cell= Math.round(sessionStats.totalScore * 100)
td.name-col-cell= session.get('creatorName') || "Anonymous" td.name-col-cell= session.get('creatorName') || "Anonymous"
td.fight-cell 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! span(data-i18n="ladder.fight") Fight!
td.spectate-cell.iconic-cell td.spectate-cell.iconic-cell
.glyphicon.glyphicon-eye-open .glyphicon.glyphicon-eye-open
@ -84,7 +84,7 @@ div#columns.row
span : span :
span= friend.team span= friend.team
br 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! span(data-i18n="ladder.fight") Fight!
else if onFacebook || onGPlus else if onFacebook || onGPlus

View file

@ -105,11 +105,11 @@ block content
a(class="play-button btn btn-illustrated btn-block btn-lg " + (team.id == 'ogres' ? 'btn-primary' : 'btn-danger'), data-team=team.id) a(class="play-button btn btn-illustrated btn-block btn-lg " + (team.id == 'ogres' ? 'btn-primary' : 'btn-danger'), data-team=team.id)
span(data-i18n="play.play_as") Play As span(data-i18n="play.play_as") Play As
| |
span= team.name span= team.displayName
div.column.col-md-2 div.column.col-md-2
.spectate-button-container .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 span(data-i18n="play.spectate") Spectate
ul.nav.nav-pills ul.nav.nav-pills

View file

@ -6,7 +6,7 @@ div#columns.row
tr tr
th(colspan=5, style="color: #{team.primaryColor}") th(colspan=5, style="color: #{team.primaryColor}")
span(data-i18n="ladder.summary_your") Your span(data-i18n="ladder.summary_your") Your
|#{team.name} |#{team.displayName}
| |
span(data-i18n="ladder.summary_matches") Matches - span(data-i18n="ladder.summary_matches") Matches -
|#{team.wins} |#{team.wins}
@ -43,13 +43,13 @@ div#columns.row
td.name-cell= match.opponentName || "Anonymous" td.name-cell= match.opponentName || "Anonymous"
td.time-cell= match.when td.time-cell= match.when
td.battle-cell 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') if (match.state === 'win')
span(data-i18n="ladder.watch_victory") Watch your victory span(data-i18n="ladder.watch_victory") Watch your victory
else else
span(data-i18n="ladder.defeat_the") Defeat the span(data-i18n="ladder.defeat_the") Defeat the
| |
| #{team.otherTeam} | #{team.otherTeamDisplayName}
if !team.matches.length if !team.matches.length
tr tr

View file

@ -12,7 +12,7 @@ block modal-body-content
option(value=option.id selected=(language === option.id))= option.name option(value=option.id selected=(language === option.id))= option.name
div#noob-view.secret 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 p
strong(data-i18n="ladder.tutorial_play") Play Tutorial strong(data-i18n="ladder.tutorial_play") Play Tutorial
span(data-i18n="ladder.tutorial_recommended") Recommended if you've never played before span(data-i18n="ladder.tutorial_recommended") Recommended if you've never played before
@ -23,8 +23,8 @@ block modal-body-content
p.tutorial-suggestion p.tutorial-suggestion
strong(data-i18n="ladder.tutorial_not_sure") Not sure what's going on? 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}-tutorial" + (league ? "?league=" + league.id : ""), data-i18n="ladder.tutorial_play_first") Play the tutorial first.
a(href="/play/level/#{levelID}?team=#{teamID}") a(href="/play/level/#{levelID}?team=#{teamID}" + (league ? "&league=" + league.id : ""))
div.play-option div.play-option
img(src=myPortrait).my-icon.only-one 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 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 span(data-i18n="ladder.warmup") Warmup
if challengers.easy 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 div.play-option.easy-option
img(src=myPortrait).my-icon.only-one 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 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 span(data-i18n="general.easy") Easy
if challengers.medium 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 div.play-option.medium-option
img(src=myPortrait).my-icon.only-one 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 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 span(data-i18n="general.medium") Medium
if challengers.hard 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 div.play-option.hard-option
img(src=myPortrait).my-icon.only-one 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 img(src="/images/pages/play/ladder/"+teamID+"_ladder_hard.png", style="border: 1px solid #{teamColor}; background: #{teamBackgroundColor}").my-team-icon.img-circle.only-one

View file

@ -7,7 +7,7 @@
.levels-link-area .levels-link-area
a.levels-link(href=homeLink || "/") a.levels-link(href=homeLink || "/")
.glyphicon.glyphicon-play .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 if isMultiplayerLevel && !observing
.multiplayer-area-container .multiplayer-area-container

View file

@ -7,7 +7,7 @@ for player in players
.player-power .player-power
.power-icon .power-icon
.power-value .power-value
.player-name= player.name .player-name= player.name || 'Anoner'
.player-health .player-health
.health-icon .health-icon
.health-bar-container .health-bar-container

View file

@ -2,9 +2,12 @@ button.btn.btn-lg.btn-illustrated.cast-button(title=castVerbose)
span(data-i18n="play_level.tome_run_button_ran") Ran span(data-i18n="play_level.tome_run_button_ran") Ran
if !observing if !observing
button.btn.btn-lg.btn-illustrated.submit-button(title=castRealTimeVerbose) if mirror
span(data-i18n="play_level.tome_submit_button") Submit .ladder-submission-view
span.spl.secret.submit-again-time else
button.btn.btn-lg.btn-illustrated.submit-button(title=castRealTimeVerbose)
button.btn.btn-lg.btn-illustrated.done-button.secret span(data-i18n="play_level.tome_submit_button") Submit
span(data-i18n="play_level.done") Done span.spl.secret.submit-again-time
button.btn.btn-lg.btn-illustrated.done-button.secret
span(data-i18n="play_level.done") Done

View file

@ -191,7 +191,7 @@ module.exports = class ClanDetailsView extends RootView
name: utils.i18n(campaign.attributes, 'fullName') or utils.i18n(campaign.attributes, 'name') name: utils.i18n(campaign.attributes, 'fullName') or utils.i18n(campaign.attributes, 'name')
levels: [] levels: []
for levelID, level of campaign.get('levels') for levelID, level of campaign.get('levels')
continue if level.slug is 'ace-of-coders' and new Date() < new Date(1441954800000) continue if level.slug is 'ace-of-coders' and new Date() < new Date(1441863900000)
campaignLevelProgression.levels.push campaignLevelProgression.levels.push
ID: levelID ID: levelID
slug: level.slug slug: level.slug

View file

@ -0,0 +1,38 @@
RootView = require 'views/core/RootView'
template = require 'templates/courses/course-details'
CocoCollection = require 'collections/CocoCollection'
Course = require 'models/Course'
CourseInstance = require 'models/CourseInstance'
# TODO: logged out experience
# TODO: no course instances
# TODO: no course instance selected
module.exports = class CourseDetailsView extends RootView
id: 'course-details-view'
template: template
constructor: (options, @courseID) ->
super options
@courseInstanceID = options.courseInstanceID
@course = new Course _id: @courseID
@supermodel.loadModel @course, 'course', cache: false
if @courseInstanceID
@courseInstance = new CourseInstance _id: @courseInstanceID
@supermodel.loadModel @courseInstance, 'course_instance', cache: false
else if !me.isAnonymous()
@courseInstances = new CocoCollection([], { url: "/db/user/#{me.id}/course_instances", model: CourseInstance})
@listenToOnce @courseInstances, 'sync', @onCourseInstancesLoaded
@supermodel.loadCollection(@courseInstances, 'course_instances')
getRenderData: ->
context = super()
context.course = @course
context.courseInstance = @courseInstance
context
onCourseInstancesLoaded: ->
if @courseInstances.models.length is 1
@courseInstance = @courseInstances.models[0]
else if @courseInstances.models.length > 0
@courseInstance = @courseInstances.models[0]

View file

@ -0,0 +1,133 @@
app = require 'core/application'
AuthModal = require 'views/core/AuthModal'
CocoCollection = require 'collections/CocoCollection'
Course = require 'models/Course'
{getCoursesPrice} = require 'core/utils'
RootView = require 'views/core/RootView'
stripeHandler = require 'core/services/stripe'
template = require 'templates/courses/course-enroll'
module.exports = class CourseEnrollView extends RootView
id: 'course-enroll-view'
template: template
events:
'click .btn-buy': 'onClickBuy'
'change .class-name': 'onNameChange'
'change .course-select': 'onChangeCourse'
'change .input-seats': 'onSeatsChange'
subscriptions:
'stripe:received-token': 'onStripeReceivedToken'
constructor: (options, @courseID=0) ->
super options
@courseID ?= options.courseID
@seats = 20
@courses = new CocoCollection([], { url: "/db/course", model: Course})
@listenTo @courses, 'sync', @onCoursesLoaded
@supermodel.loadCollection(@courses, 'courses')
getRenderData: ->
context = super()
context.className = @className
context.courses = @courses.models
context.price = @price ? 0
context.seats = @seats
context.selectedCourse = @selectedCourse
context.selectedCourseTitle = @selectedCourse?.get('name') ? 'All Courses'
context.state = @state
context.stateMessage = @stateMessage
context
afterRender: ->
super()
if @selectedCourse
@$el.find('.course-select').val(@selectedCourse.id)
else
@$el.find('.course-select').val('All Courses')
onCoursesLoaded: ->
if @courseID
@selectedCourse = _.find @courses.models, (a) => a.id is @courseID
else if @courses.models.length > 0
@selectedCourse = @courses.models[0]
@renderNewPrice()
onClickBuy: (e) ->
return @openModalView new AuthModal() if me.isAnonymous()
if @seats < 1 or not _.isFinite(@seats)
alert("Please enter the maximum number of students needed for your class.")
return
if @price is 0
@state = 'creating'
@createClass()
return
@state = undefined
@stateMessage = undefined
@render()
# Show Stripe handler
courseTitle = @selectedCourse?.get('name') ? 'All Courses'
application.tracker?.trackEvent 'Started course purchase', {course: courseTitle, price: @price, seats: @seats}
stripeHandler.open
amount: @price
description: "#{courseTitle} for #{@seats} students"
bitcoin: true
alipay: if me.get('chinaVersion') or (me.get('preferredLanguage') or 'en-US')[...2] is 'zh' then true else 'auto'
onStripeReceivedToken: (e) ->
@state = 'purchasing'
@render?()
@createClass(e.token.id)
onChangeCourse: (e) ->
@selectedCourse = _.find @courses.models, (a) -> a.id is $(e.target).val()
@renderNewPrice()
onNameChange: (e) ->
@className = $('.class-name').val()
onSeatsChange: (e) ->
@seats = $(e.target).val()
@seats = 20 if @seats < 1 or not _.isFinite(@seats)
@renderNewPrice()
createClass: (token) ->
data =
name: $('.class-name').val()
seats: @seats
token: token
data.courseID = @selectedCourse.id if @selectedCourse
jqxhr = $.post('/db/course_instance/-/create', data)
jqxhr.done (data, textStatus, jqXHR) =>
application.tracker?.trackEvent 'Finished course purchase', {course: @selectedCourse?.get('name') ? 'All Courses', price: @price, seats: @seats}
# TODO: handle fetch errors
me.fetch(cache: false).always =>
courseID = @selectedCourse?.id ? @courses.models[0]?.id
viewArgs = if data?.length > 0 then [courseInstanceID: data[0]._id, courseID] else [{}, courseID]
Backbone.Mediator.publish 'router:navigate',
route: "/courses/#{courseID}"
viewClass: 'views/courses/CourseDetailsView'
viewArgs: viewArgs
jqxhr.fail (xhr, textStatus, errorThrown) =>
console.error 'Got an error purchasing a course:', textStatus, errorThrown
application.tracker?.trackEvent 'Failed course purchase', status: textStatus
if xhr.status is 402
@state = 'declined'
@stateMessage = arguments[2]
else
@state = 'unknown_error'
@stateMessage = "#{xhr.status}: #{xhr.responseText}"
@render?()
renderNewPrice: ->
if @selectedCourse
@price = getCoursesPrice([@selectedCourse], @seats)
else
@price = getCoursesPrice(@courses.models, @seats)
@render?()

View file

@ -103,6 +103,7 @@ module.exports = class LadderPlayModal extends ModalView
{id: 'lua', name: 'Lua'} {id: 'lua', name: 'Lua'}
{id: 'io', name: 'Io (Experimental)'} {id: 'io', name: 'Io (Experimental)'}
] ]
ctx.league = @options.league
teamsList = teamDataFromLevel @level teamsList = teamDataFromLevel @level
teams = {} teams = {}
teams[team.id] = team for team in teamsList teams[team.id] = team for team in teamsList

View file

@ -104,12 +104,14 @@ module.exports = class LadderView extends RootView
onClickSpectateButton: (e) -> onClickSpectateButton: (e) ->
humanSession = @ladderTab.spectateTargets?.humans humanSession = @ladderTab.spectateTargets?.humans
ogreSession = @ladderTab.spectateTargets?.ogres ogreSession = @ladderTab.spectateTargets?.ogres
console.log humanSession, ogreSession
return unless humanSession and ogreSession return unless humanSession and ogreSession
e.preventDefault() e.preventDefault()
e.stopImmediatePropagation() e.stopImmediatePropagation()
url = "/play/spectate/#{@level.get('slug')}?session-one=#{humanSession}&session-two=#{ogreSession}" url = "/play/spectate/#{@level.get('slug')}?session-one=#{humanSession}&session-two=#{ogreSession}"
Backbone.Mediator.publish 'router:navigate', route: url 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
showPlayModal: (teamID) -> showPlayModal: (teamID) ->
session = (s for s in @sessions.models when s.get('team') is teamID)[0] session = (s for s in @sessions.models when s.get('team') is teamID)[0]

View file

@ -62,6 +62,13 @@ heroArenas = [
image: '/file/db/level/550363b4ec31df9c691ab629/MAR26-Banner_Zero%20Sum.png' image: '/file/db/level/550363b4ec31df9c691ab629/MAR26-Banner_Zero%20Sum.png'
description: 'Unleash your coding creativity in both gold gathering and battle tactics in this alpine mirror match between red sorcerer and blue sorcerer.' description: 'Unleash your coding creativity in both gold gathering and battle tactics in this alpine mirror match between red sorcerer and blue sorcerer.'
} }
{
name: 'Ace of Coders'
difficulty: 3
id: 'ace-of-coders'
image: '/file/db/level/550363b4ec31df9c691ab629/MAR26-Banner_Zero%20Sum.png'
description: 'Battle for control over the icy treasure chests as your gigantic warrior marshals his armies against his mirror-match nemesis.'
}
{ {
name: 'Cavern Survival' name: 'Cavern Survival'
difficulty: 1 difficulty: 1
@ -92,6 +99,8 @@ heroArenas = [
} }
] ]
heroArenas = _.reject heroArenas, id: 'ace-of-coders' if new Date() < new Date(1441863900000)
oldArenas = [ oldArenas = [
{ {
name: 'Criss-Cross' name: 'Criss-Cross'

View file

@ -67,6 +67,7 @@ module.exports = class MyMatchesTabView extends CocoView
ctx.level = @level ctx.level = @level
ctx.levelID = @level.get('slug') or @level.id ctx.levelID = @level.get('slug') or @level.id
ctx.teams = @teams ctx.teams = @teams
ctx.league = @options.league
convertMatch = (match, submitDate) => convertMatch = (match, submitDate) =>
opponent = match.opponents[0] opponent = match.opponents[0]
@ -116,7 +117,9 @@ module.exports = class MyMatchesTabView extends CocoView
placeholder = $(el) placeholder = $(el)
sessionID = placeholder.data('session-id') sessionID = placeholder.data('session-id')
session = _.find @sessions.models, {id: sessionID} 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 @insertSubView ladderSubmissionView, placeholder
@$el.find('.score-chart-wrapper').each (i, el) => @$el.find('.score-chart-wrapper').each (i, el) =>

View file

@ -14,7 +14,9 @@ module.exports.teamDataFromLevel = (level) ->
teams.push({ teams.push({
id: team id: team
name: _.string.titleize(team) name: _.string.titleize(team)
displayName: $.i18n.t("ladder.#{team}") # Use Red/Blue instead of Humans/Ogres
otherTeam: otherTeam otherTeam: otherTeam
otherTeamDisplayName: $.i18n.t("ladder.#{otherTeam}")
bgColor: bgColor bgColor: bgColor
primaryColor: primaryColor primaryColor: primaryColor
}) })

View file

@ -158,7 +158,8 @@ module.exports = class SpectateLevelView extends RootView
# Don't remove it; we want its decoration around on large screens. # Don't remove it; we want its decoration around on large screens.
#@removeSubView @loadingView #@removeSubView @loadingView
#@loadingView = null #@loadingView = null
Backbone.Mediator.publish 'level:set-playing', playing: true Backbone.Mediator.publish 'level:set-playing', playing: false
Backbone.Mediator.publish 'level:set-time', time: 1 # Helps to have perhaps built a few Thangs and gotten a good list of spritesheets we need to render for our initial paused frame
onSupermodelLoadedOne: => onSupermodelLoadedOne: =>
@modelsLoaded ?= 0 @modelsLoaded ?= 0
@ -256,6 +257,8 @@ module.exports = class SpectateLevelView extends RootView
startFrame = @lastWorldFramesLoaded ? 0 startFrame = @lastWorldFramesLoaded ? 0
if @world.frames.length is @world.totalFrames # Finished loading if @world.frames.length is @world.totalFrames # Finished loading
@lastWorldFramesLoaded = 0 @lastWorldFramesLoaded = 0
unless @getQueryVariable('autoplay') is false
Backbone.Mediator.publish 'level:set-playing', playing: true # Since we paused at first, now we autostart playback.
else else
@lastWorldFramesLoaded = @world.frames.length @lastWorldFramesLoaded = @world.frames.length
for [spriteName, message] in @world.thangDialogueSounds startFrame for [spriteName, message] in @world.thangDialogueSounds startFrame
@ -269,6 +272,8 @@ module.exports = class SpectateLevelView extends RootView
@sessionOne = data[0]._id @sessionOne = data[0]._id
@sessionTwo = data[1]._id @sessionTwo = data[1]._id
url = "/play/spectate/#{@levelID}?session-one=#{@sessionOne}&session-two=#{@sessionTwo}" url = "/play/spectate/#{@levelID}?session-one=#{@sessionOne}&session-two=#{@sessionTwo}"
if leagueID = @getQueryVariable 'league'
url += "&league=" + leagueID
Backbone.Mediator.publish 'router:navigate', { Backbone.Mediator.publish 'router:navigate', {
route: url, route: url,
viewClass: SpectateLevelView, viewClass: SpectateLevelView,

View file

@ -13,6 +13,7 @@ module.exports = class LadderSubmissionView extends CocoView
constructor: (options) -> constructor: (options) ->
super options super options
@session = options.session @session = options.session
@mirrorSession = options.mirrorSession
@level = options.level @level = options.level
getRenderData: -> getRenderData: ->
@ -62,20 +63,31 @@ module.exports = class LadderSubmissionView extends CocoView
console.log jqxhr.responseText console.log jqxhr.responseText
@setRankingButtonText 'failed' unless @destroyed @setRankingButtonText 'failed' unless @destroyed
@transpileSession (transpiledCode) => @transpileSession (transpiledCode) =>
ajaxData = ajaxData =
session: @session.id session: @session.id
levelID: @level.id levelID: @level.id
originalLevelID: @level.get('original') originalLevelID: @level.get('original')
levelMajorVersion: @level.get('version').major levelMajorVersion: @level.get('version').major
transpiledCode: transpiledCode transpiledCode: transpiledCode
ajaxOptions =
$.ajax '/queue/scoring', {
type: 'POST' type: 'POST'
data: ajaxData data: ajaxData
success: success success: success
error: failure 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) -> transpileSession: (callback) ->
submittedCode = @session.get('code') submittedCode = @session.get('code')

View file

@ -76,13 +76,18 @@ module.exports = class ControlBarView extends CocoView
@homeLink = '/play/ladder/' + levelID @homeLink = '/play/ladder/' + levelID
@homeViewClass = 'views/ladder/LadderView' @homeViewClass = 'views/ladder/LadderView'
@homeViewArgs.push levelID @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'] else if @level.get('type', true) in ['hero', 'hero-coop']
@homeLink = '/play' @homeLink = '/play'
@homeViewClass = 'views/play/CampaignView' @homeViewClass = 'views/play/CampaignView'
campaign = @level.get 'campaign' campaign = @level.get 'campaign'
@homeLink += '/' + campaign @homeLink += '/' + campaign
@homeViewArgs.push 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' @homeLink = '/courses/mock1'
@homeViewClass = 'views/courses/mock1/CourseDetailsView' @homeViewClass = 'views/courses/mock1/CourseDetailsView'
#campaign = @level.get 'campaign' #campaign = @level.get 'campaign'

View file

@ -85,11 +85,11 @@ module.exports = class DuelStatsView extends CocoView
'griffin-rider': 50 'griffin-rider': 50
paladin: 80 paladin: 80
artillery: 75 artillery: 75
'arrow-tower': 75 'arrow-tower': 100
palisade: 10 palisade: 10
peasant: 50 peasant: 50
powers = humans: 0, ogres: 0 powers = humans: 0, ogres: 0
for thang in @options.thangs when thang.health > 0 for thang in @options.thangs when thang.health > 0 and thang.exists
powers[thang.team] += @costTable[thang.type] or 0 if powers[thang.team]? powers[thang.team] += @costTable[thang.type] or 0 if powers[thang.team]?
for player in @players for player in @players
utils.replaceText @$find(player.team, '.power-value'), powers[player.team] utils.replaceText @$find(player.team, '.power-value'), powers[player.team]

View file

@ -323,7 +323,7 @@ module.exports = class PlayLevelView extends RootView
@surface.camera.zoomTo({x: 0, y: 0}, 0.1, 0) @surface.camera.zoomTo({x: 0, y: 0}, 0.1, 0)
findPlayerNames: -> findPlayerNames: ->
return {} unless @observing return {} unless @level.get('type') in ['ladder', 'hero-ladder', 'course-ladder']
playerNames = {} playerNames = {}
for session in [@session, @otherSession] when session?.get('team') for session in [@session, @otherSession] when session?.get('team')
playerNames[session.get('team')] = session.get('creatorName') or 'Anoner' playerNames[session.get('team')] = session.get('creatorName') or 'Anoner'

View file

@ -369,9 +369,18 @@ module.exports = class HeroVictoryModal extends ModalView
@$el.find('.sign-up-poke').toggleClass('hide', not @readyToContinue) @$el.find('.sign-up-poke').toggleClass('hide', not @readyToContinue)
onGameSubmitted: (e) -> onGameSubmitted: (e) ->
ladderURL = "/play/ladder/#{@level.get('slug')}#my-matches" @returnToLadder()
returnToLadder: ->
# Preserve the supermodel as we navigate back to the ladder. # 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) -> playSelectionSound: (hero, preload=false) ->
return unless sounds = hero.get('soundTriggers')?.selected return unless sounds = hero.get('soundTriggers')?.selected
@ -433,9 +442,7 @@ module.exports = class HeroVictoryModal extends ModalView
onClickReturnToLadder: (e) -> onClickReturnToLadder: (e) ->
@playSound 'menu-button-click' @playSound 'menu-button-click'
e.preventDefault() e.preventDefault()
route = $(e.target).data('href') @returnToLadder()
# 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')]
onClickSignupButton: (e) -> onClickSignupButton: (e) ->
e.preventDefault() e.preventDefault()

View file

@ -1,6 +1,8 @@
CocoView = require 'views/core/CocoView' CocoView = require 'views/core/CocoView'
template = require 'templates/play/level/tome/cast_button' template = require 'templates/play/level/tome/cast_button'
{me} = require 'core/auth' {me} = require 'core/auth'
LadderSubmissionView = require 'views/play/common/LadderSubmissionView'
LevelSession = require 'models/LevelSession'
module.exports = class CastButtonView extends CocoView module.exports = class CastButtonView extends CocoView
id: 'cast-button-view' id: 'cast-button-view'
@ -28,6 +30,7 @@ module.exports = class CastButtonView extends CocoView
@castShortcut = '⇧↵' @castShortcut = '⇧↵'
@updateReplayabilityInterval = setInterval @updateReplayability, 1000 @updateReplayabilityInterval = setInterval @updateReplayability, 1000
@observing = options.session.get('creator') isnt me.id @observing = options.session.get('creator') isnt me.id
@loadMirrorSession() if @options.level.get('slug') in ['ace-of-coders']
destroy: -> destroy: ->
clearInterval @updateReplayabilityInterval clearInterval @updateReplayabilityInterval
@ -42,6 +45,7 @@ module.exports = class CastButtonView extends CocoView
context.castVerbose = castShortcutVerbose + ': ' + $.i18n.t('keyboard_shortcuts.run_code') context.castVerbose = castShortcutVerbose + ': ' + $.i18n.t('keyboard_shortcuts.run_code')
context.castRealTimeVerbose = castRealTimeShortcutVerbose + ': ' + $.i18n.t('keyboard_shortcuts.run_real_time') context.castRealTimeVerbose = castRealTimeShortcutVerbose + ': ' + $.i18n.t('keyboard_shortcuts.run_real_time')
context.observing = @observing context.observing = @observing
context.mirror = @mirrorSession?
context context
afterRender: -> 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 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. @$el.find('.submit-button').hide() # Hide submit until first win so that script can explain it.
@updateReplayability() @updateReplayability()
@updateLadderSubmissionViews()
attachTo: (spellView) -> attachTo: (spellView) ->
@$el.detach().prependTo(spellView.toolbarView.$el).show() @$el.detach().prependTo(spellView.toolbarView.$el).show()
@ -96,6 +101,7 @@ module.exports = class CastButtonView extends CocoView
@casting = false @casting = false
if @hasCastOnce # Don't play this sound the first time if @hasCastOnce # Don't play this sound the first time
@playSound 'cast-end', 0.5 @playSound 'cast-end', 0.5
_.delay (=> @ladderSubmissionView?.rankSession()), 1000 if @ladderSubmissionView
@hasCastOnce = true @hasCastOnce = true
@updateCastButton() @updateCastButton()
@world = e.world @world = e.world
@ -136,6 +142,7 @@ module.exports = class CastButtonView extends CocoView
castText = $.i18n.t('play_level.tome_cast_button_ran') castText = $.i18n.t('play_level.tome_cast_button_ran')
@castButton.text castText @castButton.text castText
#@castButton.prop 'disabled', not castable #@castButton.prop 'disabled', not castable
@ladderSubmissionView?.updateButton()
updateReplayability: => updateReplayability: =>
return if @destroyed return if @destroyed
@ -148,6 +155,19 @@ module.exports = class CastButtonView extends CocoView
waitTime = moment().add(timeUntilResubmit, 'ms').fromNow() waitTime = moment().add(timeUntilResubmit, 'ms').fromNow()
submitAgainLabel.text waitTime 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')
return unless placeholder.length
@ladderSubmissionView = new LadderSubmissionView session: @options.session, level: @options.level, mirrorSession: @mirrorSession
@insertSubView @ladderSubmissionView, placeholder
onJoinedRealTimeMultiplayerGame: (e) -> onJoinedRealTimeMultiplayerGame: (e) ->
@inRealTimeMultiplayerSession = true @inRealTimeMultiplayerSession = true

View file

@ -33,7 +33,7 @@ module.exports = class GameMenuModal extends ModalView
submenus = ['guide', 'options', 'save-load', 'multiplayer'] submenus = ['guide', 'options', 'save-load', 'multiplayer']
submenus = _.without submenus, 'guide' unless docs.specificArticles?.length or docs.generalArticles?.length 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, '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 @includedSubmenus = submenus
context.showTab = @options.showTab ? submenus[0] context.showTab = @options.showTab ? submenus[0]
context.submenus = submenus context.submenus = submenus

View file

@ -72,8 +72,15 @@ module.exports = class MultiplayerView extends CocoView
e.target.select() e.target.select()
onGameSubmitted: (e) -> 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" 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: -> updateLinkSection: ->
multiplayer = @$el.find('#multiplayer').prop('checked') multiplayer = @$el.find('#multiplayer').prop('checked')

View file

@ -4,6 +4,8 @@
// mongo <address>:<port>/<database> <script file> -u <username> -p <password> // mongo <address>:<port>/<database> <script file> -u <username> -p <password>
// NOTE: uses name as unique identifier, so changing the name will insert a new course // NOTE: uses name as unique identifier, so changing the name will insert a new course
// NOTE: concepts should match actual campaign levels
// NOTE: pricePerSeat in USD cents
var documents = var documents =
[ [
@ -13,7 +15,26 @@ var documents =
campaignID: ObjectId("55b29efd1cd6abe8ce07db0d"), campaignID: ObjectId("55b29efd1cd6abe8ce07db0d"),
concepts: ['basic_syntax', 'arguments', 'while_loops', 'strings', 'variables'], concepts: ['basic_syntax', 'arguments', 'while_loops', 'strings', 'variables'],
description: "Learn basic syntax, while loops, and the CodeCombat environment.", description: "Learn basic syntax, while loops, and the CodeCombat environment.",
pricePerSeat: NumberInt(0),
screenshot: "/images/pages/courses/101_info.png" screenshot: "/images/pages/courses/101_info.png"
},
{
name: "Computer Science 2",
slug: "computer-science-2",
campaignID: ObjectId("55b29efd1cd6abe8ce07db0d"),
concepts: ['basic_syntax', 'arguments', 'while_loops', 'strings', 'variables', 'if_statements'],
description: "Introduce Arguments, Variables, If Statements, and Arithmetic.",
pricePerSeat: NumberInt(400),
screenshot: "/images/pages/courses/102_info.png"
},
{
name: "Computer Science 3",
slug: "computer-science-3",
campaignID: ObjectId("55b29efd1cd6abe8ce07db0d"),
concepts: ['if_statements', 'arithmetic'],
description: "Learn how to handle input.",
pricePerSeat: NumberInt(400),
screenshot: "/images/pages/courses/103_info.png"
} }
]; ];

View file

@ -75,6 +75,7 @@ module.exports = class Handler
props props
# sending functions # sending functions
sendUnauthorizedError: (res) -> errors.unauthorized(res)
sendForbiddenError: (res) -> errors.forbidden(res) sendForbiddenError: (res) -> errors.forbidden(res)
sendNotFoundError: (res, message) -> errors.notFound(res, message) sendNotFoundError: (res, message) -> errors.notFound(res, message)
sendMethodNotAllowed: (res, message) -> errors.badMethod(res, @allowedMethods, message) sendMethodNotAllowed: (res, message) -> errors.badMethod(res, @allowedMethods, message)

View file

@ -5,9 +5,6 @@ jsonSchema = require '../../app/schemas/models/course_instance.schema'
CourseInstanceSchema = new mongoose.Schema {}, {strict: false, minimize: false, read:config.mongo.readpref} CourseInstanceSchema = new mongoose.Schema {}, {strict: false, minimize: false, read:config.mongo.readpref}
CourseInstanceSchema.plugin plugins.NamedPlugin
CourseInstanceSchema.plugin plugins.SearchablePlugin, {searchable: ['name', 'description']}
CourseInstanceSchema.statics.privateProperties = [] CourseInstanceSchema.statics.privateProperties = []
CourseInstanceSchema.statics.editableProperties = [ CourseInstanceSchema.statics.editableProperties = [
'description' 'description'

View file

@ -1,13 +1,98 @@
mongoose = require 'mongoose' async = require 'async'
Handler = require '../commons/Handler' Handler = require '../commons/Handler'
{getCoursesPrice} = require '../../app/core/utils'
Course = require './Course'
CourseInstance = require './CourseInstance' CourseInstance = require './CourseInstance'
Prepaid = require '../prepaids/Prepaid'
CourseInstanceHandler = class CourseInstanceHandler extends Handler CourseInstanceHandler = class CourseInstanceHandler extends Handler
modelClass: CourseInstance modelClass: CourseInstance
jsonSchema: require '../../app/schemas/models/course_instance.schema' jsonSchema: require '../../app/schemas/models/course_instance.schema'
allowedMethods: ['GET', 'POST', 'PUT', 'DELETE'] allowedMethods: ['GET', 'POST', 'PUT', 'DELETE']
logError: (user, msg) ->
console.warn "Course error: #{user.get('slug')} (#{user._id}): '#{msg}'"
hasAccess: (req) -> hasAccess: (req) ->
req.method is 'GET' or req.user?.isAdmin() req.method is 'GET' or req.user?.isAdmin()
hasAccessToDocument: (req, document, method=null) ->
return true if _.find document?.get('members'), (a) -> a.equals(req.user?.get('_id'))
req.user?.isAdmin()
getByRelationship: (req, res, args...) ->
relationship = args[1]
return @createAPI(req, res) if relationship is 'create'
super arguments...
createAPI: (req, res) ->
return @sendUnauthorizedError(res) unless req.user?
# Required Input
seats = req.body.seats
unless seats > 0
@logError(req.user, 'Course create API missing required seats count')
return @sendBadInputError(res, 'Missing required seats count')
# Optional - unspecified means create instances for all courses
courseID = req.body.courseID
# Optional
name = req.body.name
# Optional - as long as course(s) are all free
stripeToken = req.body.token
@getCourses courseID, (err, courses) =>
if err
@logError(req.user, err)
return @sendDatabaseError(res, err)
price = getCoursesPrice(courses, seats)
if price > 0 and not stripeToken
@logError(req.user, 'Course create API missing required Stripe token')
return @sendBadInputError(res, 'Missing required Stripe token')
# TODO: purchase prepaid for courses, price, and seats
Prepaid.generateNewCode (code) =>
return @sendDatabaseError(res, 'Database error.') unless code
prepaid = new Prepaid
creator: req.user.get('_id')
type: 'course'
code: code
properties:
courseIDs: (course.get('_id') for course in courses)
prepaid.set('maxRedeemers', seats) if seats
prepaid.save (err) =>
return @sendDatabaseError(res, err) if err
courseInstances = []
makeCreateInstanceFn = (course, name, prepaid) =>
(done) =>
@createInstance req, course, name, prepaid, (err, newInstance)=>
courseInstances.push newInstance unless err
done(err)
# tasks = []
# tasks.push(makeCreateInstanceFn(course, name, prepaid)) for course in courses
tasks = (makeCreateInstanceFn(course, name, prepaid) for course in courses)
async.parallel tasks, (err, results) =>
return @sendDatabaseError(res, err) if err
@sendCreated(res, courseInstances)
createInstance: (req, course, name, prepaid, done) =>
courseInstance = new CourseInstance
courseID: course.get('_id')
members: [req.user.get('_id')]
name: name
ownerID: req.user.get('_id')
prepaidID: prepaid.get('_id')
courseInstance.save (err, newInstance) =>
done(err, newInstance)
getCourses: (courseID, done) =>
if courseID
Course.findById courseID, (err, document) =>
done(err, [document])
else
Course.find {}, (err, documents) =>
done(err, documents)
module.exports = new CourseInstanceHandler() module.exports = new CourseInstanceHandler()

View file

@ -379,7 +379,8 @@ PaymentHandler = class PaymentHandler extends Handler
sendPaymentHipChatMessage: (options) -> sendPaymentHipChatMessage: (options) ->
try try
message = "#{options.user?.get('name')} bought #{options.payment?.get('amount')} via #{options.payment?.get('service')}." message = "#{options.user?.get('name')} bought #{options.payment?.get('amount')} via #{options.payment?.get('service')}"
message += " for #{options.payment.get('description')}" if options.payment?.get('description')
hipchat.sendHipChatMessage message, ['tower'] hipchat.sendHipChatMessage message, ['tower']
catch e catch e
log.error "Couldn't send HipChat message on payment because of error: #{e}" log.error "Couldn't send HipChat message on payment because of error: #{e}"

View file

@ -66,7 +66,7 @@ getRandomSessions = (user, callback) ->
# Sampling by level: we pick a level, then find a human and ogre session for that level, one at random, one biased towards recent submissions. # Sampling by level: we pick a level, then find a human and ogre session for that level, one at random, one biased towards recent submissions.
#ladderLevelIDs = ['greed', 'criss-cross', 'brawlwood', 'dungeon-arena', 'gold-rush', 'sky-span'] # Let's not give any extra simulations to old ladders. #ladderLevelIDs = ['greed', 'criss-cross', 'brawlwood', 'dungeon-arena', 'gold-rush', 'sky-span'] # Let's not give any extra simulations to old ladders.
ladderLevelIDs = ['dueling-grounds', 'cavern-survival', 'multiplayer-treasure-grove', 'harrowland', 'zero-sum'] ladderLevelIDs = ['dueling-grounds', 'cavern-survival', 'multiplayer-treasure-grove', 'harrowland', 'zero-sum', 'ace-of-coders']
sampleByLevel = (callback) -> sampleByLevel = (callback) ->
levelID = _.sample ladderLevelIDs levelID = _.sample ladderLevelIDs
favorRecentHumans = Math.random() < 0.5 # We pick one session favoring recent submissions, then find another one uniformly to play against favorRecentHumans = Math.random() < 0.5 # We pick one session favoring recent submissions, then find another one uniformly to play against

View file

@ -11,6 +11,7 @@ log = require 'winston'
moment = require 'moment' moment = require 'moment'
AnalyticsLogEvent = require '../analytics/AnalyticsLogEvent' AnalyticsLogEvent = require '../analytics/AnalyticsLogEvent'
Clan = require '../clans/Clan' Clan = require '../clans/Clan'
CourseInstance = require '../courses/CourseInstance'
LevelSession = require '../levels/sessions/LevelSession' LevelSession = require '../levels/sessions/LevelSession'
LevelSessionHandler = require '../levels/sessions/level_session_handler' LevelSessionHandler = require '../levels/sessions/level_session_handler'
Payment = require '../payments/Payment' Payment = require '../payments/Payment'
@ -309,6 +310,7 @@ UserHandler = class UserHandler extends Handler
return @getLevelSessions(req, res, args[0]) if args[1] is 'level.sessions' return @getLevelSessions(req, res, args[0]) if args[1] is 'level.sessions'
return @getCandidates(req, res) if args[1] is 'candidates' return @getCandidates(req, res) if args[1] is 'candidates'
return @getClans(req, res, args[0]) if args[1] is 'clans' return @getClans(req, res, args[0]) if args[1] is 'clans'
return @getCourseInstances(req, res, args[0]) if args[1] is 'course_instances'
return @getEmployers(req, res) if args[1] is 'employers' return @getEmployers(req, res) if args[1] is 'employers'
return @getSimulatorLeaderboard(req, res, args[0]) if args[1] is 'simulatorLeaderboard' return @getSimulatorLeaderboard(req, res, args[0]) if args[1] is 'simulatorLeaderboard'
return @getMySimulatorLeaderboardRank(req, res, args[0]) if args[1] is 'simulator_leaderboard_rank' return @getMySimulatorLeaderboardRank(req, res, args[0]) if args[1] is 'simulator_leaderboard_rank'
@ -605,6 +607,13 @@ UserHandler = class UserHandler extends Handler
return @sendDatabaseError(res, err) if err return @sendDatabaseError(res, err) if err
@sendSuccess(res, documents) @sendSuccess(res, documents)
getCourseInstances: (req, res, userIDOrSlug) ->
@getDocumentForIdOrSlug userIDOrSlug, (err, user) =>
return @sendNotFoundError(res) unless user
CourseInstance.find {members: {$in: [user.get('_id')]}}, (err, documents) =>
return @sendDatabaseError(res, err) if err
@sendSuccess(res, documents)
formatCandidate: (authorized, document) -> formatCandidate: (authorized, document) ->
fields = if authorized then ['name', 'jobProfile', 'jobProfileApproved', 'photoURL', '_id'] else ['_id','jobProfile', 'jobProfileApproved'] fields = if authorized then ['name', 'jobProfile', 'jobProfileApproved', 'photoURL', '_id'] else ['_id','jobProfile', 'jobProfileApproved']
obj = _.pick document.toObject(), fields obj = _.pick document.toObject(), fields

View file

@ -30,6 +30,8 @@ models_path = [
'../../server/articles/Article' '../../server/articles/Article'
'../../server/campaigns/Campaign' '../../server/campaigns/Campaign'
'../../server/clans/Clan' '../../server/clans/Clan'
'../../server/courses/Course'
'../../server/courses/CourseInstance'
'../../server/levels/Level' '../../server/levels/Level'
'../../server/levels/components/LevelComponent' '../../server/levels/components/LevelComponent'
'../../server/levels/systems/LevelSystem' '../../server/levels/systems/LevelSystem'

View file

@ -0,0 +1,195 @@
async = require 'async'
config = require '../../../server_config'
require '../common'
stripe = require('stripe')(config.stripe.secretKey)
# TODO: add permissiosn tests
describe 'CourseInstance', ->
courseInstanceURL = getURL('/db/course_instance/-/create')
userURL = getURL('/db/user')
nameCount = 0
createName = (name) -> name + nameCount++
createCourse = (pricePerSeat, done) ->
name = createName 'course '
course = new Course
name: name
campaignID: ObjectId("55b29efd1cd6abe8ce07db0d")
concepts: ['basic_syntax', 'arguments', 'while_loops', 'strings', 'variables']
description: "Learn basic syntax, while loops, and the CodeCombat environment."
pricePerSeat: pricePerSeat
screenshot: "/images/pages/courses/101_info.png"
course.save (err, course) ->
return done(err) if err
done(err, course)
createCourseInstances = (user, courseID, seats, token, done) ->
name = createName 'course instance '
requestBody =
courseID: courseID
name: name
seats: seats
token: token
request.post {uri: courseInstanceURL, json: requestBody }, (err, res) ->
expect(err).toBeNull()
expect(res.statusCode).toBe(201)
CourseInstance.find {name: name}, (err, courseInstances) ->
expect(err).toBeNull()
makeCourseInstanceVerifyFn = (courseInstance) ->
(done) ->
expect(courseInstance.get('name')).toEqual(name)
expect(courseInstance.get('ownerID')).toEqual(user.get('_id'))
expect(courseInstance.get('members')).toContain(user.get('_id'))
query = {$and: [{creator: user.get('_id')}]}
query.$and.push {'properties.courseIDs': {$in: [courseID]}} if courseID
Prepaid.find query, (err, prepaids) ->
expect(err).toBeNull()
return done(err) if err
expect(prepaids?.length).toEqual(1)
return done() unless prepaids?.length > 0
expect(prepaids[0].get('type')).toEqual('course')
expect(prepaids[0].get('maxRedeemers')).toEqual(seats) if seats
# TODO: verify Payment
done(err)
tasks = []
for courseInstance in courseInstances
tasks.push makeCourseInstanceVerifyFn(courseInstance)
async.parallel tasks, (err) =>
return done(err) if err
done(err, courseInstances)
it 'Clear database users and clans', (done) ->
clearModels [User, Course, CourseInstance, Prepaid], (err) ->
throw err if err
done()
describe 'Single courses', ->
it 'Create for free course 1 seat', (done) ->
stripe.tokens.create {
card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' }
}, (err, token) ->
loginNewUser (user1) ->
createCourse 0, (err, course) ->
expect(err).toBeNull()
return done(err) if err
createCourseInstances user1, course.get('_id'), 1, token.id, (err, courseInstances) ->
expect(err).toBeNull()
return done(err) if err
expect(courseInstances.length).toEqual(1)
done()
it 'Create for free course no seats', (done) ->
stripe.tokens.create {
card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' }
}, (err, token) ->
loginNewUser (user1) ->
createCourse 0, (err, course) ->
expect(err).toBeNull()
return done(err) if err
name = createName 'course instance '
requestBody =
courseID: course.get('_id')
name: createName('course instance ')
request.post {uri: courseInstanceURL, json: requestBody }, (err, res) ->
expect(err).toBeNull()
expect(res.statusCode).toBe(422)
done()
it 'Create for free course no token', (done) ->
loginNewUser (user1) ->
createCourse 0, (err, course) ->
expect(err).toBeNull()
return done(err) if err
createCourseInstances user1, course.get('_id'), 2, null, (err, courseInstances) ->
expect(err).toBeNull()
return done(err) if err
expect(courseInstances.length).toEqual(1)
done()
it 'Create for paid course 1 seat', (done) ->
stripe.tokens.create {
card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' }
}, (err, token) ->
loginNewUser (user1) ->
createCourse 7000, (err, course) ->
expect(err).toBeNull()
return done(err) if err
createCourseInstances user1, course.get('_id'), 1, token.id, (err, courseInstances) ->
expect(err).toBeNull()
return done(err) if err
expect(courseInstances.length).toEqual(1)
done()
it 'Create for paid course 50 seats', (done) ->
stripe.tokens.create {
card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' }
}, (err, token) ->
loginNewUser (user1) ->
createCourse 7000, (err, course) ->
expect(err).toBeNull()
return done(err) if err
createCourseInstances user1, course.get('_id'), 50, token.id, (err, courseInstances) ->
expect(err).toBeNull()
return done(err) if err
expect(courseInstances.length).toEqual(1)
done()
it 'Create for paid course no token', (done) ->
loginNewUser (user1) ->
createCourse 7000, (err, course) ->
expect(err).toBeNull()
return done(err) if err
name = createName 'course instance '
requestBody =
courseID: course.get('_id')
name: createName('course instance ')
seats: 1
request.post {uri: courseInstanceURL, json: requestBody }, (err, res) ->
expect(err).toBeNull()
expect(res.statusCode).toBe(422)
done()
it 'Create for paid course -1 seats', (done) ->
stripe.tokens.create {
card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' }
}, (err, token) ->
loginNewUser (user1) ->
createCourse 7000, (err, course) ->
expect(err).toBeNull()
return done(err) if err
name = createName 'course instance '
requestBody =
courseID: course.get('_id')
name: createName('course instance ')
seats: -1
request.post {uri: courseInstanceURL, json: requestBody }, (err, res) ->
expect(err).toBeNull()
expect(res.statusCode).toBe(422)
done()
describe 'All Courses', ->
it 'Create for 50 seats', (done) ->
stripe.tokens.create {
card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' }
}, (err, token) ->
loginNewUser (user1) ->
createCourse 7000, (err, course1) ->
expect(err).toBeNull()
return done(err) if err
createCourse 7000, (err, course2) ->
expect(err).toBeNull()
return done(err) if err
createCourseInstances user1, null, 50, token.id, (err, courseInstances) ->
expect(err).toBeNull()
return done(err) if err
Course.find {}, (err, courses) ->
expect(err).toBeNull()
return done(err) if err
expect(courseInstances.length).toEqual(courses.length)
done()