Merge branch 'master' into production

This commit is contained in:
Matt Lott 2016-08-10 15:08:37 -07:00
commit eb70d90d9c
33 changed files with 324 additions and 134 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 MiB

View file

@ -769,7 +769,7 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription:
about:
main_title: "Wenn du das Programmieren erlernen willst, musst du (viel) Code schreiben."
main_description: "Bei CodeCombat ist es unser Job das du das mit einem Lächeln im Gesicht tust."
main_description: "Bei CodeCombat ist es unser Job, dass du das mit einem Lächeln im Gesicht tust."
mission_link: "Mission"
team_link: "Team"
story_link: "Geschichte"

View file

@ -304,6 +304,7 @@
signup_as_individual: "Sign up as an Individual"
enter_class_code: "Enter your Class Code"
enter_birthdate: "Enter your birthdate:"
parent_use_birthdate: "Parents, use your own birthdate."
ask_teacher_1: "Ask your teacher for your Class Code."
ask_teacher_2: "Not part of a class? Create an "
ask_teacher_3: "Individual Account"
@ -475,6 +476,7 @@
victory_experience_gained: "XP Gained"
victory_gems_gained: "Gems Gained"
victory_new_item: "New Item"
victory_new_hero: "New Hero"
victory_viking_code_school: "Holy smokes, that was a hard level you just beat! If you aren't already a software developer, you should be. You just got fast-tracked for acceptance with Viking Code School, where you can take your skills to the next level and become a professional web developer in 14 weeks."
victory_become_a_viking: "Become a Viking"
victory_no_progress_for_teachers: "Progress is not saved for teachers. But, you can add a student account to your classroom for yourself."
@ -571,6 +573,18 @@
tip_good_idea: "The best way to have a good idea is to have a lot of ideas. - Linus Pauling"
tip_programming_not_about_computers: "Computer Science is no more about computers than astronomy is about telescopes. - Edsger Dijkstra"
tip_mulan: "Believe you can, then you will. - Mulan"
play_game_dev_level:
created_by: "Created by {{name}}"
how_to_play_title: "How to play:"
how_to_play_1: "Use the mouse to control the hero!"
how_to_play_2: "Click anywhere on the map to move to that location."
how_to_play_3: "Click on the ogres to attack them."
restart: "Restart Level"
play: "Play Level"
play_more_codecombat: "Play More CodeCombat"
default_student_instructions: "Click to control your hero and win your game!"
back_to_coding: "Back to Coding"
game_menu:
inventory_tab: "Inventory"
@ -804,6 +818,7 @@
elliot_title: "Partnership Manager"
elliot_blurb: "Mindreader"
lisa_title: "Market Development Rep"
sean_title: "Territory Manager"
retrostyle_title: "Illustration"
retrostyle_blurb: "RetroStyle Games"
jose_title: "Music"

View file

@ -1,4 +1,4 @@
module.exports = nativeDescription: "עברית", englishDescription: "Hebrew", translation:
module.exports = nativeDescription: "עברית", englishDescription: "Hebrew", translation:
home:
slogan: "גם לשחק וגם ללמוד לתכנת"
no_ie: "המשחק לא עובד באקפלורר 8 וישן יותר. סליחה!" # Warning that only shows up in IE8 and older

View file

@ -132,7 +132,7 @@ module.exports = nativeDescription: "Nederlands (Nederland)", englishDescription
help_suff: "en we nemen contact op!"
modal:
# cancel: "Cancel"
cancel: "Annuleren"
close: "Sluiten"
okay: "Oké"
@ -256,13 +256,13 @@ module.exports = nativeDescription: "Nederlands (Nederland)", englishDescription
signup_switch: "Wil je een account maken?"
signup:
# create_student_header: "Create Student Account"
# create_teacher_header: "Create Teacher Account"
# create_individual_header: "Create Individual Account"
# create_header: "Create Account"
create_student_header: "Creëer studenten account"
create_teacher_header: "Creeër leraren account"
create_individual_header: "Creeër persoonlijk account"
create_header: "Creëer Account"
email_announcements: "Ontvang aankondigingen via e-mail" # {change}
creating: "Account aanmaken..."
# create_account: "Create Account"
create_account: "Creëer account"
sign_up: "Aanmelden"
log_in: "inloggen met wachtwoord"
required: "Je moet inloggen om daarheen te gaan."
@ -270,7 +270,7 @@ module.exports = nativeDescription: "Nederlands (Nederland)", englishDescription
school_name: "Schoolnaam en stad"
optional: "optioneel"
school_name_placeholder: "Voorbeeld middelbare school, Amsterdam"
# connect_with: "Connect with:"
connect_with: "Verbind met:"
connected_gplus_header: "Je bent ingelogd met Google+!"
connected_gplus_p: "Maak je inschrijving compleet zodat je kan inloggen met je Google+ account."
gplus_exists: "Jouw Google+ account is al gekoppeld!"
@ -280,35 +280,35 @@ module.exports = nativeDescription: "Nederlands (Nederland)", englishDescription
hey_students: "Leerlingen, voer hier de klassencode van je docent in."
birthday: "Verjaardag"
# parent_email_blurb: "We know you can't wait to learn programming — we're excited too! Your parents will receive an email with further instructions on how to create an account for you. Email {{email_link}} if you have any questions."
# classroom_not_found: "No classes exist with this Class Code. Check your spelling or ask your teacher for help."
# checking: "Checking..."
# account_exists: "This email is already in use:" # {change}
# sign_in: "Sign in"
# email_good: "Email looks good!"
# name_taken: "Username already taken! Try {{suggestedName}}?"
# name_available: "Username available!"
# name_is_email: "Username may not be an email"
# choose_type: "Choose your account type:"
# teacher_type_1: "Teach programming using CodeCombat!"
# teacher_type_2: "Set up your class"
classroom_not_found: "Er zijn geen klassen met deze klassencode. Controleer je spelling of vraag je leraar om hulp."
checking: "Controleren..."
account_exists: "Dit emailadres bestaat al:" # {change}
sign_in: "Log in"
email_good: "Email ziet er goed uit!"
name_taken: "Gebruikersnaam bestaat al! Probeer {{suggestedName}}?"
name_available: "Gebruikersnaam beschikbaar!"
name_is_email: "Gebruikersnaam mag geen email zijn"
choose_type: "Kies je account type:"
teacher_type_1: "Leer ze programmeren met gebruik van CodeCombat!"
teacher_type_2: "Maak je klas"
# teacher_type_3: "Access Course Guides"
# teacher_type_4: "View student progress"
# signup_as_teacher: "Sign up as a Teacher"
# student_type_1: "Learn to program while playing an engaging game!"
# student_type_2: "Play with your class"
# student_type_3: "Compete in arenas"
# student_type_4: "Choose your hero!"
# student_type_5: "Have your Class Code ready!"
# signup_as_student: "Sign up as a Student"
# individuals_or_parents: "Individuals & Parents"
# individual_type: "For players learning to code outside of a class. Parents should sign up for an account here."
# signup_as_individual: "Sign up as an Individual"
# enter_class_code: "Enter your Class Code"
# enter_birthdate: "Enter your birthdate:"
# ask_teacher_1: "Ask your teacher for your Class Code."
# ask_teacher_2: "Not part of a class? Create an "
# ask_teacher_3: "Individual Account"
# ask_teacher_4: " instead."
teacher_type_4: "Bekijk voortgang studenten"
signup_as_teacher: "Registreer als een docent"
student_type_1: "Leer programmeren terwijl je een uitdagend spel speelt!"
student_type_2: "Speel met je klas"
student_type_3: "Strijd in arena's"
student_type_4: "Kies je held!"
student_type_5: "Heb je klassencode bij de hand!"
signup_as_student: "Registreer als een student"
individuals_or_parents: "Individueel & Ouders"
individual_type: "Voor spelers die leren coderen buiten school. Ouders moeten zich hier aanmelden."
signup_as_individual: "Registreer als een individu (persoonlijk)"
enter_class_code: "Vul je klassencode in"
enter_birthdate: "Vul je geboortedatum in:"
ask_teacher_1: "Vraag je leraar om een klassencode."
ask_teacher_2: "Maak je geen deel uit een klas? Maak dan een "
ask_teacher_3: "Individuele account"
ask_teacher_4: "."
# about_to_join: "You're about to join:"
# enter_parent_email: "Enter your parents email address:"
# parent_email_error: "Something went wrong when trying to send the email. Check the email address and try again."

View file

@ -16,7 +16,7 @@ module.exports = nativeDescription: "Português do Brasil", englishDescription:
new_home:
slogan: "O jogo mais envolvente para aprender programação."
# classroom_edition: "Classroom Edition:"
classroom_edition: "Editar sala de aula:"
learn_to_code: "Aprenda a programar:"
teacher: "Professor"
student: "Aluno"
@ -25,38 +25,38 @@ module.exports = nativeDescription: "Português do Brasil", englishDescription:
im_a_student: "Eu sou um Aluno"
learn_more: "Aprenda mais"
classroom_in_a_box: "Uma sala de aula in-a-box para o ensino de ciência da computação."
# codecombat_is: "CodeCombat is a platform <strong>for students</strong> to learn computer science while playing through a real game."
# our_courses: "Our courses have been specifically playtested to <strong>excel in the classroom</strong>, even by teachers with little to no prior programming experience."
codecombat_is: "CodeCombat é uma plataforma <strong>para estudantes</strong> aprenderem ciência da computação através de um jogo real."
our_courses: "Nossos cursos foram pensados especificamente para <strong>o uso em sala de aula</strong>, mesmo para professores com pouco conhecimento prévio em programação."
top_screenshots_hint: "Alunos escrevem seus códigos e veem suas mudanças em tempo real."
designed_with: "Projetado com professores em mente"
real_code: "Código escrito, de verdade"
from_the_first_level: "a partir do primeiro nível"
# getting_students: "Getting students to typed code as quickly as possible is critical to learning programming syntax and proper structure."
getting_students: "Fazer os estudantes digitarem o código o mais rapido possível é fundamental para aprender a sintaxe de programação e estrurutura apropriada."
educator_resources: "Recursos do educador"
course_guides: "e guia dos cursos"
# teaching_computer_science: "Teaching computer science does not require a costly degree, because we provide tools to support educators of all backgrounds."
teaching_computer_science: "Ensinar ciência da computação não requer um diploma oneroso, pois nós disponibilizamos ferramentas para apoiar todoos os tipos de educadores."
accessible_to: "Acessível a"
everyone: "todos"
democratizing: "Democratizar o processo de aprendizagem de codificação é o objectivo da nossa filosofia. Qualquer um deve ser capaz de aprender a programar."
forgot_learning: "Eu acho que eles realmente esqueceram que estavam aprendendo de verdade."
wanted_to_do: "Programar é algo que eu sempre quis fazer, e eu nunca pensei que seria capaz de aprender isso na escola."
# why_games: "Why is learning through games important?"
# games_reward: "Games reward the productive struggle."
# encourage: "Gaming is a medium that encourages interaction, discovery, and trial-and-error. A good game challenges the player to master skills over time, which is the same critical process students go through as they learn."
# excel: "Games excel at rewarding"
# struggle: "productive struggle"
# kind_of_struggle: "the kind of struggle that results in learning thats engaging and"
why_games: "Porque é importante aprender através de jogos?"
games_reward: "Jogos recompensam o esforço produtivo."
encourage: "Jogar é um meio de encorajar a interação, descoberta, e a tentativa e erro. Um bom jogo desafia o jogador a dominar as habilidades com o tempo, que é o mesmo processo crítico que alunos passam quando eles estão aprendendo algo."
excel: "Jogos destacam-se pela recompesa do"
struggle: "esforço produtivo"
kind_of_struggle: "o tipo de esforço resultante do aprendizado é cativante,"
motivating: "motivador"
# not_tedious: "not tedious."
not_tedious: "e divertido."
gaming_is_good: "Estudos indicam que jogos são bons para o cérebro das crianças. (é verdade!)"
# game_based: "When game-based learning systems are"
compared: "comparado"
# conventional: "against conventional assessment methods, the difference is clear: games are better at helping students retain knowledge, concentrate and"
# perform_at_higher_level: "perform at a higher level of achievement"
# feedback: "Games also provide real-time feedback that allows students to adjust their solution path and understand concepts more holistically, instead of being limited to just “correct” or “incorrect” answers."
game_based: "Quando sistemas de aprendizado baseados em jogos são"
compared: "comparados"
conventional: "com métodos de avaliação convencional a diferença é clara: jogos são melhores e ajudam alunos a reter o conhecimento, concentração e"
perform_at_higher_level: "desenvolver um nível superior de realização"
feedback: "Jogos também fornecem respostas em tempo real permitindo que os alunos criem seus caminhos para a solução e entendam conceitos mais holisticamente, ao invés de limitar as repostas como “corretas” ou “incorretas”."
real_game: "Um jogo de verdade, com programação de verdade."
# great_game: "A great game is more than just badges and achievements - its about a players journey, well-designed puzzles, and the ability to tackle challenges with agency and confidence."
# agency: "CodeCombat is a game that gives players that agency and confidence with our robust typed code engine, which helps beginner and advanced students alike write proper, valid code."
great_game: "Um grande jogo é mais do que apenas emblemas e realizações - Isso é sobre a jornada de um jogador, quebra-cabeças bem desenhados, e a habilidade para enfrentar desafios com ação e confiança."
agency: "CodeCombat é um jogo que fornece aos jogadores essa ação e confiança com nosso robusto motor de digitação de código, que ajuda alunos iniciantes e avançados tanto na escrita quanto na validação do código."
# request_demo_title: "Get your students started today!"
# request_demo_subtitle: "Request a demo and get your students started in less than an hour."
# get_started_title: "Set up your class today"

View file

@ -278,6 +278,7 @@ LevelSchema = c.object {
c.extendNamedProperties LevelSchema # let's have the name be the first property
_.extend LevelSchema.properties,
description: {title: 'Description', description: 'A short explanation of what this level is about.', type: 'string', maxLength: 65536, format: 'markdown'}
studentPlayInstructions: {title: 'Student Play Instructions', description: 'Instructions for game dev levels when students play them.', type: 'string', maxLength: 65536, format: 'markdown'}
loadingTip: { type: 'string', title: 'Loading Tip', description: 'What to show for this level while it\'s loading.' }
documentation: c.object {title: 'Documentation', description: 'Documentation articles relating to this level.', 'default': {specificArticles: [], generalArticles: []}},
specificArticles: c.array {title: 'Specific Articles', description: 'Specific documentation articles that live only in this level.', uniqueItems: true }, SpecificArticleSchema
@ -312,7 +313,7 @@ _.extend LevelSchema.properties,
body: {type: 'string', format: 'markdown', title: 'Body Text', description: 'Inserted into the Victory Modal once this level is complete. Tell the player they did a good job and what they accomplished!'},
i18n: {type: 'object', format: 'i18n', props: ['body'], description: 'Help translate this victory message'}
}
i18n: {type: 'object', format: 'i18n', props: ['name', 'description', 'loadingTip'], description: 'Help translate this level'}
i18n: {type: 'object', format: 'i18n', props: ['name', 'description', 'loadingTip', 'studentPlayInstructions'], description: 'Help translate this level'}
icon: {type: 'string', format: 'image-file', title: 'Icon'}
banner: {type: 'string', format: 'image-file', title: 'Banner'}
goals: c.array {title: 'Goals', description: 'An array of goals which are visible to the player and can trigger scripts.'}, GoalSchema

View file

@ -117,6 +117,7 @@ module.exports =
'level:show-victory': c.object {required: ['showModal']},
showModal: {type: 'boolean'}
manual: { type: 'boolean' }
'level:highlight-dom': c.object {required: ['selector']},
selector: {type: 'string'}

View file

@ -16,3 +16,7 @@
.teacher-name
font-size: 14pt
.parent_birthdate
font-size: 11pt
margin-top: 20px

View file

@ -592,6 +592,79 @@ $gameControlMargin: 30px
margin: 15px 0
min-width: 100px
.beta-container
$betaImagesWidth: 1902px
width: $campaignWidth
height: $campaignHeight
display: inline-block
flex-shrink: 0
position: relative
.beta-campaign
width: $campaignWidth
height: $campaignHeight / 2
display: inline-block
flex-shrink: 0
position: relative
cursor: pointer
// http://easings.net/#easeOutBack plus tweaked a bit: http://cubic-bezier.com/#.11,.67,.08,1.42
@include transition(0.25s cubic-bezier(0.11, 0.67, 0.8, 1.42))
&:hover
@include scale($campaignHoverScale)
&.silhouette
@include filter(contrast(50%) brightness(65%))
pointer-events: none
&.locked
@include filter(contrast(80%) brightness(80%))
pointer-events: none
.campaign-label
position: absolute
top: 40%
width: 100%
text-align: center
.campaign-name, .levels-completed, .campaign-locked
margin: 0
color: white
text-shadow: black 2px 2px 0, black -2px -2px 0, black 2px -2px 0, black -2px 2px 0, black 2px 0px 0, black 0px -2px 0, black -2px 0px 0, black 0px 2px 0
.campaign-locked
margin: 32px 0
.campaign-description
margin: 0px 40px
background: transparent url(/images/level/popover_border_background.png) no-repeat
background-size: 100% 100%
padding: 12px
color: black
font-size: 12px
.levels-completed
font-size: 22px
.play-button
margin: 10px 0
min-width: 100px
color: white
.background-container
position: absolute
left: $campaignWidth / 4
width: $campaignWidth / 2
height: $campaignHeight / 2
background: transparent url(/images/pages/play/portal-beta-campaigns.png) no-repeat 0 0
background-size: $betaImagesWidth / 2
&.campaign-web-dev-1
background-position: -151px 0px
&.campaign-game-dev-1
background-position: -454px 0px
.small-nav-logo, .picoctf-powered-by
position: absolute
top: 1%

View file

@ -52,6 +52,7 @@ body.dialogue-view-active
.spell-palette-popover.popover
// Only those popovers which are our direct children (spell documentation)
top: 80px !important
left: auto !important
right: 45%
max-width: 600px

View file

@ -146,6 +146,13 @@ block content
small(data-i18n="about.lisa_title")
br
li
img(src="/images/pages/about/sean_small.png").img-thumbnail
.team-bio
h6.label.team-name Sean McNulty
small(data-i18n="about.sean_title")
br
// Part time / contract
li
a(href="http://floor.is/lava/" rel="external")

View file

@ -50,6 +50,7 @@ form.modal-body.segment-check
- var thisYear = new Date().getFullYear()
for year in _.range(thisYear, thisYear - 100, -1)
option(selected=(year == view.signupState.get('birthdayYear'))) #{year}
.parent_birthdate(data-i18n="signup.parent_use_birthdate")
default
p

View file

@ -22,12 +22,9 @@ block content
#article-treema
#article-view
h3(data-i18n="resources.patches") Patches
.patches-view
hr
div#error-view

View file

@ -7,7 +7,7 @@ mixin box
div
a.teacher-btn.btn.btn-primary.btn-lg.btn-block(data-event-action="Homepage Click Teacher Button CTA", data-i18n="new_home.im_a_teacher")
div
a.student-btn.btn.btn-forest.btn-lg.btn-block(href="/courses", data-event-action="Homepage Click Student Button CTA", data-i18n="new_home.im_a_student")
a.student-btn.btn.btn-forest.btn-lg.btn-block(href="/home#create-account-student", data-event-action="Homepage Click Student Button CTA", data-i18n="new_home.im_a_student")
h6#learn-to-code-header(data-i18n="new_home.learn_to_code")
a.btn.btn-gold.btn-lg.btn-block.play-btn(href=view.playURL, data-event-action="Homepage Play Now CTA", data-i18n="new_home.play_now")

View file

@ -100,28 +100,57 @@ if view.showAds()
else
.portal
.portals
for campaignSlug in ['dungeon', 'forest', 'desert', 'mountain', 'glacier', 'volcano']
- var campaign = campaigns[campaignSlug];
- var godmode = me.get('permissions', true).indexOf('godmode') != -1;
div(class="campaign #{campaignSlug}" + (campaign ? "" : " silhouette") + (campaign && campaign.locked && !godmode ? " locked" : ""), data-campaign-slug=campaignSlug)
.campaign-label
h2.campaign-name
if campaign
span= i18n(campaign.attributes, 'fullName')
else
span ???
if campaign && campaign.levelsTotal
h3.levels-completed
span= campaign.levelsCompleted
| /
span= campaign.levelsTotal
if campaign && campaign.locked && !godmode
h3.campaign-locked(data-i18n="play.locked") Locked
else if campaign
btn(data-i18n="common.play").btn.btn-illustrated.btn-lg.btn-success.play-button
if campaign && campaign.get('description')
p.campaign-description
span= i18n(campaign.attributes, 'description')
for campaignSlug in ['dungeon', 'beta-campaigns', 'forest', 'desert', 'mountain', 'glacier', 'volcano']
if campaignSlug === 'beta-campaigns'
- var betaSlugs = _.shuffle(['campaign-game-dev-1', 'campaign-web-dev-1']);
.beta-container
each campaignSlug in betaSlugs
- var campaign = campaigns[campaignSlug];
if !campaign
- continue;
div(class="beta-campaign" + (campaign ? "" : " silhouette") + (campaign && campaign.locked && !godmode ? " locked" : ""), data-campaign-slug=campaignSlug)
.background-container(class="#{campaignSlug}")
.campaign-label
h3.campaign-name
if campaign
span= i18n(campaign.attributes, 'fullName')
if campaign.levelsTotal
span.spl= campaign.levelsCompleted
| /
span= campaign.levelsTotal
else
span ???
if campaign && campaign.locked && !godmode
h4.campaign-locked(data-i18n="play.locked") Locked
else if campaign
btn.btn.btn-illustrated.btn-lg.btn-primary.play-button(data-i18n="common.play")
if campaign && campaign.get('description')
p.campaign-description
span= i18n(campaign.attributes, 'description')
else
- var campaign = campaigns[campaignSlug];
if !campaign
- continue;
- var godmode = me.get('permissions', true).indexOf('godmode') != -1;
div(class="campaign #{campaignSlug}" + (campaign ? "" : " silhouette") + (campaign && campaign.locked && !godmode ? " locked" : ""), data-campaign-slug=campaignSlug)
.campaign-label
h2.campaign-name
if campaign
span= i18n(campaign.attributes, 'fullName')
else
span ???
if campaign && campaign.levelsTotal
h3.levels-completed
span= campaign.levelsCompleted
| /
span= campaign.levelsTotal
if campaign && campaign.locked && !godmode
h3.campaign-locked(data-i18n="play.locked") Locked
else if campaign
btn(data-i18n="common.play").btn.btn-illustrated.btn-lg.btn-success.play-button
if campaign && campaign.get('description')
p.campaign-description
span= i18n(campaign.attributes, 'description')
.game-controls.header-font.picoctf-hide
button.btn.poll.hidden(data-i18n="[title]play.poll")

View file

@ -79,7 +79,10 @@ block modal-body-content
.reward-panel.hero(data-hero-thang-type=hero.get('original'))
.reward-image-container(class=animate ? 'pending-reward-image' : 'show')
img(src=hero.getPortraitURL())
.reward-text= animate ? 'New Hero' : hero.get('name')
if animate
.reward-text(data-i18n="play_level.victory_new_hero") New Hero
else
.reward-text= i18n(hero.attributes, 'name')
if rewards.items
for item in rewards.items

View file

@ -15,7 +15,7 @@
if view.level.id && view.session.id
h3.m-y-1= view.level.get('name')
h4 Created by #{view.session.get('creatorName')}
h4= view.state.get('creatorString')
hr
if view.state.get('loading')
@ -24,24 +24,24 @@
.progress-bar(style="width: #{view.state.get('progress')}")
if ready
h3 Goals
h3(data-i18n="play_level.goals")
for goalName in view.state.get('goalNames')
p= goalName
hr
h3 How to play:
p Use the mouse to control the hero!
p Click anywhere on the map to move to that location.
p Click on the ogres to attack them.
h3(data-i18n="play_game_dev_level.how_to_play_title")
p(data-i18n="play_game_dev_level.how_to_play_1")
p(data-i18n="play_game_dev_level.how_to_play_2")
p(data-i18n="play_game_dev_level.how_to_play_3")
if ready
.panel-footer
- var playing = view.state.get('playing')
if playing
button#play-btn.btn.btn-lg.btn-burgandy.btn-block Restart Level
button#play-btn.btn.btn-lg.btn-burgandy.btn-block(data-i18n="play_game_dev_level.restart")
else
button#play-btn.btn.btn-lg.btn-forest.btn-block Play Level
button#play-btn.btn.btn-lg.btn-forest.btn-block(data-i18n="play_game_dev_level.play")
#share-row.m-t-3
if ready
@ -55,4 +55,4 @@
span(data-i18n='sharing.copy_url')
.panel-body
a#play-more-codecombat-btn.btn.btn-lg.btn-navy-alt.pull-right(href="/") Play More CodeCombat
a#play-more-codecombat-btn.btn.btn-lg.btn-navy-alt.pull-right(href="/", data-i18n="play_game_dev_level.play_more_codecombat")

View file

@ -52,17 +52,15 @@ if view.showAds()
button.btn.btn-lg.btn-warning.banner.header-font#stop-real-time-playback-button(title="Stop real-time playback")
if view.level && view.level.isType('game-dev')
| Back to coding
span(data-i18n="play_game_dev_level.back_to_coding")
else
span(data-i18n="play_level.skip")
#how-to-play-game-dev-panel.panel.panel-default.hide.style-flat
.panel-heading
h3.panel-title How to play:
.panel-body
p Use the mouse to control the hero!
p Click anywhere on the map to move to that location.
p Click on the ogres to attack them.
h3.panel-title(data-i18n="play_game_dev_level.how_to_play_title")
.panel-body!= view.howToPlayText
.hints-view.hide

View file

@ -84,13 +84,14 @@ module.exports = class NewHomeView extends RootView
onClickStudentButton: (e) ->
window.tracker?.trackEvent $(e.target).data('event-action'), category: 'Homepage', []
@render?() if document.location.href.search('/home#create-account-student') isnt -1
onClickTeacherButton: (e) ->
window.tracker?.trackEvent $(e.target).data('event-action'), category: 'Homepage', []
if me.isTeacher()
application.router.navigate('/teachers', { trigger: true })
else
@scrollToLink('.request-demo-row', 600)
application.router.navigate('/teachers/signup', { trigger: true })
onClickViewProfile: (e) ->
window.tracker?.trackEvent $(e.target).data('event-action'), category: 'Homepage', []

View file

@ -16,7 +16,7 @@ module.exports = class SettingsTabView extends CocoView
'name', 'description', 'documentation', 'nextLevel', 'background', 'victory', 'i18n', 'icon', 'goals',
'type', 'terrain', 'showsGuide', 'banner', 'employerDescription', 'loadingTip', 'requiresSubscription',
'helpVideos', 'replayable', 'scoreTypes', 'concepts', 'picoCTFProblem', 'practice', 'practiceThresholdMinutes'
'shareable'
'shareable', 'studentPlayInstructions'
]
subscriptions:

View file

@ -17,6 +17,8 @@ module.exports = class I18NEditLevelView extends I18NEditModelView
@wrapRow 'Level description', ['description'], description, i18n[lang]?.description, []
if loadingTip = @model.get('loadingTip')
@wrapRow 'Loading tip', ['loadingTip'], loadingTip, i18n[lang]?.loadingTip, []
if studentPlayInstructions = @model.get('studentPlayInstructions')
@wrapRow 'Student Play Instructions', ['studentPlayInstructions'], studentPlayInstructions, i18n[lang]?.studentPlayInstructions, []
# goals
for goal, index in @model.get('goals') ? []

View file

@ -55,6 +55,7 @@ module.exports = class CampaignView extends RootView
'click #back-button': 'onClickBack'
'click #clear-storage-button': 'onClickClearStorage'
'click .portal .campaign': 'onClickPortalCampaign'
'click .portal .beta-campaign': 'onClickPortalCampaign'
'mouseenter .portals': 'onMouseEnterPortals'
'mouseleave .portals': 'onMouseLeavePortals'
'mousemove .portals': 'onMouseMovePortals'
@ -365,7 +366,7 @@ module.exports = class CampaignView extends RootView
return experienceScore
createLine: (o1, o2) ->
mapHeight = parseFloat($(".map").css("height"))
mapHeight = parseFloat($(".map").css("height"))
mapWidth = parseFloat($(".map").css("width"))
return unless mapHeight > 0
ratio = mapWidth / mapHeight
@ -668,7 +669,7 @@ module.exports = class CampaignView extends RootView
console.error "CampaignView hero update couldn't find hero slug for original:", hero
onClickPortalCampaign: (e) ->
campaign = $(e.target).closest('.campaign')
campaign = $(e.target).closest('.campaign, .beta-campaign')
return if campaign.is('.locked') or campaign.is('.silhouette')
campaignSlug = campaign.data('campaign-slug')
Backbone.Mediator.publish 'router:navigate',

View file

@ -33,6 +33,7 @@ module.exports = class PlayGameDevLevelView extends RootView
@state = new State({
loading: true
progress: 0
creatorString: ''
})
@supermodel.on 'update-progress', (progress) =>
@ -92,6 +93,7 @@ module.exports = class PlayGameDevLevelView extends RootView
loading: false
goalNames
shareURL
creatorString: $.i18n.t('play_game_dev_level.created_by').replace('{{name}}', @session.get('creatorName'))
})
@eventProperties = {
category: 'Play GameDev Level'

View file

@ -211,7 +211,12 @@ module.exports = class PlayLevelView extends RootView
@$el.addClass 'web-dev' # Hide some of the elements we won't be using
return
@world = @levelLoader.world
@$el.addClass 'game-dev' if @level.isType('game-dev')
if @level.isType('game-dev')
@$el.addClass 'game-dev'
@howToPlayText = utils.i18n(@level.attributes, 'studentPlayInstructions')
@howToPlayText ?= $.i18n.t('play_game_dev_level.default_student_instructions')
@howToPlayText = marked(@howToPlayText, { sanitize: true })
@renderSelectors('#how-to-play-game-dev-panel')
@$el.addClass 'hero' if @level.isType('hero', 'hero-ladder', 'hero-coop', 'course', 'course-ladder', 'game-dev') # TODO: figure out what this does and comment it
@$el.addClass 'flags' if _.any(@world.thangs, (t) -> (t.programmableProperties and 'findFlags' in t.programmableProperties) or t.inventory?.flag) or @level.get('slug') is 'sky-span'
# TODO: Update terminology to always be opponentSession or otherSession
@ -548,9 +553,9 @@ module.exports = class PlayLevelView extends RootView
onDonePressed: -> @showVictory()
onShowVictory: (e) ->
onShowVictory: (e={}) ->
$('#level-done-button').show() unless @level.isType('hero', 'hero-ladder', 'hero-coop', 'course', 'course-ladder', 'game-dev', 'web-dev')
@showVictory() if e.showModal
@showVictory(_.pick(e, 'manual')) if e.showModal
return if @victorySeen
@victorySeen = true
victoryTime = (new Date()) - @loadEndTime
@ -563,8 +568,9 @@ module.exports = class PlayLevelView extends RootView
ls: @session?.get('_id')
application.tracker?.trackTiming victoryTime, 'Level Victory Time', @levelID, @levelID
showVictory: ->
showVictory: (options={}) ->
return if @level.hasLocalChanges() # Don't award achievements when beating level changed in level editor
return if @level.isType('game-dev') and @level.get('shareable') and not options.manual
@endHighlight()
options = {level: @level, supermodel: @supermodel, session: @session, hasReceivedMemoryWarning: @hasReceivedMemoryWarning, courseID: @courseID, courseInstanceID: @courseInstanceID, world: @world}
ModalClass = if @level.isType('hero', 'hero-ladder', 'hero-coop', 'course', 'course-ladder', 'game-dev', 'web-dev') then HeroVictoryModal else VictoryModal
@ -638,18 +644,21 @@ module.exports = class PlayLevelView extends RootView
if @level.isType('game-dev')
panel = @$('#how-to-play-game-dev-panel')
panel.removeClass('hide')
lines = switch @level.get('slug')
when 'over-the-garden-wall' then ['Watch to see if the peasants are properly protected.']
when 'click-gait' then ['Move to each red "X".', 'Click on the screen to move the Knight there.']
when 'heros-journey' then ['Move to each red "X".', 'Click on the screen to move the Knight there.']
when 'a-maze-ing' then ['Move to the chest of gems.', 'Click on the screen to move the Duelist there.']
when 'gemtacular' then ['Move to each of the gems.', 'Click on the screen to move the Captain there.']
when 'vorpal-mouse' then ['Slay the ogres.', 'Click on the screen to move the Guardian there.', 'Click on the munchkins to attack them!']
when 'crushing-it' then ['Slay the ogres.', 'Click on the screen to move the Goliath there.', 'Click on the munchkins to attack them!']
when 'tabula-rasa' then ['Slay any ogres.', 'Collect any coins.', 'Click on the screen to move the Raider there.', 'Click on any munchkins to attack them!']
else ['Click to control your hero and win your game!']
html = _.map(lines, (line) -> "<p>#{line}</p>").join('')
panel.find('.panel-body').html(html)
# TODO: Remove this once these levels have studentPlayInstructions set.
if not @level.get('studentPlayInstructions')
lines = switch @level.get('slug')
when 'over-the-garden-wall' then ['Watch to see if the peasants are properly protected.']
when 'click-gait' then ['Move to each red "X".', 'Click on the screen to move the Knight there.']
when 'heros-journey' then ['Move to each red "X".', 'Click on the screen to move the Knight there.']
when 'a-maze-ing' then ['Move to the chest of gems.', 'Click on the screen to move the Duelist there.']
when 'gemtacular' then ['Move to each of the gems.', 'Click on the screen to move the Captain there.']
when 'vorpal-mouse' then ['Slay the ogres.', 'Click on the screen to move the Guardian there.', 'Click on the munchkins to attack them!']
when 'crushing-it' then ['Slay the ogres.', 'Click on the screen to move the Goliath there.', 'Click on the munchkins to attack them!']
when 'tabula-rasa' then ['Slay any ogres.', 'Collect any coins.', 'Click on the screen to move the Raider there.', 'Click on any munchkins to attack them!']
else null
if lines
html = _.map(lines, (line) -> "<p>#{line}</p>").join('')
panel.find('.panel-body').html(html)
@onWindowResize()

View file

@ -324,7 +324,7 @@ module.exports = class HeroVictoryModal extends ModalView
@playSound 'item-unlocked' if 0.5 < ratio < 0.6
else if panel.hero
thangType = @thangTypes[panel.hero]
panel.textEl.text(thangType.get('name'))
panel.textEl.text utils.i18n(thangType.attributes, 'name')
@playSelectionSound thangType if 0.5 < ratio < 0.6
if ratio is 1
panel.rootEl.removeClass('animating').find('.reward-image-container img').removeClass('pulse')
@ -413,7 +413,17 @@ module.exports = class HeroVictoryModal extends ModalView
AudioPlayer.playSound name, 1
getNextLevelCampaign: ->
{'kithgard-gates': 'forest', 'kithgard-mastery': 'forest', 'siege-of-stonehold': 'desert', 'clash-of-clones': 'mountain', 'summits-gate': 'glacier'}[@level.get('slug')] or @level.get 'campaign' # Much easier to just keep this updated than to dynamically figure it out.
# Much easier to just keep this updated than to dynamically figure it out.
# TODO: only go back to world selector if any beta campaigns are incomplete
{
'kithgard-gates': '',
'kithgard-mastery': '',
'tabula-rasa': '',
'wanted-poster': '',
'siege-of-stonehold': '',
'clash-of-clones': 'mountain',
'summits-gate': 'glacier'
}[@level.get('slug')] ? @level.get 'campaign'
getNextLevelLink: (returnToCourse=false) ->
if @level.isType('course', 'game-dev', 'web-dev') and nextLevel = @level.get('nextLevel') and not returnToCourse

View file

@ -81,7 +81,7 @@ module.exports = class CastButtonView extends CocoView
onDoneButtonClick: (e) ->
return if @options.level.hasLocalChanges() # Don't award achievements when beating level changed in level editor
@options.session.recordScores @world?.scores, @options.level
Backbone.Mediator.publish 'level:show-victory', showModal: true
Backbone.Mediator.publish 'level:show-victory', { showModal: true, manual: true }
onSpellChanged: (e) ->
@updateCastButton()

View file

@ -198,10 +198,10 @@ module.exports = class Zatanna
editor.completer.autoInsert = false
editor.completer.showPopup(editor)
# Hide popup if more than 10 suggestions
# Hide popup if too many suggestions
# TODO: Completions aren't asked for unless we show popup, so this is super hacky
# TODO: Backspacing to yield more suggestions does not close popup
if editor.completer?.completions?.filtered?.length > 10
if editor.completer?.completions?.filtered?.length > 20
editor.completer.detach()
# Update popup CSS after it's been launched
@ -244,6 +244,7 @@ module.exports = class Zatanna
addCodeCombatSnippets: (level, spellView, e) ->
snippetEntries = []
source = spellView.getSource()
haveFindNearestEnemy = false
haveFindNearest = false
for group, props of e.propGroups
@ -279,7 +280,6 @@ module.exports = class Zatanna
else 'while true'
# For now, update autocomplete to use hero instead of self/this, if hero is already used in the source.
# Later, we should make this happen all the time - or better yet update the snippets.
source = spellView.getSource()
if /hero/.test(source) or not /(self[\.\:]|this\.|\@)/.test(source)
thisToken =
'python': /self/,
@ -318,6 +318,15 @@ module.exports = class Zatanna
attackEntry.content = attackEntry.content.replace '${1:enemy}', '"${1:Enemy Name}"'
snippetEntries.push attackEntry
# Add copied hero. entries for most important ones that start with hero.
sortedEntries = _.sortBy snippetEntries, (entry) -> -1 * parseInt(entry.importance ? 0)
for entry in sortedEntries
if entry.content?.indexOf('hero.') is 0
newEntry = _.cloneDeep(entry)
entry.name = "hero.#{newEntry.name}"
snippetEntries.push(newEntry)
break if snippetEntries.length - sortedEntries.length >= 10
if haveFindNearest and not haveFindNearestEnemy
spellView.translateFindNearest()

View file

@ -51,6 +51,16 @@ function createCloseLead(zpContact, done) {
if (zpContact.phone) {
postData.contacts[0].phones = [{phone: zpContact.phone}];
}
if (zpContact.district) {
postData.custom['demo_nces_district'] = zpContact.district;
postData.custom['demo_nces_name'] = zpContact.organization;
}
if (zpContact.nces_district_id) {
postData.custom['demo_nces_district_id'] = zpContact.nces_district_id;
}
if (zpContact.nces_school_id) {
postData.custom['demo_nces_id'] = zpContact.nces_school_id;
}
const options = {
uri: `https://${closeIoApiKey}:X@app.close.io/api/v1/lead/`,
body: JSON.stringify(postData)
@ -146,16 +156,31 @@ function getZPContactsPage(contacts, searchQuery, done) {
if (err) return done(err);
const data = JSON.parse(body);
for (let contact of data.contacts) {
let organization = contact.organization_name;
if (contact.custom_fields && contact.custom_fields.school_name) organization = contact.custom_fields.school_name;
contacts.push({
organization: organization,
const newContact = {
organization: contact.organization_name,
name: contact.name,
title: contact.title,
email: contact.email,
phone: contact.phone,
data: contact
});
};
// Check custom fields, school_name set means organization_name is district name
if (contact.custom_fields) {
if (contact.custom_fields.school_name) {
newContact.district = contact.organization_name;
newContact.organization = contact.custom_fields.school_name;
// console.log(`DEBUG: found contact with school name ${newContact.email} ${contact.custom_fields.school_name}`);
}
if (contact.custom_fields.nces_district_id) {
newContact.nces_district_id = contact.custom_fields.nces_district_id;
// console.log(`DEBUG: found contact with district id ${newContact.email} ${newContact.nces_district_id}`);
}
if (contact.custom_fields.nces_school_id) {
newContact.nces_school_id = contact.custom_fields.nces_school_id;
// console.log(`DEBUG: found contact with school id ${newContact.email} ${newContact.nces_school_id}`);
}
}
contacts.push(newContact);
}
return done(null, data.pipeline_total);
});

View file

@ -68,7 +68,8 @@ LevelHandler = class LevelHandler extends Handler
'scoreTypes'
'concepts'
'picoCTFProblem'
'practiceThresholdMinutes'
'practiceThresholdMinutes',
'studentPlayInstructions'
]
postEditableProperties: ['name']

View file

@ -107,7 +107,7 @@ module.exports =
getStudents: wrap (req, res, next) ->
throw new errors.Unauthorized('You must be an administrator.') unless req.user?.isAdmin()
query = $or: [{role: 'student'}, {$and: [{schoolName: {$exists: true}}, {schoolName: {$ne: ''}}, {anonymous: false}]}]
users = yield User.find(query).select('lastIP schoolName').lean()
users = yield User.find(query).select('lastIP').lean()
for user in users
if ip = user.lastIP
user.geo = geoip.lookup(ip)

View file

@ -48,7 +48,7 @@ describe 'RequestQuoteView', ->
}
}])
})
_.defer done # Let SuperModel finish
view.supermodel.once('loaded-all', done)
it 'shows request received', ->
expect(view.$('#request-form').hasClass('hide')).toBe(true)
@ -220,7 +220,7 @@ describe 'RequestQuoteView', ->
}
}])
})
_.defer done # Let SuperModel finish
view.supermodel.once('loaded-all', done)
it 'shows form with data from the most recent request', ->
expect(view.$('input[name="firstName"]').val()).toBe('First')