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) ->
@options.isCursor = to
@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()

View file

@ -1,6 +1,8 @@
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
childMovieClips: null
@ -71,6 +73,13 @@ module.exports = class SingularSprite extends createjs.Sprite
@baseScaleY = @scaleY
if @camera and @thangType.get('name') in floors
@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
return

View file

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

View file

@ -933,7 +933,7 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription:
contribute:
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: "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."

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_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_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_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"
@ -337,7 +337,7 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription:
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_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:
inventory_tab: "Inventário"
@ -851,8 +851,8 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription:
indoor: "Interior"
desert: "Deserto"
grassy: "Relvado"
# mountain: "Mountain"
# glacier: "Glacier"
mountain: "Montanha"
glacier: "Glaciar"
small: "Pequeno"
large: "Grande"
fork_title: "Bifurcar Nova Versão"
@ -1211,7 +1211,7 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription:
delta:
added: "Adicionado"
modified: "Modificado"
# not_modified: "Not Modified"
not_modified: "Não Modificado"
deleted: "Eliminado"
moved_index: "Índice Movido"
text_diff: "Diferença de Texto"

View file

@ -1,29 +1,42 @@
#course-details-view
.concept-completion-container
position: relative
.concept-completion-background
position: absolute
height: 100%
left: 0px
top: 0px
background-color: blue
opacity: 0.25
.concept-completed-foreground
font-size: 12pt
font-size: 10pt
#editDescriptionModal .modal-dialog
.summary-container
font-size: 14pt
.statistics-container
font-size: 12pt
td
padding-right: 8px
.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
#editNameModal .modal-dialog
#editSettingsModal .modal-dialog
background-color: white
max-width: 400px
font-size: 14pt
.edit-description-input
width: 100%
.edit-name-input
width: 100%
width: 50%
.member-header
cursor: pointer

View file

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

View file

@ -2,7 +2,29 @@
@import "app/styles/bootstrap/variables"
#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
height: 626px
@media screen and ( max-height: 800px )

View file

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

View file

@ -350,9 +350,15 @@ $itemSlotGridHeight: 51px
img
margin: 1px
button
margin-top: -2px
height: 19px
//margin-top: -2px
//height: 19px
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
width: 56px
@ -360,22 +366,32 @@ $itemSlotGridHeight: 51px
display: block
button
width: 100%
height: 17px
border: 1px solid rgb(46,46,46)
background: white
background: transparent url(/images/pages/play/modal/equip-buttons.png)
width: 56px
height: 21px
border: 0
//border: 1px solid rgb(46,46,46)
//background: white
font-size: 11px
border-radius: 1px
padding: 0
@include transition(0.1s ease)
padding: 0 0 2px 0
@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
background-color: rgb(81,153,236)
button
background-color: lighten(rgb(89,136,47), 10%)
//background-color: lighten(rgb(89,136,47), 10%)
box-shadow: 1px 1px 4px #333
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
// display: inline
@ -594,3 +610,12 @@ $itemSlotGridHeight: 51px
&.male
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,49 +1,50 @@
block header
#site-nav
a(href="/")
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
block site_nav
a(href="/")
img#small-nav-logo(src="/images/pages/base/logo.png", title="CodeCombat - Learn how to code by playing a game", alt="CodeCombat")
a(href="/")
span.glyphicon.glyphicon-home
a(href="/about", data-i18n="nav.about")
a(href='/teachers', data-i18n="nav.teachers") Teachers
a(href='/clans', data-i18n="clans.clans") Clans
a(href='http://discourse.codecombat.com/', data-i18n="nav.forum")
a(href='/community', data-i18n="nav.community")
//a(href='/play/ladder', data-i18n="home.multiplayer").multiplayer-nav-link
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
a(href="/")
img#small-nav-logo(src="/images/pages/base/logo.png", title="CodeCombat - Learn how to code by playing a game", alt="CodeCombat")
a(href="/")
span.glyphicon.glyphicon-home
a(href="/about", data-i18n="nav.about")
a(href='/teachers', data-i18n="nav.teachers") Teachers
a(href='/clans', data-i18n="clans.clans") Clans
a(href='http://discourse.codecombat.com/', data-i18n="nav.forum")
a(href='/community', data-i18n="nav.community")
//a(href='/play/ladder', data-i18n="home.multiplayer").multiplayer-nav-link
if me.get('anonymous') === false
span.dropdown
button.btn.btn-sm.header-font.dropdown-toggle(href="#", data-toggle="dropdown")
if me.get('photoURL')
img.account-settings-image(src=me.getPhotoURL(18), alt="")
else
i.glyphicon.glyphicon-user
span.spl.spr(data-i18n="nav.account" href="/account")
span.caret
ul.dropdown-menu(role="menu")
li.user-dropdown-header
span.user-level= me.level()
a(href="/user/#{me.getSlugOrID()}")
div.img-circle(style="background-image: url(#{me.getPhotoURL()})")
h3=me.displayName()
li
a(href="/user/#{me.getSlugOrID()}" data-i18n="nav.profile")
li
a(href="/account/settings", data-i18n="play.settings")
li
a(href="/account/payments", data-i18n="account.payments")
li
a(href="/account/subscription", data-i18n="account.subscription")
li
a#logout-button(data-i18n="login.log_out")
else
button.btn.btn-sm.btn-primary.header-font.signup-button(data-i18n="login.sign_up")
button.btn.btn-sm.btn-default.header-font.login-button(data-i18n="login.log_in")
select.language-dropdown.form-control
if me.get('anonymous') === false
span.dropdown
button.btn.btn-sm.header-font.dropdown-toggle(href="#", data-toggle="dropdown")
if me.get('photoURL')
img.account-settings-image(src=me.getPhotoURL(18), alt="")
else
i.glyphicon.glyphicon-user
span.spl.spr(data-i18n="nav.account" href="/account")
span.caret
ul.dropdown-menu(role="menu")
li.user-dropdown-header
span.user-level= me.level()
a(href="/user/#{me.getSlugOrID()}")
div.img-circle(style="background-image: url(#{me.getPhotoURL()})")
h3=me.displayName()
li
a(href="/user/#{me.getSlugOrID()}" data-i18n="nav.profile")
li
a(href="/account/settings", data-i18n="play.settings")
li
a(href="/account/payments", data-i18n="account.payments")
li
a(href="/account/subscription", data-i18n="account.subscription")
li
a#logout-button(data-i18n="login.log_out")
else
button.btn.btn-sm.btn-primary.header-font.signup-button(data-i18n="login.sign_up")
button.btn.btn-sm.btn-default.header-font.login-button(data-i18n="login.log_in")
select.language-dropdown.form-control
block outer_content

View file

@ -5,7 +5,6 @@ block content
//- DO NOT localize / i18n
div TODO: fix ugly tabs
div TODO: not enrolled yet
div
span *UNDER CONSTRUCTION, send feedback to
a.spl(href='mailto:team@codecombat.com') team@codecombat.com
@ -14,169 +13,183 @@ block content
span.spl Student view
div(style='border-bottom: 1px solid black;')
.modal#editNameModal
.modal#editSettingsModal
.modal-dialog
.modal-header
button.close(data-dismiss='modal')
span ×
h3.modal-title Edit Class Name
h3.modal-title Edit Class Settings
.modal-body
p This title will be displayed to everyone enrolled in this class.
input.edit-name-input(type='text', value="#{instance.name}")
p This title will be displayed to everyone in the class.
p
input.edit-name-input(type='text', value="#{instance.name}")
p This description will be displayed to everyone in the class.
p
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
button.btn.edit-name-save-btn(data-i18n="common.save_changes")
button.btn.btn-save-settings(data-i18n="common.save_changes")
.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
.modal-footer
button.btn.edit-description-save-btn(data-i18n="common.save_changes")
h1= course.title
p= course.description
h3= instance.name
if !studentMode
span.spl
button.btn.btn-xs.edit-class-name-btn(data-toggle='modal', data-target='#editNameModal') edit class name
h1= instance.name
small.spl (#{course.title})
p
if instance.description
each line in instance.description.split('\n')
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
.form-group
span.spr Select language
select.form-control.select-language
option(value="Python") Python
option(value="JavaScript") JavaScript
option(value="All Languages") All Languages
p
button.btn.btn-xs.btn-edit-settings(data-toggle='modal', data-target='#editSettingsModal') edit class settings
div(role='tabpanel')
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')
a(href='#progress', aria-controls='progress', role='tab', data-toggle='tab') Class
li(role='presentation')
a(href='#invite', aria-controls='invite', role='tab', data-toggle='tab') Add Students
li(role='presentation')
a(href='#levels', aria-controls='levels', role='tab', data-toggle='tab') Levels
li(role='presentation')
a(href='#levels', aria-controls='levels', role='tab', data-toggle='tab') Levels
.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')
if instance.students
h3 Summary
- var averagePlaytime = Math.round(Math.random() * 30) + 30
p
strong(style='font-size:12pt;') Average Play Time
p #{averagePlaytime} minutes
p
strong(style='font-size:12pt;') Concepts Covered
table.table.table-condensed
+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
.container-fluid.summary-container
.row
.col-md-6
h3 Statistics
table.statistics-container
tr
td Total students:
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
- var conceptCompletion = Math.round(parseFloat(conceptsCompleted[concept]) / instance.students.length * 100)
if isNaN(conceptCompletion)
- conceptCompletion = 0
tr
td.concept-completion-container
span.concept-summary(style="width:#{conceptCompletion}%;")
span.concept-completed-foreground(data-i18n="concepts." + concept)
span.spl - #{conceptCompletion}%
span.concept-completion-background(style="width:#{conceptCompletion}%;")
h3 Students
table.table.table-condensed
thead
tr
th
span.member-header.spr Name
if memberSort === 'nameAsc'
span.member-header.glyphicon.glyphicon-chevron-up
else if memberSort === 'nameDesc'
span.member-header.glyphicon.glyphicon-chevron-down
th
span.progress-header.spr Progress
if memberSort === 'progressAsc'
span.progress-header.glyphicon.glyphicon-chevron-up
else if memberSort === 'progressDesc'
span.progress-header.glyphicon.glyphicon-chevron-down
else
span(style='padding-left:16px;')
span.progress-key.progress-key-complete complete
span.progress-key.progress-key-started started
span.progress-key not started
if maxLastStartedIndex > 30
input.expand-progress-checkbox(type='checkbox')
span.spl.expand-progress-label(data-i18n="clans.exp_levels") Expand levels
tbody
each student in instance.students
tr
td
a= student
td.progress-cell
.level-progression-concepts Concepts
each concept in courseConcepts
if userConceptsMap[student] && userConceptsMap[student][concept] === 'complete'
span.spr.progress-concept-cell.progress-concept-cell-complete(data-i18n="concepts." + concept)
else if userConceptsMap[student] && userConceptsMap[student][concept] === 'started'
span.spr.progress-concept-cell.progress-concept-cell-started(data-i18n="concepts." + concept)
else
span.spr.progress-concept-cell.progress-concept-cell-not-started(data-i18n="concepts." + concept)
.level-progression-levels Levels
- var i = 0
each level in course.levels
if userLevelStateMap[student][level] === 'complete'
span.progress-level-cell.progress-level-cell-complete #{i + 1}
if showExpandedProgress || i === 0 || i === course.levels.length - 1
span.spl #{level}
.level-popup-container
h3 #{i + 1}. #{level}
p
div
- var playTime = Math.round(Math.random() * 600)
span Time to solve
span : #{playTime} seconds
div
- var completionDate = new Date()
- completionDate.setUTCDate(completionDate.getUTCDate() - Math.round(Math.random() * 60))
span Completed on
span : #{moment(completionDate).format('MMMM Do YYYY, h:mm:ss a')}
strong(data-i18n="clans.view_solution") Click to view solution.
else if userLevelStateMap[student][level] === 'started'
span.progress-level-cell.progress-level-cell-started #{i + 1} #{level}
else
span.progress-level-cell.level-progression-level-not-started #{i + 1}
if showExpandedProgress || i === 0
span.spl #{level}
- i++
h3 Students
table.table.table-condensed
thead
tr
th
span.member-header.spr Name
if memberSort === 'nameAsc'
span.member-header.glyphicon.glyphicon-chevron-up
else if memberSort === 'nameDesc'
span.member-header.glyphicon.glyphicon-chevron-down
th
span.progress-header.spr Progress
if memberSort === 'progressAsc'
span.progress-header.glyphicon.glyphicon-chevron-up
else if memberSort === 'progressDesc'
span.progress-header.glyphicon.glyphicon-chevron-down
else
span(style='padding-left:16px;')
span.progress-key.progress-key-complete complete
span.progress-key.progress-key-started started
span.progress-key not started
if maxLastStartedIndex > 30
input.expand-progress-checkbox(type='checkbox')
span.spl.expand-progress-label(data-i18n="clans.exp_levels") Expand levels
tbody
each student in instance.students
tr
td
a= student
td.progress-cell
.level-progression-concepts Concepts
each concept in courseConcepts
if userConceptsMap[student] && userConceptsMap[student][concept] === 'complete'
span.spr.progress-concept-cell.progress-concept-cell-complete(data-i18n="concepts." + concept)
else if userConceptsMap[student] && userConceptsMap[student][concept] === 'started'
span.spr.progress-concept-cell.progress-concept-cell-started(data-i18n="concepts." + concept)
else
span.spr.progress-concept-cell.progress-concept-cell-not-started(data-i18n="concepts." + concept)
.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
.level-progression-levels Levels
- var i = 0
each level in course.levels
if userLevelStateMap[student][level] === 'complete'
span.progress-level-cell.progress-level-cell-complete #{i + 1}
if showExpandedProgress || i === 0 || i === course.levels.length - 1
span.spl #{level}
.level-popup-container
h3 #{i + 1}. #{level}
p
div
- var playTime = Math.round(Math.random() * 600)
span Time to solve
span : #{playTime} seconds
div
- var completionDate = new Date()
- completionDate.setUTCDate(completionDate.getUTCDate() - Math.round(Math.random() * 60))
span Completed on
span : #{moment(completionDate).format('MMMM Do YYYY, h:mm:ss a')}
strong(data-i18n="clans.view_solution") Click to view solution.
else if userLevelStateMap[student][level] === 'started'
span.progress-level-cell.progress-level-cell-started #{i + 1} #{level}
else
span.progress-level-cell.level-progression-level-not-started #{i + 1}
if showExpandedProgress || i === 0
span.spl #{level}
- i++
mixin levels-tab
table.table.table-striped.table-condensed

View file

@ -29,7 +29,7 @@ block content
each inst in instances
option= inst.name
.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
.col-md-12
div.or Or
@ -43,39 +43,34 @@ block content
.col-md-8
input.code-input(type='text', placeholder="Enter unlock code")
.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
.col-md-12
div.or Or
.row.button-row.center
.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
//- p.center.gameplay-img-container
//- 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.
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.
.container-fluid
.row
.col-md-12
p.center
button.btn.btn-success.btn-lg.btn-buy Enroll Now!
.row
.col-md-6
div With CodeCombat courses, you and your students will:
ul
li Learn more in less time
li With no experience necesssary
li Monitor student progress
br
li No coding experience necesssary
li Easily monitor student progress
div Purchase a course for your entire class. It's easy to sign up your students!
.col-md-6
p
.well.well-sm
div.praise-quote "#{praise.quote}"
div.caption-text - #{praise.source}
//- h2.center Available Courses
h2.center Choose Your Course:
.container-fluid
- var i = 0
@ -95,13 +90,13 @@ mixin course-block(course, courseID)
span.spr #{course.title}
strong #{course.unlocked ? '[ enrolled ]' : ''}
.panel-body
strong Topics
ul
each topic in course.topics
li= topic
strong Hours of content: #{course.duration}
.container-fluid
.row.button-row.center
.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'}
.row.button-row
.col-md-6
strong Topics
ul
each topic in course.topics
li= topic
strong Hours of content: #{course.duration}
.col-md-6.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'}

View file

@ -1,5 +1,10 @@
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
#spacer

View file

@ -90,6 +90,7 @@ else
.game-controls.header-font
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.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")
@ -118,9 +119,6 @@ else
button#logout-button.btn.btn-illustrated.btn-warning(data-i18n="login.log_out") Log Out
if me.isPremium()
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")
.glyphicon.glyphicon-volume-off

View file

@ -10,11 +10,9 @@ module.exports = class CourseDetailsView extends RootView
events:
'change .expand-progress-checkbox': 'onExpandedProgressCheckbox'
'change .select-session': 'onChangeSession'
'change .student-mode-checkbox': 'onChangeStudent'
'click .btn-play-level': 'onClickPlayLevel'
'click .edit-description-save-btn': 'onEditDescriptionSave'
'click .edit-name-save-btn': 'onEditNameSave'
'click .btn-save-settings': 'onClickSaveSettings'
'click .member-header': 'onClickMemberHeader'
'click .progress-header': 'onClickProgressHeader'
'mouseenter .progress-level-cell': 'onMouseEnterPoint'
@ -49,6 +47,14 @@ module.exports = class CourseDetailsView extends RootView
conceptsCompleted[concept]++
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
initData: ->
@ -144,26 +150,12 @@ module.exports = class CourseDetailsView extends RootView
@render?()
$('.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) ->
@showExpandedProgress = $('.expand-progress-checkbox').prop('checked')
# TODO: why does render reset the checkbox to be unchecked?
@render?()
$('.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) ->
@memberSort = if @memberSort is 'nameAsc' then 'nameDesc' else 'nameAsc'
@sortMembers()
@ -183,16 +175,12 @@ module.exports = class CourseDetailsView extends RootView
viewArgs: [{}, levelSlug]
}
onEditDescriptionSave: (e) ->
description = $('.edit-description-input').val()
@instances[@currentInstanceIndex].description = description
$('#editDescriptionModal').modal('hide')
@render?()
onEditNameSave: (e) ->
onClickSaveSettings: (e) ->
if name = $('.edit-name-input').val()
@instances[@currentInstanceIndex].name = name
$('#editNameModal').modal('hide')
description = $('.edit-description-input').val()
@instances[@currentInstanceIndex].description = description
$('#editSettingsModal').modal('hide')
@render?()
onMouseEnterPoint: (e) ->

View file

@ -40,7 +40,7 @@ data.courses = [
image: '/images/pages/courses/102_info.png'
},
{
title: 'CS Course 3'
title: 'Computer Science 3'
description: 'Learn how to handle input.'
topics: ['If Statements', 'Arithmetic', 'Input Handling']
duration: 5
@ -48,7 +48,7 @@ data.courses = [
image: '/images/pages/courses/103_info.png'
},
{
title: 'CS 4'
title: 'Computer Science 4'
description: 'Time to tackle arrays and some pvp stuff.'
topics: ['Loops', 'Break Statements', 'Arrays']
duration: 5
@ -56,7 +56,7 @@ data.courses = [
image: '/images/pages/courses/104_info.png'
},
{
title: 'Course 5'
title: 'Computer Science 5'
description: 'Time to tackle arrays and some PVP.'
topics: ['Break Statements', 'Arrays', 'Object Literals']
duration: 5
@ -64,7 +64,7 @@ data.courses = [
image: '/images/pages/courses/105_info.png'
},
{
title: 'Course 6'
title: 'Computer Science 6'
description: 'For loops!'
topics: ['Break Statements', 'Object Literals', 'For loops']
duration: 5
@ -72,7 +72,7 @@ data.courses = [
image: '/images/pages/courses/106_info.png'
},
{
title: 'Course 7'
title: 'Computer Science 7'
description: 'Functions!'
topics: ['Object Literals', 'For loops', 'Functions']
duration: 5
@ -80,7 +80,7 @@ data.courses = [
image: '/images/pages/courses/107_info.png'
},
{
title: 'CS 108'
title: 'Computer Science 8'
description: 'Maths.'
topics: ['For loops', 'Functions', 'Math Operations']
duration: 5
@ -88,7 +88,7 @@ data.courses = [
image: '/images/pages/courses/107_info.png'
},
{
title: 'Computer Science 109'
title: 'Computer Science 9'
description: 'Vectors and strings.'
topics: ['Vectors', 'Advanced Strings']
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-enter').data('course-id', courseID)
$('#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) ->
$('#continueModal').modal('hide')
@ -65,7 +69,8 @@ module.exports = class CoursesView extends RootView
$('#continueModal').modal('hide')
courseID = $(e.target).data('course-id')
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'
viewArgs = [{}, courseID, instanceID]
navigationEvent = route: "/courses/mock1/#{courseID}", viewClass: viewClass, viewArgs: viewArgs

View file

@ -30,7 +30,7 @@ class LevelSessionsCollection extends CocoCollection
constructor: (model) ->
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
url: '/db/campaign'
@ -261,6 +261,7 @@ module.exports = class CampaignView extends RootView
annotateLevel: (level) ->
level.position ?= { x: 10, y: 10 }
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 @editorMode
level.locked = false if @campaign?.get('name') is 'Auditions'
@ -302,12 +303,22 @@ module.exports = class CampaignView extends RootView
determineNextLevel: (levels) ->
foundNext = false
dontPointTo = ['lost-viking', 'kithgard-mastery'] # Challenge levels we don't want most players bashing heads against
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)
unless foundNext
for nextLevelOriginal in level.nextLevels
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 (
me.isPremium() 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'
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) ->
p1 = x: o1.x, y: 0.66 * o1.y + 0.5
p2 = x: o2.x, y: 0.66 * o2.y + 0.5

View file

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

View file

@ -73,7 +73,7 @@ module.exports = class PollModal extends ModalView
onClickAnswer: (e) ->
$selectedAnswer = $(e.target).closest('.answer')
pollVotes = @userPollsRecord.get('polls') ? {}
pollVotes[@poll.id] = $selectedAnswer.data('answer')
pollVotes[@poll.id] = $selectedAnswer.data('answer').toString()
@userPollsRecord.set 'polls', pollVotes
@updateAnswers true
@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 scriptStartTime = new Date();
var startDay = '2015-05-10';
var endDay = '2015-06-11';
var startDay = '2015-07-01';
var endDay = '2015-08-06';
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);
}