Merge branch 'master' into production

This commit is contained in:
Nick Winter 2015-08-08 09:34:50 -07:00
commit e650ab52d4
27 changed files with 413 additions and 283 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 107 KiB

After

Width:  |  Height:  |  Size: 126 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

View file

@ -31,4 +31,6 @@ module.exports = class FlagLank extends IndieLank
toggleCursor: (to) -> toggleCursor: (to) ->
@options.isCursor = to @options.isCursor = to
@thang.alpha = if to then 0.33 else 0.67 # 1.0 is for flags that have been placed @thang.alpha = if to then 0.33 else 0.67 # 1.0 is for flags that have been placed
#@thang.action = if to then 'idle' else 'appear' # TODO: why doesn't this work? Does it not render the action or something?
@thang.action = 'appear'
@updateAlpha() @updateAlpha()

View file

@ -1,6 +1,8 @@
SpriteBuilder = require 'lib/sprites/SpriteBuilder' SpriteBuilder = require 'lib/sprites/SpriteBuilder'
floors = ['Dungeon Floor', 'Indoor Floor', 'Grass', 'Grass01', 'Grass02', 'Grass03', 'Grass04', 'Grass05', 'Goal Trigger', 'Obstacle', 'Sand 01', 'Sand 02', 'Sand 03', 'Sand 04', 'Sand 05', 'Sand 06', 'Talus 1', 'Talus 2', 'Talus 3', 'Talus 4', 'Talus 5', 'Talus 6', 'Firn 1', 'Firn 2', 'Firn 3', 'Firn 4', 'Firn 5', 'Firn 6', 'Ice Rink 1', 'Ice Rink 2', 'Ice Rink 3'] floors = ['Dungeon Floor', 'Indoor Floor', 'Grass', 'Grass01', 'Grass02', 'Grass03', 'Grass04', 'Grass05', 'Goal Trigger', 'Obstacle', 'Sand 01', 'Sand 02', 'Sand 03', 'Sand 04', 'Sand 05', 'Sand 06', 'Talus 1', 'Talus 2', 'Talus 3', 'Talus 4', 'Talus 5', 'Talus 6', 'Firn 1', 'Firn 2', 'Firn 3', 'Firn 4', 'Firn 5', 'Firn 6', 'Ice Rink 1', 'Ice Rink 2', 'Ice Rink 3', 'Firn Cliff']
cliffs = ['Dungeon Pit', 'Grass Cliffs']
module.exports = class SingularSprite extends createjs.Sprite module.exports = class SingularSprite extends createjs.Sprite
childMovieClips: null childMovieClips: null
@ -71,6 +73,13 @@ module.exports = class SingularSprite extends createjs.Sprite
@baseScaleY = @scaleY @baseScaleY = @scaleY
if @camera and @thangType.get('name') in floors if @camera and @thangType.get('name') in floors
@baseScaleY *= @camera.y2x @baseScaleY *= @camera.y2x
else if @camera and @thangType.get('name') in cliffs
if actionName is 'idle_side'
@baseScaleX *= @camera.x2y# / 0.85
@baseScaleY *= @camera.y2x * 0.85
else
@baseScaleY *= @camera.y2x / 0.85
console.log 'it is a cliff!', actionName, @baseScaleX, @baseScaleY
@currentAnimation = actionName @currentAnimation = actionName
return return

View file

@ -27,7 +27,7 @@ module.exports.thangNames = thangNames =
'Godel' 'Godel'
'Goreball' 'Goreball'
'Gordok' 'Gordok'
'Toremon' 'Gorylo'
'Gort' 'Gort'
'Kog' 'Kog'
'Kogpole' 'Kogpole'
@ -37,11 +37,13 @@ module.exports.thangNames = thangNames =
'Oogre' 'Oogre'
'Raack' 'Raack'
'Ragtime' 'Ragtime'
'Raort'
'Rexxar' 'Rexxar'
'Skoggen' 'Skoggen'
'Smerk' 'Smerk'
'Snortt' 'Snortt'
'Thabt' 'Thabt'
'Toremon'
'Treg' 'Treg'
'Ursa' 'Ursa'
'Vorobun' 'Vorobun'
@ -184,6 +186,7 @@ module.exports.thangNames = thangNames =
'Belch' 'Belch'
'Booz' 'Booz'
'Brusentsov' 'Brusentsov'
'Demonik'
'Dronck' 'Dronck'
'Gorlog' 'Gorlog'
'Grumus' 'Grumus'
@ -204,7 +207,7 @@ module.exports.thangNames = thangNames =
'Trogdor' 'Trogdor'
'Trung' 'Trung'
'Vargutt' 'Vargutt'
'Demonik' 'Vyle'
] ]
'Ogre F': [ 'Ogre F': [
# Female # Female

View file

@ -933,7 +933,7 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription:
contribute: contribute:
page_title: "Mitwirken" page_title: "Mitwirken"
intro_blurb: "CodeCombat ist zu 100% Open Source! Hunderte hingebungsvolle Spieler haben uns geholfen das Spiel zu dem zu machen was es heute ist. Tritt uns bei und schreibe das nächste Kapitel in CodeCombat' Aufgabe, der Welt das Programmieren zu lehren!" intro_blurb: "CodeCombat ist zu 100% Open Source! Hunderte hingebungsvolle Spieler haben uns geholfen das Spiel zu dem zu machen, was es heute ist. Tritt uns bei und schreibe das nächste Kapitel in CodeCombats Mission, der Welt das Programmieren zu lehren!"
alert_account_message_intro: "Hey du!" alert_account_message_intro: "Hey du!"
alert_account_message: "Um Klassen-Emails abonnieren zu können, musst du dich zuerst anmelden." alert_account_message: "Um Klassen-Emails abonnieren zu können, musst du dich zuerst anmelden."
archmage_introduction: "Einer der größten Vorteile daran ein Spiel aufzubauen, ist es, dass so viele verschiedene Aspekte eine Rolle spielen. Grafiken, Sound, Echtzeit Networking, Social Networking und natürlich viele der gewöhnlichen Aspekte des Programmierens, von low-level Datenbankmanagement und Server Administration bis hin zum Aufbau von Design und Interface. Es gibt viel zu tun und wenn du ein erfahrener Programmierer bist, mit einer Veranlagung dazu, wirklich knallhart bei CodeCombat einzutauchen, dann könnte diese Klasse etwas für dich sein. Wir würden uns wahnsinnig über deine Hilfe dabei freuen, das beste Programmierspiel der Welt aufzubauen." archmage_introduction: "Einer der größten Vorteile daran ein Spiel aufzubauen, ist es, dass so viele verschiedene Aspekte eine Rolle spielen. Grafiken, Sound, Echtzeit Networking, Social Networking und natürlich viele der gewöhnlichen Aspekte des Programmierens, von low-level Datenbankmanagement und Server Administration bis hin zum Aufbau von Design und Interface. Es gibt viel zu tun und wenn du ein erfahrener Programmierer bist, mit einer Veranlagung dazu, wirklich knallhart bei CodeCombat einzutauchen, dann könnte diese Klasse etwas für dich sein. Wir würden uns wahnsinnig über deine Hilfe dabei freuen, das beste Programmierspiel der Welt aufzubauen."

View file

@ -293,7 +293,7 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription:
tip_scrub_shortcut: "Usa Ctrl+[ para rebobinar e Ctrl+] para avançar." tip_scrub_shortcut: "Usa Ctrl+[ para rebobinar e Ctrl+] para avançar."
tip_guide_exists: "Clica no guia, dentro do menu do jogo (no topo da página), para informações úteis." tip_guide_exists: "Clica no guia, dentro do menu do jogo (no topo da página), para informações úteis."
tip_open_source: "O CodeCombat é 100% open source!" tip_open_source: "O CodeCombat é 100% open source!"
# tip_tell_friends: "Enjoying CodeCombat? Tell your friends about us!" tip_tell_friends: "Estás a gostar do CodeCombat? Fala de nós aos teus amigos!"
tip_beta_launch: "O CodeCombat lançou o seu beta em outubro de 2013." tip_beta_launch: "O CodeCombat lançou o seu beta em outubro de 2013."
tip_think_solution: "Pensa na solução, não no problema." tip_think_solution: "Pensa na solução, não no problema."
tip_theory_practice: "Teoricamente, não há diferença entre a teoria e a prática. Mas na prática, há. - Yogi Berra" tip_theory_practice: "Teoricamente, não há diferença entre a teoria e a prática. Mas na prática, há. - Yogi Berra"
@ -337,7 +337,7 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription:
tip_recurse: "Iterar é humano, recursar é divino. - L. Peter Deutsch" tip_recurse: "Iterar é humano, recursar é divino. - L. Peter Deutsch"
tip_free_your_mind: "Tens de libertar tudo, Neo. Medo, dúvida e descrença. Liberta a tua mente. - Morpheus" tip_free_your_mind: "Tens de libertar tudo, Neo. Medo, dúvida e descrença. Liberta a tua mente. - Morpheus"
tip_strong_opponents: "Até o mais forte dos adversários tem uma fraqueza. - Itachi Uchiha" tip_strong_opponents: "Até o mais forte dos adversários tem uma fraqueza. - Itachi Uchiha"
# tip_paper_and_pen: "Before you start coding, you can always plan with a sheet of paper and a pen." tip_paper_and_pen: "Antes de começares a programar, podes sempre planear com uma folha de papel e uma caneta."
game_menu: game_menu:
inventory_tab: "Inventário" inventory_tab: "Inventário"
@ -851,8 +851,8 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription:
indoor: "Interior" indoor: "Interior"
desert: "Deserto" desert: "Deserto"
grassy: "Relvado" grassy: "Relvado"
# mountain: "Mountain" mountain: "Montanha"
# glacier: "Glacier" glacier: "Glaciar"
small: "Pequeno" small: "Pequeno"
large: "Grande" large: "Grande"
fork_title: "Bifurcar Nova Versão" fork_title: "Bifurcar Nova Versão"
@ -1211,7 +1211,7 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription:
delta: delta:
added: "Adicionado" added: "Adicionado"
modified: "Modificado" modified: "Modificado"
# not_modified: "Not Modified" not_modified: "Não Modificado"
deleted: "Eliminado" deleted: "Eliminado"
moved_index: "Índice Movido" moved_index: "Índice Movido"
text_diff: "Diferença de Texto" text_diff: "Diferença de Texto"

View file

@ -1,29 +1,42 @@
#course-details-view #course-details-view
.concept-completion-container .concept-completion-container
position: relative font-size: 10pt
.concept-completion-background
position: absolute .summary-container
height: 100% font-size: 14pt
left: 0px
top: 0px .statistics-container
background-color: blue
opacity: 0.25
.concept-completed-foreground
font-size: 12pt font-size: 12pt
td
padding-right: 8px
#editDescriptionModal .modal-dialog .table-concepts-summary
width: 100%
.concept-summary
width: 100%
background-color: white
cursor: default
display: inline-block
white-space: nowrap
font-size: 9pt
font-weight: normal
border: 1px solid gray
border-radius: 5px
margin: 0px
padding: 2px
background-color: white background-color: white
#editNameModal .modal-dialog #editSettingsModal .modal-dialog
background-color: white background-color: white
max-width: 400px font-size: 14pt
.edit-description-input .edit-description-input
width: 100% width: 100%
.edit-name-input .edit-name-input
width: 100% width: 50%
.member-header .member-header
cursor: pointer cursor: pointer

View file

@ -1,5 +1,8 @@
#courses-view #courses-view
.btn-continue
margin-top: 40px
.center .center
text-align: center text-align: center

View file

@ -3,6 +3,28 @@
#home-view #home-view
#kids-coding-container
$coding-image-size: 272px
position: relative
width: 1000px + $coding-image-size
margin: 0px auto
@media screen and ( max-width: 1400px )
display: none
.kid-coding
position: absolute
z-index: 1
top: -57px
@media screen and ( max-height: 800px )
top: 50px
&#boy-coding
left: -$coding-image-size / 2
&#girl-coding
right: -$coding-image-size / 2
#spacer #spacer
height: 626px height: 626px
@media screen and ( max-height: 800px ) @media screen and ( max-height: 800px )

View file

@ -365,19 +365,21 @@ $gameControlMargin: 30px
&:active, &.highlighted &:active, &.highlighted
@include box-shadow(0 0 20px white) @include box-shadow(0 0 20px white)
&.heroes &.items
background-position: (-1 * $gameControlSize) 0px background-position: (-1 * $gameControlSize) 0px
&.achievements &.heroes
background-position: (-2 * $gameControlSize) 0px background-position: (-2 * $gameControlSize) 0px
&.account &.achievements
//background-position: (-3 * $gameControlSize) 0px
background-position: (-4 * $gameControlSize) 0px
&.settings
background-position: (-4 * $gameControlSize) 0px
&.gems
background-position: (-5 * $gameControlSize) 0px
&.poll
background-position: (-3 * $gameControlSize) 0px background-position: (-3 * $gameControlSize) 0px
&.account
//background-position: (-4 * $gameControlSize) 0px
background-position: (-5 * $gameControlSize) 0px
&.settings
background-position: (-5 * $gameControlSize) 0px
&.gems
background-position: (-6 * $gameControlSize) 0px
&.poll
background-position: (-4 * $gameControlSize) 0px
.tooltip .tooltip
font-size: 24px font-size: 24px

View file

@ -350,9 +350,15 @@ $itemSlotGridHeight: 51px
img img
margin: 1px margin: 1px
button button
margin-top: -2px //margin-top: -2px
height: 19px //height: 19px
font-size: 12px font-size: 12px
margin: -1px 1px 1px 1px
&:active, &.active
background-position: -57px * 2 0
color: white
text-shadow: 0 1px 0 black, 1px 0 0 black, -1px 0 0 black, 0 -1px 0 black
img img
width: 56px width: 56px
@ -360,22 +366,32 @@ $itemSlotGridHeight: 51px
display: block display: block
button button
width: 100% background: transparent url(/images/pages/play/modal/equip-buttons.png)
height: 17px width: 56px
border: 1px solid rgb(46,46,46) height: 21px
background: white border: 0
//border: 1px solid rgb(46,46,46)
//background: white
font-size: 11px font-size: 11px
border-radius: 1px border-radius: 1px
padding: 0 padding: 0 0 2px 0
@include transition(0.1s ease) @include transition(font-size 0.1s ease)
//text-shadow: 0 1px 0 white, 1px 0 0 white, -1px 0 0 white, 0 -1px 0 white
text-transform: uppercase
font-weight: bold
color: black
text-spacing: 0px
&.active &.active
background-color: rgb(81,153,236) background-color: rgb(81,153,236)
button button
background-color: lighten(rgb(89,136,47), 10%) //background-color: lighten(rgb(89,136,47), 10%)
box-shadow: 1px 1px 4px #333 box-shadow: 1px 1px 4px #333
color: white color: white
text-shadow: 0 1px 0 black, 1px 0 0 black, -1px 0 0 black, 0 -1px 0 black
background-position: -57px 0
//.status-message .should-equip-message //.status-message .should-equip-message
// display: inline // display: inline
@ -594,3 +610,12 @@ $itemSlotGridHeight: 51px
&.male &.male
left: -16px left: -16px
body[lang='ja'], body[lang^='zh'], body[lang='ca'], body[lang^='es'], body[lang^='pt'], body[lang='ro'], body[lang='fi'], body[lang='sv'], body[lang='uk'], body[lang='vi'], body[lang='cz']
#inventory-modal #unequipped .item button
font-weight: normal
letter-spacing: -1px
padding-bottom: 1px
body[lang='ro'], body[lang='fi'], body[lang='uk']
#inventory-modal #unequipped .item button
text-transform: none

View file

@ -1,5 +1,6 @@
block header block header
#site-nav #site-nav
block site_nav
a(href="/") a(href="/")
img#nav-logo(src="/images/pages/base/logo.png", title="CodeCombat - Learn how to code by playing a game", alt="CodeCombat") img#nav-logo(src="/images/pages/base/logo.png", title="CodeCombat - Learn how to code by playing a game", alt="CodeCombat")
div#site-nav-links div#site-nav-links

View file

@ -5,7 +5,6 @@ block content
//- DO NOT localize / i18n //- DO NOT localize / i18n
div TODO: fix ugly tabs div TODO: fix ugly tabs
div TODO: not enrolled yet
div div
span *UNDER CONSTRUCTION, send feedback to span *UNDER CONSTRUCTION, send feedback to
a.spl(href='mailto:team@codecombat.com') team@codecombat.com a.spl(href='mailto:team@codecombat.com') team@codecombat.com
@ -14,60 +13,51 @@ block content
span.spl Student view span.spl Student view
div(style='border-bottom: 1px solid black;') div(style='border-bottom: 1px solid black;')
.modal#editNameModal .modal#editSettingsModal
.modal-dialog .modal-dialog
.modal-header .modal-header
button.close(data-dismiss='modal') button.close(data-dismiss='modal')
span × span ×
h3.modal-title Edit Class Name h3.modal-title Edit Class Settings
.modal-body .modal-body
p This title will be displayed to everyone enrolled in this class. p This title will be displayed to everyone in the class.
p
input.edit-name-input(type='text', value="#{instance.name}") input.edit-name-input(type='text', value="#{instance.name}")
.modal-footer p This description will be displayed to everyone in the class.
button.btn.edit-name-save-btn(data-i18n="common.save_changes") p
.modal#editDescriptionModal
.modal-dialog
.modal-header
button.close(data-dismiss='modal')
span ×
h3.modal-title Edit Class Description
.modal-body
p This description will be displayed to everyone enrolled in this class.
textarea.edit-description-input(rows=2)= instance.description textarea.edit-description-input(rows=2)= instance.description
p Select programming languages available to the class:
p
select.form-control.select-language
option(value="Python") Python
option(value="JavaScript") JavaScript
option(value="All Languages") All Languages
p
input(type='checkbox', checked)
span.spl Show student progress to everyone in the class
.modal-footer .modal-footer
button.btn.edit-description-save-btn(data-i18n="common.save_changes") button.btn.btn-save-settings(data-i18n="common.save_changes")
h1= course.title h1= instance.name
p= course.description small.spl (#{course.title})
h3= instance.name
if !studentMode
span.spl
button.btn.btn-xs.edit-class-name-btn(data-toggle='modal', data-target='#editNameModal') edit class name
p p
if instance.description if instance.description
each line in instance.description.split('\n') each line in instance.description.split('\n')
div= line div= line
if !studentMode
span.spl
button.btn.btn-xs.edit-description-btn(data-toggle='modal', data-target='#editDescriptionModal') edit class description
else if !studentMode
div
button.btn.btn-xs.edit-description-btn add class description
if !studentMode if !studentMode
.form-group p
span.spr Select language button.btn.btn-xs.btn-edit-settings(data-toggle='modal', data-target='#editSettingsModal') edit class settings
select.form-control.select-language
option(value="Python") Python
option(value="JavaScript") JavaScript
option(value="All Languages") All Languages
div(role='tabpanel') div(role='tabpanel')
ul.nav.nav-tabs(role='tablist') ul.nav.nav-tabs(role='tablist')
if !studentMode if studentMode
li.active(role='presentation')
a(href='#levels', aria-controls='levels', role='tab', data-toggle='tab') Levels
li(role='presentation')
a(href='#progress', aria-controls='progress', role='tab', data-toggle='tab') Class
else
li.active(role='presentation') li.active(role='presentation')
a(href='#progress', aria-controls='progress', role='tab', data-toggle='tab') Class a(href='#progress', aria-controls='progress', role='tab', data-toggle='tab') Class
li(role='presentation') li(role='presentation')
@ -76,26 +66,64 @@ block content
a(href='#levels', aria-controls='levels', role='tab', data-toggle='tab') Levels a(href='#levels', aria-controls='levels', role='tab', data-toggle='tab') Levels
.tab-content .tab-content
if !studentMode if studentMode
.tab-pane.active#levels(role='tabpanel')
+levels-tab
.tab-pane#progress(role='tabpanel')
+progress-tab
else
.tab-pane.active#progress(role='tabpanel') .tab-pane.active#progress(role='tabpanel')
+progress-tab
.tab-pane#invite(role='tabpanel')
br
p Invite students to join this class.
if course.title !== 'Introduction to Computer Science'
p Student unlock code: #{instance.code}
p Class capacity: 34/50
textarea.textarea-emails(rows=3, placeholder="Enter student emails to invite, one per line")
div(style='margin-top:10px;')
button.btn.btn-success.btn-invite Send Invites
.tab-pane#levels(role='tabpanel')
+levels-tab
mixin progress-tab
if instance.students if instance.students
h3 Summary .container-fluid.summary-container
- var averagePlaytime = Math.round(Math.random() * 30) + 30 .row
p .col-md-6
strong(style='font-size:12pt;') Average Play Time h3 Statistics
p #{averagePlaytime} minutes table.statistics-container
p tr
strong(style='font-size:12pt;') Concepts Covered td Total students:
table.table.table-condensed td #{instance.students.length}
tr
td Average level play time:
td #{stats.averageLevelPlaytime} seconds
tr
td Total play time:
td #{stats.totalPlayTime} seconds
tr
td Average levels completed:
td #{stats.averageLevelsCompleted}
tr
td Total levels completed:
td #{stats.totalLevelsCompleted}
tr
td Last level completed:
td #{stats.lastLevelCompleted}
.col-md-6
h3 Concepts Covered
table.table-concepts-summary
each concept in courseConcepts each concept in courseConcepts
- var conceptCompletion = Math.round(parseFloat(conceptsCompleted[concept]) / instance.students.length * 100) - var conceptCompletion = Math.round(parseFloat(conceptsCompleted[concept]) / instance.students.length * 100)
if isNaN(conceptCompletion) if isNaN(conceptCompletion)
- conceptCompletion = 0 - conceptCompletion = 0
tr tr
td.concept-completion-container td.concept-completion-container
span.concept-summary(style="width:#{conceptCompletion}%;")
span.concept-completed-foreground(data-i18n="concepts." + concept) span.concept-completed-foreground(data-i18n="concepts." + concept)
span.spl - #{conceptCompletion}% span.spl - #{conceptCompletion}%
span.concept-completion-background(style="width:#{conceptCompletion}%;")
h3 Students h3 Students
table.table.table-condensed table.table.table-condensed
thead thead
@ -163,21 +191,6 @@ block content
span.spl #{level} span.spl #{level}
- i++ - i++
.tab-pane#invite(role='tabpanel')
p Invite students to join this class.
if course.title !== 'Introduction to Computer Science'
p Student unlock code: #{instance.code}
p Class capacity: 34/50
textarea.textarea-emails(rows=3, placeholder="Enter student emails to invite, one per line")
div
button.btn.btn-success.btn-invite Send Invites
.tab-pane#levels(role='tabpanel')
+levels-tab
else
.tab-pane.active#levels(role='tabpanel')
+levels-tab
mixin levels-tab mixin levels-tab
table.table.table-striped.table-condensed table.table.table-striped.table-condensed
thead thead

View file

@ -29,7 +29,7 @@ block content
each inst in instances each inst in instances
option= inst.name option= inst.name
.col-md-4 .col-md-4
button.btn.btn-success.btn-enter(data-course-id="#{courseID}") Enter button.btn.btn-success.btn-enter Enter
.row.button-row.center.row-pick-class .row.button-row.center.row-pick-class
.col-md-12 .col-md-12
div.or Or div.or Or
@ -43,39 +43,34 @@ block content
.col-md-8 .col-md-8
input.code-input(type='text', placeholder="Enter unlock code") input.code-input(type='text', placeholder="Enter unlock code")
.col-md-4 .col-md-4
button.btn.btn-success.btn-enroll(data-course-id="#{courseID}") Enroll button.btn.btn-success.btn-enroll Enroll
.row.button-row.center .row.button-row.center
.col-md-12 .col-md-12
div.or Or div.or Or
.row.button-row.center .row.button-row.center
.col-md-12 .col-md-12
button.btn.btn-success.btn-lg.btn-buy(data-course-id="#{courseID}") Buy this course button.btn.btn-success.btn-lg.btn-buy Buy this course
h1.center Courses h1.center Courses on CodeCombat
.info-container .info-container
//- p.center.gameplay-img-container p Courses are designed to introduce computer science concepts using CodeCombat's fun and engaging environment. CodeCombat levels are organized around key topics to encourage progressive learning, over the course of 5 hours.
//- img(src="/images/pages/courses/101_info.png" width='800')
p A course is a selection of CodeCombat levels designed to introduce computer science concepts in a fun and engaging environment.
.container-fluid .container-fluid
.row
.col-md-12
p.center
button.btn.btn-success.btn-lg.btn-buy Enroll Now!
.row .row
.col-md-6 .col-md-6
div With CodeCombat courses, you and your students will:
ul ul
li Learn more in less time li Learn more in less time
li With no experience necesssary li No coding experience necesssary
li Monitor student progress li Easily monitor student progress
br
div Purchase a course for your entire class. It's easy to sign up your students!
.col-md-6 .col-md-6
p .well.well-sm
div.praise-quote "#{praise.quote}" div.praise-quote "#{praise.quote}"
div.caption-text - #{praise.source} div.caption-text - #{praise.source}
//- h2.center Available Courses h2.center Choose Your Course:
.container-fluid .container-fluid
- var i = 0 - var i = 0
@ -95,13 +90,13 @@ mixin course-block(course, courseID)
span.spr #{course.title} span.spr #{course.title}
strong #{course.unlocked ? '[ enrolled ]' : ''} strong #{course.unlocked ? '[ enrolled ]' : ''}
.panel-body .panel-body
.container-fluid
.row.button-row
.col-md-6
strong Topics strong Topics
ul ul
each topic in course.topics each topic in course.topics
li= topic li= topic
strong Hours of content: #{course.duration} strong Hours of content: #{course.duration}
.container-fluid .col-md-6.center
.row.button-row.center button.btn.btn-lg.btn-success.btn-continue(data-toggle='modal', data-target="#continueModal", data-course-title="#{course.title}", data-course-id="#{courseID}") #{course.unlocked ? 'Continue' : 'Enter'}
.col-md-12
br
button.btn.btn-success.btn-continue(data-toggle='modal', data-target="#continueModal", data-course-title="#{course.title}", data-course-id="#{courseID}") #{course.unlocked ? 'Continue' : 'Enter'}

View file

@ -1,5 +1,10 @@
extends /templates/base extends /templates/base
block append site_nav
#kids-coding-container
img#boy-coding.kid-coding(src="/images/pages/home/boy_coding.png", alt="", draggable="false")
img#girl-coding.kid-coding(src="/images/pages/home/girl_coding.png", alt="", draggable="false")
block outer_content block outer_content
#spacer #spacer

View file

@ -90,6 +90,7 @@ else
.game-controls.header-font .game-controls.header-font
button.btn.poll.hidden(data-i18n="[title]play.poll") button.btn.poll.hidden(data-i18n="[title]play.poll")
a.btn.clans(href="/clans", data-i18n="[title]clans.clans")
button.btn.items(data-toggle='coco-modal', data-target='play/modal/PlayItemsModal', data-i18n="[title]play.items") button.btn.items(data-toggle='coco-modal', data-target='play/modal/PlayItemsModal', data-i18n="[title]play.items")
button.btn.heroes(data-toggle='coco-modal', data-target='play/modal/PlayHeroesModal', data-i18n="[title]play.heroes") button.btn.heroes(data-toggle='coco-modal', data-target='play/modal/PlayHeroesModal', data-i18n="[title]play.heroes")
button.btn.achievements(data-toggle='coco-modal', data-target='play/modal/PlayAchievementsModal', data-i18n="[title]play.achievements") button.btn.achievements(data-toggle='coco-modal', data-target='play/modal/PlayAchievementsModal', data-i18n="[title]play.achievements")
@ -118,9 +119,6 @@ else
button#logout-button.btn.btn-illustrated.btn-warning(data-i18n="login.log_out") Log Out button#logout-button.btn.btn-illustrated.btn-warning(data-i18n="login.log_out") Log Out
if me.isPremium() if me.isPremium()
button.btn.btn-illustrated.btn-primary(data-i18n="nav.contact", data-toggle="coco-modal", data-target="core/ContactModal") Contact button.btn.btn-illustrated.btn-primary(data-i18n="nav.contact", data-toggle="coco-modal", data-target="core/ContactModal") Contact
span.spl
a.btn.btn-illustrated(href='/clans', data-i18n="clans.clans") Clans
button.btn.btn-lg.btn-inverse.campaign-control-button#volume-button(data-i18n="[title]play.adjust_volume", title="Adjust volume") button.btn.btn-lg.btn-inverse.campaign-control-button#volume-button(data-i18n="[title]play.adjust_volume", title="Adjust volume")
.glyphicon.glyphicon-volume-off .glyphicon.glyphicon-volume-off

View file

@ -10,11 +10,9 @@ module.exports = class CourseDetailsView extends RootView
events: events:
'change .expand-progress-checkbox': 'onExpandedProgressCheckbox' 'change .expand-progress-checkbox': 'onExpandedProgressCheckbox'
'change .select-session': 'onChangeSession'
'change .student-mode-checkbox': 'onChangeStudent' 'change .student-mode-checkbox': 'onChangeStudent'
'click .btn-play-level': 'onClickPlayLevel' 'click .btn-play-level': 'onClickPlayLevel'
'click .edit-description-save-btn': 'onEditDescriptionSave' 'click .btn-save-settings': 'onClickSaveSettings'
'click .edit-name-save-btn': 'onEditNameSave'
'click .member-header': 'onClickMemberHeader' 'click .member-header': 'onClickMemberHeader'
'click .progress-header': 'onClickProgressHeader' 'click .progress-header': 'onClickProgressHeader'
'mouseenter .progress-level-cell': 'onMouseEnterPoint' 'mouseenter .progress-level-cell': 'onMouseEnterPoint'
@ -49,6 +47,14 @@ module.exports = class CourseDetailsView extends RootView
conceptsCompleted[concept]++ conceptsCompleted[concept]++
context.conceptsCompleted = conceptsCompleted context.conceptsCompleted = conceptsCompleted
stats =
averageLevelPlaytime: _.random(30, 240)
averageLevelsCompleted: _.random(1, @course.levels.length)
stats.totalPlayTime = context.instance.students?.length * stats.averageLevelPlaytime ? 0
stats.totalLevelsCompleted = context.instance.students?.length * stats.averageLevelsCompleted ? 0
stats.lastLevelCompleted = @course.levels[@maxLastStartedIndex] ? @course.levels[@course.levels.length - 1]
context.stats = stats
context context
initData: -> initData: ->
@ -144,26 +150,12 @@ module.exports = class CourseDetailsView extends RootView
@render?() @render?()
$('.student-mode-checkbox').attr('checked', @options.studentMode) $('.student-mode-checkbox').attr('checked', @options.studentMode)
onChangeSession: (e) ->
@showExpandedProgress = false
newSessionValue = $(e.target).val()
for val, index in @instances when val.name is newSessionValue
@currentInstanceIndex = index
@updateLevelMaps()
@onCampaignSync()
onExpandedProgressCheckbox: (e) -> onExpandedProgressCheckbox: (e) ->
@showExpandedProgress = $('.expand-progress-checkbox').prop('checked') @showExpandedProgress = $('.expand-progress-checkbox').prop('checked')
# TODO: why does render reset the checkbox to be unchecked? # TODO: why does render reset the checkbox to be unchecked?
@render?() @render?()
$('.expand-progress-checkbox').attr('checked', @showExpandedProgress) $('.expand-progress-checkbox').attr('checked', @showExpandedProgress)
onClickEditClassName: (e) ->
alert 'TODO: Popup for editing name for this course session'
onClickEditClassDescription: (e) ->
alert 'TODO: Popup for editing description for this course session'
onClickMemberHeader: (e) -> onClickMemberHeader: (e) ->
@memberSort = if @memberSort is 'nameAsc' then 'nameDesc' else 'nameAsc' @memberSort = if @memberSort is 'nameAsc' then 'nameDesc' else 'nameAsc'
@sortMembers() @sortMembers()
@ -183,16 +175,12 @@ module.exports = class CourseDetailsView extends RootView
viewArgs: [{}, levelSlug] viewArgs: [{}, levelSlug]
} }
onEditDescriptionSave: (e) -> onClickSaveSettings: (e) ->
description = $('.edit-description-input').val()
@instances[@currentInstanceIndex].description = description
$('#editDescriptionModal').modal('hide')
@render?()
onEditNameSave: (e) ->
if name = $('.edit-name-input').val() if name = $('.edit-name-input').val()
@instances[@currentInstanceIndex].name = name @instances[@currentInstanceIndex].name = name
$('#editNameModal').modal('hide') description = $('.edit-description-input').val()
@instances[@currentInstanceIndex].description = description
$('#editSettingsModal').modal('hide')
@render?() @render?()
onMouseEnterPoint: (e) -> onMouseEnterPoint: (e) ->

View file

@ -40,7 +40,7 @@ data.courses = [
image: '/images/pages/courses/102_info.png' image: '/images/pages/courses/102_info.png'
}, },
{ {
title: 'CS Course 3' title: 'Computer Science 3'
description: 'Learn how to handle input.' description: 'Learn how to handle input.'
topics: ['If Statements', 'Arithmetic', 'Input Handling'] topics: ['If Statements', 'Arithmetic', 'Input Handling']
duration: 5 duration: 5
@ -48,7 +48,7 @@ data.courses = [
image: '/images/pages/courses/103_info.png' image: '/images/pages/courses/103_info.png'
}, },
{ {
title: 'CS 4' title: 'Computer Science 4'
description: 'Time to tackle arrays and some pvp stuff.' description: 'Time to tackle arrays and some pvp stuff.'
topics: ['Loops', 'Break Statements', 'Arrays'] topics: ['Loops', 'Break Statements', 'Arrays']
duration: 5 duration: 5
@ -56,7 +56,7 @@ data.courses = [
image: '/images/pages/courses/104_info.png' image: '/images/pages/courses/104_info.png'
}, },
{ {
title: 'Course 5' title: 'Computer Science 5'
description: 'Time to tackle arrays and some PVP.' description: 'Time to tackle arrays and some PVP.'
topics: ['Break Statements', 'Arrays', 'Object Literals'] topics: ['Break Statements', 'Arrays', 'Object Literals']
duration: 5 duration: 5
@ -64,7 +64,7 @@ data.courses = [
image: '/images/pages/courses/105_info.png' image: '/images/pages/courses/105_info.png'
}, },
{ {
title: 'Course 6' title: 'Computer Science 6'
description: 'For loops!' description: 'For loops!'
topics: ['Break Statements', 'Object Literals', 'For loops'] topics: ['Break Statements', 'Object Literals', 'For loops']
duration: 5 duration: 5
@ -72,7 +72,7 @@ data.courses = [
image: '/images/pages/courses/106_info.png' image: '/images/pages/courses/106_info.png'
}, },
{ {
title: 'Course 7' title: 'Computer Science 7'
description: 'Functions!' description: 'Functions!'
topics: ['Object Literals', 'For loops', 'Functions'] topics: ['Object Literals', 'For loops', 'Functions']
duration: 5 duration: 5
@ -80,7 +80,7 @@ data.courses = [
image: '/images/pages/courses/107_info.png' image: '/images/pages/courses/107_info.png'
}, },
{ {
title: 'CS 108' title: 'Computer Science 8'
description: 'Maths.' description: 'Maths.'
topics: ['For loops', 'Functions', 'Math Operations'] topics: ['For loops', 'Functions', 'Math Operations']
duration: 5 duration: 5
@ -88,7 +88,7 @@ data.courses = [
image: '/images/pages/courses/107_info.png' image: '/images/pages/courses/107_info.png'
}, },
{ {
title: 'Computer Science 109' title: 'Computer Science 9'
description: 'Vectors and strings.' description: 'Vectors and strings.'
topics: ['Vectors', 'Advanced Strings'] topics: ['Vectors', 'Advanced Strings']
duration: 5 duration: 5

View file

@ -51,6 +51,10 @@ module.exports = class CoursesView extends RootView
$('#continueModal').find('.btn-enroll').data('course-id', courseID) $('#continueModal').find('.btn-enroll').data('course-id', courseID)
$('#continueModal').find('.btn-enter').data('course-id', courseID) $('#continueModal').find('.btn-enter').data('course-id', courseID)
$('#continueModal .row-pick-class').show() if @courses[courseID]?.unlocked $('#continueModal .row-pick-class').show() if @courses[courseID]?.unlocked
if courseTitle is 'Introduction to Computer Science'
$('#continueModal .btn-buy').prop('innerText', 'Get this FREE course!')
else
$('#continueModal .btn-buy').prop('innerText', 'Buy this course')
onClickEnroll: (e) -> onClickEnroll: (e) ->
$('#continueModal').modal('hide') $('#continueModal').modal('hide')
@ -65,7 +69,8 @@ module.exports = class CoursesView extends RootView
$('#continueModal').modal('hide') $('#continueModal').modal('hide')
courseID = $(e.target).data('course-id') courseID = $(e.target).data('course-id')
instanceName = $('.select-session').val() instanceName = $('.select-session').val()
instanceID = index for val, index in @instances when val.name is instanceName for val, index in @instances when val.name is instanceName
instanceID = index
viewClass = require 'views/courses/mock1/CourseDetailsView' viewClass = require 'views/courses/mock1/CourseDetailsView'
viewArgs = [{}, courseID, instanceID] viewArgs = [{}, courseID, instanceID]
navigationEvent = route: "/courses/mock1/#{courseID}", viewClass: viewClass, viewArgs: viewArgs navigationEvent = route: "/courses/mock1/#{courseID}", viewClass: viewClass, viewArgs: viewArgs

View file

@ -30,7 +30,7 @@ class LevelSessionsCollection extends CocoCollection
constructor: (model) -> constructor: (model) ->
super() super()
@url = "/db/user/#{me.id}/level.sessions?project=state.complete,levelID,state.difficulty" @url = "/db/user/#{me.id}/level.sessions?project=state.complete,levelID,state.difficulty,playtime"
class CampaignsCollection extends CocoCollection class CampaignsCollection extends CocoCollection
url: '/db/campaign' url: '/db/campaign'
@ -261,6 +261,7 @@ module.exports = class CampaignView extends RootView
annotateLevel: (level) -> annotateLevel: (level) ->
level.position ?= { x: 10, y: 10 } level.position ?= { x: 10, y: 10 }
level.locked = not me.ownsLevel level.original level.locked = not me.ownsLevel level.original
level.locked = true if level.slug is 'kithgard-mastery' and @calculateExperienceScore() is 0
level.locked = false if @levelStatusMap[level.slug] in ['started', 'complete'] level.locked = false if @levelStatusMap[level.slug] in ['started', 'complete']
level.locked = false if @editorMode level.locked = false if @editorMode
level.locked = false if @campaign?.get('name') is 'Auditions' level.locked = false if @campaign?.get('name') is 'Auditions'
@ -302,12 +303,22 @@ module.exports = class CampaignView extends RootView
determineNextLevel: (levels) -> determineNextLevel: (levels) ->
foundNext = false foundNext = false
dontPointTo = ['lost-viking', 'kithgard-mastery'] # Challenge levels we don't want most players bashing heads against
for level in levels for level in levels
# Iterate through all levels in order and look to find the first unlocked one that meets all our criteria for being pointed out as the next level.
level.nextLevels = (reward.level for reward in level.rewards ? [] when reward.level) level.nextLevels = (reward.level for reward in level.rewards ? [] when reward.level)
unless foundNext unless foundNext
for nextLevelOriginal in level.nextLevels for nextLevelOriginal in level.nextLevels
nextLevel = _.find levels, original: nextLevelOriginal nextLevel = _.find levels, original: nextLevelOriginal
dontPointTo = ['lost-viking','kithgard-mastery']
# If it's a challenge level, we efficiently determine whether we actually do want to point it out.
if nextLevel and nextLevel.slug is 'kithgard-mastery' and not nextLevel.locked and not @levelStatusMap[nextLevel.slug] and @calculateExperienceScore() >= 3
unless (timesPointedOut = storage.load("pointed-out-#{nextLevel.slug}") or 0) > 3
# We may determineNextLevel more than once per render, so we can't just do this once. But we do give up after a couple highlights.
dontPointTo = _.without dontPointTo, nextLevel.slug
storage.save "pointed-out-#{nextLevel.slug}", timesPointedOut + 1
# Should we point this level out?
if nextLevel and not nextLevel.locked and not nextLevel.disabled and @levelStatusMap[nextLevel.slug] isnt 'complete' and nextLevel.slug not in dontPointTo and not nextLevel.replayable and ( if nextLevel and not nextLevel.locked and not nextLevel.disabled and @levelStatusMap[nextLevel.slug] isnt 'complete' and nextLevel.slug not in dontPointTo and not nextLevel.replayable and (
me.isPremium() or me.isPremium() or
not nextLevel.requiresSubscription or not nextLevel.requiresSubscription or
@ -320,6 +331,15 @@ module.exports = class CampaignView extends RootView
if not foundNext and levels[0] and not levels[0].locked and @levelStatusMap[levels[0].slug] isnt 'complete' if not foundNext and levels[0] and not levels[0].locked and @levelStatusMap[levels[0].slug] isnt 'complete'
levels[0].next = true levels[0].next = true
calculateExperienceScore: ->
adultPoint = me.get('ageRange') in ['18-24', '25-34', '35-44', '45-100'] # They have to have answered the poll for this, likely after Shadow Guard.
speedPoints = 0
for [levelSlug, speedThreshold] in [['dungeons-of-kithgard', 50], ['gems-in-the-deep', 55], ['shadow-guard', 55], ['forgetful-gemsmith', 40], ['true-names', 40]]
if _.find(@sessions.models, (session) -> session.get('levelID') is levelSlug)?.attributes.playtime <= speedThreshold
++speedPoints
experienceScore = adultPoint + speedPoints # 0-6 score of how likely we think they are to be experienced and ready for Kithgard Mastery
return experienceScore
createLine: (o1, o2) -> createLine: (o1, o2) ->
p1 = x: o1.x, y: 0.66 * o1.y + 0.5 p1 = x: o1.x, y: 0.66 * o1.y + 0.5
p2 = x: o2.x, y: 0.66 * o2.y + 0.5 p2 = x: o2.x, y: 0.66 * o2.y + 0.5

View file

@ -141,6 +141,7 @@ module.exports = class HeroVictoryModal extends ModalView
getRenderData: -> getRenderData: ->
c = super() c = super()
c.levelName = utils.i18n @level.attributes, 'name' c.levelName = utils.i18n @level.attributes, 'name'
if @level.get('type', true) isnt 'hero'
c.victoryText = utils.i18n @level.get('victory') ? {}, 'body' c.victoryText = utils.i18n @level.get('victory') ? {}, 'body'
earnedAchievementMap = _.indexBy(@newEarnedAchievements or [], (ea) -> ea.get('achievement')) earnedAchievementMap = _.indexBy(@newEarnedAchievements or [], (ea) -> ea.get('achievement'))
for achievement in (@achievements?.models or []) for achievement in (@achievements?.models or [])

View file

@ -73,7 +73,7 @@ module.exports = class PollModal extends ModalView
onClickAnswer: (e) -> onClickAnswer: (e) ->
$selectedAnswer = $(e.target).closest('.answer') $selectedAnswer = $(e.target).closest('.answer')
pollVotes = @userPollsRecord.get('polls') ? {} pollVotes = @userPollsRecord.get('polls') ? {}
pollVotes[@poll.id] = $selectedAnswer.data('answer') pollVotes[@poll.id] = $selectedAnswer.data('answer').toString()
@userPollsRecord.set 'polls', pollVotes @userPollsRecord.set 'polls', pollVotes
@updateAnswers true @updateAnswers true
@userPollsRecord.save {polls: pollVotes}, {success: => @awardRandomGems?()} @userPollsRecord.save {polls: pollVotes}, {success: => @awardRandomGems?()}

View file

@ -11,8 +11,8 @@
var excludedLevels = ['deadly-dungeon-rescue', 'kithgard-brawl', 'cavern-survival', 'kithgard-mastery', 'destroying-angel', 'kithgard-apprentice', 'wild-horses', 'lost-viking', 'forest-flower-grove', 'boulder-woods', 'the-trials']; var excludedLevels = ['deadly-dungeon-rescue', 'kithgard-brawl', 'cavern-survival', 'kithgard-mastery', 'destroying-angel', 'kithgard-apprentice', 'wild-horses', 'lost-viking', 'forest-flower-grove', 'boulder-woods', 'the-trials'];
var scriptStartTime = new Date(); var scriptStartTime = new Date();
var startDay = '2015-05-10'; var startDay = '2015-07-01';
var endDay = '2015-06-11'; var endDay = '2015-08-06';
log("Dates: " + startDay + " to " + endDay); log("Dates: " + startDay + " to " + endDay);

View file

@ -0,0 +1,25 @@
// Print out teacher survey counts by day
// Usage:
// mongo <address>:<port>/<database> <script file> -u <username> -p <password>
var surveyDayMap = {};
var cursor = db['trial.requests'].find({type: 'subscription'});
while (cursor.hasNext()) {
var doc = cursor.next();
var date = doc._id.getTimestamp();
var day = date.toISOString().substring(0, 10);
if (!surveyDayMap[day]) surveyDayMap[day] = 0;
surveyDayMap[day]++;
}
var surveysSorted = [];
for (var day in surveyDayMap) {
surveysSorted.push({day: day, count: surveyDayMap[day]});
}
surveysSorted.sort(function(a, b) {return b.day.localeCompare(a.day);});
print("Number of teacher surveys per day:")
for (var i = 0; i < surveysSorted.length; i++) {
var stars = new Array(surveysSorted[i].count + 1).join('*');
print(surveysSorted[i].day + "\t" + surveysSorted[i].count + "\t" + stars);
}