From 942b69ea9f1f12464972ca1881f6199e8710b212 Mon Sep 17 00:00:00 2001 From: Ruben Vereecken <ruben.vereecken@student.uantwerpen.be> Date: Mon, 3 Mar 2014 20:41:35 +0100 Subject: [PATCH 001/100] Created hasWriteAccess on Cocomodel, used it for level access --- app/models/CocoModel.coffee | 21 +++++++++++++++++++++ app/templates/editor/level/edit.jade | 7 +++---- app/templates/kinds/search.jade | 1 - app/views/editor/level/edit.coffee | 1 + 4 files changed, 25 insertions(+), 5 deletions(-) diff --git a/app/models/CocoModel.coffee b/app/models/CocoModel.coffee index f4551b904..74dc336bf 100644 --- a/app/models/CocoModel.coffee +++ b/app/models/CocoModel.coffee @@ -184,4 +184,25 @@ class CocoModel extends Backbone.Model @isObjectID: (s) -> s.length is 24 and s.match(/[a-z0-9]/gi)?.length is 24 + hasReadAccess: (actor) -> + # actor is a User object + + if @get('permissions')? + for permission in @get('permissions') + if permission.target is 'public' or actor.get('_id') is permission.target + return true if permission.access in ['owner', 'read'] + + return false + + hasWriteAccess: (actor) -> + # actor is a User object + + if @get('permissions')? + for permission in @get('permissions') + if permission.target is 'public' or actor.get('_id') is permission.target + return true if permission.access in ['owner', 'write'] + + return false + + module.exports = CocoModel diff --git a/app/templates/editor/level/edit.jade b/app/templates/editor/level/edit.jade index cdc97cd2a..e63cdd2b9 100644 --- a/app/templates/editor/level/edit.jade +++ b/app/templates/editor/level/edit.jade @@ -29,10 +29,9 @@ block outer_content ul.nav.navbar-nav.navbar-right - li(data-toggle="coco-modal", data-target="modal/revert", data-i18n="revert.revert").btn.btn-primary.navbar-btn#revert-button Revert - - li(data-i18n="common.save").btn.btn-primary.navbar-btn#commit-level-start-button Save - li(data-i18n="common.fork").btn.btn-primary.navbar-btn#fork-level-start-button Fork + li(data-toggle="coco-modal", data-target="modal/revert", data-i18n="revert.revert", disabled=authorized === true ? undefined : "true").btn.btn-primary.navbar-btn#revert-button Revert + li(data-i18n="common.save", disabled=authorized === true ? undefined : "true").btn.btn-primary.navbar-btn#commit-level-start-button Save + li(data-i18n="common.fork", disabled=authorized === true ? undefined : "true").btn.btn-primary.navbar-btn#fork-level-start-button Fork li(title="⌃↩ or ⌘↩: Play preview of current level", data-i18n="common.play")#play-button.btn.btn-inverse.banner.navbar-btn Play! li.divider diff --git a/app/templates/kinds/search.jade b/app/templates/kinds/search.jade index eda4c0f2b..296581a47 100644 --- a/app/templates/kinds/search.jade +++ b/app/templates/kinds/search.jade @@ -11,7 +11,6 @@ block content a.btn.btn-primary.open-modal-button(data-toggle="coco-modal", data-target="modal/signup", role="button") Sign Up to Create a New #{modelLabel} else a.btn.btn-primary.open-modal-button(href='#new-model-modal', role="button", data-toggle="modal" data-i18n="#{currentNew}") Create a New Something - input#search(data-i18n="[placeholder]#{currentSearch}") hr div.results diff --git a/app/views/editor/level/edit.coffee b/app/views/editor/level/edit.coffee index bbafd529d..c79439800 100644 --- a/app/views/editor/level/edit.coffee +++ b/app/views/editor/level/edit.coffee @@ -63,6 +63,7 @@ module.exports = class EditorLevelView extends View getRenderData: (context={}) -> context = super(context) context.level = @level + context.authorized = me.isAdmin() or @level.hasWriteAccess(me) context afterRender: -> From ee920afea9cdb78e933550de89c8cf4730221381 Mon Sep 17 00:00:00 2001 From: Ruben Vereecken <ruben.vereecken@student.uantwerpen.be> Date: Mon, 3 Mar 2014 21:13:02 +0100 Subject: [PATCH 002/100] Disabled buttons for non-owners in Article Editor --- app/models/CocoModel.coffee | 4 ++-- app/templates/editor/article/edit.jade | 6 +++--- app/views/editor/article/edit.coffee | 1 + app/views/editor/level/scripts_tab_view.coffee | 1 + 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/app/models/CocoModel.coffee b/app/models/CocoModel.coffee index 74dc336bf..ba4ff850c 100644 --- a/app/models/CocoModel.coffee +++ b/app/models/CocoModel.coffee @@ -194,8 +194,8 @@ class CocoModel extends Backbone.Model return false - hasWriteAccess: (actor) -> - # actor is a User object + hasWriteAccess: (actor) -> + # actor is a User object if @get('permissions')? for permission in @get('permissions') diff --git a/app/templates/editor/article/edit.jade b/app/templates/editor/article/edit.jade index a729bf67a..e7dff7cd7 100644 --- a/app/templates/editor/article/edit.jade +++ b/app/templates/editor/article/edit.jade @@ -10,9 +10,9 @@ block content li.active | #{article.attributes.name} - button(data-toggle="coco-modal", data-target="modal/revert", data-i18n="revert.revert").btn.btn-primary#revert-button Revert - button(data-i18n="article.edit_btn_preview").btn.btn-primary#preview-button Preview - button(data-toggle="coco-modal", data-target="modal/save_version", data-i18n="common.save").btn.btn-primary#save-button Save + button(data-toggle="coco-modal", data-target="modal/revert", data-i18n="revert.revert", disabled=authorized === true ? undefined : "true").btn.btn-primary#revert-button Revert + button(data-i18n="article.edit_btn_preview", disabled=authorized === true ? undefined : "true").btn.btn-primary#preview-button Preview + button(data-toggle="coco-modal", data-target="modal/save_version", data-i18n="common.save", disabled=authorized === true ? undefined : "true").btn.btn-primary#save-button Save h3(data-i18n="article.edit_article_title") Edit Article span diff --git a/app/views/editor/article/edit.coffee b/app/views/editor/article/edit.coffee index 05df5291d..d3c2d4c1c 100644 --- a/app/views/editor/article/edit.coffee +++ b/app/views/editor/article/edit.coffee @@ -56,6 +56,7 @@ module.exports = class ArticleEditView extends View getRenderData: (context={}) -> context = super(context) context.article = @article + context.authorized = me.isAdmin() or @article.hasWriteAccess(me) context openPreview: => diff --git a/app/views/editor/level/scripts_tab_view.coffee b/app/views/editor/level/scripts_tab_view.coffee index 45b5c210d..9da71e31c 100644 --- a/app/views/editor/level/scripts_tab_view.coffee +++ b/app/views/editor/level/scripts_tab_view.coffee @@ -59,6 +59,7 @@ module.exports = class ScriptsTabView extends View thangIDs: thangIDs dimensions: @dimensions supermodel: @supermodel + readOnly: not me.isAdmin() and not @level.hasWriteAccess(me) callbacks: change: @onScriptChanged nodeClasses: From 05b4f536c10fc2811ece438e825a85b569ebc1ec Mon Sep 17 00:00:00 2001 From: Ruben Vereecken <ruben.vereecken@student.uantwerpen.be> Date: Mon, 3 Mar 2014 21:22:04 +0100 Subject: [PATCH 003/100] Fixed a link to point correctly --- app/templates/editor/article/edit.jade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/templates/editor/article/edit.jade b/app/templates/editor/article/edit.jade index e7dff7cd7..7c6a7136a 100644 --- a/app/templates/editor/article/edit.jade +++ b/app/templates/editor/article/edit.jade @@ -6,7 +6,7 @@ block content li a(href="/editor", data-i18n="editor.main_title") CodeCombat Editors li - a(href="/editor/thang", data-i18n="editor.article_title") Article Editor + a(href="/editor/article", data-i18n="editor.article_title") Article Editor li.active | #{article.attributes.name} From 0aa8e7bb7d4d4219eb5ad2242dc4ee06f0bc6b39 Mon Sep 17 00:00:00 2001 From: Ruben Vereecken <ruben.vereecken@student.uantwerpen.be> Date: Mon, 3 Mar 2014 22:21:56 +0100 Subject: [PATCH 004/100] Added owner-based save enabling on Thang Types. NOTE server-sided doesn't work yet --- app/templates/editor/thang/edit.jade | 9 ++++----- app/views/editor/article/edit.coffee | 2 +- app/views/editor/level/scripts_tab_view.coffee | 2 +- app/views/editor/level/settings_tab_view.coffee | 1 + app/views/editor/level/systems_tab_view.coffee | 1 + app/views/editor/thang/colors_tab_view.coffee | 1 + app/views/editor/thang/edit.coffee | 2 ++ 7 files changed, 11 insertions(+), 7 deletions(-) diff --git a/app/templates/editor/thang/edit.jade b/app/templates/editor/thang/edit.jade index c7b80fea0..af30eb2c1 100644 --- a/app/templates/editor/thang/edit.jade +++ b/app/templates/editor/thang/edit.jade @@ -12,9 +12,8 @@ block content img#portrait.img-thumbnail - button.btn.btn-primary#save-button(data-toggle="coco-modal", data-target="modal/save_version") - | Save - button.btn.btn-primary#revert-button(data-toggle="coco-modal", data-target="modal/revert", data-i18n="revert.revert") Revert + button.btn.btn-primary#save-button(data-toggle="coco-modal", data-target="modal/save_version", disabled=authorized === true ? undefined : "true") Save + button.btn.btn-primary#revert-button(data-toggle="coco-modal", data-target="modal/revert", data-i18n="revert.revert", disabled=authorized === true ? undefined : "true") Revert h3 Edit Thang Type: "#{thangType.attributes.name}" @@ -38,9 +37,9 @@ block content select#animations-select for animation in animations option #{animation} - button.btn.btn-small.btn-primary#upload-button + button(disabled=authorized === true ? undefined : "true").btn.btn-small.btn-primary#upload-button i.icon-upload - button.btn.btn-small.btn-primary#clear-button + button(disabled=authorized === true ? undefined : "true").btn.btn-small.btn-primary#clear-button i.icon-remove input#real-upload-button(type="file") diff --git a/app/views/editor/article/edit.coffee b/app/views/editor/article/edit.coffee index d3c2d4c1c..77f95ac40 100644 --- a/app/views/editor/article/edit.coffee +++ b/app/views/editor/article/edit.coffee @@ -37,9 +37,9 @@ module.exports = class ArticleEditView extends View data: data filePath: "db/thang.type/#{@article.get('original')}" schema: Article.schema.attributes + readOnly: true unless me.isAdmin() or @article.hasWriteAccess(me) callbacks: change: @pushChangesToPreview - options.readOnly = true unless me.isAdmin() @treema = @$el.find('#article-treema').treema(options) @treema.build() diff --git a/app/views/editor/level/scripts_tab_view.coffee b/app/views/editor/level/scripts_tab_view.coffee index 9da71e31c..f0088ad21 100644 --- a/app/views/editor/level/scripts_tab_view.coffee +++ b/app/views/editor/level/scripts_tab_view.coffee @@ -59,7 +59,7 @@ module.exports = class ScriptsTabView extends View thangIDs: thangIDs dimensions: @dimensions supermodel: @supermodel - readOnly: not me.isAdmin() and not @level.hasWriteAccess(me) + readOnly: true unless me.isAdmin() or @level.hasWriteAccess(me) callbacks: change: @onScriptChanged nodeClasses: diff --git a/app/views/editor/level/settings_tab_view.coffee b/app/views/editor/level/settings_tab_view.coffee index 2dbfcf165..4e2518be6 100644 --- a/app/views/editor/level/settings_tab_view.coffee +++ b/app/views/editor/level/settings_tab_view.coffee @@ -29,6 +29,7 @@ module.exports = class SettingsTabView extends View supermodel: @supermodel schema: schema data: data + readOnly: true unless me.isAdmin() or @level.hasWriteAccess(me) callbacks: {change: @onSettingsChanged} thangIDs: thangIDs nodeClasses: diff --git a/app/views/editor/level/systems_tab_view.coffee b/app/views/editor/level/systems_tab_view.coffee index 23a58617a..a4b481754 100644 --- a/app/views/editor/level/systems_tab_view.coffee +++ b/app/views/editor/level/systems_tab_view.coffee @@ -69,6 +69,7 @@ module.exports = class SystemsTabView extends View supermodel: @supermodel schema: Level.schema.get('properties').systems data: systems + readOnly: true unless me.isAdmin() or @level.hasWriteAccess(me) callbacks: change: @onSystemsChanged select: @onSystemSelected diff --git a/app/views/editor/thang/colors_tab_view.coffee b/app/views/editor/thang/colors_tab_view.coffee index 4f42a15fc..b1ba229dc 100644 --- a/app/views/editor/thang/colors_tab_view.coffee +++ b/app/views/editor/thang/colors_tab_view.coffee @@ -115,6 +115,7 @@ module.exports = class ColorsTabView extends CocoView treemaOptions = data: data schema: schema + readOnly: true unless me.isAdmin() or @thangType.hasWriteAccess(me) callbacks: change: @onColorGroupsChanged select: @onColorGroupSelected diff --git a/app/views/editor/thang/edit.coffee b/app/views/editor/thang/edit.coffee index de0cc062e..a8310cdf2 100644 --- a/app/views/editor/thang/edit.coffee +++ b/app/views/editor/thang/edit.coffee @@ -57,6 +57,7 @@ module.exports = class ThangTypeEditView extends View context = super(context) context.thangType = @thangType context.animations = @getAnimationNames() + context.authorized = me.isAdmin() or @thangType.hasWriteAccess(me) context getAnimationNames: -> @@ -328,6 +329,7 @@ module.exports = class ThangTypeEditView extends View schema: schema files: @files filePath: "db/thang.type/#{@thangType.get('original')}" + readOnly: true unless me.isAdmin() or @thangType.hasWriteAccess(me) callbacks: change: @pushChangesToPreview select: @onSelectNode From b7fbf67e8946f7bb193986d4c823b1d7b01a9719 Mon Sep 17 00:00:00 2001 From: Ruben Vereecken <ruben.vereecken@student.uantwerpen.be> Date: Mon, 3 Mar 2014 22:52:37 +0100 Subject: [PATCH 005/100] Enabled Fork button again. Previous commits solve issue #455 --- app/templates/editor/level/edit.jade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/templates/editor/level/edit.jade b/app/templates/editor/level/edit.jade index e63cdd2b9..bbf0026d5 100644 --- a/app/templates/editor/level/edit.jade +++ b/app/templates/editor/level/edit.jade @@ -31,7 +31,7 @@ block outer_content ul.nav.navbar-nav.navbar-right li(data-toggle="coco-modal", data-target="modal/revert", data-i18n="revert.revert", disabled=authorized === true ? undefined : "true").btn.btn-primary.navbar-btn#revert-button Revert li(data-i18n="common.save", disabled=authorized === true ? undefined : "true").btn.btn-primary.navbar-btn#commit-level-start-button Save - li(data-i18n="common.fork", disabled=authorized === true ? undefined : "true").btn.btn-primary.navbar-btn#fork-level-start-button Fork + li(data-i18n="common.fork").btn.btn-primary.navbar-btn#fork-level-start-button Fork li(title="⌃↩ or ⌘↩: Play preview of current level", data-i18n="common.play")#play-button.btn.btn-inverse.banner.navbar-btn Play! li.divider From d43ff5d21729a85cf33dcd966e6d5915b78962a2 Mon Sep 17 00:00:00 2001 From: Ruben Vereecken <ruben.vereecken@student.uantwerpen.be> Date: Tue, 4 Mar 2014 11:24:38 +0100 Subject: [PATCH 006/100] Added an anonymous check on the Fork button --- app/templates/editor/level/edit.jade | 2 +- app/views/editor/level/edit.coffee | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/templates/editor/level/edit.jade b/app/templates/editor/level/edit.jade index bbf0026d5..7d2c7ac66 100644 --- a/app/templates/editor/level/edit.jade +++ b/app/templates/editor/level/edit.jade @@ -31,7 +31,7 @@ block outer_content ul.nav.navbar-nav.navbar-right li(data-toggle="coco-modal", data-target="modal/revert", data-i18n="revert.revert", disabled=authorized === true ? undefined : "true").btn.btn-primary.navbar-btn#revert-button Revert li(data-i18n="common.save", disabled=authorized === true ? undefined : "true").btn.btn-primary.navbar-btn#commit-level-start-button Save - li(data-i18n="common.fork").btn.btn-primary.navbar-btn#fork-level-start-button Fork + li(data-i18n="common.fork", disabled=anonymous ? "true": undefined).btn.btn-primary.navbar-btn#fork-level-start-button Fork li(title="⌃↩ or ⌘↩: Play preview of current level", data-i18n="common.play")#play-button.btn.btn-inverse.banner.navbar-btn Play! li.divider diff --git a/app/views/editor/level/edit.coffee b/app/views/editor/level/edit.coffee index c79439800..962db3fac 100644 --- a/app/views/editor/level/edit.coffee +++ b/app/views/editor/level/edit.coffee @@ -64,6 +64,7 @@ module.exports = class EditorLevelView extends View context = super(context) context.level = @level context.authorized = me.isAdmin() or @level.hasWriteAccess(me) + context.anonymous = me.get('anonymous') context afterRender: -> From 7885b21b4b516a873cf288f7f9e32e4f91377530 Mon Sep 17 00:00:00 2001 From: iraladson <aladso@saic.edu> Date: Wed, 5 Mar 2014 14:03:57 -0600 Subject: [PATCH 007/100] Update names.coffee --- app/lib/world/names.coffee | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/lib/world/names.coffee b/app/lib/world/names.coffee index a7a3f256e..789c9b0f8 100644 --- a/app/lib/world/names.coffee +++ b/app/lib/world/names.coffee @@ -61,6 +61,8 @@ module.exports.thangNames = thangNames = "Joan" "Helga" "Annie" + "Lukaz" + "Gorgin" ] "Peasant": [ "Yorik" @@ -83,6 +85,8 @@ module.exports.thangNames = thangNames = "Bernadette" "Hershell" "Gawain" + "Durfkor" + "Paps" ] "Archer F": [ "Phoebe" @@ -117,6 +121,8 @@ module.exports.thangNames = thangNames = "Simon" "Robin" "Quinn" + "Arty" + "Gimsley" ] "Ogre Munchkin M": [ "Brack" From 8d046c31b990fbfdeef066422fa95954aa43253b Mon Sep 17 00:00:00 2001 From: iraladson <aladso@saic.edu> Date: Wed, 5 Mar 2014 14:06:29 -0600 Subject: [PATCH 008/100] Update names.coffee "Added more names for #53 --- app/lib/world/names.coffee | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/lib/world/names.coffee b/app/lib/world/names.coffee index a7a3f256e..b23f8b862 100644 --- a/app/lib/world/names.coffee +++ b/app/lib/world/names.coffee @@ -117,6 +117,7 @@ module.exports.thangNames = thangNames = "Simon" "Robin" "Quinn" + "Fidsdale" ] "Ogre Munchkin M": [ "Brack" @@ -179,6 +180,7 @@ module.exports.thangNames = thangNames = "Borgag" "Grognar" "Ironjaw" + "Tuguro" ] "Ogre Fangrider": [ "Dreek" From 398fa3c426fba3c14fb8dcad581241ef337e58c0 Mon Sep 17 00:00:00 2001 From: Darredevil <alex.darredevil@gmail.com> Date: Thu, 6 Mar 2014 00:12:58 +0200 Subject: [PATCH 009/100] Update ro.coffee so much to translate.... --- app/locale/ro.coffee | 174 +++++++++++++++++++++---------------------- 1 file changed, 87 insertions(+), 87 deletions(-) diff --git a/app/locale/ro.coffee b/app/locale/ro.coffee index 6bdb8e75a..3551d730b 100644 --- a/app/locale/ro.coffee +++ b/app/locale/ro.coffee @@ -96,8 +96,8 @@ module.exports = nativeDescription: "limba română", englishDescription: "Roman diplomat_suggestion: title: "Ajută-ne să traducem CodeCombat!" sub_heading: "Avem nevoie de abilitățile tale lingvistice." - pitch_body: "We develop CodeCombat in English, but we already have players all over the world. Many of them want to play in Romanian but don't speak English, so if you can speak both, please consider signing up to be a Diplomat and help translate both the CodeCombat website and all the levels into Romanian." #are these still needed?? - missing_translations: "Until we can translate everything into Romanian, you'll see English when Romanian isn't available." # is this still needed? + pitch_body: "CodeCombat este dezvoltat in limba engleza , dar deja avem jucatări din toate colțurile lumii.Mulți dintre ei vor să joace in română și nu vorbesc engleză.Dacă poți vorbi ambele te rugăm să te gândești dacă ai dori să devi un Diplomat și să ne ajuți sa traducem atât jocul cât și site-ul." + missing_translations: "Until we can translate everything into Romanian, you'll see English when Romanian isn't available." learn_more: "Află mai multe despre cum să fi un Diplomat" subscribe_as_diplomat: "Înscrie-te ca Diplomat" @@ -130,95 +130,95 @@ module.exports = nativeDescription: "limba română", englishDescription: "Roman new_password_verify: "Verifică" email_subscriptions: "Subscripție Email" email_announcements: "Anunțuri" - email_notifications_description: "Get periodic notifications for your account." -# email_announcements_description: "Get emails on the latest news and developments at CodeCombat." -# contributor_emails: "Contributor Class Emails" -# contribute_prefix: "We're looking for people to join our party! Check out the " -# contribute_page: "contribute page" -# contribute_suffix: " to find out more." -# email_toggle: "Toggle All" -# error_saving: "Error Saving" -# saved: "Changes Saved" -# password_mismatch: "Password does not match." + email_notifications_description: "Primește notificări periodic pentru contul tău." + email_announcements_description: "Primește email-uri cu ultimele știri despre CodeCombat." + contributor_emails: "Contributor Class Emails" + contribute_prefix: "Căutăm oameni să se alăture distracției! Intră pe " + contribute_page: "pagina de contribuție" + contribute_suffix: " pentru a afla mai multe." + email_toggle: "Alege tot" + error_saving: "Salvare erori" + saved: "Modificări salvate" + password_mismatch: "Parola nu se potrivește." -# account_profile: -# edit_settings: "Edit Settings" -# profile_for_prefix: "Profile for " -# profile_for_suffix: "" -# profile: "Profile" -# user_not_found: "No user found. Check the URL?" -# gravatar_not_found_mine: "We couldn't find your profile associated with:" -# gravatar_not_found_email_suffix: "." -# gravatar_signup_prefix: "Sign up at " -# gravatar_signup_suffix: " to get set up!" -# gravatar_not_found_other: "Alas, there's no profile associated with this person's email address." -# gravatar_contact: "Contact" -# gravatar_websites: "Websites" -# gravatar_accounts: "As Seen On" -# gravatar_profile_link: "Full Gravatar Profile" + account_profile: + edit_settings: "Modifică setările" + profile_for_prefix: "Profil pentru " + profile_for_suffix: "" + profile: "Profil" + user_not_found: "Utilizator negăsit. Verifică URL-ul??" + gravatar_not_found_mine: "N-am putut găsi profilul asociat cu:" + gravatar_not_found_email_suffix: "." + gravatar_signup_prefix: "Înscrie-te la " + gravatar_signup_suffix: " pentru a fi gata!" #sounds funny # to get set up!" + gravatar_not_found_other: "Din păcate nu este asociat nici un profil cu această adresă de email." + gravatar_contact: "Contact" + gravatar_websites: "Website-uri" + gravatar_accounts: "Așa cum apare la" + gravatar_profile_link: "Full Gravatar Profile" #better leave this one as it is -# play_level: -# level_load_error: "Level could not be loaded: " -# done: "Done" -# grid: "Grid" -# customize_wizard: "Customize Wizard" -# home: "Home" -# guide: "Guide" -# multiplayer: "Multiplayer" -# restart: "Restart" -# goals: "Goals" -# action_timeline: "Action Timeline" -# click_to_select: "Click on a unit to select it." -# reload_title: "Reload All Code?" -# reload_really: "Are you sure you want to reload this level back to the beginning?" -# reload_confirm: "Reload All" -# victory_title_prefix: "" -# victory_title_suffix: " Complete" -# victory_sign_up: "Sign Up to Save Progress" -# victory_sign_up_poke: "Want to save your code? Create a free account!" -# victory_rate_the_level: "Rate the level: " -# victory_play_next_level: "Play Next Level" -# victory_go_home: "Go Home" -# victory_review: "Tell us more!" -# victory_hour_of_code_done: "Are You Done?" -# victory_hour_of_code_done_yes: "Yes, I'm finished with my Hour of Code™!" -# multiplayer_title: "Multiplayer Settings" -# multiplayer_link_description: "Give this link to anyone to have them join you." -# multiplayer_hint_label: "Hint:" -# multiplayer_hint: " Click the link to select all, then press ⌘-C or Ctrl-C to copy the link." -# multiplayer_coming_soon: "More multiplayer features to come!" -# guide_title: "Guide" -# tome_minion_spells: "Your Minions' Spells" -# tome_read_only_spells: "Read-Only Spells" -# tome_other_units: "Other Units" -# tome_cast_button_castable: "Cast Spell" -# tome_cast_button_casting: "Casting" -# tome_cast_button_cast: "Spell Cast" -# tome_autocast_delay: "Autocast Delay" -# tome_select_spell: "Select a Spell" -# tome_select_a_thang: "Select Someone for " -# tome_available_spells: "Available Spells" -# hud_continue: "Continue (press shift-space)" -# spell_saved: "Spell Saved" + play_level: + level_load_error: "Nivelul nu a putut fi încărcat: " + done: "Gata" + grid: "Grilă" + customize_wizard: "Personalizează Wizard-ul" + home: "Acasă" + guide: "Ghid" + multiplayer: "Multiplayer" + restart: "Restart" + goals: "Obiective" + action_timeline: "Timeline-ul acțiunii" + click_to_select: "Apasă pe o unitate pentru a o selecta." + reload_title: "Reîncarcă tot Codul?" + reload_really: "Ești sigur că vrei să reîncarci nivelul de la început?" + reload_confirm: "Reload All" + victory_title_prefix: "" + victory_title_suffix: " Terminat" + victory_sign_up: "Înscrie-te pentru a salva progresul" + victory_sign_up_poke: "Vrei să-ți salvezi codul? Crează un cont gratis!" + victory_rate_the_level: "Rate the level: " + victory_play_next_level: "Joacă nivelul următor" + victory_go_home: "Acasă" + victory_review: "Spune-ne mai multe!" + victory_hour_of_code_done: "Ai terminat?" + victory_hour_of_code_done_yes: "Da, am terminat Hour of Code™!" + multiplayer_title: "Setări Multiplayer" + multiplayer_link_description: "Împărtășește acest link cu cei care vor să ți se alăture." + multiplayer_hint_label: "Hint:" + multiplayer_hint: " Apasă pe link pentru a selecta tot, apoi apasă ⌘-C sau Ctrl-C pentru a copia link-ul." + multiplayer_coming_soon: "Mai multe feature-uri multiplayer în curând!" + guide_title: "Ghid" + tome_minion_spells: "Vrăjile Minion-ilor tăi" + tome_read_only_spells: "Vrăji Read-Only" + tome_other_units: "Alte unități" + tome_cast_button_castable: "Aplică Vraja" + tome_cast_button_casting: "Se încarcă" + tome_cast_button_cast: "Aplică Vraja" + tome_autocast_delay: "Întârziere Autocast" + tome_select_spell: "Alege o vrajă" + tome_select_a_thang: "Alege pe cineva pentru " + tome_available_spells: "Vrăjile disponibile" + hud_continue: "Continuă (apasă shift-space)" + spell_saved: "Vrajă salvată" -# admin: -# av_title: "Admin Views" -# av_entities_sub_title: "Entities" -# av_entities_users_url: "Users" -# av_entities_active_instances_url: "Active Instances" -# av_other_sub_title: "Other" -# av_other_debug_base_url: "Base (for debugging base.jade)" -# u_title: "User List" -# lg_title: "Latest Games" + admin: + av_title: "Admin vede" + av_entities_sub_title: "Entități" + av_entities_users_url: "Utilizatori" + av_entities_active_instances_url: "Instanțe active" + av_other_sub_title: "Altele" + av_other_debug_base_url: "Base (pentru debugging base.jade)" + u_title: "Listă utilizatori" + lg_title: "Ultimele jocuri" -# editor: -# main_title: "CodeCombat Editors" -# main_description: "Build your own levels, campaigns, units and educational content. We provide all the tools you need!" -# article_title: "Article Editor" -# article_description: "Write articles that give players overviews of programming concepts which can be used across a variety of levels and campaigns." -# thang_title: "Thang Editor" -# thang_description: "Build units, defining their default logic, graphics and audio. Currently only supports importing Flash exported vector graphics." -# level_title: "Level Editor" + editor: + main_title: "Editori CodeCombat" + main_description: "Construiește propriile nivele,campanii,unități și conținut educațional.Noi îți furnizăm toate uneltele necesare!" + article_title: "Editor Articol" + article_description: "Scrie articole care oferă jucătorilor cunoștințe despre conceptele de programare care pot fi folosite pe o varietate de nivele și campanii." + thang_title: "Editor Thang" + thang_description: "Construiește unități ,definește logica lor,grafica și sunetul.Momentan suportă numai importare de grafică vectorială exportată din Flash." + level_title: "Editor Nivele" # level_description: "Includes the tools for scripting, uploading audio, and constructing custom logic to create all sorts of levels. Everything we use ourselves!" # security_notice: "Many major features in these editors are not currently enabled by default. As we improve the security of these systems, they will be made generally available. If you'd like to use these features sooner, " # contact_us: "contact us!" From aa3167f30344c811d459aa21a4225d93febb2aa2 Mon Sep 17 00:00:00 2001 From: Scott Erickson <sderickson@gmail.com> Date: Wed, 5 Mar 2014 15:06:20 -0800 Subject: [PATCH 010/100] Temporary hack fix while mongodb text search is broken. --- app/treema-ext.coffee | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/app/treema-ext.coffee b/app/treema-ext.coffee index 674e6215a..1a8bdbe40 100644 --- a/app/treema-ext.coffee +++ b/app/treema-ext.coffee @@ -279,12 +279,23 @@ class LatestVersionReferenceNode extends TreemaNode search: => term = @getValEl().find('input').val() return if term is @lastTerm + + # HACK while search is broken + if @collection + @lastTerm = term + @searchCallback() + return + @getSearchResultsEl().empty() if @lastTerm and not term return unless term @lastTerm = term @getSearchResultsEl().empty().append('Searching') @collection = new LatestVersionCollection() - @collection.url = "#{@url}?term=#{term}&project=true" + + # HACK while search is broken +# @collection.url = "#{@url}?term=#{term}&project=true" + @collection.url = "#{@url}?term=#{''}&project=true" + @collection.fetch() @collection.on 'sync', @searchCallback @@ -295,6 +306,10 @@ class LatestVersionReferenceNode extends TreemaNode row = $('<div></div>').addClass('treema-search-result-row') text = @formatDocument(model) continue unless text? + + # HACK while search is broken + continue unless text.toLowerCase().indexOf(@lastTerm.toLowerCase()) >= 0 + row.addClass('treema-search-selected') if first first = false row.text(text) From 99f43028a02e77473124340745ef68531351f542 Mon Sep 17 00:00:00 2001 From: Akaza Akari <tt@a-kar.in> Date: Wed, 5 Mar 2014 19:39:14 -0800 Subject: [PATCH 011/100] Add thang sound panning for #454 --- app/lib/AudioPlayer.coffee | 15 +++++++++++++-- app/lib/surface/CocoSprite.coffee | 9 ++++++++- app/lib/surface/Surface.coffee | 1 + 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/app/lib/AudioPlayer.coffee b/app/lib/AudioPlayer.coffee index b5eb4f70d..9c55282f2 100644 --- a/app/lib/AudioPlayer.coffee +++ b/app/lib/AudioPlayer.coffee @@ -38,6 +38,7 @@ class AudioPlayer extends CocoClass constructor: () -> super() @ext = if createjs.Sound.getCapability('mp3') then '.mp3' else '.ogg' + @camera = null @listenToSound() @createNewManifest() @soundsToPlayWhenLoaded = {} @@ -51,6 +52,13 @@ class AudioPlayer extends CocoClass # So for now, we'll just load through SoundJS instead. createjs.Sound.on 'fileload', @onSoundLoaded + applyPanning: (options, pos) -> + sup = @camera.worldToSurface pos + svp = @camera.surfaceViewport + pan = Math.max -1, Math.min 1, ((sup.x - svp.x) - svp.width / 2) / svp.width * 2 + # TODO: derive new volume from old one and distance ratio + volume: options.volume, delay: options.delay, pan: pan + # PUBLIC LOADING METHODS soundForDialogue: (message, soundTriggers) -> @@ -78,8 +86,11 @@ class AudioPlayer extends CocoClass @preloadInterfaceSounds [name] unless filename of cache @soundsToPlayWhenLoaded[name] = volume - playSound: (name, volume=1, delay=0) -> - instance = createjs.Sound.play name, {volume: (me.get('volume') ? 1) * volume, delay: delay} + playSound: (name, volume=1, delay=0, pos=null) -> + audioOptions = {volume: (me.get('volume') ? 1) * volume, delay: delay} + unless @camera is null or pos is null + audioOptions = @applyPanning audioOptions, pos + instance = createjs.Sound.play name, audioOptions instance # # TODO: load Interface sounds somehow, somewhere, somewhen diff --git a/app/lib/surface/CocoSprite.coffee b/app/lib/surface/CocoSprite.coffee index 3a56f77e7..9104f1b37 100644 --- a/app/lib/surface/CocoSprite.coffee +++ b/app/lib/surface/CocoSprite.coffee @@ -188,6 +188,13 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass return 0 unless @thang.bobHeight @thang.bobHeight * (1 + Math.sin(@age * Math.PI / @thang.bobTime)) + getWorldPosition: -> + p1 = @thang.pos + if bobOffset = @getBobOffset() + p1 = p1.copy?() or _.clone(p1) + p1.z += bobOffset + x: p1.x, y: p1.y, z: if @thang.isLand then 0 else p1.z - @thang.depth / 2 + updatePosition: -> return unless @thang?.pos and @options.camera? [p0, p1] = [@lastPos, @thang.pos] @@ -477,6 +484,6 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass return null unless sound delay = if withDelay and sound.delay then 1000 * sound.delay / createjs.Ticker.getFPS() else 0 name = AudioPlayer.nameForSoundReference sound - instance = AudioPlayer.playSound name, volume, delay + instance = AudioPlayer.playSound name, volume, delay, @getWorldPosition() # console.log @thang?.id, "played sound", name, "with delay", delay, "volume", volume, "and got sound instance", instance instance diff --git a/app/lib/surface/Surface.coffee b/app/lib/surface/Surface.coffee index ec006d449..c5e15813d 100644 --- a/app/lib/surface/Surface.coffee +++ b/app/lib/surface/Surface.coffee @@ -360,6 +360,7 @@ module.exports = Surface = class Surface extends CocoClass canvasHeight = parseInt(@canvas.attr('height'), 10) @camera?.destroy() @camera = new Camera canvasWidth, canvasHeight + AudioPlayer.camera = @camera @layers.push @surfaceLayer = new Layer name: "Surface", layerPriority: 0, transform: Layer.TRANSFORM_SURFACE, camera: @camera @layers.push @surfaceTextLayer = new Layer name: "Surface Text", layerPriority: 1, transform: Layer.TRANSFORM_SURFACE_TEXT, camera: @camera @layers.push @screenLayer = new Layer name: "Screen", layerPriority: 2, transform: Layer.TRANSFORM_SCREEN, camera: @camera From 51d7bd656d1b17e35c1ff7e484e10fadf37b4ed5 Mon Sep 17 00:00:00 2001 From: Akaza Akari <tt@a-kar.in> Date: Wed, 5 Mar 2014 19:54:58 -0800 Subject: [PATCH 012/100] Made updatePosition use getWorldPosition --- app/lib/surface/CocoSprite.coffee | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/app/lib/surface/CocoSprite.coffee b/app/lib/surface/CocoSprite.coffee index 9104f1b37..57146866a 100644 --- a/app/lib/surface/CocoSprite.coffee +++ b/app/lib/surface/CocoSprite.coffee @@ -197,12 +197,9 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass updatePosition: -> return unless @thang?.pos and @options.camera? + wop = @getWorldPosition() [p0, p1] = [@lastPos, @thang.pos] - if bobOffset = @getBobOffset() - p1 = p1.copy?() or _.clone(p1) - p1.z += bobOffset return if p0 and p0.x is p1.x and p0.y is p1.y and p0.z is p1.z and not @options.camera.tweeningZoomTo - wop = x: p1.x, y: p1.y, z: if @thang.isLand then 0 else p1.z - @thang.depth / 2 sup = @options.camera.worldToSurface wop [@displayObject.x, @displayObject.y] = [sup.x, sup.y] @lastPos = p1.copy?() or _.clone(p1) From 030da44c23ae66668405f5f7cce0fc3a5bcf778e Mon Sep 17 00:00:00 2001 From: Akaza Akari <tt@a-kar.in> Date: Wed, 5 Mar 2014 20:12:42 -0800 Subject: [PATCH 013/100] Added volume manipulation, adjusted panning parameters --- app/lib/AudioPlayer.coffee | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/lib/AudioPlayer.coffee b/app/lib/AudioPlayer.coffee index 9c55282f2..40b07974d 100644 --- a/app/lib/AudioPlayer.coffee +++ b/app/lib/AudioPlayer.coffee @@ -55,8 +55,9 @@ class AudioPlayer extends CocoClass applyPanning: (options, pos) -> sup = @camera.worldToSurface pos svp = @camera.surfaceViewport - pan = Math.max -1, Math.min 1, ((sup.x - svp.x) - svp.width / 2) / svp.width * 2 - # TODO: derive new volume from old one and distance ratio + pan = Math.max -1, Math.min 1, ((sup.x - svp.x) - svp.width / 2) / svp.width + dst = @camera.distanceRatioTo pos + vol = Math.min 1, options.volume / Math.pow (dst + 0.2), 2 volume: options.volume, delay: options.delay, pan: pan # PUBLIC LOADING METHODS From 929d10588e70dfd2eb5a45169b19947256ad6b5e Mon Sep 17 00:00:00 2001 From: Akaza Akari <tt@a-kar.in> Date: Thu, 6 Mar 2014 16:02:42 +0800 Subject: [PATCH 014/100] Translate passages on Contribute and About page --- app/locale/zh-HANS.coffee | 86 +++++++++++++++++++-------------------- 1 file changed, 43 insertions(+), 43 deletions(-) diff --git a/app/locale/zh-HANS.coffee b/app/locale/zh-HANS.coffee index f6eebce5d..4f999712c 100644 --- a/app/locale/zh-HANS.coffee +++ b/app/locale/zh-HANS.coffee @@ -271,16 +271,16 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese who_description_prefix: "在2013年开始一起编写 CodeCombat。在2008年时,我们还创造" who_description_suffix: "并且发展出了首选的学习如何写中文和日文的Web和IOS应用" who_description_ending: "现在是时候教人们如何写代码了。" -# why_paragraph_1: "When making Skritter, George didn't know how to program and was constantly frustrated by his inability to implement his ideas. Afterwards, he tried learning, but the lessons were too slow. His housemate, wanting to reskill and stop teaching, tried Codecademy, but \"got bored.\" Each week another friend started Codecademy, then dropped off. We realized it was the same problem we'd solved with Skritter: people learning a skill via slow, intensive lessons when what they need is fast, extensive practice. We know how to fix that." -# why_paragraph_2: "Need to learn to code? You don't need lessons. You need to write a lot of code and have a great time doing it." -# why_paragraph_3_prefix: "That's what programming is about. It's gotta be fun. Not fun like" -# why_paragraph_3_italic: "yay a badge" -# why_paragraph_3_center: "but fun like" -# why_paragraph_3_italic_caps: "NO MOM I HAVE TO FINISH THE LEVEL!" -# why_paragraph_3_suffix: "That's why CodeCombat is a multiplayer game, not a gamified lesson course. We won't stop until you can't stop--but this time, that's a good thing." -# why_paragraph_4: "If you're going to get addicted to some game, get addicted to this one and become one of the wizards of the tech age." -# why_ending: "And hey, it's free. " -# why_ending_url: "Start wizarding now!" + why_paragraph_1: "当我们制作 Skritter 的时候,George 不会写程序,对于不能实现他的灵感这一点很苦恼。他试着学了学,但是那些课程都太慢了。他的室友想不再教书学习新技能,试了试 CodeAcademy,但是觉得“太无聊了。”每个星期都会有个熟人尝试 CodeAcademy,然后无一例外地放弃掉。我们发现这和 Skritter 想要解决的是一个问题:人们想要的是高速学习、充分练习,得到的却是缓慢、冗长的课程。我们知道该怎么办了。" + why_paragraph_2: "你想学编程?你不用上课。你需要的是写好多代码,并且享受这个过程。" + why_paragraph_3_prefix: "这才是编程的要义。编程必须要好玩。不是" + why_paragraph_3_italic: "哇又一个奖章诶" + why_paragraph_3_center: "那种“好玩”,而是" + why_paragraph_3_italic_caps: "不老妈,我德先把这关打完!" + why_paragraph_3_suffix: "这就是为什么 CodeCombat 是个多人游戏,而不是一个游戏化的编程课。你不停,我们就不停——但这次这是件好事。" + why_paragraph_4: "如果你一定要对游戏上瘾,那就对这个游戏上瘾,然后成为科技时代的法师吧。" + why_ending: "再说,这游戏还是免费的。" + why_ending_url: "开始学习法术吧!" # george_description: "CEO, business guy, web designer, game designer, and champion of beginning programmers everywhere." # scott_description: "Programmer extraordinaire, software architect, kitchen wizard, and master of finances. Scott is the reasonable one." # nick_description: "Programming wizard, eccentric motivation mage, and upside-down experimenter. Nick can do anything and chooses to build CodeCombat." @@ -350,18 +350,18 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese contribute: page_title: "贡献" -# character_classes_title: "Character Classes" -# introduction_desc_intro: "We have high hopes for CodeCombat." -# introduction_desc_pref: "We want to be where programmers of all stripes come to learn and play together, introduce others to the wonderful world of coding, and reflect the best parts of the community. We can't and don't want to do that alone; what makes projects like GitHub, Stack Overflow and Linux great are the people who use them and build on them. To that end, " -# introduction_desc_github_url: "CodeCombat is totally open source" -# introduction_desc_suf: ", and we aim to provide as many ways as possible for you to take part and make this project as much yours as ours." -# introduction_desc_ending: "We hope you'll join our party!" + character_classes_title: "贡献者职业" + introduction_desc_intro: "我们对 CodeCombat 有很高的期望。" + introduction_desc_pref: "我们希望所有的程序员一起来学习和游戏,让其他人也见识到代码的美妙,并且展现出社区的最好一面。我们不能也不想独自完成这个目标:让 GitHub、Stack Overflow 和 Linux 真正伟大的是它们的用户。为了完成这个目标," + introduction_desc_github_url: "我们把 CodeCombat 完全开源" + introduction_desc_suf: ",而且我们希望提供尽可能多的方法让你来参加这个项目,与我们一起创造。" + introduction_desc_ending: "我们希望你也会加入进来!" # introduction_desc_signature: "- Nick, George, Scott, Michael, and Jeremy" -# alert_account_message_intro: "Hey there!" -# alert_account_message_pref: "To subscribe for class emails, you'll need to " -# alert_account_message_suf: "first." -# alert_account_message_create_url: "create an account" -# archmage_summary: "Interested in working on game graphics, user interface design, database and server organization, multiplayer networking, physics, sound, or game engine performance? Want to help build a game to help other people learn what you are good at? We have a lot to do and if you are an experienced programmer and want to develop for CodeCombat, this class is for you. We would love your help building the best programming game ever." + alert_account_message_intro: "你好!" + alert_account_message_pref: "要订阅贡献者邮件,你得先" + alert_account_message_suf: "。" + alert_account_message_create_url: "创建账号" + archmage_summary: "你对游戏图像、界面设计、数据库和服务器运营、多人在线、物理、声音、游戏引擎性能感兴趣吗?想做一个教别人编程的游戏吗?如果你有编程经验,想要开发 CodeCombat ,那就选择这个职业吧。我们会非常高兴在制作史上最好的编程游戏的过程中得到你的帮助。" # archmage_introduction: "One of the best parts about building games is they synthesize so many different things. Graphics, sound, real-time networking, social networking, and of course many of the more common aspects of programming, from low-level database management, and server administration to user facing design and interface building. There's a lot to do, and if you're an experienced programmer with a hankering to really dive into the nitty-gritty of CodeCombat, this class might be for you. We would love to have your help building the best programming game ever." # class_attributes: "Class Attributes" # archmage_attribute_1_pref: "Knowledge in " @@ -374,10 +374,10 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese # join_desc_4: "and we'll go from there!" # join_url_email: "Email us" # join_url_hipchat: "public HipChat room" -# more_about_archmage: "Learn More About Becoming an Archmage" + more_about_archmage: "了解成为大法师的方法" # archmage_subscribe_desc: "Get emails on new coding opportunities and announcements." -# artisan_summary_pref: "Want to design levels and expand CodeCombat's arsenal? People are playing through our content at a pace faster than we can build! Right now, our level editor is barebone, so be wary. Making levels will be a little challenging and buggy. If you have visions of campaigns spanning for-loops to" -# artisan_summary_suf: "then this class is for you." + artisan_summary_pref: "想要设计 CodeCombat 的关卡吗?人们玩的比我们做的快多了!现在我们的关卡编辑器还很基本,所以做起关卡来会有点麻烦,还会有bug。只要你有制作关卡的灵感,不管是简单的for循环还是" + artisan_summary_suf: "这种东西,这个职业都很适合你。" # artisan_introduction_pref: "We must construct additional levels! People be clamoring for more content, and we can only build so many ourselves. Right now your workstation is level one; our level editor is barely usable even by its creators, so be wary. If you have visions of campaigns spanning for-loops to" # artisan_introduction_suf: "then this class might be for you." # artisan_attribute_1: "Any experience in building content like this would be nice, such as using Blizzard's level editors. But not required!" @@ -388,52 +388,52 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese # artisan_join_step2: "Create a new level and explore existing levels." # artisan_join_step3: "Find us in our public HipChat room for help." # artisan_join_step4: "Post your levels on the forum for feedback." -# more_about_artisan: "Learn More About Becoming an Artisan" + more_about_artisan: "了解成为工匠的方法" # artisan_subscribe_desc: "Get emails on level editor updates and announcements." -# adventurer_sumamry: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." + adventurer_sumamry: "丑话说在前面,你就是那个挡枪子的,而且你会伤得很重。我们需要人手来测试崭新的关卡,并且提出改进意见。做一个好游戏是一个漫长的过程,没人第一次就能搞对。如果你能忍得了这些,而且身体健壮,那这个职业就是你的了。" # adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." # adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." # adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." # adventurer_join_pref: "Either get together with (or recruit!) an Artisan and work with them, or check the box below to receive emails when there are new levels to test. We'll also be posting about levels to review on our networks like" # adventurer_forum_url: "our forum" # adventurer_join_suf: "so if you prefer to be notified those ways, sign up there!" -# more_about_adventurer: "Learn More About Becoming an Adventurer" + more_about_adventurer: "了解成为冒险家的方法" # adventurer_subscribe_desc: "Get emails when there are new levels to test." -# scribe_summary_pref: "CodeCombat is not just going to be a bunch of levels. It will also be a resource of programming knowledge that players can hook into. That way, each Artisan can link to a detailed article that for the player's edification: documentation akin to what the " -# scribe_summary_sufx: " has built. If you enjoy explaining programming concepts, then this class is for you." + scribe_summary_pref: "CodeCombat 不只是一堆关卡的集合,它还是玩家们编程知识的来源。这样的话,每个工匠都能链接详尽的文档,以供玩家们学习,类似于" + scribe_summary_sufx: "那些。如果你喜欢解释编程概念,那这个职业适合你。" # scribe_introduction_pref: "CodeCombat isn't just going to be a bunch of levels. It will also include a resource for knowledge, a wiki of programming concepts that levels can hook into. That way rather than each Artisan having to describe in detail what a comparison operator is, they can simply link their level to the Article describing them that is already written for the player's edification. Something along the lines of what the " # scribe_introduction_url_mozilla: "Mozilla Developer Network" # scribe_introduction_suf: " has built. If your idea of fun is articulating the concepts of programming in Markdown form, then this class might be for you." # scribe_attribute_1: "Skill in words is pretty much all you need. Not only grammar and spelling, but able to convey complicated ideas to others." # contact_us_url: "Contact us" # scribe_join_description: "tell us a little about yourself, your experience with programming and what sort of things you'd like to write about. We'll go from there!" -# more_about_scribe: "Learn More About Becoming a Scribe" + more_about_scribe: "了解成为文书的方法" # scribe_subscribe_desc: "Get emails about article writing announcements." - diplomat_summary: "在其他国家不讲英语,很多人对于CodeCombat有很大的兴趣。我们正在寻找愿意花时间翻译网站语料库的词语的译者,这样 CodeCombat 就能尽快地遍及世界各地。如果你想帮助 CodeCombat 的国际化,那么这个类就是给你的。" - diplomat_introduction_pref: "如果有一件事情是从 " - diplomat_launch_url: "launch in October" - diplomat_introduction_suf: "学来的,Combat有相当大的兴趣在其他国家发展。我们正在构建一个译者兵团把单词一个一个的翻译,让CodeCombat尽可能地访问到世界各地。如果你喜欢偷偷地瞄一眼即将到来的内容,并让你的国民尽快学习到CodeCombat,那么这个类可能适合你。" - diplomat_attribute_1: "会流利的英语和能翻译的语言。当传递复杂思想,难得的是能很好的同时掌握这两个。" - diplomat_join_pref_github: "找到你自己的语言文件 " - diplomat_github_url: "在GitHub网站" - diplomat_join_suf_github: "在线编辑它,然后提交一个合并请求。同时,选中下面这个复选框来关注最新的国际化开发!" - more_about_diplomat: "了解更多“如何成为一名外交官(翻译者)”" + diplomat_summary: "很多国家不说英文,但是人们对 CodeCombat 兴致很高!我们需要具有热情的翻译者,来把这个网站上的文字尽快带向全世界。如果你想帮我们走向全球,那这个职业适合你。" + diplomat_introduction_pref: "如果说我们从" + diplomat_launch_url: "十月的发布" + diplomat_introduction_suf: "中得到了什么启发:那就是全球的人对 CodeCombat 都很感兴趣。我们召集了一群翻译者,尽快地把网站上的信息翻译成各国文字。如果你对即将发布的新内容感兴趣,想让你的国家的人们玩上,就快来成为外交官吧。" + diplomat_attribute_1: "既会说流利的英语,也熟悉自己的语言。编程是一件很复杂的事情,而要翻译复杂的概念,你必须对两种语言都在行!" + diplomat_join_pref_github: "在" + diplomat_github_url: "GitHub" + diplomat_join_suf_github: "找到你的语言文件,在线编辑它,然后提交一个合并请求。同时,选中下面这个复选框来关注最新的国际化开发!" + more_about_diplomat: "了解成为外交官的方法" diplomat_subscribe_desc: "接受有关国际化开发和翻译情况的邮件" -# ambassador_summary: "We are trying to build a community, and every community needs a support team when there are troubles. We have got chats, emails, and social networks so that our users can get acquainted with the game. If you want to help people get involved, have fun, and learn some programming, then this class is for you." + ambassador_summary: "我们要建立一个社区,而当社区遇到麻烦的时候,就要支持人员出场了。我们运用 IRC、电邮、社交网站等多种平台帮助玩家熟悉游戏。如果你想帮人们参与进来,学习编程,然后玩的开心,那这个职业属于你。" # ambassador_introduction: "This is a community we're building, and you are the connections. We've got Olark chats, emails, and social networks with lots of people to talk with and help get acquainted with the game and learn from. If you want to help people get involved and have fun, and get a good feel of the pulse of CodeCombat and where we're going, then this class might be for you." # ambassador_attribute_1: "Communication skills. Be able to identify the problems players are having and help them solve them. Also, keep the rest of us informed about what players are saying, what they like and don't like and want more of!" # ambassador_join_desc: "tell us a little about yourself, what you've done and what you'd be interested in doing. We'll go from there!" # ambassador_join_note_strong: "Note" # ambassador_join_note_desc: "One of our top priorities is to build multiplayer where players having difficulty solving levels can summon higher level wizards to help them. This will be a great way for ambassadors to do their thing. We'll keep you posted!" -# more_about_ambassador: "Learn More About Becoming an Ambassador" + more_about_ambassador: "了解成为使节的方法" # ambassador_subscribe_desc: "Get emails on support updates and multiplayer developments." -# counselor_summary: "None of the above roles fit what you are interested in? Do not worry, we are on the lookout for anybody who wants a hand in the development of CodeCombat! If you are interested in teaching, game development, open source management, or anything else that you think will be relevant to us, then this class is for you." + counselor_summary: "以上的职业都不适合你?没关系,我们欢迎每一个想参与 CodeCombat 开发的人!如果你熟悉教学、游戏开发、开源管理,或者任何你觉得和我们有关的方面,那这个职业属于你。" # counselor_introduction_1: "Do you have life experience? A different perspective on things that can help us decide how to shape CodeCombat? Of all these roles, this will probably take the least time, but individually you may make the most difference. We're on the lookout for wisened sages, particularly in areas like: teaching, game development, open source project management, technical recruiting, entrepreneurship, or design." # counselor_introduction_2: "Or really anything that is relevant to the development of CodeCombat. If you have knowledge and want to share it to help grow this project, then this class might be for you." # counselor_attribute_1: "Experience, in any of the areas above or something you think might be helpful." # counselor_attribute_2: "A little bit of free time!" # counselor_join_desc: "tell us a little about yourself, what you've done and what you'd be interested in doing. We'll put you in our contact list and be in touch when we could use advice (not too often)." -# more_about_counselor: "Learn More About Becoming a Counselor" + more_about_counselor: "了解成为顾问的方式" changes_auto_save: "在您切换复选框时,更改将自动保存。" diligent_scribes: "我们勤奋的文书:" powerful_archmages: "我们强力的大法师:" From ad3019a6cbefe573e7b8d1e88cbd900ffedd3ff1 Mon Sep 17 00:00:00 2001 From: Akaza Akari <tt@a-kar.in> Date: Thu, 6 Mar 2014 16:51:21 +0800 Subject: [PATCH 015/100] Translated more strings --- app/locale/zh-HANS.coffee | 80 +++++++++++++++++++-------------------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/app/locale/zh-HANS.coffee b/app/locale/zh-HANS.coffee index 4f999712c..caa2b9802 100644 --- a/app/locale/zh-HANS.coffee +++ b/app/locale/zh-HANS.coffee @@ -200,7 +200,7 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese tome_available_spells: "可用的法术" hud_continue: "继续(按 Shift-空格)" spell_saved: "咒语已保存" -# skip_tutorial: "Skip (esc)" + skip_tutorial: "跳过(esc)" admin: av_title: "管理员视图" @@ -321,32 +321,32 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese code_title: "代码 - MIT" code_description_prefix: "所有由 CodeCombat 拥有或者托管在 codecombat.com 的代码,在 GitHub 版本库或者 codecombat.com 数据库,以上许可协议都依照" mit_license_url: "MIT 许可证" -# code_description_suffix: "This includes all code in Systems and Components that are made available by CodeCombat for the purpose of creating levels." -# art_title: "Art/Music - Creative Commons " -# art_description_prefix: "All common content is available under the" + code_description_suffix: "这包括所有 CodeCombat 公开的制作关卡用的系统和组件代码。" + art_title: "美术和音乐 - Creative Commons" + art_description_prefix: "所有共通的内容都在" # cc_license_url: "Creative Commons Attribution 4.0 International License" -# art_description_suffix: "Common content is anything made generally available by CodeCombat for the purpose of creating Levels. This includes:" + art_description_suffix: "条款下公开。共通内容是指所有 CodeCombat 发布出来用于制作关卡的内容。这包括:" art_music: "音乐" art_sound: "声效" - art_artwork: "艺术品" - art_sprites: "小妖精" -# art_other: "Any and all other non-code creative works that are made available when creating Levels." -# art_access: "Currently there is no universal, easy system for fetching these assets. In general, fetch them from the URLs as used by the site, contact us for assistance, or help us in extending the site to make these assets more easily accessible." -# art_paragraph_1: "For attribution, please name and link to codecombat.com near where the source is used or where appropriate for the medium. For example:" -# use_list_1: "If used in a movie or another game, include codecombat.com in the credits." -# use_list_2: "If used on a website, include a link near the usage, for example underneath an image, or in a general attributions page where you might also mention other Creative Commons works and open source software being used on the site. Something that's already clearly referencing CodeCombat, such as a blog post mentioning CodeCombat, does not need some separate attribution." -# art_paragraph_2: "If the content being used is created not by CodeCombat but instead by a user of codecombat.com, attribute them instead, and follow attribution directions provided in that resource's description if there are any." + art_artwork: "图画" + art_sprites: "精灵" + art_other: "所有制作关卡时公开的,不是代码的创造性产品。" + art_access: "目前还没有简便通用的下载素材的方式。一般来讲,从网站上使用的URL下载,或者联系我们寻找帮助。当然你也可以帮我们扩展网站,让这些资源更容易下载。" + art_paragraph_1: "关于署名,请说明并在使用处附近,或对媒体形式来说合适的地方提供一个 codecombat.com 的链接。举例:" + use_list_1: "如果是用在电影里或者其他游戏里,请在制作人员表中加入 codecombat.com 。" + use_list_2: "如果用在网站上,将链接在使用的地方附近,比如图片下面,或者一个你放置其他 Creative Commons 署名和开源软件协议的专门页面。如果你的内容明确提到关于 CodeCombat,那你就不需要额外署名。" + art_paragraph_2: "如果你使用的内容不是由 CodeCombat 制作,而是由 codecombat.com 上其他的用户制作的,那你应该给他们署名。如果相应资源的页面上有署名指示,那你应该遵循那些指示。" rights_title: "版权所有" rights_desc: "所有关卡由他们自己版权所有。这包括" rights_scripts: "脚本" rights_unit: "单元配置" rights_description: "描述" rights_writings: "作品" -# rights_media: "Media (sounds, music) and any other creative content made specifically for that Level and not made generally available when creating Levels." -# rights_clarification: "To clarify, anything that is made available in the Level Editor for the purpose of making levels is under CC, whereas the content created with the Level Editor or uploaded in the course of creation of Levels is not." -# nutshell_title: "In a Nutshell" -# nutshell_description: "Any resources we provide in the Level Editor are free to use as you like for creating Levels. But we reserve the right to restrict distribution of the Levels themselves (that are created on codecombat.com) so that they may be charged for in the future, if that's what ends up happening." -# canonical: "The English version of this document is the definitive, canonical version. If there are any discrepencies between translations, the English document takes precedence." + rights_media: "声音、音乐以及其他专门为某个关卡制作,而不对其他关卡开放的创造性内容" + rights_clarification: "澄清:所有在关卡编辑器里公开用于制作关卡的资源都是在CC协议下发布的,而使用关卡编辑器制作,或者在关卡制作过程中上传的内容则不是。" + nutshell_title: "简而言之" + nutshell_description: "我们在关卡编辑器里公开的任何资源,你都可以在制作关卡时随意使用,但我们保留限制在 codecombat.com 之上创建的关卡本身传播的权利,因为我们以后可能决定为它们收费。" + canonical: "这篇说明的英文版本是权威版本。如果各个翻译版本之间有任何冲突,以英文版为准。" contribute: page_title: "贡献" @@ -362,20 +362,20 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese alert_account_message_suf: "。" alert_account_message_create_url: "创建账号" archmage_summary: "你对游戏图像、界面设计、数据库和服务器运营、多人在线、物理、声音、游戏引擎性能感兴趣吗?想做一个教别人编程的游戏吗?如果你有编程经验,想要开发 CodeCombat ,那就选择这个职业吧。我们会非常高兴在制作史上最好的编程游戏的过程中得到你的帮助。" -# archmage_introduction: "One of the best parts about building games is they synthesize so many different things. Graphics, sound, real-time networking, social networking, and of course many of the more common aspects of programming, from low-level database management, and server administration to user facing design and interface building. There's a lot to do, and if you're an experienced programmer with a hankering to really dive into the nitty-gritty of CodeCombat, this class might be for you. We would love to have your help building the best programming game ever." -# class_attributes: "Class Attributes" -# archmage_attribute_1_pref: "Knowledge in " -# archmage_attribute_1_suf: ", or a desire to learn. Most of our code is in this language. If you're a fan of Ruby or Python, you'll feel right at home. It's JavaScript, but with a nicer syntax." -# archmage_attribute_2: "Some experience in programming and personal initiative. We'll help you get oriented, but we can't spend much time training you." -# how_to_join: "How To Join" -# join_desc_1: "Anyone can help out! Just check out our " -# join_desc_2: "to get started, and check the box below to mark yourself as a brave Archmage and get the latest news by email. Want to chat about what to do or how to get more deeply involved? " -# join_desc_3: ", or find us in our " -# join_desc_4: "and we'll go from there!" -# join_url_email: "Email us" -# join_url_hipchat: "public HipChat room" + archmage_introduction: "制作游戏的时候,最令人激动人心的事情莫过于整合诸多的要素。图像、音响、实事网络交流、社交网络,以及从底层数据库管理到服务器运维,再到用户界面的设计和实现的各种编程方面。制作游戏有很多事情要做,因此如果你有编程经验,还有深入 CodeCombat 的细节中的干劲,你可能应该选择这个职业。我们会非常高兴在制作史上最好的编程游戏的过程中得到你的帮助。" + class_attributes: "职业特性" + archmage_attribute_1_pref: "了解" + archmage_attribute_1_suf: ",或者想要学习。我们的多数代码都是用它写就的。如果你喜欢 Ruby 或者 Python,那你肯定会感到很熟悉。它就是 JavaScript,但它的语法更友好。" + archmage_attribute_2: "编程经验和干劲。我们可以帮你走上正规,但恐怕没多少时间培训你。" + how_to_join: "如何加入" + join_desc_1: "谁都可以帮忙!先看看我们的" + join_desc_2: ",然后勾上下面的复选框,这样你就会作为勇敢的大法师收到我们的电邮。如果你想和开发人员聊天或者更深入地参与,可以" + join_desc_3: "或者去我们的" + join_desc_4: ",然后我们有话好说!" + join_url_email: "给我们发邮件" + join_url_hipchat: " HipChat 聊天室" more_about_archmage: "了解成为大法师的方法" -# archmage_subscribe_desc: "Get emails on new coding opportunities and announcements." + archmage_subscribe_desc: "通过电子邮件获得新的编码机会和公告。" artisan_summary_pref: "想要设计 CodeCombat 的关卡吗?人们玩的比我们做的快多了!现在我们的关卡编辑器还很基本,所以做起关卡来会有点麻烦,还会有bug。只要你有制作关卡的灵感,不管是简单的for循环还是" artisan_summary_suf: "这种东西,这个职业都很适合你。" # artisan_introduction_pref: "We must construct additional levels! People be clamoring for more content, and we can only build so many ourselves. Right now your workstation is level one; our level editor is barely usable even by its creators, so be wary. If you have visions of campaigns spanning for-loops to" @@ -389,7 +389,7 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese # artisan_join_step3: "Find us in our public HipChat room for help." # artisan_join_step4: "Post your levels on the forum for feedback." more_about_artisan: "了解成为工匠的方法" -# artisan_subscribe_desc: "Get emails on level editor updates and announcements." + artisan_subscribe_desc: "通过电子邮件获得关卡编辑器更新和公告。" adventurer_sumamry: "丑话说在前面,你就是那个挡枪子的,而且你会伤得很重。我们需要人手来测试崭新的关卡,并且提出改进意见。做一个好游戏是一个漫长的过程,没人第一次就能搞对。如果你能忍得了这些,而且身体健壮,那这个职业就是你的了。" # adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." # adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." @@ -398,7 +398,7 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese # adventurer_forum_url: "our forum" # adventurer_join_suf: "so if you prefer to be notified those ways, sign up there!" more_about_adventurer: "了解成为冒险家的方法" -# adventurer_subscribe_desc: "Get emails when there are new levels to test." + adventurer_subscribe_desc: "通过电子邮件获得新关卡通知。" scribe_summary_pref: "CodeCombat 不只是一堆关卡的集合,它还是玩家们编程知识的来源。这样的话,每个工匠都能链接详尽的文档,以供玩家们学习,类似于" scribe_summary_sufx: "那些。如果你喜欢解释编程概念,那这个职业适合你。" # scribe_introduction_pref: "CodeCombat isn't just going to be a bunch of levels. It will also include a resource for knowledge, a wiki of programming concepts that levels can hook into. That way rather than each Artisan having to describe in detail what a comparison operator is, they can simply link their level to the Article describing them that is already written for the player's edification. Something along the lines of what the " @@ -408,7 +408,7 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese # contact_us_url: "Contact us" # scribe_join_description: "tell us a little about yourself, your experience with programming and what sort of things you'd like to write about. We'll go from there!" more_about_scribe: "了解成为文书的方法" -# scribe_subscribe_desc: "Get emails about article writing announcements." + scribe_subscribe_desc: "通过电子邮件获得写作新文档的通知。" diplomat_summary: "很多国家不说英文,但是人们对 CodeCombat 兴致很高!我们需要具有热情的翻译者,来把这个网站上的文字尽快带向全世界。如果你想帮我们走向全球,那这个职业适合你。" diplomat_introduction_pref: "如果说我们从" diplomat_launch_url: "十月的发布" @@ -426,13 +426,13 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese # ambassador_join_note_strong: "Note" # ambassador_join_note_desc: "One of our top priorities is to build multiplayer where players having difficulty solving levels can summon higher level wizards to help them. This will be a great way for ambassadors to do their thing. We'll keep you posted!" more_about_ambassador: "了解成为使节的方法" -# ambassador_subscribe_desc: "Get emails on support updates and multiplayer developments." + ambassador_subscribe_desc: "通过电子邮件获得支持系统的现状,以及多人游戏方面的新进展。" counselor_summary: "以上的职业都不适合你?没关系,我们欢迎每一个想参与 CodeCombat 开发的人!如果你熟悉教学、游戏开发、开源管理,或者任何你觉得和我们有关的方面,那这个职业属于你。" -# counselor_introduction_1: "Do you have life experience? A different perspective on things that can help us decide how to shape CodeCombat? Of all these roles, this will probably take the least time, but individually you may make the most difference. We're on the lookout for wisened sages, particularly in areas like: teaching, game development, open source project management, technical recruiting, entrepreneurship, or design." -# counselor_introduction_2: "Or really anything that is relevant to the development of CodeCombat. If you have knowledge and want to share it to help grow this project, then this class might be for you." -# counselor_attribute_1: "Experience, in any of the areas above or something you think might be helpful." -# counselor_attribute_2: "A little bit of free time!" -# counselor_join_desc: "tell us a little about yourself, what you've done and what you'd be interested in doing. We'll put you in our contact list and be in touch when we could use advice (not too often)." + counselor_introduction_1: "也许你有人生的经验,也许你对 CodeCombat 的发展有独特的观点。在所有这些角色中,这个角色花费的时间可能最少,但作为个人你的价值却最高。我们在寻找各方面的贤人,尤其是在教学、游戏开发、开源软件管理、技术企业招聘、创业或者设计方面的。" + counselor_introduction_2: "任何和 CodeCombat 的开发有关系的又可以。如果你有知识,并且希望分享给我们,帮这个项目成长,那这个职业属于你。" + counselor_attribute_1: "经验。上述的任何领域,或者你认为对我们有帮助的领域。" + counselor_attribute_2: "一点用来谈笑风生的时间!" + counselor_join_desc: ",向我们介绍以下你自己:你做过什么、对什么有兴趣。当我们需要你的建议的时候,我们会联系你的(不会很经常)。" more_about_counselor: "了解成为顾问的方式" changes_auto_save: "在您切换复选框时,更改将自动保存。" diligent_scribes: "我们勤奋的文书:" From d3dd0eb32d6ff703622be27c970d78331117265f Mon Sep 17 00:00:00 2001 From: Nick Winter <livelily@gmail.com> Date: Thu, 6 Mar 2014 09:25:16 -0800 Subject: [PATCH 016/100] Fixed i18n tag typo: adventurer_sumamry -> adventurer_summary. --- app/locale/ar.coffee | 2 +- app/locale/bg.coffee | 2 +- app/locale/cs.coffee | 2 +- app/locale/da.coffee | 2 +- app/locale/de.coffee | 2 +- app/locale/el.coffee | 2 +- app/locale/en-AU.coffee | 2 +- app/locale/en-GB.coffee | 2 +- app/locale/en-US.coffee | 2 +- app/locale/es-419.coffee | 2 +- app/locale/es-ES.coffee | 2 +- app/locale/es.coffee | 2 +- app/locale/fa.coffee | 2 +- app/locale/fi.coffee | 2 +- app/locale/fr.coffee | 2 +- app/locale/he.coffee | 2 +- app/locale/hi.coffee | 2 +- app/locale/hu.coffee | 2 +- app/locale/id.coffee | 2 +- app/locale/it.coffee | 2 +- app/locale/ja.coffee | 2 +- app/locale/ko.coffee | 2 +- app/locale/lt.coffee | 2 +- app/locale/ms-BA.coffee | 2 +- app/locale/nb.coffee | 2 +- app/locale/nl.coffee | 28 ++++++++++++++-------------- app/locale/nn.coffee | 2 +- app/locale/no.coffee | 2 +- app/locale/pl.coffee | 2 +- app/locale/pt-BR.coffee | 2 +- app/locale/pt-PT.coffee | 2 +- app/locale/pt.coffee | 2 +- app/locale/ro.coffee | 8 ++++---- app/locale/ru.coffee | 2 +- app/locale/sk.coffee | 2 +- app/locale/sl.coffee | 2 +- app/locale/sr.coffee | 2 +- app/locale/sv.coffee | 2 +- app/locale/th.coffee | 2 +- app/locale/tr.coffee | 2 +- app/locale/uk.coffee | 2 +- app/locale/ur.coffee | 2 +- app/locale/vi.coffee | 2 +- app/locale/zh-HANS.coffee | 2 +- app/locale/zh-HANT.coffee | 2 +- app/locale/zh.coffee | 2 +- 46 files changed, 62 insertions(+), 62 deletions(-) diff --git a/app/locale/ar.coffee b/app/locale/ar.coffee index 1f0584c85..32716882e 100644 --- a/app/locale/ar.coffee +++ b/app/locale/ar.coffee @@ -390,7 +390,7 @@ module.exports = nativeDescription: "العربية", englishDescription: "Arabi # artisan_join_step4: "Post your levels on the forum for feedback." # more_about_artisan: "Learn More About Becoming an Artisan" # artisan_subscribe_desc: "Get emails on level editor updates and announcements." -# adventurer_sumamry: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." +# adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." # adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." # adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." # adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." diff --git a/app/locale/bg.coffee b/app/locale/bg.coffee index fd038c56c..2396688fe 100644 --- a/app/locale/bg.coffee +++ b/app/locale/bg.coffee @@ -390,7 +390,7 @@ module.exports = nativeDescription: "български език", englishDescri # artisan_join_step4: "Post your levels on the forum for feedback." # more_about_artisan: "Learn More About Becoming an Artisan" # artisan_subscribe_desc: "Get emails on level editor updates and announcements." -# adventurer_sumamry: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." +# adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." # adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." # adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." # adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." diff --git a/app/locale/cs.coffee b/app/locale/cs.coffee index 121f0da4f..ee87db33d 100644 --- a/app/locale/cs.coffee +++ b/app/locale/cs.coffee @@ -390,7 +390,7 @@ module.exports = nativeDescription: "čeština", englishDescription: "Czech", tr artisan_join_step4: "Zveřejněte vaši úroveň na fóru pro připomínkování." more_about_artisan: "Dozvědět se více o tom, jak se stát kreativním Řemeslníkem" artisan_subscribe_desc: "Dostávat emailem oznámení a informace o aktualizacích editoru úrovní." -# adventurer_sumamry: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." +# adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." adventurer_introduction: "Ujasněme si dopředu jednu věc o vaší roli: budete jako tank. Projdete ohněm. Potřebujeme někoho, kdo odzkouší zbrusu nové úrovně a pomůže identifikovat kde je možno je zlepšit. Ten boj bude ohromný - tvorba her je dlouhý proces, který nikdo nezvládne na první pokus. Máte-li na to a vydržíte-li to, pak toto je vaše skupina." adventurer_attribute_1: "Touha po učení se. Vy se chcete naučit programovat a my vás to chceme naučit. Jenom, v tomto případě to budete vy, kdo bude vyučovat." adventurer_attribute_2: "Charismatický. Buďte mírný a pečlivě artikulujte co a jak je potřeba zlepšit." diff --git a/app/locale/da.coffee b/app/locale/da.coffee index ad47c71fc..d3a40c86f 100644 --- a/app/locale/da.coffee +++ b/app/locale/da.coffee @@ -390,7 +390,7 @@ module.exports = nativeDescription: "dansk", englishDescription: "Danish", trans # artisan_join_step4: "Post your levels on the forum for feedback." # more_about_artisan: "Learn More About Becoming an Artisan" # artisan_subscribe_desc: "Get emails on level editor updates and announcements." -# adventurer_sumamry: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." +# adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." # adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." # adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." # adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." diff --git a/app/locale/de.coffee b/app/locale/de.coffee index 04b640ce8..7e9f4b9ee 100644 --- a/app/locale/de.coffee +++ b/app/locale/de.coffee @@ -390,7 +390,7 @@ module.exports = nativeDescription: "Deutsch", englishDescription: "German", tra # artisan_join_step4: "Post your levels on the forum for feedback." # more_about_artisan: "Learn More About Becoming an Artisan" # artisan_subscribe_desc: "Get emails on level editor updates and announcements." -# adventurer_sumamry: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." +# adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." # adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." # adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." # adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." diff --git a/app/locale/el.coffee b/app/locale/el.coffee index 91fd4be36..831c4eee9 100644 --- a/app/locale/el.coffee +++ b/app/locale/el.coffee @@ -390,7 +390,7 @@ module.exports = nativeDescription: "ελληνικά", englishDescription: "Gre # artisan_join_step4: "Post your levels on the forum for feedback." # more_about_artisan: "Learn More About Becoming an Artisan" # artisan_subscribe_desc: "Get emails on level editor updates and announcements." -# adventurer_sumamry: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." +# adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." # adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." # adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." # adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." diff --git a/app/locale/en-AU.coffee b/app/locale/en-AU.coffee index 8c36e3ef7..cce71adb9 100644 --- a/app/locale/en-AU.coffee +++ b/app/locale/en-AU.coffee @@ -390,7 +390,7 @@ module.exports = nativeDescription: "English (AU)", englishDescription: "English # artisan_join_step4: "Post your levels on the forum for feedback." # more_about_artisan: "Learn More About Becoming an Artisan" # artisan_subscribe_desc: "Get emails on level editor updates and announcements." -# adventurer_sumamry: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." +# adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." # adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." # adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." # adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." diff --git a/app/locale/en-GB.coffee b/app/locale/en-GB.coffee index 1da9d218a..7dcad9c36 100644 --- a/app/locale/en-GB.coffee +++ b/app/locale/en-GB.coffee @@ -390,7 +390,7 @@ module.exports = nativeDescription: "English (UK)", englishDescription: "English # artisan_join_step4: "Post your levels on the forum for feedback." # more_about_artisan: "Learn More About Becoming an Artisan" # artisan_subscribe_desc: "Get emails on level editor updates and announcements." -# adventurer_sumamry: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." +# adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." # adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." # adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." # adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." diff --git a/app/locale/en-US.coffee b/app/locale/en-US.coffee index 9d3b2dc5a..1bcf71a71 100644 --- a/app/locale/en-US.coffee +++ b/app/locale/en-US.coffee @@ -390,7 +390,7 @@ module.exports = nativeDescription: "English (US)", englishDescription: "English # artisan_join_step4: "Post your levels on the forum for feedback." # more_about_artisan: "Learn More About Becoming an Artisan" # artisan_subscribe_desc: "Get emails on level editor updates and announcements." -# adventurer_sumamry: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." +# adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." # adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." # adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." # adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." diff --git a/app/locale/es-419.coffee b/app/locale/es-419.coffee index 3c2614713..237fb3a69 100644 --- a/app/locale/es-419.coffee +++ b/app/locale/es-419.coffee @@ -390,7 +390,7 @@ module.exports = nativeDescription: "español (América Latina)", englishDescrip # artisan_join_step4: "Post your levels on the forum for feedback." # more_about_artisan: "Learn More About Becoming an Artisan" # artisan_subscribe_desc: "Get emails on level editor updates and announcements." -# adventurer_sumamry: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." +# adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." # adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." # adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." # adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." diff --git a/app/locale/es-ES.coffee b/app/locale/es-ES.coffee index e45550e08..be9c83a7d 100644 --- a/app/locale/es-ES.coffee +++ b/app/locale/es-ES.coffee @@ -390,7 +390,7 @@ module.exports = nativeDescription: "español (ES)", englishDescription: "Spanis artisan_join_step4: "Publica tus niveles en el foro para recibir comentarios críticos." more_about_artisan: "Aprende más sobre convertirte en un Artesano creativo" artisan_subscribe_desc: "Recibe correos sobre actualizaciones del editor de niveles y anuncios." -# adventurer_sumamry: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." +# adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." adventurer_introduction: "Hablemos claro sobre tu papel: eres el tanque. Vas a recibir fuertes daños. Necesitamos gente para probar nuestros flamantes niveles y ayudar a mejorarlos. El dolor será enorme; hacer buenos juegos es un proceso largo y nadie lo consigue a la primera. Si puedes resistir y tener una puntuación alta en Resistencia, entonces esta Clase es para ti." adventurer_attribute_1: "Estar sediento de conocimientos. Quieres aprender a programar y nosotros queremos enseñarte cómo hacerlo. Aunque en este caso es más probable que seas tú el que esté haciendo la mayor parte de la enseñanza." adventurer_attribute_2: "Carismático. Se amable pero claro a la hora de desglosar qué necesita ser mejorado y sugiere de qué formas podría hacerse." diff --git a/app/locale/es.coffee b/app/locale/es.coffee index b4009323b..f576a0c85 100644 --- a/app/locale/es.coffee +++ b/app/locale/es.coffee @@ -390,7 +390,7 @@ module.exports = nativeDescription: "español", englishDescription: "Spanish", t # artisan_join_step4: "Post your levels on the forum for feedback." # more_about_artisan: "Learn More About Becoming an Artisan" # artisan_subscribe_desc: "Get emails on level editor updates and announcements." -# adventurer_sumamry: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." +# adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." # adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." # adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." # adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." diff --git a/app/locale/fa.coffee b/app/locale/fa.coffee index 28a4bd760..a0e1c046b 100644 --- a/app/locale/fa.coffee +++ b/app/locale/fa.coffee @@ -390,7 +390,7 @@ module.exports = nativeDescription: "فارسی", englishDescription: "Persian", # artisan_join_step4: "Post your levels on the forum for feedback." # more_about_artisan: "Learn More About Becoming an Artisan" # artisan_subscribe_desc: "Get emails on level editor updates and announcements." -# adventurer_sumamry: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." +# adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." # adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." # adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." # adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." diff --git a/app/locale/fi.coffee b/app/locale/fi.coffee index 57e5151e4..5c78e1914 100644 --- a/app/locale/fi.coffee +++ b/app/locale/fi.coffee @@ -390,7 +390,7 @@ module.exports = nativeDescription: "suomi", englishDescription: "Finnish", tran # artisan_join_step4: "Post your levels on the forum for feedback." # more_about_artisan: "Learn More About Becoming an Artisan" # artisan_subscribe_desc: "Get emails on level editor updates and announcements." -# adventurer_sumamry: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." +# adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." # adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." # adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." # adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." diff --git a/app/locale/fr.coffee b/app/locale/fr.coffee index 1469c7269..330b029f8 100644 --- a/app/locale/fr.coffee +++ b/app/locale/fr.coffee @@ -390,7 +390,7 @@ module.exports = nativeDescription: "français", englishDescription: "French", t artisan_join_step4: "Postez vos niveaux dans le forum pour avoir des retours." more_about_artisan: "En apprendre plus sur comment devenir un Artisan créatif" artisan_subscribe_desc: "Recevoir un email sur les annonces et mises à jour de l'éditeur de niveaux." -# adventurer_sumamry: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." +# adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." adventurer_introduction: "Soyons clair à propos de votre rôle : vous êtes le tank. Vous allez subir beaucoup de dommages. Nous avons besoin de gens pour essayer les nouveaux niveaux et aider à identifier comment améliorer les choses. La douleur sera énorme; faire de bons jeux est une longue tâche et personne n'y arrive du premier coup. Si vous pouvez résister et avez un gros score de constitution, alors cette classe est faite pour vous." adventurer_attribute_1: "Une soif d'apprendre. Vous voulez apprendre à développer et nous voulons vous apprendre. Vous allez toutefois faire la plupart de l'apprentissage." adventurer_attribute_2: "Charismatique. Soyez doux mais exprimez-vous sur ce qui a besoin d'être amélioré, et faites des propositions sur comment l'améliorer." diff --git a/app/locale/he.coffee b/app/locale/he.coffee index ddcb7484c..f1e34dc8e 100644 --- a/app/locale/he.coffee +++ b/app/locale/he.coffee @@ -390,7 +390,7 @@ module.exports = nativeDescription: "עברית", englishDescription: "Hebrew", # artisan_join_step4: "Post your levels on the forum for feedback." # more_about_artisan: "Learn More About Becoming an Artisan" # artisan_subscribe_desc: "Get emails on level editor updates and announcements." -# adventurer_sumamry: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." +# adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." # adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." # adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." # adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." diff --git a/app/locale/hi.coffee b/app/locale/hi.coffee index 596ecd1ce..c144127a5 100644 --- a/app/locale/hi.coffee +++ b/app/locale/hi.coffee @@ -390,7 +390,7 @@ module.exports = nativeDescription: "मानक हिन्दी", englishDe # artisan_join_step4: "Post your levels on the forum for feedback." # more_about_artisan: "Learn More About Becoming an Artisan" # artisan_subscribe_desc: "Get emails on level editor updates and announcements." -# adventurer_sumamry: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." +# adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." # adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." # adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." # adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." diff --git a/app/locale/hu.coffee b/app/locale/hu.coffee index ad6cd0273..bed717d2c 100644 --- a/app/locale/hu.coffee +++ b/app/locale/hu.coffee @@ -390,7 +390,7 @@ module.exports = nativeDescription: "magyar", englishDescription: "Hungarian", t # artisan_join_step4: "Post your levels on the forum for feedback." # more_about_artisan: "Learn More About Becoming an Artisan" # artisan_subscribe_desc: "Get emails on level editor updates and announcements." -# adventurer_sumamry: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." +# adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." # adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." # adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." # adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." diff --git a/app/locale/id.coffee b/app/locale/id.coffee index 20d16335f..9b938c230 100644 --- a/app/locale/id.coffee +++ b/app/locale/id.coffee @@ -390,7 +390,7 @@ module.exports = nativeDescription: "Bahasa Indonesia", englishDescription: "Ind # artisan_join_step4: "Post your levels on the forum for feedback." # more_about_artisan: "Learn More About Becoming an Artisan" # artisan_subscribe_desc: "Get emails on level editor updates and announcements." -# adventurer_sumamry: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." +# adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." # adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." # adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." # adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." diff --git a/app/locale/it.coffee b/app/locale/it.coffee index a95f98916..883ba4321 100644 --- a/app/locale/it.coffee +++ b/app/locale/it.coffee @@ -390,7 +390,7 @@ module.exports = nativeDescription: "italiano", englishDescription: "Italian", t # artisan_join_step4: "Post your levels on the forum for feedback." more_about_artisan: "Leggi di più su cosa vuol dire diventare un creativo Artigiano" # artisan_subscribe_desc: "Get emails on level editor updates and announcements." -# adventurer_sumamry: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." +# adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." # adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." # adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." # adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." diff --git a/app/locale/ja.coffee b/app/locale/ja.coffee index 4c9e4dfb9..195269c4d 100644 --- a/app/locale/ja.coffee +++ b/app/locale/ja.coffee @@ -390,7 +390,7 @@ module.exports = nativeDescription: "日本語", englishDescription: "Japanese", # artisan_join_step4: "Post your levels on the forum for feedback." # more_about_artisan: "Learn More About Becoming an Artisan" # artisan_subscribe_desc: "Get emails on level editor updates and announcements." -# adventurer_sumamry: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." +# adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." # adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." # adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." # adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." diff --git a/app/locale/ko.coffee b/app/locale/ko.coffee index 882f3d4ac..55fdd32f1 100644 --- a/app/locale/ko.coffee +++ b/app/locale/ko.coffee @@ -390,7 +390,7 @@ module.exports = nativeDescription: "한국어", englishDescription: "Korean", t # artisan_join_step4: "Post your levels on the forum for feedback." # more_about_artisan: "Learn More About Becoming an Artisan" # artisan_subscribe_desc: "Get emails on level editor updates and announcements." -# adventurer_sumamry: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." +# adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." # adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." # adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." # adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." diff --git a/app/locale/lt.coffee b/app/locale/lt.coffee index 1008a8bfb..64cfe04f2 100644 --- a/app/locale/lt.coffee +++ b/app/locale/lt.coffee @@ -390,7 +390,7 @@ module.exports = nativeDescription: "lietuvių kalba", englishDescription: "Lith # artisan_join_step4: "Post your levels on the forum for feedback." # more_about_artisan: "Learn More About Becoming an Artisan" # artisan_subscribe_desc: "Get emails on level editor updates and announcements." -# adventurer_sumamry: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." +# adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." # adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." # adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." # adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." diff --git a/app/locale/ms-BA.coffee b/app/locale/ms-BA.coffee index 29c914f23..e551e3e0f 100644 --- a/app/locale/ms-BA.coffee +++ b/app/locale/ms-BA.coffee @@ -390,7 +390,7 @@ module.exports = nativeDescription: "Bahasa Melayu", englishDescription: "Bahasa # artisan_join_step4: "Post your levels on the forum for feedback." # more_about_artisan: "Learn More About Becoming an Artisan" # artisan_subscribe_desc: "Get emails on level editor updates and announcements." -# adventurer_sumamry: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." +# adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." # adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." # adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." # adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." diff --git a/app/locale/nb.coffee b/app/locale/nb.coffee index db74e540d..68569c841 100644 --- a/app/locale/nb.coffee +++ b/app/locale/nb.coffee @@ -390,7 +390,7 @@ module.exports = nativeDescription: "Norsk Bokmål", englishDescription: "Norweg # artisan_join_step4: "Post your levels on the forum for feedback." # more_about_artisan: "Learn More About Becoming an Artisan" # artisan_subscribe_desc: "Get emails on level editor updates and announcements." -# adventurer_sumamry: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." +# adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." # adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." # adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." # adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." diff --git a/app/locale/nl.coffee b/app/locale/nl.coffee index 508d6e384..f4c45edc5 100644 --- a/app/locale/nl.coffee +++ b/app/locale/nl.coffee @@ -397,28 +397,28 @@ module.exports = nativeDescription: "Nederlands", englishDescription: "Dutch", t artisan_join_step4: "Maak een bericht over jouw level op ons forum voor feedback." more_about_artisan: "Leer meer over hoe je een Creatieve Ambachtsman kan worden." artisan_subscribe_desc: "Ontvang e-mails met nieuws over de Level Editor." - adventurer_sumamry: "Laten we duidelijk zijn over je rol: jij bent de tank. Jij krijgt de zware klappen te verduren. We hebben mensen nodig om spiksplinternieuwe levels te proberen en te kijken hoe deze beter kunnen. De pijn zal groot zijn, het maken van een goede game is een lang proces en niemand doet het de eerste keer goed. Als jij dit kan verduren en een hoge constitution score hebt, dan is dit de klasse voor jou." + adventurer_summary: "Laten we duidelijk zijn over je rol: jij bent de tank. Jij krijgt de zware klappen te verduren. We hebben mensen nodig om spiksplinternieuwe levels te proberen en te kijken hoe deze beter kunnen. De pijn zal groot zijn, het maken van een goede game is een lang proces en niemand doet het de eerste keer goed. Als jij dit kan verduren en een hoge constitution score hebt, dan is dit de klasse voor jou." adventurer_introduction: "Laten we duidelijk zijn over je rol: jij bent de tank. Jij krijgt de zware klappen te verduren. We hebben mensen nodig om spiksplinternieuwe levels te proberen en te kijken hoe deze beter kunnen. De pijn zal groot zijn, het maken van een goede game is een lang proces en niemand doet het de eerste keer goed. Als jij dit kan verduren en een hoge constitution score hebt, dan is dit de klasse voor jou." adventurer_attribute_1: "Een wil om te leren. Jij wilt leren hoe je programmeert en wij willen het jou leren. Je zal overigens zelf het meeste leren doen." adventurer_attribute_2: "Charismatisch. Wees netjes maar duidelijk over wat er beter kan en geef suggesties over hoe het beter kan." - adventurer_join_pref: "Werk samen met een Ambachtsman of recruteer er een, of tik het veld hieronder aan om e-mails te ontvangen wanneer er nieuwe levels zijn om te testen. We zullen ook posten over levels die beoordeeld moeten worden op onze netwerken zoals" + adventurer_join_pref: "Werk samen met een Ambachtsman of recruteer er een, of tik het veld hieronder aan om e-mails te ontvangen wanneer er nieuwe levels zijn om te testen. We zullen ook posten over levels die beoordeeld moeten worden op onze netwerken zoals" adventurer_forum_url: "ons forum" - adventurer_join_suf: "dus als je liever op deze manier wordt geïnformeerd, schrijf je daar in!" + adventurer_join_suf: "dus als je liever op deze manier wordt geïnformeerd, schrijf je daar in!" more_about_adventurer: "Leer meer over hoe je een dappere avonturier kunt worden." adventurer_subscribe_desc: "Ontvang e-mails wanneer er nieuwe levels zijn die getest moeten worden." scribe_summary_pref: "CodeCombat is meer dan slechts een aantal levels, het zal ook een bron van kennis kennis zijn en een wiki met programmeerconcepten waar levels op in kunnen gaan. Op die manier zal een Ambachtslied een link kunnen geven naar een artikel wat past bij een level. Net zoiets als het " scribe_summary_sufx: " heeft gebouwd. Als jij het leuk vindt programmeerconcepten uit te leggen, dan is deze klasse iets voor jou." - scribe_introduction_pref: "CodeCombat is meer dan slechts een aantal levels, het zal ook een bron van kennis kennis zijn en een wiki met programmeerconcepten waar levels op in kunnen gaan. Op die manier zal elk Ambachtslied niet in detail hoeven uit te leggen wat een vergelijkingsoperator is, maar een link kunnen geven naar een artikel wat deze informatie bevat voor de speler. Net zoiets als het " + scribe_introduction_pref: "CodeCombat is meer dan slechts een aantal levels, het zal ook een bron van kennis kennis zijn en een wiki met programmeerconcepten waar levels op in kunnen gaan. Op die manier zal elk Ambachtslied niet in detail hoeven uit te leggen wat een vergelijkingsoperator is, maar een link kunnen geven naar een artikel wat deze informatie bevat voor de speler. Net zoiets als het " scribe_introduction_url_mozilla: "Mozilla Developer Network" scribe_introduction_suf: " heeft gebouwd. Als jij het leuk vindt om programmeerconcepten uit te leggen in Markdown-vorm, dan is deze klasse wellicht iets voor jou." scribe_attribute_1: "Taal-skills zijn praktisch alles wat je nodig hebt. Niet alleen grammatica of spelling, maar ook moeilijke ideeën overbrengen aan anderen." contact_us_url: "Contacteer ons" - scribe_join_description: "vertel ons wat over jezelf, je ervaring met programmeren en over wat voor soort dingen je graag zou schrijven. Verder zien we wel!" - more_about_scribe: "Leer meer over het worden van een ijverige Klerk." - - scribe_subscribe_desc: "Ontvang e-mails met aankondigingen over het schrijven van artikelen." + scribe_join_description: "vertel ons wat over jezelf, je ervaring met programmeren en over wat voor soort dingen je graag zou schrijven. Verder zien we wel!" + more_about_scribe: "Leer meer over het worden van een ijverige Klerk." + + scribe_subscribe_desc: "Ontvang e-mails met aankondigingen over het schrijven van artikelen." diplomat_summary: "Er is grote interesse in CodeCombat in landen waar geen Engels wordt gesproken! We zijn op zoek naar vertalers wie tijd willen spenderen aan het vertalen van de site's corpus aan woorden zodat CodeCombat zo snel mogelijk toegankelijk wordt voor heel de wereld. Als jij wilt helpen met CodeCombat internationaal maken, dan is dit de klasse voor jou." - diplomat_introduction_pref: "Dus, als er iets is wat we geleerd hebben van de " + diplomat_introduction_pref: "Dus, als er iets is wat we geleerd hebben van de " diplomat_launch_url: "release in oktober" diplomat_introduction_suf: "dan is het wel dat er een significante interesse is in CodeCombat in andere landen, vooral Brazilië! We zijn een corps aan vertalers aan het creëren dat ijverig de ene set woorden in een andere omzet om CodeCombat zo toegankelijk te maken als mogelijk in heel de wereld. Als jij het leuk vindt glimpsen op te vangen van aankomende content en deze levels zo snel mogelijk naar je landgenoten te krijgen, dan is dit de klasse voor jou." diplomat_attribute_1: "Vloeiend Engels en de taal waar naar je wilt vertalen kunnen spreken. Wanneer je moeilijke ideeën wilt overbrengen, is het belangrijk beide goed te kunnen!" @@ -429,17 +429,17 @@ module.exports = nativeDescription: "Nederlands", englishDescription: "Dutch", t diplomat_subscribe_desc: "Ontvang e-mails over i18n ontwikkelingen en levels om te vertalen." ambassador_summary: "We proberen een gemeenschap te bouwen en elke gemeenschap heeft een supportteam nodig wanneer er problemen zijn. We hebben chats, e-mails en sociale netwerken zodat onze gebruikers het spel kunnen leren kennen. Als jij mensen wilt helpen betrokken te raken, plezier te hebben en wat te leren programmeren, dan is dit wellicht de klasse voor jou." ambassador_attribute_1: "Communicatieskills. Problemen die spelers hebben kunnen identificeren en ze helpen deze op te lossen. Verder zul je ook de rest van ons geïnformeerd houden over wat de spelers zeggen, wat ze leuk vinden, wat ze minder vinden en waar er meer van moet zijn!" - ambassador_join_desc: "vertel ons wat over jezelf, wat je hebt gedaan en wat je graag zou doen. We zien verder wel!" + ambassador_join_desc: "vertel ons wat over jezelf, wat je hebt gedaan en wat je graag zou doen. We zien verder wel!" ambassador_join_note_strong: "Opmerking" - ambassador_join_note_desc: "Een van onze topprioriteiten is om een multiplayer te bouwen waar spelers die moeite hebben een level op te lossen een wizard met een hoger level kunnen oproepen om te helpen. Dit zal een goede manier zijn voor ambassadeurs om hun ding te doen. We houden je op de hoogte!" + ambassador_join_note_desc: "Een van onze topprioriteiten is om een multiplayer te bouwen waar spelers die moeite hebben een level op te lossen een wizard met een hoger level kunnen oproepen om te helpen. Dit zal een goede manier zijn voor ambassadeurs om hun ding te doen. We houden je op de hoogte!" more_about_ambassador: "Leer meer over het worden van een behulpzame Ambassadeur" - ambassador_subscribe_desc: "Ontvang e-mails met updates over ondersteuning en multiplayer-ontwikkelingen." + ambassador_subscribe_desc: "Ontvang e-mails met updates over ondersteuning en multiplayer-ontwikkelingen." counselor_summary: "Geen van de rollen hierboven in jouw interessegebied? Maak je geen zorgen, we zijn op zoek naar iedereen die wil helpen met het ontwikkelen van CodeCombat! Als je geïnteresseerd bent in lesgeven, gameontwikkeling, open source management of iets anders waarvan je denkt dat het relevant voor ons is, dan is dit de klasse voor jou." counselor_introduction_1: "Heb jij levenservaring? Een afwijkend perspectief op zaken die ons kunnen helpen CodeCombat te vormen? Van alle rollen neemt deze wellicht de minste tijd in, maar individueel maak je misschien het grootste verschil. We zijn op zoek naar wijze tovenaars, vooral in het gebied van lesgeven, gameontwikkeling, open source projectmanagement, technische recrutering, ondernemerschap of design." - counselor_introduction_2: "Of eigenlijk alles wat relevant is voor de ontwikkeling van CodeCombat. Als jij kennis hebt en deze wilt dezen om dit project te laten groeien, dan is dit misschien de klasse voor jou." + counselor_introduction_2: "Of eigenlijk alles wat relevant is voor de ontwikkeling van CodeCombat. Als jij kennis hebt en deze wilt dezen om dit project te laten groeien, dan is dit misschien de klasse voor jou." counselor_attribute_1: "Ervaring, in enig van de bovenstaande gebieden of iets anders waarvan je denkt dat het behulpzaam zal zijn." counselor_attribute_2: "Een beetje vrije tijd!" - counselor_join_desc: "vertel ons wat over jezelf, wat je hebt gedaan en wat je graag wilt doen. We zullen je in onze contactlijst zetten en je benaderen wanneer we je advies kunnen gebruiken (niet te vaak)." + counselor_join_desc: "vertel ons wat over jezelf, wat je hebt gedaan en wat je graag wilt doen. We zullen je in onze contactlijst zetten en je benaderen wanneer we je advies kunnen gebruiken (niet te vaak)." more_about_counselor: "Leer meer over het worden van een waardevolle Raadgever" changes_auto_save: "Veranderingen worden automatisch opgeslagen wanneer je het vierkantje aan- of afvinkt." diligent_scribes: "Onze ijverige Klerks:" diff --git a/app/locale/nn.coffee b/app/locale/nn.coffee index c9e770bfa..7bd5b0307 100644 --- a/app/locale/nn.coffee +++ b/app/locale/nn.coffee @@ -390,7 +390,7 @@ module.exports = nativeDescription: "Norwegian Nynorsk", englishDescription: "No # artisan_join_step4: "Post your levels on the forum for feedback." # more_about_artisan: "Learn More About Becoming an Artisan" # artisan_subscribe_desc: "Get emails on level editor updates and announcements." -# adventurer_sumamry: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." +# adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." # adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." # adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." # adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." diff --git a/app/locale/no.coffee b/app/locale/no.coffee index bc0e69e0c..78fc15832 100644 --- a/app/locale/no.coffee +++ b/app/locale/no.coffee @@ -390,7 +390,7 @@ module.exports = nativeDescription: "Norsk", englishDescription: "Norwegian", tr # artisan_join_step4: "Post your levels on the forum for feedback." # more_about_artisan: "Learn More About Becoming an Artisan" # artisan_subscribe_desc: "Get emails on level editor updates and announcements." -# adventurer_sumamry: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." +# adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." # adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." # adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." # adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." diff --git a/app/locale/pl.coffee b/app/locale/pl.coffee index 9988be45e..32f394f1c 100644 --- a/app/locale/pl.coffee +++ b/app/locale/pl.coffee @@ -390,7 +390,7 @@ module.exports = nativeDescription: "język polski", englishDescription: "Polish # artisan_join_step4: "Post your levels on the forum for feedback." # more_about_artisan: "Learn More About Becoming an Artisan" # artisan_subscribe_desc: "Get emails on level editor updates and announcements." -# adventurer_sumamry: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." +# adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." # adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." # adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." # adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." diff --git a/app/locale/pt-BR.coffee b/app/locale/pt-BR.coffee index 40f374f81..be8e8079f 100644 --- a/app/locale/pt-BR.coffee +++ b/app/locale/pt-BR.coffee @@ -390,7 +390,7 @@ module.exports = nativeDescription: "português do Brasil", englishDescription: artisan_join_step4: "Publique seus níveis no fórum para avaliação." more_about_artisan: "Saiba Mais Sobre Como Se Tornar Um Artesão Criativo" artisan_subscribe_desc: "Receba emails com novidades sobre o editor de níveis e anúncios." -# adventurer_sumamry: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." +# adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." adventurer_introduction: "Vamos ser claros sobre o seu papel: você é o tanque. Você vai tomar dano pesado. Precisamos de pessoas para experimentar níveis inéditos e ajudar a identificar como fazer as coisas melhorarem. A dor será enorme, fazer bons jogos é um processo longo e ninguém acerta na primeira vez. Se você pode suportar e ter uma alta pontuação de constituição, então esta classe pode ser para você." adventurer_attribute_1: "Sede de aprendizado. Você quer aprender a codificar e nós queremos ensiná-lo a codificar. Você provavelmente vai fazer a maior parte do ensino neste caso." adventurer_attribute_2: "Carismático. Seja gentil, mas articulado sobre o que precisa melhorar, e ofereça sugestões sobre como melhorar." diff --git a/app/locale/pt-PT.coffee b/app/locale/pt-PT.coffee index 77fb7afad..57278318b 100644 --- a/app/locale/pt-PT.coffee +++ b/app/locale/pt-PT.coffee @@ -390,7 +390,7 @@ module.exports = nativeDescription: "Português europeu", englishDescription: "P # artisan_join_step4: "Post your levels on the forum for feedback." # more_about_artisan: "Learn More About Becoming an Artisan" # artisan_subscribe_desc: "Get emails on level editor updates and announcements." -# adventurer_sumamry: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." +# adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." # adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." # adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." # adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." diff --git a/app/locale/pt.coffee b/app/locale/pt.coffee index 38a21790c..ee3395e7f 100644 --- a/app/locale/pt.coffee +++ b/app/locale/pt.coffee @@ -390,7 +390,7 @@ module.exports = nativeDescription: "português", englishDescription: "Portugues # artisan_join_step4: "Post your levels on the forum for feedback." # more_about_artisan: "Learn More About Becoming an Artisan" # artisan_subscribe_desc: "Get emails on level editor updates and announcements." -# adventurer_sumamry: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." +# adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." # adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." # adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." # adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." diff --git a/app/locale/ro.coffee b/app/locale/ro.coffee index 3551d730b..1fee5fe47 100644 --- a/app/locale/ro.coffee +++ b/app/locale/ro.coffee @@ -96,8 +96,8 @@ module.exports = nativeDescription: "limba română", englishDescription: "Roman diplomat_suggestion: title: "Ajută-ne să traducem CodeCombat!" sub_heading: "Avem nevoie de abilitățile tale lingvistice." - pitch_body: "CodeCombat este dezvoltat in limba engleza , dar deja avem jucatări din toate colțurile lumii.Mulți dintre ei vor să joace in română și nu vorbesc engleză.Dacă poți vorbi ambele te rugăm să te gândești dacă ai dori să devi un Diplomat și să ne ajuți sa traducem atât jocul cât și site-ul." - missing_translations: "Until we can translate everything into Romanian, you'll see English when Romanian isn't available." + pitch_body: "CodeCombat este dezvoltat in limba engleza , dar deja avem jucatări din toate colțurile lumii.Mulți dintre ei vor să joace in română și nu vorbesc engleză.Dacă poți vorbi ambele te rugăm să te gândești dacă ai dori să devi un Diplomat și să ne ajuți sa traducem atât jocul cât și site-ul." + missing_translations: "Until we can translate everything into Romanian, you'll see English when Romanian isn't available." learn_more: "Află mai multe despre cum să fi un Diplomat" subscribe_as_diplomat: "Înscrie-te ca Diplomat" @@ -105,7 +105,7 @@ module.exports = nativeDescription: "limba română", englishDescription: "Roman title: "Setări Wizard" customize_avatar: "Personalizează-ți Avatarul" clothes: "Haine" - trim: "Margine" + trim: "Margine" cloud: "Nor" spell: "Vrajă" boots: "Încălțăminte" @@ -389,7 +389,7 @@ module.exports = nativeDescription: "limba română", englishDescription: "Roman # artisan_join_step4: "Post your levels on the forum for feedback." # more_about_artisan: "Learn More About Becoming an Artisan" # artisan_subscribe_desc: "Get emails on level editor updates and announcements." -# adventurer_sumamry: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." +# adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." # adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." # adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." # adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." diff --git a/app/locale/ru.coffee b/app/locale/ru.coffee index 57cfc1847..51f6d1a7e 100644 --- a/app/locale/ru.coffee +++ b/app/locale/ru.coffee @@ -390,7 +390,7 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi artisan_join_step4: "Разместите свои уровни на форуме для обратной связи." more_about_artisan: "Узнать больше о том, как стать Ремесленником" artisan_subscribe_desc: "Получать email-ы об обновлениях редактора уровней и объявления." - adventurer_sumamry: "Позвольте внести ясность о вашей роли: вы танк. Вы собираетесь принять тяжелые повреждения. Нам нужны люди, чтобы испытать совершенно новые уровни и помочь определить, как сделать лучше. Боль будет огромной; создание хороших игр - длительный процесс и никто не делает это правильно в первый раз. Если вы можете выдержать и имеете высокий балл конституции (D&D), этот класс для вас." + adventurer_summary: "Позвольте внести ясность о вашей роли: вы танк. Вы собираетесь принять тяжелые повреждения. Нам нужны люди, чтобы испытать совершенно новые уровни и помочь определить, как сделать лучше. Боль будет огромной; создание хороших игр - длительный процесс и никто не делает это правильно в первый раз. Если вы можете выдержать и имеете высокий балл конституции (D&D), этот класс для вас." adventurer_introduction: "Позвольте внести ясность о вашей роли: вы танк. Вы собираетесь принять тяжелые повреждения. Нам нужны люди, чтобы испытать совершенно новые уровни и помочь определить, как сделать лучше. Боль будет огромной; создание хороших игр - длительный процесс и никто не делает это правильно в первый раз. Если вы можете выдержать и имеете высокий балл конституции (D&D), этот класс для вас." adventurer_attribute_1: "Жажда обучения. Вы хотите научиться программировать и мы хотим научить вас программировать. Вы, вероятно, проведёте большую часть обучения в процессе." adventurer_attribute_2: "Харизматичность. Будьте нежны, но ясно формулируйте, что нуждается в улучшении и вносите свои предложения по улучшению." diff --git a/app/locale/sk.coffee b/app/locale/sk.coffee index c9e3e4023..ae22ef336 100644 --- a/app/locale/sk.coffee +++ b/app/locale/sk.coffee @@ -390,7 +390,7 @@ module.exports = nativeDescription: "slovenčina", englishDescription: "Slovak", # artisan_join_step4: "Post your levels on the forum for feedback." # more_about_artisan: "Learn More About Becoming an Artisan" # artisan_subscribe_desc: "Get emails on level editor updates and announcements." -# adventurer_sumamry: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." +# adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." # adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." # adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." # adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." diff --git a/app/locale/sl.coffee b/app/locale/sl.coffee index b0cbdd3f5..89b759c19 100644 --- a/app/locale/sl.coffee +++ b/app/locale/sl.coffee @@ -390,7 +390,7 @@ module.exports = nativeDescription: "slovenščina", englishDescription: "Sloven # artisan_join_step4: "Post your levels on the forum for feedback." # more_about_artisan: "Learn More About Becoming an Artisan" # artisan_subscribe_desc: "Get emails on level editor updates and announcements." -# adventurer_sumamry: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." +# adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." # adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." # adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." # adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." diff --git a/app/locale/sr.coffee b/app/locale/sr.coffee index c9676fa65..d3c351f39 100644 --- a/app/locale/sr.coffee +++ b/app/locale/sr.coffee @@ -390,7 +390,7 @@ module.exports = nativeDescription: "српски", englishDescription: "Serbian # artisan_join_step4: "Post your levels on the forum for feedback." # more_about_artisan: "Learn More About Becoming an Artisan" # artisan_subscribe_desc: "Get emails on level editor updates and announcements." -# adventurer_sumamry: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." +# adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." # adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." # adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." # adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." diff --git a/app/locale/sv.coffee b/app/locale/sv.coffee index d8b41ec50..001ce3eca 100644 --- a/app/locale/sv.coffee +++ b/app/locale/sv.coffee @@ -390,7 +390,7 @@ module.exports = nativeDescription: "Svenska", englishDescription: "Swedish", tr # artisan_join_step4: "Post your levels on the forum for feedback." # more_about_artisan: "Learn More About Becoming an Artisan" # artisan_subscribe_desc: "Get emails on level editor updates and announcements." -# adventurer_sumamry: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." +# adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." # adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." # adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." # adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." diff --git a/app/locale/th.coffee b/app/locale/th.coffee index 9b2134eb8..76f1aa37d 100644 --- a/app/locale/th.coffee +++ b/app/locale/th.coffee @@ -390,7 +390,7 @@ module.exports = nativeDescription: "ไทย", englishDescription: "Thai", tra # artisan_join_step4: "Post your levels on the forum for feedback." # more_about_artisan: "Learn More About Becoming an Artisan" # artisan_subscribe_desc: "Get emails on level editor updates and announcements." -# adventurer_sumamry: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." +# adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." # adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." # adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." # adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." diff --git a/app/locale/tr.coffee b/app/locale/tr.coffee index 4a256defe..39e268908 100644 --- a/app/locale/tr.coffee +++ b/app/locale/tr.coffee @@ -390,7 +390,7 @@ module.exports = nativeDescription: "Türkçe", englishDescription: "Turkish", t # artisan_join_step4: "Post your levels on the forum for feedback." # more_about_artisan: "Learn More About Becoming an Artisan" # artisan_subscribe_desc: "Get emails on level editor updates and announcements." -# adventurer_sumamry: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." +# adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." # adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." # adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." # adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." diff --git a/app/locale/uk.coffee b/app/locale/uk.coffee index bf67c2138..52d4fd3b4 100644 --- a/app/locale/uk.coffee +++ b/app/locale/uk.coffee @@ -390,7 +390,7 @@ module.exports = nativeDescription: "українська мова", englishDesc # artisan_join_step4: "Post your levels on the forum for feedback." # more_about_artisan: "Learn More About Becoming an Artisan" # artisan_subscribe_desc: "Get emails on level editor updates and announcements." -# adventurer_sumamry: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." +# adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." # adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." # adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." # adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." diff --git a/app/locale/ur.coffee b/app/locale/ur.coffee index 8237971a3..75a2c2e38 100644 --- a/app/locale/ur.coffee +++ b/app/locale/ur.coffee @@ -390,7 +390,7 @@ module.exports = nativeDescription: "اُردُو", englishDescription: "Urdu", # artisan_join_step4: "Post your levels on the forum for feedback." # more_about_artisan: "Learn More About Becoming an Artisan" # artisan_subscribe_desc: "Get emails on level editor updates and announcements." -# adventurer_sumamry: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." +# adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." # adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." # adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." # adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." diff --git a/app/locale/vi.coffee b/app/locale/vi.coffee index c3a00a167..bb7ac3b89 100644 --- a/app/locale/vi.coffee +++ b/app/locale/vi.coffee @@ -390,7 +390,7 @@ module.exports = nativeDescription: "Tiếng Việt", englishDescription: "Vietn # artisan_join_step4: "Post your levels on the forum for feedback." # more_about_artisan: "Learn More About Becoming an Artisan" # artisan_subscribe_desc: "Get emails on level editor updates and announcements." -# adventurer_sumamry: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." +# adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." # adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." # adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." # adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." diff --git a/app/locale/zh-HANS.coffee b/app/locale/zh-HANS.coffee index caa2b9802..fd0e42cbf 100644 --- a/app/locale/zh-HANS.coffee +++ b/app/locale/zh-HANS.coffee @@ -390,7 +390,7 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese # artisan_join_step4: "Post your levels on the forum for feedback." more_about_artisan: "了解成为工匠的方法" artisan_subscribe_desc: "通过电子邮件获得关卡编辑器更新和公告。" - adventurer_sumamry: "丑话说在前面,你就是那个挡枪子的,而且你会伤得很重。我们需要人手来测试崭新的关卡,并且提出改进意见。做一个好游戏是一个漫长的过程,没人第一次就能搞对。如果你能忍得了这些,而且身体健壮,那这个职业就是你的了。" + adventurer_summary: "丑话说在前面,你就是那个挡枪子的,而且你会伤得很重。我们需要人手来测试崭新的关卡,并且提出改进意见。做一个好游戏是一个漫长的过程,没人第一次就能搞对。如果你能忍得了这些,而且身体健壮,那这个职业就是你的了。" # adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." # adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." # adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." diff --git a/app/locale/zh-HANT.coffee b/app/locale/zh-HANT.coffee index d38532973..b6cf92b26 100644 --- a/app/locale/zh-HANT.coffee +++ b/app/locale/zh-HANT.coffee @@ -390,7 +390,7 @@ module.exports = nativeDescription: "繁体中文", englishDescription: "Chinese # artisan_join_step4: "Post your levels on the forum for feedback." # more_about_artisan: "Learn More About Becoming an Artisan" # artisan_subscribe_desc: "Get emails on level editor updates and announcements." -# adventurer_sumamry: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." +# adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." # adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." # adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." # adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." diff --git a/app/locale/zh.coffee b/app/locale/zh.coffee index aacf8fe4d..452df3628 100644 --- a/app/locale/zh.coffee +++ b/app/locale/zh.coffee @@ -390,7 +390,7 @@ module.exports = nativeDescription: "中文", englishDescription: "Chinese", tra # artisan_join_step4: "Post your levels on the forum for feedback." # more_about_artisan: "Learn More About Becoming an Artisan" # artisan_subscribe_desc: "Get emails on level editor updates and announcements." -# adventurer_sumamry: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." +# adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." # adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." # adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." # adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." From a03403de944d7e2f7a24de7e42c488c3f2646fa8 Mon Sep 17 00:00:00 2001 From: Nick Winter <livelily@gmail.com> Date: Thu, 6 Mar 2014 09:35:58 -0800 Subject: [PATCH 017/100] Started some more name categories. --- app/lib/world/names.coffee | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/app/lib/world/names.coffee b/app/lib/world/names.coffee index 44c446583..3b1cd0080 100644 --- a/app/lib/world/names.coffee +++ b/app/lib/world/names.coffee @@ -88,6 +88,9 @@ module.exports.thangNames = thangNames = "Durfkor" "Paps" ] + "Peasant F": [ + "Hilda" + ] "Archer F": [ "Phoebe" "Mira" @@ -238,3 +241,19 @@ module.exports.thangNames = thangNames = "Rakash" "Drumbaa" ] + "Burl": [ + "Borlit" + "Burlosh" + ] + "Griffin Rider": [ + "Aeoldan" + ] + "Potion Master": [ + "Snake" + ] + "Librarian": [ + "Hushbaum" + ] + "Equestrian": [ + "Reynaldo" + ] From 40a32d7b43e582efaa91ccfaafde56580d023889 Mon Sep 17 00:00:00 2001 From: Nick Winter <livelily@gmail.com> Date: Thu, 6 Mar 2014 14:53:37 -0800 Subject: [PATCH 018/100] Fixed array / string ThangState key serialization conflicts. Fixed #333. --- app/lib/world/thang_state.coffee | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/lib/world/thang_state.coffee b/app/lib/world/thang_state.coffee index 964f25eda..7db97aca6 100644 --- a/app/lib/world/thang_state.coffee +++ b/app/lib/world/thang_state.coffee @@ -51,7 +51,8 @@ module.exports = class ThangState else if type is 'array' specialKey = storage[@frameIndex] value = @specialKeysToValues[specialKey] - value = value.split('\x1E') # Record Separator + # Remove leading and trailing Group Separators and split by any Record Separators to restore the array of strings + value = value.substring(1, value.length - 1).split('\x1E') if value else value = storage[@frameIndex] value @@ -133,7 +134,8 @@ module.exports = class ThangState storage[frameIndex] = specialKey storage[frameIndex] = specialKey else if type is 'array' - value = value.join '\x1E' # Record Separator + # We make sure the array keys won't collide with any string keys by using some unprintable characters. + value = '\x1D' + value.join('\x1E') + '\x1D' # Group Separator, Record Separator(s), Group Separator specialKey = specialValuesToKeys[value] unless specialKey specialKey = specialKeysToValues.length From 041f4512e8c02387d3aa0ec788a7f69da9293d18 Mon Sep 17 00:00:00 2001 From: Scott Erickson <sderickson@gmail.com> Date: Thu, 6 Mar 2014 15:52:09 -0800 Subject: [PATCH 019/100] Set up marks to all load dynamically, and set up effect marks, which appear above a thang's head and rotate between them if there are multiple. --- app/lib/surface/CocoSprite.coffee | 42 +++++++++++++++++++++++++++++-- app/lib/surface/Mark.coffee | 35 +++++++++++++++++++++++--- app/lib/surface/SpriteBoss.coffee | 10 +++----- app/models/Level.coffee | 10 -------- 4 files changed, 74 insertions(+), 23 deletions(-) diff --git a/app/lib/surface/CocoSprite.coffee b/app/lib/surface/CocoSprite.coffee index 57146866a..279ef087b 100644 --- a/app/lib/surface/CocoSprite.coffee +++ b/app/lib/surface/CocoSprite.coffee @@ -82,6 +82,7 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass @imageObject?.off 'animationend', @playNextAction @playNextAction = null @displayObject?.off() + clearInterval @effectInterval if @effectInterval super() toString: -> "<CocoSprite: #{@thang?.id}>" @@ -375,18 +376,55 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass scale *= @options.resolutionFactor if prop is 'registration' pos.x *= scale pos.y *= scale + if @thang + scaleFactor = @thang.scaleFactor ? 1 + pos.x *= @thang.scaleFactorX ? scaleFactor + pos.y *= @thang.scaleFactorY ? scaleFactor pos updateMarks: -> return unless @options.camera - @addMark 'repair', null, @options.markThangTypes.repair if @thang?.errorsOut + @addMark 'repair', null, 'repair' if @thang?.errorsOut @marks.repair?.toggle @thang?.errorsOut @addMark('bounds').toggle true if @thang?.drawsBounds @addMark('shadow').toggle true unless @thangType.get('shadow') is 0 mark.update() for name, mark of @marks +# @thang.effectNames = ['berserk', 'confused', 'controlled', 'cursed', 'fear', 'poison', 'paralyzed', 'regeneration', 'sleep', 'slowed', 'speed'] + @updateEffectMarks() if @thang?.effectNames?.length + + updateEffectMarks: -> + return if _.isEqual @thang.effectNames, @previousEffectNames + for effect in @thang.effectNames + mark = @addMark effect, @options.floatingLayer, effect + mark.statusEffect = true + mark.toggle 'on' + mark.show() + + if @previousEffectNames + for effect in @previousEffectNames + mark = @marks[effect] + mark.toggle 'off' + + if @thang.effectNames.length > 1 and not @effectInterval + @rotateEffect() + @effectInterval = setInterval @rotateEffect, 1500 + + else if @effectInterval and @thang.effectNames.length <= 1 + @clearInterval @effectInterval + @effectInterval = null + + @previousEffectNames = @thang.effectNames + + rotateEffect: => + effects = (m.name for m in _.values(@marks) when m.on and m.statusEffect and m.mark) + effects.sort() + @effectIndex ?= 0 + @effectIndex = (@effectIndex + 1) % effects.length + @marks[effect].hide() for effect in effects + @marks[effects[@effectIndex]].show() setHighlight: (to, delay) -> - @addMark 'highlight', @options.floatingLayer, @options.markThangTypes.highlight if to + @addMark 'highlight', @options.floatingLayer, 'highlight' if to @marks.highlight?.highlightDelay = delay @marks.highlight?.toggle to and not @dimmed diff --git a/app/lib/surface/Mark.coffee b/app/lib/surface/Mark.coffee index c012b537c..5a2e7689f 100644 --- a/app/lib/surface/Mark.coffee +++ b/app/lib/surface/Mark.coffee @@ -1,5 +1,7 @@ CocoClass = require 'lib/CocoClass' Camera = require './Camera' +ThangType = require 'models/ThangType' +markThangTypes = {} module.exports = class Mark extends CocoClass subscriptions: {} @@ -20,6 +22,7 @@ module.exports = class Mark extends CocoClass destroy: -> @mark?.parent?.removeChild @mark @markSprite?.destroy() + @thangType?.off 'sync', @onLoadedThangType, @ @sprite = null super() @@ -27,7 +30,9 @@ module.exports = class Mark extends CocoClass toggle: (to) -> return @ if to is @on + return @toggleTo = to unless @mark @on = to + delete @toggleTo if @on @layer.addChild @mark @layer.updateLayerOrder() @@ -52,7 +57,7 @@ module.exports = class Mark extends CocoClass else if @name is 'debug' then @buildDebug() else if @thangType then @buildSprite() else console.error "Don't know how to build mark for", @name - @mark.mouseEnabled = false + @mark?.mouseEnabled = false @ buildBounds: -> @@ -126,15 +131,34 @@ module.exports = class Mark extends CocoClass @mark.graphics.endFill() buildSprite: -> - #console.log "building", @name, "with thangtype", @thangType + if _.isString @thangType + thangType = markThangTypes[@thangType] + return @loadThangType() if not thangType + @thangType = thangType + + return @thangType.once 'sync', @onLoadedThangType, @ if not @thangType.loaded CocoSprite = require './CocoSprite' markSprite = new CocoSprite @thangType, @thangType.spriteOptions markSprite.queueAction 'idle' @mark = markSprite.displayObject @markSprite = markSprite + loadThangType: -> + name = @thangType + @thangType = new ThangType() + @thangType.url = -> "/db/thang.type/#{name}" + @thangType.once 'sync', @onLoadedThangType, @ + @thangType.fetch() + markThangTypes[name] = @thangType + window.mtt = markThangTypes + + onLoadedThangType: -> + @build() + @toggle(@toggleTo) if @toggleTo? + update: (pos=null) -> - return false unless @on + return false unless @on and @mark + @mark.alpha = if @hidden then 0 else 1 @updatePosition pos @updateRotation() @updateScale() @@ -156,10 +180,11 @@ module.exports = class Mark extends CocoClass pos ?= @sprite?.displayObject @mark.x = pos.x @mark.y = pos.y - if @name is 'highlight' + if @statusEffect or @name is 'highlight' offset = @sprite.getOffset 'aboveHead' @mark.x += offset.x @mark.y += offset.y + @mark.y -= 3 if @statusEffect updateRotation: -> if @name is 'debug' or (@name is 'shadow' and @sprite.thang?.shape in ["rectangle", "box"]) @@ -187,3 +212,5 @@ module.exports = class Mark extends CocoClass stop: -> @markSprite?.stop() play: -> @markSprite?.play() + hide: -> @hidden = true + show: -> @hidden = false diff --git a/app/lib/surface/SpriteBoss.coffee b/app/lib/surface/SpriteBoss.coffee index 130d017b7..f84d52a28 100644 --- a/app/lib/surface/SpriteBoss.coffee +++ b/app/lib/surface/SpriteBoss.coffee @@ -48,10 +48,6 @@ module.exports = class SpriteBoss extends CocoClass thangTypeFor: (type) -> _.find @options.thangTypes, (m) -> m.get('original') is type or m.get('name') is type - markThangTypes: -> - highlight: @thangTypeFor "Highlight" - repair: @thangTypeFor "Repair" - createLayers: -> @spriteLayers = {} for [name, priority] in [ @@ -87,11 +83,11 @@ module.exports = class SpriteBoss extends CocoClass sprite createMarks: -> - @targetMark = new Mark name: 'target', camera: @camera, layer: @spriteLayers["Ground"], thangType: @thangTypeFor("Target") - @selectionMark = new Mark name: 'selection', camera: @camera, layer: @spriteLayers["Ground"], thangType: @thangTypeFor("Selection") + @targetMark = new Mark name: 'target', camera: @camera, layer: @spriteLayers["Ground"], thangType: 'target' + @selectionMark = new Mark name: 'selection', camera: @camera, layer: @spriteLayers["Ground"], thangType: 'selection' createSpriteOptions: (options) -> - _.extend options, camera: @camera, resolutionFactor: 4, groundLayer: @spriteLayers["Ground"], textLayer: @surfaceTextLayer, floatingLayer: @spriteLayers["Floating"], markThangTypes: @markThangTypes(), spriteSheetCache: @spriteSheetCache, showInvisible: @options.showInvisible + _.extend options, camera: @camera, resolutionFactor: 4, groundLayer: @spriteLayers["Ground"], textLayer: @surfaceTextLayer, floatingLayer: @spriteLayers["Floating"], spriteSheetCache: @spriteSheetCache, showInvisible: @options.showInvisible createIndieSprites: (indieSprites, withWizards) -> unless @indieSprites diff --git a/app/models/Level.coffee b/app/models/Level.coffee index fca1f8249..176b09aa6 100644 --- a/app/models/Level.coffee +++ b/app/models/Level.coffee @@ -115,15 +115,5 @@ module.exports = class Level extends CocoModel model = CocoModel.getOrMakeModelFromLink link, shouldLoadProjection models.push model if model else if path is '/' - # We also we need to make sure we grab the Wizard ThangType and the Marks. Hackitrooooid! - for [type, original] in [ - ["Highlight", "529f8fdbdacd325127000003"] - ["Selection", "52aa5f7520fccb0000000002"] - ["Target", "52b32ad97385ec3d03000001"] - ["Repair", "52bcc4591f766a891c000003"] - ] - link = "/db/thang_type/#{original}/version" - model = CocoModel.getOrMakeModelFromLink link, shouldLoadProjection - models.push model if model models.push ThangType.loadUniversalWizard() models From ea406bd3c7e2d9adf19e9b2a80aa91ec2fa7d849 Mon Sep 17 00:00:00 2001 From: Nick Winter <livelily@gmail.com> Date: Thu, 6 Mar 2014 15:52:51 -0800 Subject: [PATCH 020/100] Fixed some event listeners on destroyed loaders. --- app/lib/God.coffee | 2 +- app/lib/LevelLoader.coffee | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/app/lib/God.coffee b/app/lib/God.coffee index f7563517e..3839013a6 100644 --- a/app/lib/God.coffee +++ b/app/lib/God.coffee @@ -30,7 +30,7 @@ module.exports = class God @createWorld() fillWorkerPool: => - return unless Worker + return unless Worker and not @dead @workerPool ?= [] if @workerPool.length < @maxWorkerPoolSize @workerPool.push @createWorker() diff --git a/app/lib/LevelLoader.coffee b/app/lib/LevelLoader.coffee index 52865a8b1..18ae9f82f 100644 --- a/app/lib/LevelLoader.coffee +++ b/app/lib/LevelLoader.coffee @@ -70,6 +70,7 @@ module.exports = class LevelLoader extends CocoClass @session.loaded and ((not @opponentSession) or @opponentSession.loaded) onSessionLoaded: -> + return if @destroyed # TODO: maybe have all non versioned models do this? Or make it work to PUT/PATCH to relative urls if @session.loaded @session.url = -> '/db/level.session/' + @id @@ -171,6 +172,7 @@ module.exports = class LevelLoader extends CocoClass t0 = new Date() @spriteSheetsToBuild += 1 thangType.once 'build-complete', => + return if @destroyed @spriteSheetsBuilt += 1 @notifyProgress() console.log "Built", thangType.get('name'), 'after', ((new Date()) - t0), 'ms' From 1b61b928b0f4945173d41ec17dff01f13647ebbf Mon Sep 17 00:00:00 2001 From: Scott Erickson <sderickson@gmail.com> Date: Thu, 6 Mar 2014 16:21:06 -0800 Subject: [PATCH 021/100] Couple tweaks to the cocosprite for effect marks. --- app/lib/surface/CocoSprite.coffee | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/lib/surface/CocoSprite.coffee b/app/lib/surface/CocoSprite.coffee index 279ef087b..998fdccda 100644 --- a/app/lib/surface/CocoSprite.coffee +++ b/app/lib/surface/CocoSprite.coffee @@ -389,10 +389,11 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass @addMark('bounds').toggle true if @thang?.drawsBounds @addMark('shadow').toggle true unless @thangType.get('shadow') is 0 mark.update() for name, mark of @marks -# @thang.effectNames = ['berserk', 'confused', 'controlled', 'cursed', 'fear', 'poison', 'paralyzed', 'regeneration', 'sleep', 'slowed', 'speed'] +# @thang.effectNames = ['berserk', 'confuse', 'control', 'curse', 'fear', 'poison', 'paralyze', 'regen', 'sleep', 'slow', 'speed'] @updateEffectMarks() if @thang?.effectNames?.length updateEffectMarks: -> + @thang.effectNames = (e for e in @thang.effectNames when e) # hack because empty strings appear in the array return if _.isEqual @thang.effectNames, @previousEffectNames for effect in @thang.effectNames mark = @addMark effect, @options.floatingLayer, effect @@ -449,6 +450,7 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass @labels[name] addMark: (name, layer, thangType=null) -> + console.log 'what are my effects?', @thang.effectNames unless name @marks[name] ?= new Mark name: name, sprite: @, camera: @options.camera, layer: layer ? @options.groundLayer, thangType: thangType @marks[name] From b6335a30ae89fc70c8508c1db7c6908a42358635 Mon Sep 17 00:00:00 2001 From: Nick Winter <livelily@gmail.com> Date: Thu, 6 Mar 2014 16:32:13 -0800 Subject: [PATCH 022/100] More fixes for empty string array serialization. --- app/lib/surface/CocoSprite.coffee | 14 ++++++-------- app/lib/world/thang_state.coffee | 14 ++++++++++---- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/app/lib/surface/CocoSprite.coffee b/app/lib/surface/CocoSprite.coffee index 998fdccda..daac71ec5 100644 --- a/app/lib/surface/CocoSprite.coffee +++ b/app/lib/surface/CocoSprite.coffee @@ -389,31 +389,30 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass @addMark('bounds').toggle true if @thang?.drawsBounds @addMark('shadow').toggle true unless @thangType.get('shadow') is 0 mark.update() for name, mark of @marks -# @thang.effectNames = ['berserk', 'confuse', 'control', 'curse', 'fear', 'poison', 'paralyze', 'regen', 'sleep', 'slow', 'speed'] + #@thang.effectNames = ['berserk', 'confuse', 'control', 'curse', 'fear', 'poison', 'paralyze', 'regen', 'sleep', 'slow', 'speed'] @updateEffectMarks() if @thang?.effectNames?.length - + updateEffectMarks: -> - @thang.effectNames = (e for e in @thang.effectNames when e) # hack because empty strings appear in the array return if _.isEqual @thang.effectNames, @previousEffectNames for effect in @thang.effectNames mark = @addMark effect, @options.floatingLayer, effect mark.statusEffect = true mark.toggle 'on' mark.show() - + if @previousEffectNames for effect in @previousEffectNames mark = @marks[effect] mark.toggle 'off' - + if @thang.effectNames.length > 1 and not @effectInterval @rotateEffect() @effectInterval = setInterval @rotateEffect, 1500 - + else if @effectInterval and @thang.effectNames.length <= 1 @clearInterval @effectInterval @effectInterval = null - + @previousEffectNames = @thang.effectNames rotateEffect: => @@ -450,7 +449,6 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass @labels[name] addMark: (name, layer, thangType=null) -> - console.log 'what are my effects?', @thang.effectNames unless name @marks[name] ?= new Mark name: name, sprite: @, camera: @options.camera, layer: layer ? @options.groundLayer, thangType: thangType @marks[name] diff --git a/app/lib/world/thang_state.coffee b/app/lib/world/thang_state.coffee index 7db97aca6..7ea6a9687 100644 --- a/app/lib/world/thang_state.coffee +++ b/app/lib/world/thang_state.coffee @@ -50,9 +50,12 @@ module.exports = class ThangState value = @thang.world.getThangByID @specialKeysToValues[specialKey] else if type is 'array' specialKey = storage[@frameIndex] - value = @specialKeysToValues[specialKey] - # Remove leading and trailing Group Separators and split by any Record Separators to restore the array of strings - value = value.substring(1, value.length - 1).split('\x1E') if value + valueString = @specialKeysToValues[specialKey] + if valueString and valueString.length > 1 + # Trim leading Group Separator and trailing Record Separator, split by Record Separators, restore string array. + value = valueString.substring(1, valueString.length - 1).split '\x1E' + else + value = [] else value = storage[@frameIndex] value @@ -135,7 +138,10 @@ module.exports = class ThangState storage[frameIndex] = specialKey else if type is 'array' # We make sure the array keys won't collide with any string keys by using some unprintable characters. - value = '\x1D' + value.join('\x1E') + '\x1D' # Group Separator, Record Separator(s), Group Separator + stringPieces = ['\x1D'] # Group Separator + for element in value + stringPieces.push element, '\x1E' # Record Separator(s) + value = stringPieces.join('') specialKey = specialValuesToKeys[value] unless specialKey specialKey = specialKeysToValues.length From 5b11e132b2e1ec57c44555f1f0511ab3318cef50 Mon Sep 17 00:00:00 2001 From: Scott Erickson <sderickson@gmail.com> Date: Thu, 6 Mar 2014 17:13:16 -0800 Subject: [PATCH 023/100] Fixed the effect marks so they properly go away. --- app/lib/surface/CocoSprite.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/lib/surface/CocoSprite.coffee b/app/lib/surface/CocoSprite.coffee index daac71ec5..5c082320b 100644 --- a/app/lib/surface/CocoSprite.coffee +++ b/app/lib/surface/CocoSprite.coffee @@ -390,7 +390,7 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass @addMark('shadow').toggle true unless @thangType.get('shadow') is 0 mark.update() for name, mark of @marks #@thang.effectNames = ['berserk', 'confuse', 'control', 'curse', 'fear', 'poison', 'paralyze', 'regen', 'sleep', 'slow', 'speed'] - @updateEffectMarks() if @thang?.effectNames?.length + @updateEffectMarks() if @thang?.effectNames?.length or @previousEffectNames?.length updateEffectMarks: -> return if _.isEqual @thang.effectNames, @previousEffectNames @@ -403,7 +403,7 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass if @previousEffectNames for effect in @previousEffectNames mark = @marks[effect] - mark.toggle 'off' + mark.toggle false if @thang.effectNames.length > 1 and not @effectInterval @rotateEffect() From 40e896868be316ee8e607db5c9a370c6a85c36c6 Mon Sep 17 00:00:00 2001 From: Nick Winter <livelily@gmail.com> Date: Thu, 6 Mar 2014 17:21:24 -0800 Subject: [PATCH 024/100] Fixed CocoSprite getOffset to not apply scale to registration points. --- app/lib/surface/CocoSprite.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/lib/surface/CocoSprite.coffee b/app/lib/surface/CocoSprite.coffee index 5c082320b..5173c5141 100644 --- a/app/lib/surface/CocoSprite.coffee +++ b/app/lib/surface/CocoSprite.coffee @@ -376,7 +376,7 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass scale *= @options.resolutionFactor if prop is 'registration' pos.x *= scale pos.y *= scale - if @thang + if @thang and prop isnt 'registration' scaleFactor = @thang.scaleFactor ? 1 pos.x *= @thang.scaleFactorX ? scaleFactor pos.y *= @thang.scaleFactorY ? scaleFactor From a4edb76c982bf7eeecb3b03616132feefe6a7f72 Mon Sep 17 00:00:00 2001 From: Scott Erickson <sderickson@gmail.com> Date: Thu, 6 Mar 2014 17:25:40 -0800 Subject: [PATCH 025/100] Fixed a couple of the bugs with marks. --- app/lib/surface/CocoSprite.coffee | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/lib/surface/CocoSprite.coffee b/app/lib/surface/CocoSprite.coffee index 5c082320b..545addaa6 100644 --- a/app/lib/surface/CocoSprite.coffee +++ b/app/lib/surface/CocoSprite.coffee @@ -410,13 +410,14 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass @effectInterval = setInterval @rotateEffect, 1500 else if @effectInterval and @thang.effectNames.length <= 1 - @clearInterval @effectInterval + clearInterval @effectInterval @effectInterval = null @previousEffectNames = @thang.effectNames rotateEffect: => effects = (m.name for m in _.values(@marks) when m.on and m.statusEffect and m.mark) + return unless effects.length effects.sort() @effectIndex ?= 0 @effectIndex = (@effectIndex + 1) % effects.length From 45cd7e31cae2fee22c0870b4b622545f3f0c0d92 Mon Sep 17 00:00:00 2001 From: Michael Schmatz <schmatz@umich.edu> Date: Thu, 6 Mar 2014 18:48:41 -0800 Subject: [PATCH 026/100] Made simulator level-independent --- app/views/play/ladder/my_matches_tab.coffee | 5 +++-- server/queues/scoring.coffee | 24 +++++++++++++-------- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/app/views/play/ladder/my_matches_tab.coffee b/app/views/play/ladder/my_matches_tab.coffee index 00aec7c8c..6b2cf71be 100644 --- a/app/views/play/ladder/my_matches_tab.coffee +++ b/app/views/play/ladder/my_matches_tab.coffee @@ -94,10 +94,11 @@ module.exports = class MyMatchesTabView extends CocoView @setRankingButtonText(button, 'ranking') success = => @setRankingButtonText(button, 'ranked') failure = => @setRankingButtonText(button, 'failed') - + + ajaxData = { session: sessionID, levelID: @level.attributes.original, levelMajorVersion: @level.attributes.version.major } $.ajax '/queue/scoring', { type: 'POST' - data: { session: sessionID } + data: ajaxData success: success failure: failure } diff --git a/server/queues/scoring.coffee b/server/queues/scoring.coffee index c527f921f..dd2a51030 100644 --- a/server/queues/scoring.coffee +++ b/server/queues/scoring.coffee @@ -48,6 +48,9 @@ addPairwiseTaskToQueue = (taskPair, cb) -> module.exports.createNewTask = (req, res) -> requestSessionID = req.body.session + requestLevelID = req.body.levelID + requestLevelMajorVersion = parseInt(req.body.levelMajorVersion) + validatePermissions req, requestSessionID, (error, permissionsAreValid) -> if err? then return errors.serverError res, "There was an error validating permissions" unless permissionsAreValid then return errors.forbidden res, "You do not have the permissions to submit that game to the leaderboard" @@ -60,7 +63,7 @@ module.exports.createNewTask = (req, res) -> updateSessionToSubmit sessionToSubmit, (err, data) -> if err? then return errors.serverError res, "There was an error updating the session" opposingTeam = calculateOpposingTeam(sessionToSubmit.team) - fetchInitialSessionsToRankAgainst opposingTeam, (err, sessionsToRankAgainst) -> + fetchInitialSessionsToRankAgainst opposingTeam,requestLevelID, requestLevelMajorVersion, (err, sessionsToRankAgainst) -> if err? then return errors.serverError res, "There was an error fetching the sessions to rank against" taskPairs = generateTaskPairs(sessionsToRankAgainst, sessionToSubmit) @@ -139,7 +142,10 @@ module.exports.processTaskResult = (req, res) -> opponentID = _.pull(_.keys(newScoresObject), originalSessionID) sessionNewScore = newScoresObject[originalSessionID].totalScore opponentNewScore = newScoresObject[opponentID].totalScore - findNearestBetterSessionID originalSessionID, sessionNewScore, opponentNewScore, opponentID ,opposingTeam, (err, opponentSessionID) -> + + levelOriginalID = levelSession.level.original + levelOriginalMajorVersion = levelSession.level.majorVersion + findNearestBetterSessionID levelOriginalID, levelOriginalMajorVersion, originalSessionID, sessionNewScore, opponentNewScore, opponentID ,opposingTeam, (err, opponentSessionID) -> if err? then return errors.serverError res, "There was an error finding the nearest sessionID!" unless opponentSessionID then return sendResponseObject req, res, {"message":"There were no more games to rank(game is at top!"} @@ -181,7 +187,7 @@ determineIfSessionShouldContinueAndUpdateLog = (sessionID, sessionRank, cb) -> cb null, true -findNearestBetterSessionID = (sessionID, sessionTotalScore, opponentSessionTotalScore, opponentSessionID, opposingTeam, cb) -> +findNearestBetterSessionID = (levelOriginalID, levelMajorVersion, sessionID, sessionTotalScore, opponentSessionTotalScore, opponentSessionID, opposingTeam, cb) -> retrieveAllOpponentSessionIDs sessionID, (err, opponentSessionIDs) -> if err? then return cb err, null @@ -190,8 +196,8 @@ findNearestBetterSessionID = (sessionID, sessionTotalScore, opponentSessionTotal $gt:opponentSessionTotalScore _id: $nin: opponentSessionIDs - "level.original": "52d97ecd32362bc86e004e87" - "level.majorVersion": 0 + "level.original": levelOriginalID + "level.majorVersion": levelMajorVersion submitted: true submittedCode: $exists: true @@ -298,16 +304,16 @@ updateSessionToSubmit = (sessionToUpdate, callback) -> numberOfLosses: 0 LevelSession.update {_id: sessionToUpdate._id}, sessionUpdateObject, callback -fetchInitialSessionsToRankAgainst = (opposingTeam, callback) -> +fetchInitialSessionsToRankAgainst = (opposingTeam, levelID, levelMajorVersion, callback) -> console.log "Fetching sessions to rank against for opposing team #{opposingTeam}" findParameters = - "level.original": "52d97ecd32362bc86e004e87" - "level.majorVersion": 0 + "level.original": levelID + "level.majorVersion": levelMajorVersion submitted: true submittedCode: $exists: true team: opposingTeam - + sortParameters = totalScore: 1 From 6e47416d242d7d050a0f88136a4c4862cd92fb91 Mon Sep 17 00:00:00 2001 From: Nick Winter <livelily@gmail.com> Date: Thu, 6 Mar 2014 19:09:15 -0800 Subject: [PATCH 027/100] Fixed some level IDs. --- app/views/editor/components/main.coffee | 2 ++ app/views/play/level/control_bar_view.coffee | 2 +- app/views/play/level/tome/cast_button_view.coffee | 2 +- app/views/play/level/tome/tome_view.coffee | 1 + 4 files changed, 5 insertions(+), 2 deletions(-) diff --git a/app/views/editor/components/main.coffee b/app/views/editor/components/main.coffee index 87a4ed3b6..fdcc55a6e 100644 --- a/app/views/editor/components/main.coffee +++ b/app/views/editor/components/main.coffee @@ -18,6 +18,7 @@ module.exports = class ThangComponentEditView extends CocoView @callback = options.callback render: => + return if @destroyed for model in [Level, LevelComponent] (new model()).on 'schema-loaded', @render unless model.schema?.loaded if not @componentCollection @@ -35,6 +36,7 @@ module.exports = class ThangComponentEditView extends CocoView @buildAddComponentTreema() onComponentsSync: => + return if @destroyed @supermodel.addCollection @componentCollection @render() diff --git a/app/views/play/level/control_bar_view.coffee b/app/views/play/level/control_bar_view.coffee index 392eb4ebf..06d689f82 100644 --- a/app/views/play/level/control_bar_view.coffee +++ b/app/views/play/level/control_bar_view.coffee @@ -54,7 +54,7 @@ module.exports = class ControlBarView extends View c.ladderGame = @ladderGame c.homeLink = "/" levelID = @level.get('slug') - if levelID in ["brawlwood", "brawlwood-tutorial"] + if levelID in ["brawlwood", "brawlwood-tutorial", "dungeon-arena"] levelID = 'brawlwood' if levelID is 'brawlwood-tutorial' c.homeLink = "/play/ladder/" + levelID c diff --git a/app/views/play/level/tome/cast_button_view.coffee b/app/views/play/level/tome/cast_button_view.coffee index 98c8ab3c8..15d59f194 100644 --- a/app/views/play/level/tome/cast_button_view.coffee +++ b/app/views/play/level/tome/cast_button_view.coffee @@ -35,7 +35,7 @@ module.exports = class CastButtonView extends View # TODO: use a User setting instead of localStorage delay = localStorage.getItem 'autocastDelay' delay ?= 5000 - if @levelID in ['brawlwood', 'brawlwood-tutorial'] + if @levelID in ['brawlwood', 'brawlwood-tutorial', 'dungeon-arena'] delay = 90019001 @setAutocastDelay delay diff --git a/app/views/play/level/tome/tome_view.coffee b/app/views/play/level/tome/tome_view.coffee index f7be355f6..0d18a9f1d 100644 --- a/app/views/play/level/tome/tome_view.coffee +++ b/app/views/play/level/tome/tome_view.coffee @@ -130,6 +130,7 @@ module.exports = class TomeView extends View unless method.cloneOf skipProtectAPI = @getQueryVariable("skip_protect_api") is "true" or @options.levelID isnt 'brawlwood' skipProtectAPI = true # gah, it's so slow :( and somehow still affects simulation + #skipProtectAPI = false if @options.levelID is 'dungeon-arena' skipFlow = @getQueryVariable("skip_flow") is "true" or @options.levelID is 'brawlwood' spell = @spells[spellKey] = new Spell programmableMethod: method, spellKey: spellKey, pathComponents: pathPrefixComponents.concat(pathComponents), session: @options.session, supermodel: @supermodel, skipFlow: skipFlow, skipProtectAPI: skipProtectAPI, worker: @worker for thangID, spellKeys of @thangSpells From d14f8623595a93c036fc4b4231b56016e84a2ec3 Mon Sep 17 00:00:00 2001 From: Scott Erickson <sderickson@gmail.com> Date: Fri, 7 Mar 2014 11:23:06 -0800 Subject: [PATCH 028/100] Ladder view can now be linked to to play against a specific team. --- app/views/play/ladder_view.coffee | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/views/play/ladder_view.coffee b/app/views/play/ladder_view.coffee index 8f6a046de..723a7910d 100644 --- a/app/views/play/ladder_view.coffee +++ b/app/views/play/ladder_view.coffee @@ -66,6 +66,7 @@ module.exports = class LadderView extends RootView @insertSubView(@ladderTab = new LadderTabView({}, @level, @sessions)) @insertSubView(@myMatchesTab = new MyMatchesTabView({}, @level, @sessions)) setInterval(@fetchSessionsAndRefreshViews.bind(@), 10000) + @showPlayModal(document.location.hash[1..]) if document.location.hash and @sessions.loaded fetchSessionsAndRefreshViews: -> @sessions.fetch({"success": @refreshViews}) @@ -112,8 +113,9 @@ module.exports = class LadderView extends RootView $("#simulation-status-text").text @simulationStatus onClickPlayButton: (e) -> - button = $(e.target).closest('.play-button') - teamID = button.data('team') + @showPlayModal($(e.target).closest('.play-button').data('team')) + + showPlayModal: (teamID) -> session = (s for s in @sessions.models when s.get('team') is teamID)[0] modal = new LadderPlayModal({}, @level, session, teamID) @openModalView modal From 5274f22467293101147095bed8d512f18d6dab50 Mon Sep 17 00:00:00 2001 From: Shrihari <gfxindia@gmail.com> Date: Sat, 8 Mar 2014 01:10:12 +0530 Subject: [PATCH 029/100] Fixed Spell Debug View width bug --- app/styles/play/level/tome/spell_debug.sass | 1 + app/views/play/level/tome/spell_debug_view.coffee | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/app/styles/play/level/tome/spell_debug.sass b/app/styles/play/level/tome/spell_debug.sass index d0f97a28f..e02326b55 100644 --- a/app/styles/play/level/tome/spell_debug.sass +++ b/app/styles/play/level/tome/spell_debug.sass @@ -3,6 +3,7 @@ .spell-debug-view position: absolute z-index: 9001 + min-width: 250px max-width: 400px padding: 10px background: transparent url(/images/level/popover_background.png) diff --git a/app/views/play/level/tome/spell_debug_view.coffee b/app/views/play/level/tome/spell_debug_view.coffee index 5f4cfdd30..c35314932 100644 --- a/app/views/play/level/tome/spell_debug_view.coffee +++ b/app/views/play/level/tome/spell_debug_view.coffee @@ -61,7 +61,9 @@ module.exports = class DebugView extends View @variableChain = chain offsetX = e.domEvent.offsetX ? e.clientX - $(e.domEvent.target).offset().left offsetY = e.domEvent.offsetY ? e.clientY - $(e.domEvent.target).offset().top - @pos = {left: offsetX + 50, top: offsetY + 50} + w = $(document).width() + offsetX = w - $(e.domEvent.target).offset().left - 300 if e.clientX + 300 > w + @pos = {left: offsetX + 50, top: offsetY + 20} @markerRange = new Range pos.row, start, pos.row, end else @variableChain = @markerRange = null From 610fc0694bbf6b2750d64e10d7284b52dde8c111 Mon Sep 17 00:00:00 2001 From: Scott Erickson <sderickson@gmail.com> Date: Fri, 7 Mar 2014 11:57:15 -0800 Subject: [PATCH 030/100] Turning off view caching by default now. Should help lower the memory footprint. --- app/lib/Router.coffee | 2 +- app/views/kinds/CocoView.coffee | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/lib/Router.coffee b/app/lib/Router.coffee index 435fdb7d4..db70b8c74 100644 --- a/app/lib/Router.coffee +++ b/app/lib/Router.coffee @@ -90,7 +90,7 @@ module.exports = class CocoRouter extends Backbone.Router @cache[route].fromCache = true return @cache[route] view = @getView(route) - @cache[route] = view unless view and view.cache is false + @cache[route] = view if view?.cache return view routeDirectly: (path, args) -> diff --git a/app/views/kinds/CocoView.coffee b/app/views/kinds/CocoView.coffee index 2811318c6..dbdb14146 100644 --- a/app/views/kinds/CocoView.coffee +++ b/app/views/kinds/CocoView.coffee @@ -10,7 +10,7 @@ makeScopeName = -> "view-scope-#{classCount++}" module.exports = class CocoView extends Backbone.View startsLoading: false - cache: true # signals to the router to keep this view around + cache: false # signals to the router to keep this view around template: -> '' events: From cf2a0a999e63e8e45ea00d03d454d9440ab4e7da Mon Sep 17 00:00:00 2001 From: Scott Erickson <sderickson@gmail.com> Date: Fri, 7 Mar 2014 11:57:31 -0800 Subject: [PATCH 031/100] Set up the ladder play modal to point to the tutorial level dynamically. --- app/templates/play/ladder/play_modal.jade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/templates/play/ladder/play_modal.jade b/app/templates/play/ladder/play_modal.jade index 6c1cd22d6..5e7953896 100644 --- a/app/templates/play/ladder/play_modal.jade +++ b/app/templates/play/ladder/play_modal.jade @@ -8,7 +8,7 @@ block modal-body-content p.tutorial-suggestion span Not sure what's going on? | - a(href="/play/level/brawlwood-tutorial") Play the tutorial first. + a(href="/play/level/#{levelID}-tutorial") Play the tutorial first. a(href="/play/level/#{levelID}?team=#{teamID}") div.play-option From 4e24e2a2442ef04a3c8b9ddfbf844727b93cf874 Mon Sep 17 00:00:00 2001 From: Alexei Nikitin <mr-a1@yandex.ru> Date: Fri, 7 Mar 2014 12:10:25 -0800 Subject: [PATCH 032/100] Update ru.coffee --- app/locale/ru.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/locale/ru.coffee b/app/locale/ru.coffee index 51f6d1a7e..dcc68a5a2 100644 --- a/app/locale/ru.coffee +++ b/app/locale/ru.coffee @@ -200,7 +200,7 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi tome_available_spells: "Доступные заклинания" hud_continue: "Продолжить (нажмите Shift+Пробел)" spell_saved: "Заклинание сохранено" -# skip_tutorial: "Skip (esc)" + skip_tutorial: "Пропуск (Esc)"y admin: av_title: "Админ панель" @@ -298,7 +298,7 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi practices_title: "Лучшие уважаемые практики" practices_description: "Это наши обещания тебе, игрок, менее юридическим языком." privacy_title: "Конфиденциальность" - privacy_description: "Мы не будем продавать какой-либо личной информации. Мы намерены заработать деньги с помощью рекрутинга в конечном счете, но будьте уверены, мы не будем распространять вашу личную информацию заинтересованным компаниям без вашего явного согласия." + privacy_description: "Мы не будем продавать какой-либо личной информации. Мы намерены заработать деньги с помощью рекрутинга в конечном счёте, но будьте уверены, мы не будем распространять вашу личную информацию заинтересованным компаниям без вашего явного согласия." security_title: "Безопасность" security_description: "Мы стремимся сохранить вашу личную информацию в безопасности. Как проект с открытым исходным кодом, наш сайт в свободном доступе для всех для пересмотра и совершенствования систем безопасности." email_title: "Email" From 517ef8611bc4eabb7d2e833534cb5286c785d2a1 Mon Sep 17 00:00:00 2001 From: Alexei Nikitin <mr-a1@yandex.ru> Date: Fri, 7 Mar 2014 12:13:15 -0800 Subject: [PATCH 033/100] Update ru.coffee --- app/locale/ru.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/locale/ru.coffee b/app/locale/ru.coffee index dcc68a5a2..0ee54cb73 100644 --- a/app/locale/ru.coffee +++ b/app/locale/ru.coffee @@ -200,7 +200,7 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi tome_available_spells: "Доступные заклинания" hud_continue: "Продолжить (нажмите Shift+Пробел)" spell_saved: "Заклинание сохранено" - skip_tutorial: "Пропуск (Esc)"y + skip_tutorial: "Пропуск (Esc)" admin: av_title: "Админ панель" From d8d917ecf9e52b742ee66ed2576ec8353fb0c6a0 Mon Sep 17 00:00:00 2001 From: Scott Erickson <sderickson@gmail.com> Date: Fri, 7 Mar 2014 12:15:16 -0800 Subject: [PATCH 034/100] Set up better teardown for the ladder view. --- app/lib/CocoClass.coffee | 1 + app/lib/simulator/Simulator.coffee | 7 ++++++- app/views/kinds/CocoView.coffee | 1 + app/views/play/ladder_view.coffee | 7 ++++++- 4 files changed, 14 insertions(+), 2 deletions(-) diff --git a/app/lib/CocoClass.coffee b/app/lib/CocoClass.coffee index 22dc25f64..67a8e4cac 100644 --- a/app/lib/CocoClass.coffee +++ b/app/lib/CocoClass.coffee @@ -20,6 +20,7 @@ module.exports = class CocoClass destroy: -> # teardown subscriptions, prevent new ones @stopListening?() + @off() @unsubscribeAll() @stopListeningToShortcuts() @[key] = undefined for key of @ diff --git a/app/lib/simulator/Simulator.coffee b/app/lib/simulator/Simulator.coffee index f92bccb98..f41c0acb3 100644 --- a/app/lib/simulator/Simulator.coffee +++ b/app/lib/simulator/Simulator.coffee @@ -10,6 +10,11 @@ module.exports = class Simulator @trigger 'statusUpdate', 'Starting simulation!' @retryDelayInSeconds = 10 @taskURL = '/queue/scoring' + + destroy: -> + @off() + @cleanupSimulation() + # TODO: More teardown? fetchAndSimulateTask: => @trigger 'statusUpdate', 'Fetching simulation data!' @@ -99,7 +104,7 @@ module.exports = class Simulator @fetchAndSimulateTask() cleanupSimulation: -> - @god.destroy() + @god?.destroy() @god = null @world = null @level = null diff --git a/app/views/kinds/CocoView.coffee b/app/views/kinds/CocoView.coffee index dbdb14146..806a2d25c 100644 --- a/app/views/kinds/CocoView.coffee +++ b/app/views/kinds/CocoView.coffee @@ -37,6 +37,7 @@ module.exports = class CocoView extends Backbone.View destroy: -> @stopListening() + @off() @stopListeningToShortcuts() @undelegateEvents() # removes both events and subs view.destroy() for id, view of @subviews diff --git a/app/views/play/ladder_view.coffee b/app/views/play/ladder_view.coffee index 723a7910d..81e2f6558 100644 --- a/app/views/play/ladder_view.coffee +++ b/app/views/play/ladder_view.coffee @@ -65,7 +65,7 @@ module.exports = class LadderView extends RootView return if @startsLoading @insertSubView(@ladderTab = new LadderTabView({}, @level, @sessions)) @insertSubView(@myMatchesTab = new MyMatchesTabView({}, @level, @sessions)) - setInterval(@fetchSessionsAndRefreshViews.bind(@), 10000) + @refreshInterval = setInterval(@fetchSessionsAndRefreshViews.bind(@), 10000) @showPlayModal(document.location.hash[1..]) if document.location.hash and @sessions.loaded fetchSessionsAndRefreshViews: -> @@ -119,3 +119,8 @@ module.exports = class LadderView extends RootView session = (s for s in @sessions.models when s.get('team') is teamID)[0] modal = new LadderPlayModal({}, @level, session, teamID) @openModalView modal + + destroy: -> + clearInterval @refreshInterval + @simulator.destroy() + super() From 317ae5cc329a1318874468eddebd2d99f6debd23 Mon Sep 17 00:00:00 2001 From: Alexei Nikitin <mr-a1@yandex.ru> Date: Fri, 7 Mar 2014 12:17:32 -0800 Subject: [PATCH 035/100] Update scribe.jade --- app/templates/contribute/scribe.jade | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/templates/contribute/scribe.jade b/app/templates/contribute/scribe.jade index 9bd404ad2..eafab49b6 100644 --- a/app/templates/contribute/scribe.jade +++ b/app/templates/contribute/scribe.jade @@ -16,7 +16,7 @@ block content span span(data-i18n="classes.scribe_title_description") (Article Editor) p - span(data-i18n="account_settings.scribe_introduction_pref") + span(data-i18n="contribute.scribe_introduction_pref") | CodeCombat isn't just going to be a bunch of levels. | It will also include a resource for knowledge, a wiki of programming concepts that levels can hook into. | That way rather than each Artisan having to describe in detail what a comparison operator is, they @@ -24,7 +24,7 @@ block content | Something along the lines of what the a(href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide", data-i18n="contribute.scribe_introduction_url_mozilla") | Mozilla Developer Network - span(data-i18n="account_settings.scribe_introduction_suf") + span(data-i18n="contribute.scribe_introduction_suf") | has built. If your idea of fun is articulating the concepts of programming in Markdown form, | then this class might be for you. @@ -77,4 +77,4 @@ block content li mattinsler - div.clearfix \ No newline at end of file + div.clearfix From b002ae376b06f9e91cf59a5f7c912b73e3640a20 Mon Sep 17 00:00:00 2001 From: Alexei Nikitin <mr-a1@yandex.ru> Date: Fri, 7 Mar 2014 12:19:14 -0800 Subject: [PATCH 036/100] Update counselor.jade --- app/templates/contribute/counselor.jade | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/templates/contribute/counselor.jade b/app/templates/contribute/counselor.jade index 821a86694..36a79d8cc 100644 --- a/app/templates/contribute/counselor.jade +++ b/app/templates/contribute/counselor.jade @@ -38,7 +38,7 @@ block content h4(data-i18n="contribute.how_to_join") How to Join p - a(title='Contact', tabindex=-1, data-toggle="coco-modal", data-target="modal/contact", data-i18n="contact_us_url") + a(title='Contact', tabindex=-1, data-toggle="coco-modal", data-target="modal/contact", data-i18n="contribute.contact_us_url") | Contact us span , span(data-i18n="contribute.counselor_join_desc") @@ -46,4 +46,4 @@ block content | be interested in doing. We'll put you in our contact list and be in touch | when we could use advice (not too often). - div.clearfix \ No newline at end of file + div.clearfix From 535881b6373f02e8f22dfa083a8da7196de50a25 Mon Sep 17 00:00:00 2001 From: Alexei Nikitin <mr-a1@yandex.ru> Date: Fri, 7 Mar 2014 12:20:17 -0800 Subject: [PATCH 037/100] Update ambassador.jade --- app/templates/contribute/ambassador.jade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/templates/contribute/ambassador.jade b/app/templates/contribute/ambassador.jade index ef17d2735..dc1048ac6 100644 --- a/app/templates/contribute/ambassador.jade +++ b/app/templates/contribute/ambassador.jade @@ -31,7 +31,7 @@ block content h4(data-i18n="contribute.how_to_join") How to Join p - a(title='Contact', tabindex=-1, data-toggle="coco-modal", data-target="modal/contact", data-i18n="contact_us_url") + a(title='Contact', tabindex=-1, data-toggle="coco-modal", data-target="modal/contact", data-i18n="contribute.contact_us_url") | Contact us span , span(data-i18n="contribute.ambassador_join_desc") From 16f2379af9afce26e287c9baa6b36a26acd5c29e Mon Sep 17 00:00:00 2001 From: Alexei Nikitin <mr-a1@yandex.ru> Date: Fri, 7 Mar 2014 12:27:12 -0800 Subject: [PATCH 038/100] Update ru.coffee --- app/locale/ru.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/locale/ru.coffee b/app/locale/ru.coffee index 0ee54cb73..1fdd72d87 100644 --- a/app/locale/ru.coffee +++ b/app/locale/ru.coffee @@ -400,7 +400,7 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi more_about_adventurer: "Узнать больше о том, как стать Искателем приключений" adventurer_subscribe_desc: "Получать email-ы при появлении новых уровней для тестирования." scribe_summary_pref: "CodeCombat будет не просто кучей уровней. Он также будет ресурсом знаний в области программирования, к которому игроки могут присоединиться. Таким образом, каждый Ремесленник может ссылаться на подробную статью для назидания игрока: документация сродни тому, что создана " - scribe_summary_sufx: ". Если вам нравится объяснять концепции программирования, этот класс для вас." + scribe_summary_suf: ". Если вам нравится объяснять концепции программирования, этот класс для вас." scribe_introduction_pref: "CodeCombat будет не просто кучей уровней. Он также включает в себя ресурс для познания, вики концепций программирования, которые уровни могут включать. Таким образом, вместо того, чтобы каждому Ремесленнику необходимо было подробно описывать, что такое оператор сравнения, они могут просто связать их уровень с уже написанной в назидание игрокам статьёй, описывающей их. Что-то по аналогии с " scribe_introduction_url_mozilla: "Mozilla Developer Network" scribe_introduction_suf: ". Если ваше представление о веселье это формулирование концепций программирования в форме Markdown, этот класс для вас." From a3afa802d9eb5068af4e9b3239b74663d4f69ea9 Mon Sep 17 00:00:00 2001 From: Alexei Nikitin <mr-a1@yandex.ru> Date: Fri, 7 Mar 2014 12:33:18 -0800 Subject: [PATCH 039/100] Update ru.coffee --- app/locale/ru.coffee | 30 ++---------------------------- 1 file changed, 2 insertions(+), 28 deletions(-) diff --git a/app/locale/ru.coffee b/app/locale/ru.coffee index 1fdd72d87..8f2189174 100644 --- a/app/locale/ru.coffee +++ b/app/locale/ru.coffee @@ -129,6 +129,7 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi new_password: "Новый пароль" new_password_verify: "Подтверждение пароля" email_subscriptions: "Email-подписки" + email_notifications: "Уведомления" email_announcements: "Оповещения" email_notifications_description: "Получать периодические уведомления для вашего аккаунта." email_announcements_description: "Получать email-оповещения о последних новостях CodeCombat." @@ -429,31 +430,4 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi ambassador_subscribe_desc: "Получать email-ы о разработке мультиплеера и обновлениях в системе поддержки." counselor_summary: "Ни одна из вышеупомянутых ролей не соответствует тому, в чём вы заинтересованы? Не волнуйтесь, мы в поисках тех, кто хочет приложить руку к разработке CodeCombat! Если вы заинтересованы в обучении, разработке игр, управлением проектами с открытым исходным кодом, или в чём-нибудь ещё, что, как вы думаете, будет актуально для нас, то этот класс для вас." counselor_introduction_1: "У вас есть жизненный опыт? Другая точка зрения на вещи, которые могут помочь нам решить, как формировать CodeCombat? Из всех этих ролей, эта, возможно, займёт меньше всего времени, но по отдельности, вы можете сделать наибольшие изменения. Мы в поисках морщинистых мудрецов, особенно в таких областях, как: обучение, разработка игр, управление проектами с открытым исходным кодом, технической рекрутинг, предпринимательство или дизайн." - counselor_introduction_2: "Или действительно всё, что имеет отношение к развитию CodeCombat. Если у вас есть знания и вы хотите поделиться ими, чтобы помочь вырастить этот проект, то этот класс для вас." - counselor_attribute_1: "Опыт, в любой из областей выше, или в том, что, как вы думаете, может быть полезным." - counselor_attribute_2: "Немного свободного времени!" - counselor_join_desc: "расскажите нам немного о себе, чем вы занимались и чем хотели бы заниматься. Мы поместим вас в наш список контактов и выйдем на связь, когда нам понадобится совет(не слишком часто)." - more_about_counselor: "Узнать больше о том, как стать Советником" - changes_auto_save: "Изменения сохраняются автоматически при переключении флажков." - diligent_scribes: "Наши старательные Писари:" - powerful_archmages: "Наши могущественные Архимаги:" - creative_artisans: "Наши творческие Ремесленники:" - brave_adventurers: "Наши отважные Искатели приключений:" - translating_diplomats: "Наши переводящие Дипломаты:" - helpful_ambassadors: "Наши полезные Послы:" - - classes: - archmage_title: "Архимаг" - archmage_title_description: "(программист)" - artisan_title: "Ремесленник" - artisan_title_description: "(создатель уровней)" - adventurer_title: "Искатель приключений" - adventurer_title_description: "(тестировщик уровней)" - scribe_title: "Писарь" - scribe_title_description: "(редактор статей)" - diplomat_title: "Дипломат" - diplomat_title_description: "(переводчик)" - ambassador_title: "Посол" - ambassador_title_description: "(поддержка)" - counselor_title: "Советник" - counselor_title_description: "(эксперт/учитель)" + counselor_introduction_2: "Или действительно всё, что имеет отношение к развитию CodeCombat From 683c578dcbc0e5bcffff0d38b1c313b09a514e85 Mon Sep 17 00:00:00 2001 From: Alexei Nikitin <mr-a1@yandex.ru> Date: Fri, 7 Mar 2014 12:36:17 -0800 Subject: [PATCH 040/100] Fix lolwtf deleting --- app/locale/ru.coffee | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/app/locale/ru.coffee b/app/locale/ru.coffee index 8f2189174..36bcf1ac1 100644 --- a/app/locale/ru.coffee +++ b/app/locale/ru.coffee @@ -430,4 +430,31 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi ambassador_subscribe_desc: "Получать email-ы о разработке мультиплеера и обновлениях в системе поддержки." counselor_summary: "Ни одна из вышеупомянутых ролей не соответствует тому, в чём вы заинтересованы? Не волнуйтесь, мы в поисках тех, кто хочет приложить руку к разработке CodeCombat! Если вы заинтересованы в обучении, разработке игр, управлением проектами с открытым исходным кодом, или в чём-нибудь ещё, что, как вы думаете, будет актуально для нас, то этот класс для вас." counselor_introduction_1: "У вас есть жизненный опыт? Другая точка зрения на вещи, которые могут помочь нам решить, как формировать CodeCombat? Из всех этих ролей, эта, возможно, займёт меньше всего времени, но по отдельности, вы можете сделать наибольшие изменения. Мы в поисках морщинистых мудрецов, особенно в таких областях, как: обучение, разработка игр, управление проектами с открытым исходным кодом, технической рекрутинг, предпринимательство или дизайн." - counselor_introduction_2: "Или действительно всё, что имеет отношение к развитию CodeCombat + counselor_introduction_2: "Или действительно всё, что имеет отношение к развитию CodeCombat. Если у вас есть знания и вы хотите поделиться ими, чтобы помочь вырастить этот проект, то этот класс для вас." + counselor_attribute_1: "Опыт, в любой из областей выше, или в том, что, как вы думаете, может быть полезным." + counselor_attribute_2: "Немного свободного времени!" + counselor_join_desc: "расскажите нам немного о себе, чем вы занимались и чем хотели бы заниматься. Мы поместим вас в наш список контактов и выйдем на связь, когда нам понадобится совет(не слишком часто)." + more_about_counselor: "Узнать больше о том, как стать Советником" + changes_auto_save: "Изменения сохраняются автоматически при переключении флажков." + diligent_scribes: "Наши старательные Писари:" + powerful_archmages: "Наши могущественные Архимаги:" + creative_artisans: "Наши творческие Ремесленники:" + brave_adventurers: "Наши отважные Искатели приключений:" + translating_diplomats: "Наши переводящие Дипломаты:" + helpful_ambassadors: "Наши полезные Послы:" + + classes: + archmage_title: "Архимаг" + archmage_title_description: "(программист)" + artisan_title: "Ремесленник" + artisan_title_description: "(создатель уровней)" + adventurer_title: "Искатель приключений" + adventurer_title_description: "(тестировщик уровней)" + scribe_title: "Писарь" + scribe_title_description: "(редактор статей)" + diplomat_title: "Дипломат" + diplomat_title_description: "(переводчик)" + ambassador_title: "Посол" + ambassador_title_description: "(поддержка)" + counselor_title: "Советник" + counselor_title_description: "(эксперт/учитель)" From be034855385fe83b706385547a9755a5e04fa32b Mon Sep 17 00:00:00 2001 From: Scott Erickson <sderickson@gmail.com> Date: Fri, 7 Mar 2014 12:58:17 -0800 Subject: [PATCH 041/100] Set up the ladder play modal to be more forthright getting new players to play the tutorial. --- app/styles/play/ladder/play_modal.sass | 7 ++ app/templates/play/ladder/play_modal.jade | 119 ++++++++++++---------- app/views/play/ladder/play_modal.coffee | 17 ++++ 3 files changed, 88 insertions(+), 55 deletions(-) diff --git a/app/styles/play/ladder/play_modal.sass b/app/styles/play/ladder/play_modal.sass index 7d87d475b..465052114 100644 --- a/app/styles/play/ladder/play_modal.sass +++ b/app/styles/play/ladder/play_modal.sass @@ -1,7 +1,14 @@ #ladder-play-modal + #noob-view p + font-size: 30px + + #skip-tutorial-button + font-size: 16px + .tutorial-suggestion text-align: center font-size: 18px + margin: 10px 0 30px .play-option margin-bottom: 15px diff --git a/app/templates/play/ladder/play_modal.jade b/app/templates/play/ladder/play_modal.jade index 5e7953896..25d492b6f 100644 --- a/app/templates/play/ladder/play_modal.jade +++ b/app/templates/play/ladder/play_modal.jade @@ -5,68 +5,77 @@ block modal-header-content block modal-body-content - p.tutorial-suggestion - span Not sure what's going on? - | - a(href="/play/level/#{levelID}-tutorial") Play the tutorial first. + div#noob-view.secret + a(href="/play/level/#{levelID}-tutorial").btn.btn-success.btn-block.btn-lg + p + strong Play Tutorial + span Recommended if you've never played before + span.btn.btn-primary.btn-block.btn-lg#skip-tutorial-button Skip Tutorial - a(href="/play/level/#{levelID}?team=#{teamID}") - div.play-option - img(src=myPortrait).my-icon.only-one - img(src="/images/pages/play/ladder/"+teamID+"_ladder_tutorial.png", style="border: 1px solid #{teamColor}; background: #{teamBackgroundColor}").my-team-icon.img-circle.only-one - img(src=genericPortrait).opponent-icon - img(src="/images/pages/play/ladder/"+otherTeamID+"_ladder_tutorial.png", style="border: 1px solid #{opponentTeamColor}; background: #{opponentTeamBackgroundColor}").opponent-team-icon.img-circle - div.my-name.name-label.only-one - span= myName - div.opponent-name.name-label - span Simple AI - div.difficulty - span Warmup - div.vs VS + div#normal-view - if challengers.easy - a(href="/play/level/#{levelID}?team=#{teamID}&opponent=#{challengers.easy.sessionID}") - div.play-option.easy-option + p.tutorial-suggestion + strong Not sure what's going on? + | + a(href="/play/level/#{levelID}-tutorial") Play the tutorial first. + + a(href="/play/level/#{levelID}?team=#{teamID}") + div.play-option img(src=myPortrait).my-icon.only-one - img(src="/images/pages/play/ladder/"+teamID+"_ladder_easy.png", style="border: 1px solid #{teamColor}; background: #{teamBackgroundColor}").my-team-icon.img-circle.only-one - img(src=challengers.easy.opponentImageSource||genericPortrait).opponent-icon - img(src="/images/pages/play/ladder/"+otherTeamID+"_ladder_easy.png", style="border: 1px solid #{opponentTeamColor}; background: #{opponentTeamBackgroundColor}").opponent-team-icon.img-circle + img(src="/images/pages/play/ladder/"+teamID+"_ladder_tutorial.png", style="border: 1px solid #{teamColor}; background: #{teamBackgroundColor}").my-team-icon.img-circle.only-one + img(src=genericPortrait).opponent-icon + img(src="/images/pages/play/ladder/"+otherTeamID+"_ladder_tutorial.png", style="border: 1px solid #{opponentTeamColor}; background: #{opponentTeamBackgroundColor}").opponent-team-icon.img-circle div.my-name.name-label.only-one span= myName div.opponent-name.name-label - span= challengers.easy.opponentName + span Simple AI div.difficulty - span Easy - div.vs VS - - if challengers.medium - a(href="/play/level/#{levelID}?team=#{teamID}&opponent=#{challengers.medium.sessionID}") - div.play-option.medium-option - img(src=myPortrait).my-icon.only-one - img(src="/images/pages/play/ladder/"+teamID+"_ladder_medium.png", style="border: 1px solid #{teamColor}; background: #{teamBackgroundColor}").my-team-icon.img-circle.only-one - img(src=challengers.medium.opponentImageSource||genericPortrait).opponent-icon - img(src="/images/pages/play/ladder/"+otherTeamID+"_ladder_medium.png", style="border: 1px solid #{opponentTeamColor}; background: #{opponentTeamBackgroundColor}").opponent-team-icon.img-circle - div.my-name.name-label.only-one - span= myName - div.opponent-name.name-label - span= challengers.medium.opponentName - div.difficulty - span Medium - div.vs VS - - if challengers.hard - a(href="/play/level/#{levelID}?team=#{teamID}&opponent=#{challengers.hard.sessionID}") - div.play-option.hard-option - img(src=myPortrait).my-icon.only-one - img(src="/images/pages/play/ladder/"+teamID+"_ladder_hard.png", style="border: 1px solid #{teamColor}; background: #{teamBackgroundColor}").my-team-icon.img-circle.only-one - img(src=challengers.hard.opponentImageSource||genericPortrait).opponent-icon - img(src="/images/pages/play/ladder/"+otherTeamID+"_ladder_hard.png", style="border: 1px solid #{opponentTeamColor}; background: #{opponentTeamBackgroundColor}").opponent-team-icon.img-circle - div.my-name.name-label.only-one - span= myName - div.opponent-name.name-label - span= challengers.hard.opponentName - div.difficulty - span Hard + span Warmup div.vs VS + + if challengers.easy + a(href="/play/level/#{levelID}?team=#{teamID}&opponent=#{challengers.easy.sessionID}") + div.play-option.easy-option + img(src=myPortrait).my-icon.only-one + img(src="/images/pages/play/ladder/"+teamID+"_ladder_easy.png", style="border: 1px solid #{teamColor}; background: #{teamBackgroundColor}").my-team-icon.img-circle.only-one + img(src=challengers.easy.opponentImageSource||genericPortrait).opponent-icon + img(src="/images/pages/play/ladder/"+otherTeamID+"_ladder_easy.png", style="border: 1px solid #{opponentTeamColor}; background: #{opponentTeamBackgroundColor}").opponent-team-icon.img-circle + div.my-name.name-label.only-one + span= myName + div.opponent-name.name-label + span= challengers.easy.opponentName + div.difficulty + span Easy + div.vs VS + + if challengers.medium + a(href="/play/level/#{levelID}?team=#{teamID}&opponent=#{challengers.medium.sessionID}") + div.play-option.medium-option + img(src=myPortrait).my-icon.only-one + img(src="/images/pages/play/ladder/"+teamID+"_ladder_medium.png", style="border: 1px solid #{teamColor}; background: #{teamBackgroundColor}").my-team-icon.img-circle.only-one + img(src=challengers.medium.opponentImageSource||genericPortrait).opponent-icon + img(src="/images/pages/play/ladder/"+otherTeamID+"_ladder_medium.png", style="border: 1px solid #{opponentTeamColor}; background: #{opponentTeamBackgroundColor}").opponent-team-icon.img-circle + div.my-name.name-label.only-one + span= myName + div.opponent-name.name-label + span= challengers.medium.opponentName + div.difficulty + span Medium + div.vs VS + + if challengers.hard + a(href="/play/level/#{levelID}?team=#{teamID}&opponent=#{challengers.hard.sessionID}") + div.play-option.hard-option + img(src=myPortrait).my-icon.only-one + img(src="/images/pages/play/ladder/"+teamID+"_ladder_hard.png", style="border: 1px solid #{teamColor}; background: #{teamBackgroundColor}").my-team-icon.img-circle.only-one + img(src=challengers.hard.opponentImageSource||genericPortrait).opponent-icon + img(src="/images/pages/play/ladder/"+otherTeamID+"_ladder_hard.png", style="border: 1px solid #{opponentTeamColor}; background: #{opponentTeamBackgroundColor}").opponent-team-icon.img-circle + div.my-name.name-label.only-one + span= myName + div.opponent-name.name-label + span= challengers.hard.opponentName + div.difficulty + span Hard + div.vs VS block modal-footer \ No newline at end of file diff --git a/app/views/play/ladder/play_modal.coffee b/app/views/play/ladder/play_modal.coffee index 28f689b14..e5d83fc3b 100644 --- a/app/views/play/ladder/play_modal.coffee +++ b/app/views/play/ladder/play_modal.coffee @@ -10,6 +10,10 @@ module.exports = class LadderPlayModal extends View template: template closeButton: true startsLoading: true + @shownTutorialButton: false + + events: + 'click #skip-tutorial-button': 'hideTutorialButtons' constructor: (options, @level, @session, @team) -> super(options) @@ -56,6 +60,7 @@ module.exports = class LadderPlayModal extends View finishRendering: -> @startsLoading = false @render() + @maybeShowTutorialButtons() getRenderData: -> ctx = super() @@ -87,6 +92,18 @@ module.exports = class LadderPlayModal extends View ctx.myName = me.get('name') || 'Newcomer' ctx + + maybeShowTutorialButtons: -> + return if @session or LadderPlayModal.shownTutorialButton + @$el.find('#normal-view').addClass('secret') + @$el.find('.modal-header').addClass('secret') + @$el.find('#noob-view').removeClass('secret') + LadderPlayModal.shownTutorialButton = true + + hideTutorialButtons: -> + @$el.find('#normal-view').removeClass('secret') + @$el.find('.modal-header').removeClass('secret') + @$el.find('#noob-view').addClass('secret') # Choosing challengers From 17231bce06f7c37adfc89f462ea9a9d75c1241b8 Mon Sep 17 00:00:00 2001 From: Scott Erickson <sderickson@gmail.com> Date: Fri, 7 Mar 2014 13:13:58 -0800 Subject: [PATCH 042/100] Added showsGuide and type properties to level. --- app/views/editor/level/settings_tab_view.coffee | 7 ++++++- server/levels/level_schema.coffee | 3 ++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/app/views/editor/level/settings_tab_view.coffee b/app/views/editor/level/settings_tab_view.coffee index 4e2518be6..7a1290db1 100644 --- a/app/views/editor/level/settings_tab_view.coffee +++ b/app/views/editor/level/settings_tab_view.coffee @@ -8,7 +8,12 @@ module.exports = class SettingsTabView extends View id: 'editor-level-settings-tab-view' className: 'tab-pane' template: template - editableSettings: ['name', 'description', 'documentation', 'nextLevel', 'background', 'victory', 'i18n', 'icon', 'goals'] # not thangs or scripts or the backend stuff + + # not thangs or scripts or the backend stuff + editableSettings: [ + 'name', 'description', 'documentation', 'nextLevel', 'background', 'victory', 'i18n', 'icon', 'goals', + 'type', 'showsGuide' + ] subscriptions: 'level-loaded': 'onLevelLoaded' diff --git a/server/levels/level_schema.coffee b/server/levels/level_schema.coffee index d0d448267..cbfb84497 100644 --- a/server/levels/level_schema.coffee +++ b/server/levels/level_schema.coffee @@ -226,7 +226,8 @@ _.extend LevelSchema.properties, i18n: {type: "object", format: 'i18n', props: ['name', 'description'], description: "Help translate this level"} icon: { type: 'string', format: 'image-file', title: 'Icon' } goals: c.array {title: 'Goals', description: 'An array of goals which are visible to the player and can trigger scripts.'}, GoalSchema - + type: c.shortString(title: "Type", description: "What kind of level this is.", "enum": ['campaign', 'ladder']) + showsGuide: c.shortString(title: "Shows Guide", description: "If the guide is shown at the beginning of the level.", "enum": ['first-time', 'always']) c.extendBasicProperties LevelSchema, 'level' c.extendSearchableProperties LevelSchema From 2f68e64dc5d728ea96203f0fc9fa3c7474c6f090 Mon Sep 17 00:00:00 2001 From: Scott Erickson <sderickson@gmail.com> Date: Fri, 7 Mar 2014 13:14:27 -0800 Subject: [PATCH 043/100] Almost forgot to add to the level handler the new properties. --- server/levels/level_handler.coffee | 2 ++ 1 file changed, 2 insertions(+) diff --git a/server/levels/level_handler.coffee b/server/levels/level_handler.coffee index 0cf0a2066..da79be8d6 100644 --- a/server/levels/level_handler.coffee +++ b/server/levels/level_handler.coffee @@ -20,6 +20,8 @@ LevelHandler = class LevelHandler extends Handler 'i18n' 'icon' 'goals' + 'type' + 'showsGuide' ] postEditableProperties: ['name'] From 990a2e34e56394ee93f6d5238226b6899ccc0d7f Mon Sep 17 00:00:00 2001 From: Scott Erickson <sderickson@gmail.com> Date: Fri, 7 Mar 2014 15:15:49 -0800 Subject: [PATCH 044/100] Utilizing the new level type property in the level handler. --- server/levels/level_handler.coffee | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/server/levels/level_handler.coffee b/server/levels/level_handler.coffee index da79be8d6..94bf33dd8 100644 --- a/server/levels/level_handler.coffee +++ b/server/levels/level_handler.coffee @@ -49,10 +49,11 @@ LevelHandler = class LevelHandler extends Handler majorVersion: level.version.major creator: req.user.id - # TODO: generalize this for levels that need teams if req.query.team? sessionQuery.team = req.query.team - else if level.name is 'Project DotA' + + # TODO: generalize this for levels based on their teams + else if level.get('type') is 'ladder' sessionQuery.team = 'humans' Session.findOne(sessionQuery).exec (err, doc) => From 13b9a9cb5853bbe256c35d9d94ea8f8906a5bc36 Mon Sep 17 00:00:00 2001 From: Scott Erickson <sderickson@gmail.com> Date: Fri, 7 Mar 2014 15:16:13 -0800 Subject: [PATCH 045/100] Added dungeon arena tutorial to a few hardcoded places. --- app/views/play/level/control_bar_view.coffee | 2 +- app/views/play/level/tome/cast_button_view.coffee | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/play/level/control_bar_view.coffee b/app/views/play/level/control_bar_view.coffee index 06d689f82..bae8753b6 100644 --- a/app/views/play/level/control_bar_view.coffee +++ b/app/views/play/level/control_bar_view.coffee @@ -54,7 +54,7 @@ module.exports = class ControlBarView extends View c.ladderGame = @ladderGame c.homeLink = "/" levelID = @level.get('slug') - if levelID in ["brawlwood", "brawlwood-tutorial", "dungeon-arena"] + if levelID in ["brawlwood", "brawlwood-tutorial", "dungeon-arena", "dungeon-arena-tutorial"] levelID = 'brawlwood' if levelID is 'brawlwood-tutorial' c.homeLink = "/play/ladder/" + levelID c diff --git a/app/views/play/level/tome/cast_button_view.coffee b/app/views/play/level/tome/cast_button_view.coffee index 15d59f194..af45ca57c 100644 --- a/app/views/play/level/tome/cast_button_view.coffee +++ b/app/views/play/level/tome/cast_button_view.coffee @@ -35,7 +35,7 @@ module.exports = class CastButtonView extends View # TODO: use a User setting instead of localStorage delay = localStorage.getItem 'autocastDelay' delay ?= 5000 - if @levelID in ['brawlwood', 'brawlwood-tutorial', 'dungeon-arena'] + if @levelID in ['brawlwood', 'brawlwood-tutorial', 'dungeon-arena', 'dungeon-arena-tutorial'] delay = 90019001 @setAutocastDelay delay From 8fa9c9c41052eed0048c3dd7cbbbeecc8f171fe7 Mon Sep 17 00:00:00 2001 From: Scott Erickson <sderickson@gmail.com> Date: Fri, 7 Mar 2014 15:18:56 -0800 Subject: [PATCH 046/100] Set up the level play view to show the guide on startup based on the showGuide property. --- app/lib/LevelLoader.coffee | 1 + app/views/kinds/CocoView.coffee | 5 +++-- app/views/play/level_view.coffee | 27 +++++++++++++++++++++++++-- 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/app/lib/LevelLoader.coffee b/app/lib/LevelLoader.coffee index 18ae9f82f..a0e1d78b7 100644 --- a/app/lib/LevelLoader.coffee +++ b/app/lib/LevelLoader.coffee @@ -229,6 +229,7 @@ module.exports = class LevelLoader extends CocoClass notifyProgress: -> Backbone.Mediator.publish 'level-loader:progress-changed', progress: @progress() @initWorld() if @allDone() + @trigger 'progress' @trigger 'loaded-all' if @progress() is 1 destroy: -> diff --git a/app/views/kinds/CocoView.coffee b/app/views/kinds/CocoView.coffee index 806a2d25c..b95abd12e 100644 --- a/app/views/kinds/CocoView.coffee +++ b/app/views/kinds/CocoView.coffee @@ -99,10 +99,11 @@ module.exports = class CocoView extends Backbone.View view = application.router.getView(target, '_modal') # could set up a system for loading cached modals, if told to @openModalView(view) - openModalView: (modalView) -> - return if @waitingModal # can only have one waiting at once + openModalView: (modalView, softly=false) -> + return if waitingModal # can only have one waiting at once if visibleModal waitingModal = modalView + return if softly return visibleModal.hide() if visibleModal.$el.is(':visible') # close, then this will get called again return @modalClosed(visibleModal) # was closed, but modalClosed was not called somehow modalView.render() diff --git a/app/views/play/level_view.coffee b/app/views/play/level_view.coffee index 11fa59ba3..a7faa74cb 100644 --- a/app/views/play/level_view.coffee +++ b/app/views/play/level_view.coffee @@ -16,6 +16,7 @@ LevelLoader = require 'lib/LevelLoader' LevelSession = require 'models/LevelSession' Level = require 'models/Level' LevelComponent = require 'models/LevelComponent' +Article = require 'models/Article' Camera = require 'lib/surface/Camera' AudioPlayer = require 'lib/AudioPlayer' @@ -105,7 +106,8 @@ module.exports = class PlayLevelView extends View load: -> @levelLoader = new LevelLoader supermodel: @supermodel, levelID: @levelID, sessionID: @sessionID, opponentSessionID: @getQueryVariable('opponent'), team: @getQueryVariable("team") - @levelLoader.once 'loaded-all', @onLevelLoaderLoaded + @levelLoader.once 'loaded-all', @onLevelLoaderLoaded, @ + @levelLoader.on 'progress', @onLevelLoaderProgressChanged, @ @god = new God() getRenderData: -> @@ -124,7 +126,28 @@ module.exports = class PlayLevelView extends View @$el.find('#level-done-button').hide() super() - onLevelLoaderLoaded: => + onLevelLoaderProgressChanged: -> + return if @seenDocs + return unless showFrequency = @levelLoader.level.get('showGuide') + session = @levelLoader.session + diff = new Date().getTime() - new Date(session.get('created')).getTime() + return if showFrequency is 'first-time' and diff > (5 * 60 * 1000) + return unless @levelLoader.level.loaded + articles = @levelLoader.supermodel.getModels Article + for article in articles + return unless article.loaded + @showGuide() + + showGuide: -> + @seenDocs = true + DocsModal = require './level/modal/docs_modal' + options = {docs: @levelLoader.level.get('documentation'), supermodel: @supermodel} + @openModalView(new DocsModal(options), true) + Backbone.Mediator.subscribeOnce 'modal-closed', @onLevelLoaderLoaded, @ + return true + + onLevelLoaderLoaded: -> + return unless @levelLoader.progress() is 1 # double check, since closing the guide may trigger this early # Save latest level played in local storage if window.currentModal and not window.currentModal.destroyed @loadingScreen.showReady() From ec86a07906f3facf802a5ddb5ce8be8b2befc347 Mon Sep 17 00:00:00 2001 From: Nick Winter <livelily@gmail.com> Date: Fri, 7 Mar 2014 15:20:54 -0800 Subject: [PATCH 047/100] Changed Camera min/max zoom. --- app/lib/surface/Camera.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/lib/surface/Camera.coffee b/app/lib/surface/Camera.coffee index 76323e5e8..07c4fbbcc 100644 --- a/app/lib/surface/Camera.coffee +++ b/app/lib/surface/Camera.coffee @@ -5,8 +5,8 @@ CocoClass = require 'lib/CocoClass' r2d = (radians) -> radians * 180 / Math.PI d2r = (degrees) -> degrees / 180 * Math.PI -MAX_ZOOM = 8 -MIN_ZOOM = 0.1 +MAX_ZOOM = 4 +MIN_ZOOM = 0.05 DEFAULT_ZOOM = 2.0 DEFAULT_TARGET = {x:0, y:0} DEFAULT_TIME = 1000 From 98b36d299ca09793794a4dca1cdddeec8c354c7f Mon Sep 17 00:00:00 2001 From: Alexei Nikitin <mr-a1@yandex.ru> Date: Sat, 8 Mar 2014 05:05:35 +0400 Subject: [PATCH 048/100] Update article model --- app/views/play/level/modal/docs_modal.coffee | 3 ++- server/articles/article_handler.coffee | 2 +- server/articles/article_schema.coffee | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/views/play/level/modal/docs_modal.coffee b/app/views/play/level/modal/docs_modal.coffee index c7b77a287..3e9ab5cf8 100644 --- a/app/views/play/level/modal/docs_modal.coffee +++ b/app/views/play/level/modal/docs_modal.coffee @@ -25,7 +25,8 @@ module.exports = class DocsModal extends View @docs = specific.concat(general) marked.setOptions {gfm: true, sanitize: false, smartLists: true, breaks: false} @docs = _.cloneDeep(@docs) - doc.html = marked(doc.body) for doc in @docs + doc.html = marked(doc.i18n?[me.lang()]?.body or doc.body) for doc in @docs + doc.name = (doc.i18n?[me.lang()]?.name or doc.name) for doc in @docs doc.slug = _.string.slugify(doc.name) for doc in @docs super() diff --git a/server/articles/article_handler.coffee b/server/articles/article_handler.coffee index ac4fb4b97..b519b8b9f 100644 --- a/server/articles/article_handler.coffee +++ b/server/articles/article_handler.coffee @@ -3,7 +3,7 @@ Handler = require('../commons/Handler') ArticleHandler = class ArticleHandler extends Handler modelClass: Article - editableProperties: ['body', 'name'] + editableProperties: ['body', 'name', 'i18n'] hasAccess: (req) -> req.method is 'GET' or req.user?.isAdmin() diff --git a/server/articles/article_schema.coffee b/server/articles/article_schema.coffee index 08226d183..48ae42821 100644 --- a/server/articles/article_schema.coffee +++ b/server/articles/article_schema.coffee @@ -4,6 +4,7 @@ ArticleSchema = c.object() c.extendNamedProperties ArticleSchema # name first ArticleSchema.properties.body = { type: 'string', title: 'Content', format: 'markdown' } +ArticleSchema.properties.i18n = { type: 'object', title: 'i18n', format: 'i18n', props: ['body'] } c.extendBasicProperties(ArticleSchema, 'article') c.extendSearchableProperties(ArticleSchema) From fe2ced1a67fbc7288541cc1ff02ec8491d3438cf Mon Sep 17 00:00:00 2001 From: Scott Erickson <sderickson@gmail.com> Date: Fri, 7 Mar 2014 17:37:11 -0800 Subject: [PATCH 049/100] Gave the health bar a border, made it a little more high res. --- app/lib/surface/CocoSprite.coffee | 4 ++-- app/lib/surface/sprite_utils.coffee | 14 +++++++++++++- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/app/lib/surface/CocoSprite.coffee b/app/lib/surface/CocoSprite.coffee index 7c7748121..cdbde389e 100644 --- a/app/lib/surface/CocoSprite.coffee +++ b/app/lib/surface/CocoSprite.coffee @@ -328,7 +328,7 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass return if @thang.health is @lastHealth @lastHealth = @thang.health healthPct = Math.max(@thang.health / @thang.maxHealth, 0) - bar.scaleX = healthPct + bar.scaleX = healthPct / bar.baseScale healthOffset = @getOffset 'aboveHead' [bar.x, bar.y] = [healthOffset.x - bar.width / 2, healthOffset.y] @@ -357,7 +357,7 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass bar = @healthBar = createProgressBar(healthColor, healthOffset.y) bar.x = healthOffset.x - bar.width / 2 bar.name = 'health bar' - bar.cache 0, -bar.height / 2, bar.width, bar.height + bar.cache 0, -bar.height * bar.baseScale / 2, bar.width * bar.baseScale, bar.height * bar.baseScale @displayObject.addChild bar getActionProp: (prop, subProp, def=null) -> diff --git a/app/lib/surface/sprite_utils.coffee b/app/lib/surface/sprite_utils.coffee index 6d106e0aa..68984f370 100644 --- a/app/lib/surface/sprite_utils.coffee +++ b/app/lib/surface/sprite_utils.coffee @@ -1,16 +1,28 @@ PROG_BAR_WIDTH = 20 PROG_BAR_HEIGHT = 2 +PROG_BAR_SCALE = 2.5 +EDGE_SIZE = 0.3 module.exports.createProgressBar = createProgressBar = (color, y, width=PROG_BAR_WIDTH, height=PROG_BAR_HEIGHT) -> g = new createjs.Graphics() g.setStrokeStyle(1) + + sWidth = width * PROG_BAR_SCALE + sHeight = height * PROG_BAR_SCALE + sEdge = EDGE_SIZE * PROG_BAR_SCALE + + g.beginFill(createjs.Graphics.getRGB(0, 0, 0)) + g.drawRect(0, -sHeight/2, sWidth, sHeight, sHeight) g.beginFill(createjs.Graphics.getRGB(color...)) - g.drawRoundRect(0, -1, width, height, height) + g.drawRoundRect(sEdge, sEdge - sHeight/2, sWidth-sEdge*2, sHeight-sEdge*2, sHeight-sEdge*2) s = new createjs.Shape(g) s.x = -width / 2 s.y = y s.z = 100 + s.baseScale = PROG_BAR_SCALE + s.scaleX = 1 / PROG_BAR_SCALE + s.scaleY = 1 / PROG_BAR_SCALE s.width = width s.height = height return s From 2f378106caa2251c675cd4bd28576ec83a0c5fdf Mon Sep 17 00:00:00 2001 From: Michael Schmatz <schmatz@umich.edu> Date: Fri, 7 Mar 2014 18:16:48 -0800 Subject: [PATCH 050/100] Added checking level ladder status before submitting --- app/views/play/ladder/my_matches_tab.coffee | 2 +- server/queues/scoring.coffee | 40 ++++++++++++--------- 2 files changed, 25 insertions(+), 17 deletions(-) diff --git a/app/views/play/ladder/my_matches_tab.coffee b/app/views/play/ladder/my_matches_tab.coffee index 6b2cf71be..b08a15083 100644 --- a/app/views/play/ladder/my_matches_tab.coffee +++ b/app/views/play/ladder/my_matches_tab.coffee @@ -95,7 +95,7 @@ module.exports = class MyMatchesTabView extends CocoView success = => @setRankingButtonText(button, 'ranked') failure = => @setRankingButtonText(button, 'failed') - ajaxData = { session: sessionID, levelID: @level.attributes.original, levelMajorVersion: @level.attributes.version.major } + ajaxData = { session: sessionID, levelID: @level.id, originalLevelID: @level.attributes.original, levelMajorVersion: @level.attributes.version.major } $.ajax '/queue/scoring', { type: 'POST' data: ajaxData diff --git a/server/queues/scoring.coffee b/server/queues/scoring.coffee index dd2a51030..1e2cd3f29 100644 --- a/server/queues/scoring.coffee +++ b/server/queues/scoring.coffee @@ -8,6 +8,7 @@ db = require './../routes/db' mongoose = require 'mongoose' queues = require '../commons/queue' LevelSession = require '../levels/sessions/LevelSession' +Level = require '../levels/Level' TaskLog = require './task/ScoringTask' bayes = new (require 'bayesian-battle')() @@ -48,7 +49,8 @@ addPairwiseTaskToQueue = (taskPair, cb) -> module.exports.createNewTask = (req, res) -> requestSessionID = req.body.session - requestLevelID = req.body.levelID + requestLevelID = req.body.originalLevelID + requestCurrentLevelID = req.body.levelID requestLevelMajorVersion = parseInt(req.body.levelMajorVersion) validatePermissions req, requestSessionID, (error, permissionsAreValid) -> @@ -56,21 +58,27 @@ module.exports.createNewTask = (req, res) -> unless permissionsAreValid then return errors.forbidden res, "You do not have the permissions to submit that game to the leaderboard" return errors.badInput res, "The session ID is invalid" unless typeof requestSessionID is "string" - - fetchSessionToSubmit requestSessionID, (err, sessionToSubmit) -> - if err? then return errors.serverError res, "There was an error finding the given session." - - updateSessionToSubmit sessionToSubmit, (err, data) -> - if err? then return errors.serverError res, "There was an error updating the session" - opposingTeam = calculateOpposingTeam(sessionToSubmit.team) - fetchInitialSessionsToRankAgainst opposingTeam,requestLevelID, requestLevelMajorVersion, (err, sessionsToRankAgainst) -> - if err? then return errors.serverError res, "There was an error fetching the sessions to rank against" - - taskPairs = generateTaskPairs(sessionsToRankAgainst, sessionToSubmit) - sendEachTaskPairToTheQueue taskPairs, (taskPairError) -> - if taskPairError? then return errors.serverError res, "There was an error sending the task pairs to the queue" - - sendResponseObject req, res, {"message":"All task pairs were succesfully sent to the queue"} + Level.findOne({_id: requestCurrentLevelID}).lean().select('type').exec (err, levelWithType) -> + if err? then return errors.serverError res, "There was an error finding the level type" + + if not levelWithType.type or levelWithType.type isnt "ladder" + console.log "The level type of level with ID #{requestLevelID} is #{levelWithType.type}" + return errors.badInput res, "That level isn't a ladder level" + + fetchSessionToSubmit requestSessionID, (err, sessionToSubmit) -> + if err? then return errors.serverError res, "There was an error finding the given session." + + updateSessionToSubmit sessionToSubmit, (err, data) -> + if err? then return errors.serverError res, "There was an error updating the session" + opposingTeam = calculateOpposingTeam(sessionToSubmit.team) + fetchInitialSessionsToRankAgainst opposingTeam,requestLevelID, requestLevelMajorVersion, (err, sessionsToRankAgainst) -> + if err? then return errors.serverError res, "There was an error fetching the sessions to rank against" + + taskPairs = generateTaskPairs(sessionsToRankAgainst, sessionToSubmit) + sendEachTaskPairToTheQueue taskPairs, (taskPairError) -> + if taskPairError? then return errors.serverError res, "There was an error sending the task pairs to the queue" + + sendResponseObject req, res, {"message":"All task pairs were succesfully sent to the queue"} module.exports.dispatchTaskToConsumer = (req, res) -> if isUserAnonymous(req) then return errors.forbidden res, "You need to be logged in to simulate games" From 085fb82cc999aa7e7961434b6adbcc3df5563db9 Mon Sep 17 00:00:00 2001 From: Nick Winter <livelily@gmail.com> Date: Fri, 7 Mar 2014 21:02:10 -0800 Subject: [PATCH 051/100] Fixed a multiplayer link and debug mark alpha. --- app/lib/surface/CocoSprite.coffee | 2 +- app/lib/surface/Mark.coffee | 4 ++-- app/lib/surface/Surface.coffee | 2 +- app/templates/play/level/modal/multiplayer.jade | 2 +- server/articles/article_schema.coffee | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/lib/surface/CocoSprite.coffee b/app/lib/surface/CocoSprite.coffee index cdbde389e..b454031e1 100644 --- a/app/lib/surface/CocoSprite.coffee +++ b/app/lib/surface/CocoSprite.coffee @@ -389,7 +389,7 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass @addMark('bounds').toggle true if @thang?.drawsBounds @addMark('shadow').toggle true unless @thangType.get('shadow') is 0 mark.update() for name, mark of @marks - #@thang.effectNames = ['berserk', 'confuse', 'control', 'curse', 'fear', 'poison', 'paralyze', 'regen', 'sleep', 'slow', 'speed'] + #@thang.effectNames = ['berserk', 'confuse', 'control', 'curse', 'fear', 'poison', 'paralyze', 'regen', 'sleep', 'slow', 'haste'] @updateEffectMarks() if @thang?.effectNames?.length or @previousEffectNames?.length updateEffectMarks: -> diff --git a/app/lib/surface/Mark.coffee b/app/lib/surface/Mark.coffee index 5a2e7689f..62801a60f 100644 --- a/app/lib/surface/Mark.coffee +++ b/app/lib/surface/Mark.coffee @@ -151,14 +151,14 @@ module.exports = class Mark extends CocoClass @thangType.fetch() markThangTypes[name] = @thangType window.mtt = markThangTypes - + onLoadedThangType: -> @build() @toggle(@toggleTo) if @toggleTo? update: (pos=null) -> return false unless @on and @mark - @mark.alpha = if @hidden then 0 else 1 + @mark.visible = not @hidden @updatePosition pos @updateRotation() @updateScale() diff --git a/app/lib/surface/Surface.coffee b/app/lib/surface/Surface.coffee index c5e15813d..feb87fc93 100644 --- a/app/lib/surface/Surface.coffee +++ b/app/lib/surface/Surface.coffee @@ -302,7 +302,7 @@ module.exports = Surface = class Surface extends CocoClass world: @world ) - if @lastFrame < @world.totalFrames and @currentFrame >= @world.totalFrames + if @lastFrame < @world.totalFrames and @currentFrame >= @world.totalFrames - 1 @spriteBoss.stop() @playbackOverScreen.show() @ended = true diff --git a/app/templates/play/level/modal/multiplayer.jade b/app/templates/play/level/modal/multiplayer.jade index bc8cc39b5..2135e1977 100644 --- a/app/templates/play/level/modal/multiplayer.jade +++ b/app/templates/play/level/modal/multiplayer.jade @@ -30,7 +30,7 @@ if me.get('anonymous') p Sign in or create an account and get your solution on the leaderboard! else - a#go-to-leaderboard-button.btn.btn-primary(href="/play/ladder/#{levelSlug}/team/#{team}") Go to the leaderboard! + a#go-to-leaderboard-button.btn.btn-primary(href="/play/ladder/#{levelSlug}#my-matches") Go to the leaderboard! p You can submit your game to be ranked from the leaderboard page. .modal-footer diff --git a/server/articles/article_schema.coffee b/server/articles/article_schema.coffee index 48ae42821..1fd4769f7 100644 --- a/server/articles/article_schema.coffee +++ b/server/articles/article_schema.coffee @@ -10,4 +10,4 @@ c.extendBasicProperties(ArticleSchema, 'article') c.extendSearchableProperties(ArticleSchema) c.extendVersionedProperties(ArticleSchema, 'article') -module.exports = ArticleSchema \ No newline at end of file +module.exports = ArticleSchema From 68099bbd7cc505721ceddf142069a3065275fc8d Mon Sep 17 00:00:00 2001 From: Nick Winter <livelily@gmail.com> Date: Fri, 7 Mar 2014 21:20:09 -0800 Subject: [PATCH 052/100] Victory modal for ladder games takes you back to the ladder matches. --- app/templates/play/level/modal/victory.jade | 4 +++- app/views/play/ladder_view.coffee | 5 +++-- app/views/play/level/modal/victory_modal.coffee | 1 + 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/app/templates/play/level/modal/victory.jade b/app/templates/play/level/modal/victory.jade index 5dca8f3f6..45c7f567f 100644 --- a/app/templates/play/level/modal/victory.jade +++ b/app/templates/play/level/modal/victory.jade @@ -14,7 +14,9 @@ div!= body .modal-footer - if hasNextLevel + if level.get('type') === 'ladder' + a.btn.btn-primary(href="/play/ladder/#{level.get('slug')}#my-matches", data-dismiss="modal", data-i18n="play_level.victory_go_home") Go Home + else if hasNextLevel button.btn.btn-primary.next-level-button(data-dismiss="modal", data-i18n="play_level.victory_play_next_level") Play Next Level else a.btn.btn-primary(href="/", data-dismiss="modal", data-i18n="play_level.victory_go_home") Go Home diff --git a/app/views/play/ladder_view.coffee b/app/views/play/ladder_view.coffee index 81e2f6558..7b6597380 100644 --- a/app/views/play/ladder_view.coffee +++ b/app/views/play/ladder_view.coffee @@ -72,6 +72,7 @@ module.exports = class LadderView extends RootView @sessions.fetch({"success": @refreshViews}) refreshViews: => + return if @destroyed @ladderTab.refreshLadder() @myMatchesTab.refreshMatches() console.log "refreshed views!" @@ -114,12 +115,12 @@ module.exports = class LadderView extends RootView onClickPlayButton: (e) -> @showPlayModal($(e.target).closest('.play-button').data('team')) - + showPlayModal: (teamID) -> session = (s for s in @sessions.models when s.get('team') is teamID)[0] modal = new LadderPlayModal({}, @level, session, teamID) @openModalView modal - + destroy: -> clearInterval @refreshInterval @simulator.destroy() diff --git a/app/views/play/level/modal/victory_modal.coffee b/app/views/play/level/modal/victory_modal.coffee index e2fa6b607..6773982ad 100644 --- a/app/views/play/level/modal/victory_modal.coffee +++ b/app/views/play/level/modal/victory_modal.coffee @@ -64,6 +64,7 @@ module.exports = class VictoryModal extends View c.me = me c.hasNextLevel = _.isObject(@level.get('nextLevel')) and (@level.get('name') isnt "Mobile Artillery") c.levelName = @level.get('i18n')?[me.lang()]?.name ? @level.get('name') + c.level = @level if me.get 'hourOfCode' # Show the Hour of Code "I'm Done" tracking pixel after they played for 30 minutes elapsed = (new Date() - new Date(me.get('dateCreated'))) From bcd8c837b4461d841aebca2669ba66b58d489a20 Mon Sep 17 00:00:00 2001 From: Yinkan Li <liyinkan.biz@gmail.com> Date: Sat, 8 Mar 2014 23:53:47 +0800 Subject: [PATCH 053/100] =?UTF-8?q?Update=20=EF=BC=9A=20employers=20for=20?= =?UTF-8?q?Chinese=20simplified?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update : employers for Chinese simplified --- app/locale/zh-HANS.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/locale/zh-HANS.coffee b/app/locale/zh-HANS.coffee index fd0e42cbf..274caf775 100644 --- a/app/locale/zh-HANS.coffee +++ b/app/locale/zh-HANS.coffee @@ -31,7 +31,7 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese about: "关于" contact: "联系" twitter_follow: "关注" - employers: "雇佣我们" + employers: "招募信息" versions: save_version_title: "保存新版本" From 452857e66cf7b215d5bd44c260f5681bf55dc283 Mon Sep 17 00:00:00 2001 From: Yinkan Li <liyinkan.biz@gmail.com> Date: Sat, 8 Mar 2014 23:56:05 +0800 Subject: [PATCH 054/100] update Chinese Simplified update legal concat --- app/locale/zh-HANS.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/locale/zh-HANS.coffee b/app/locale/zh-HANS.coffee index 274caf775..6611b070d 100644 --- a/app/locale/zh-HANS.coffee +++ b/app/locale/zh-HANS.coffee @@ -27,9 +27,9 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese admin: "管理" home: "首页" contribute: "贡献" - legal: "法律" + legal: "版权声明" about: "关于" - contact: "联系" + contact: "联系我们" twitter_follow: "关注" employers: "招募信息" From bfad68b03b0ac3212e5b76d4ea933fbad890cd13 Mon Sep 17 00:00:00 2001 From: Yinkan Li <liyinkan.biz@gmail.com> Date: Sun, 9 Mar 2014 00:01:13 +0800 Subject: [PATCH 055/100] refine skip_tutorial MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit refine skip_tutorial using Chinese () --- app/locale/zh-HANS.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/locale/zh-HANS.coffee b/app/locale/zh-HANS.coffee index 6611b070d..21f328154 100644 --- a/app/locale/zh-HANS.coffee +++ b/app/locale/zh-HANS.coffee @@ -200,7 +200,7 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese tome_available_spells: "可用的法术" hud_continue: "继续(按 Shift-空格)" spell_saved: "咒语已保存" - skip_tutorial: "跳过(esc)" + skip_tutorial: "跳过(esc)" admin: av_title: "管理员视图" From 1c44e3df1eaaa22fe253ec0210e7ee4d48e76055 Mon Sep 17 00:00:00 2001 From: Nick Winter <livelily@gmail.com> Date: Sat, 8 Mar 2014 11:37:33 -0800 Subject: [PATCH 056/100] Fixed bug with linking to #my-matches tab. --- app/views/play/ladder/play_modal.coffee | 16 ++++++++-------- app/views/play/ladder_view.coffee | 4 +++- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/app/views/play/ladder/play_modal.coffee b/app/views/play/ladder/play_modal.coffee index e5d83fc3b..ebe51e68d 100644 --- a/app/views/play/ladder/play_modal.coffee +++ b/app/views/play/ladder/play_modal.coffee @@ -11,7 +11,7 @@ module.exports = class LadderPlayModal extends View closeButton: true startsLoading: true @shownTutorialButton: false - + events: 'click #skip-tutorial-button': 'hideTutorialButtons' @@ -21,7 +21,7 @@ module.exports = class LadderPlayModal extends View @otherTeam = if team is 'ogres' then 'humans' else 'ogres' @startLoadingChallengersMaybe() @wizardType = ThangType.loadUniversalWizard() - + # PART 1: Load challengers from the db unless some are in the matches startLoadingChallengersMaybe: -> @@ -49,12 +49,12 @@ module.exports = class LadderPlayModal extends View type: 'POST' success: success }) - + # PART 3: Make sure wizard is loaded - + checkWizardLoaded: -> if @wizardType.loaded then @finishRendering() else @wizardType.once 'sync', @finishRendering, @ - + # PART 4: Render finishRendering: -> @@ -69,7 +69,7 @@ module.exports = class LadderPlayModal extends View ctx.teamName = _.string.titleize @team ctx.teamID = @team ctx.otherTeamID = @otherTeam - + teamsList = teamDataFromLevel @level teams = {} teams[team.id] = team for team in teamsList @@ -104,7 +104,7 @@ module.exports = class LadderPlayModal extends View @$el.find('#normal-view').removeClass('secret') @$el.find('.modal-header').removeClass('secret') @$el.find('#noob-view').addClass('secret') - + # Choosing challengers getChallengers: -> @@ -165,7 +165,7 @@ class ChallengersData @hardPlayer = new LeaderboardCollection(@level, {order:-1, scoreOffset: score + 5, limit: 1, team: @otherTeam}) @hardPlayer.fetch() @hardPlayer.once 'sync', @challengerLoaded, @ - + challengerLoaded: -> if @allLoaded() @loaded = true diff --git a/app/views/play/ladder_view.coffee b/app/views/play/ladder_view.coffee index 7b6597380..7ed43660e 100644 --- a/app/views/play/ladder_view.coffee +++ b/app/views/play/ladder_view.coffee @@ -66,7 +66,9 @@ module.exports = class LadderView extends RootView @insertSubView(@ladderTab = new LadderTabView({}, @level, @sessions)) @insertSubView(@myMatchesTab = new MyMatchesTabView({}, @level, @sessions)) @refreshInterval = setInterval(@fetchSessionsAndRefreshViews.bind(@), 10000) - @showPlayModal(document.location.hash[1..]) if document.location.hash and @sessions.loaded + hash = document.location.hash[1..] if document.location.hash + unless hash in ['my-matches', 'simulate', 'ladder'] + @showPlayModal(hash) if @sessions.loaded fetchSessionsAndRefreshViews: -> @sessions.fetch({"success": @refreshViews}) From b0238d74a40693d616cbd40101dcf700a94372b1 Mon Sep 17 00:00:00 2001 From: Nick Winter <livelily@gmail.com> Date: Sat, 8 Mar 2014 11:50:10 -0800 Subject: [PATCH 057/100] Let's just show 200 leaderboard entries for now until we have paging. --- app/views/play/ladder/ladder_tab.coffee | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/views/play/ladder/ladder_tab.coffee b/app/views/play/ladder/ladder_tab.coffee index 323ca86fe..b8b2d3615 100644 --- a/app/views/play/ladder/ladder_tab.coffee +++ b/app/views/play/ladder/ladder_tab.coffee @@ -55,7 +55,8 @@ module.exports = class LadderTabView extends CocoView class LeaderboardData constructor: (@level, @team, @session) -> _.extend @, Backbone.Events - @topPlayers = new LeaderboardCollection(@level, {order:-1, scoreOffset: HIGHEST_SCORE, team: @team, limit: if @session then 10 else 20}) + limit = 200 # if @session then 10 else 20 # We need to figure out paging. + @topPlayers = new LeaderboardCollection(@level, {order:-1, scoreOffset: HIGHEST_SCORE, team: @team, limit: limit}) @topPlayers.fetch() @topPlayers.comparator = (model) -> return -model.get('totalScore') From 2861f62248483687088a473e5046f8988cfc9ec3 Mon Sep 17 00:00:00 2001 From: Scott Erickson <sderickson@gmail.com> Date: Sat, 8 Mar 2014 13:06:04 -0800 Subject: [PATCH 058/100] Simple fix for a race condition where the user schema might load after /auth/whoami, overwriting /auth/whoami's cookie. --- app/models/CocoModel.coffee | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/models/CocoModel.coffee b/app/models/CocoModel.coffee index ba4ff850c..919d9a6b9 100644 --- a/app/models/CocoModel.coffee +++ b/app/models/CocoModel.coffee @@ -22,7 +22,8 @@ class CocoModel extends Backbone.Model if @constructor.schema?.loaded @addSchemaDefaults() else - @loadSchema() + {me} = require 'lib/auth' + @loadSchema() if me?.loaded @once 'sync', @onLoaded, @ @saveBackup = _.debounce(@saveBackup, 500) From afbe2200a68d84dfcfd39c53046cde74f8f2fe72 Mon Sep 17 00:00:00 2001 From: Scott Erickson <sderickson@gmail.com> Date: Sat, 8 Mar 2014 13:52:34 -0800 Subject: [PATCH 059/100] Fixed a bug with the new race condition fix. Moving the schemas to the application would be good. --- app/models/CocoModel.coffee | 7 +++---- app/views/editor/thang/edit.coffee | 1 + 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/models/CocoModel.coffee b/app/models/CocoModel.coffee index 919d9a6b9..3f4c9455c 100644 --- a/app/models/CocoModel.coffee +++ b/app/models/CocoModel.coffee @@ -16,6 +16,7 @@ class CocoModel extends Backbone.Model initialize: -> super() + @constructor.schema ?= new CocoSchema(@urlRoot) if not @constructor.className console.error("#{@} needs a className set.") @markToRevert() @@ -52,10 +53,8 @@ class CocoModel extends Backbone.Model @backedUp = {} loadSchema: -> - unless @constructor.schema - @constructor.schema = new CocoSchema(@urlRoot) - @constructor.schema.fetch() - + return if @constructor.schema.loading + @constructor.schema.fetch() @constructor.schema.once 'sync', => @constructor.schema.loaded = true @addSchemaDefaults() diff --git a/app/views/editor/thang/edit.coffee b/app/views/editor/thang/edit.coffee index a8310cdf2..833ce3e0e 100644 --- a/app/views/editor/thang/edit.coffee +++ b/app/views/editor/thang/edit.coffee @@ -42,6 +42,7 @@ module.exports = class ThangTypeEditView extends View @thangType = new ThangType(_id: @thangTypeID) @thangType.saveBackups = true @thangType.fetch() + @thangType.loadSchema() @thangType.schema().once 'sync', @onThangTypeSync, @ @thangType.once 'sync', @onThangTypeSync, @ @refreshAnimation = _.debounce @refreshAnimation, 500 From 57f1588dea9c12ff869baa9222f202ebb7b6a72f Mon Sep 17 00:00:00 2001 From: Scott Erickson <sderickson@gmail.com> Date: Sat, 8 Mar 2014 14:34:25 -0800 Subject: [PATCH 060/100] Fixed another bug related to the race condition fix. --- app/models/SuperModel.coffee | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/app/models/SuperModel.coffee b/app/models/SuperModel.coffee index 36a7fab72..9ce14c75d 100644 --- a/app/models/SuperModel.coffee +++ b/app/models/SuperModel.coffee @@ -2,6 +2,7 @@ class SuperModel constructor: -> @models = {} @collections = {} + @schemas = {} _.extend(@, Backbone.Events) populateModel: (model) -> @@ -25,8 +26,11 @@ class SuperModel @removeEventsFromModel(model) modelLoaded: (model) -> + model.loadSchema() schema = model.schema() - return schema.once('sync', => @modelLoaded(model)) unless schema.loaded + unless schema.loaded + @schemas[schema.urlRoot] = schema + return schema.once('sync', => @modelLoaded(model)) refs = model.getReferencedModels(model.attributes, schema.attributes, '/', @shouldLoadProjection) refs = [] unless @mustPopulate is model or @shouldPopulate(model) # console.log 'Loaded', model.get('name') @@ -96,9 +100,12 @@ class SuperModel total = 0 loaded = 0 - for key, model of @models + for model in _.values @models total += 1 loaded += 1 if model.loaded + for schema in _.values @schemas + total += 1 + loaded += 1 if schema.loaded return 1.0 unless total return loaded / total From be12d38e34063496b798830fead91929293c679b Mon Sep 17 00:00:00 2001 From: Nick Winter <livelily@gmail.com> Date: Sat, 8 Mar 2014 14:38:35 -0800 Subject: [PATCH 061/100] Taking into account null rects and library width/height subtraction on sprite parsing. --- app/lib/sprites/SpriteParser.coffee | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/app/lib/sprites/SpriteParser.coffee b/app/lib/sprites/SpriteParser.coffee index 75288c5fa..a5e1f9472 100644 --- a/app/lib/sprites/SpriteParser.coffee +++ b/app/lib/sprites/SpriteParser.coffee @@ -24,6 +24,11 @@ module.exports = class SpriteParser @animationLongKeys[longKey] = shortKey parse: (source) -> + # Grab the library properties' width/height so we can subtract half of each from frame bounds + properties = source.match(/.*lib\.properties = \{\n.*?width: (\d+),\n.*?height: (\d+)/im) + @width = parseInt(properties[1] ? "0", 10) + @height = parseInt(properties[2] ? "0", 10) + options = {loc: false, range: true} ast = esprima.parse source, options blocks = @findBlocks ast, source @@ -178,11 +183,18 @@ module.exports = class SpriteParser else if arg.type is 'AssignmentExpression' bounds = @grabFunctionArguments argSource.replace('rect=', ''), true lastRect = bounds + else if arg.type is 'Literal' and arg.value is null + bounds = [0, 0, 1, 1] # Let's try this. frameBounds.push bounds else console.log "Didn't have multiframe bounds for this movie clip!" frameBounds = [nominalBounds] + # Subtract half of width/height parsed from lib.properties + for bounds in frameBounds + bounds[0] -= @width / 2 + bounds[1] -= @height / 2 + functionExpressions.push {name: name, bounds: nominalBounds, frameBounds: frameBounds, expression: node.parent.parent, kind: kind} @walk ast, null, gatherFunctionExpressions functionExpressions From 5bde5347571950730e23f9f1b5b9bbe4a1cb2e34 Mon Sep 17 00:00:00 2001 From: Nick Winter <livelily@gmail.com> Date: Sat, 8 Mar 2014 14:53:17 -0800 Subject: [PATCH 062/100] Fixed parsing for ThangTypes that don't have lib.properties. --- app/lib/sprites/SpriteParser.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/lib/sprites/SpriteParser.coffee b/app/lib/sprites/SpriteParser.coffee index a5e1f9472..9dd45dbcc 100644 --- a/app/lib/sprites/SpriteParser.coffee +++ b/app/lib/sprites/SpriteParser.coffee @@ -26,8 +26,8 @@ module.exports = class SpriteParser parse: (source) -> # Grab the library properties' width/height so we can subtract half of each from frame bounds properties = source.match(/.*lib\.properties = \{\n.*?width: (\d+),\n.*?height: (\d+)/im) - @width = parseInt(properties[1] ? "0", 10) - @height = parseInt(properties[2] ? "0", 10) + @width = parseInt(properties?[1] ? "0", 10) + @height = parseInt(properties?[2] ? "0", 10) options = {loc: false, range: true} ast = esprima.parse source, options From b3964571e1dd752dd467c3f99f67da27acfad550 Mon Sep 17 00:00:00 2001 From: Nick Winter <livelily@gmail.com> Date: Sat, 8 Mar 2014 15:04:11 -0800 Subject: [PATCH 063/100] Fixed bug importing animations into existing ThangTypes that had only containers. --- app/lib/sprites/SpriteParser.coffee | 5 ++++- app/views/editor/thang/edit.coffee | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/app/lib/sprites/SpriteParser.coffee b/app/lib/sprites/SpriteParser.coffee index 9dd45dbcc..15fe749cd 100644 --- a/app/lib/sprites/SpriteParser.coffee +++ b/app/lib/sprites/SpriteParser.coffee @@ -2,7 +2,10 @@ module.exports = class SpriteParser constructor: (@thangTypeModel) -> # Create a new ThangType, or work with one we've been building @thangType = _.cloneDeep(@thangTypeModel.attributes.raw) - @thangType ?= {shapes: {}, containers: {}, animations: {}} + @thangType ?= {} + @thangType.shapes ?= {} + @thangType.containers ?= {} + @thangType.animations ?= {} # Internal parser state @shapeLongKeys = {} diff --git a/app/views/editor/thang/edit.coffee b/app/views/editor/thang/edit.coffee index 833ce3e0e..5a5d188f2 100644 --- a/app/views/editor/thang/edit.coffee +++ b/app/views/editor/thang/edit.coffee @@ -316,7 +316,7 @@ module.exports = class ThangTypeEditView extends View @thangType.set 'actions', undefined @clearDisplayObject() @treema.set('/', @getThangData()) - + getThangData: -> data = _.cloneDeep(@thangType.attributes) data = _.pick data, (value, key) => not (key in ['components']) From 39c465720433f3c6f991c2ec362e69c8f60c7cae Mon Sep 17 00:00:00 2001 From: Nick Winter <livelily@gmail.com> Date: Sat, 8 Mar 2014 15:43:56 -0800 Subject: [PATCH 064/100] Fixed HUD showing actions backwardly. Added rank numbering to leaderboard. --- app/templates/play/ladder/ladder_tab.jade | 6 ++++-- app/views/play/level/hud_view.coffee | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/app/templates/play/ladder/ladder_tab.jade b/app/templates/play/ladder/ladder_tab.jade index c3090caa3..9fc48c340 100644 --- a/app/templates/play/ladder/ladder_tab.jade +++ b/app/templates/play/ladder/ladder_tab.jade @@ -3,17 +3,19 @@ div#columns.row div.column.col-md-6 table.table.table-bordered.table-condensed.table-hover tr - th(colspan=3, style="color: #{team.primaryColor}") + th(colspan=4, style="color: #{team.primaryColor}") span= team.name span Leaderboard tr + th Rank th Score th.name-col-cell Name th - for session in team.leaderboard.topPlayers.models + for session, rank in team.leaderboard.topPlayers.models - var myRow = session.get('creator') == me.id tr(class=myRow ? "success" : "") + td.rank-cell= rank + 1 td.score-cell= Math.round(session.get('totalScore') * 100) td.name-col-cell= session.get('creatorName') || "Anonymous" td diff --git a/app/views/play/level/hud_view.coffee b/app/views/play/level/hud_view.coffee index 1951e3539..662c9fc8f 100644 --- a/app/views/play/level/hud_view.coffee +++ b/app/views/play/level/hud_view.coffee @@ -151,7 +151,7 @@ module.exports = class HUDView extends View createActions: -> actions = @$el.find('.thang-actions tbody').empty() showActions = @thang.world and not _.isEmpty(@thang.actions) and 'action' in @thang.hudProperties ? [] - @$el.find('.thang-actions').toggleClass 'secret', showActions + @$el.find('.thang-actions').toggleClass 'secret', not showActions return unless showActions @buildActionTimespans() for actionName, action of @thang.actions From bfebd0b47ac4cf7747e710207514d27f6c5e628a Mon Sep 17 00:00:00 2001 From: Alexei Nikitin <mr-a1@yandex.ru> Date: Sun, 9 Mar 2014 04:13:30 +0400 Subject: [PATCH 065/100] Add revert i18n --- app/locale/ru.coffee | 2 ++ app/templates/editor/article/edit.jade | 2 +- app/templates/editor/level/edit.jade | 2 +- app/templates/editor/thang/edit.jade | 2 +- app/templates/modal/revert.jade | 2 +- 5 files changed, 6 insertions(+), 4 deletions(-) diff --git a/app/locale/ru.coffee b/app/locale/ru.coffee index 36bcf1ac1..1cb8f0c53 100644 --- a/app/locale/ru.coffee +++ b/app/locale/ru.coffee @@ -226,6 +226,8 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi contact_us: "свяжитесь с нами!" hipchat_prefix: "Также вы можете найти нас в нашей" hipchat_url: "комнате HipChat." + revert: "Откатить" + revert_models: "Откатить Модели" level_some_options: "Ещё опции" level_tab_thangs: "Объекты" level_tab_scripts: "Скрипты" diff --git a/app/templates/editor/article/edit.jade b/app/templates/editor/article/edit.jade index 7c6a7136a..3bd861bd9 100644 --- a/app/templates/editor/article/edit.jade +++ b/app/templates/editor/article/edit.jade @@ -10,7 +10,7 @@ block content li.active | #{article.attributes.name} - button(data-toggle="coco-modal", data-target="modal/revert", data-i18n="revert.revert", disabled=authorized === true ? undefined : "true").btn.btn-primary#revert-button Revert + button(data-toggle="coco-modal", data-target="modal/revert", data-i18n="editor.revert", disabled=authorized === true ? undefined : "true").btn.btn-primary#revert-button Revert button(data-i18n="article.edit_btn_preview", disabled=authorized === true ? undefined : "true").btn.btn-primary#preview-button Preview button(data-toggle="coco-modal", data-target="modal/save_version", data-i18n="common.save", disabled=authorized === true ? undefined : "true").btn.btn-primary#save-button Save diff --git a/app/templates/editor/level/edit.jade b/app/templates/editor/level/edit.jade index 7d2c7ac66..4166c81a1 100644 --- a/app/templates/editor/level/edit.jade +++ b/app/templates/editor/level/edit.jade @@ -29,7 +29,7 @@ block outer_content ul.nav.navbar-nav.navbar-right - li(data-toggle="coco-modal", data-target="modal/revert", data-i18n="revert.revert", disabled=authorized === true ? undefined : "true").btn.btn-primary.navbar-btn#revert-button Revert + li(data-toggle="coco-modal", data-target="modal/revert", data-i18n="editor.revert", disabled=authorized === true ? undefined : "true").btn.btn-primary.navbar-btn#revert-button Revert li(data-i18n="common.save", disabled=authorized === true ? undefined : "true").btn.btn-primary.navbar-btn#commit-level-start-button Save li(data-i18n="common.fork", disabled=anonymous ? "true": undefined).btn.btn-primary.navbar-btn#fork-level-start-button Fork li(title="⌃↩ or ⌘↩: Play preview of current level", data-i18n="common.play")#play-button.btn.btn-inverse.banner.navbar-btn Play! diff --git a/app/templates/editor/thang/edit.jade b/app/templates/editor/thang/edit.jade index af30eb2c1..52422fe9a 100644 --- a/app/templates/editor/thang/edit.jade +++ b/app/templates/editor/thang/edit.jade @@ -13,7 +13,7 @@ block content img#portrait.img-thumbnail button.btn.btn-primary#save-button(data-toggle="coco-modal", data-target="modal/save_version", disabled=authorized === true ? undefined : "true") Save - button.btn.btn-primary#revert-button(data-toggle="coco-modal", data-target="modal/revert", data-i18n="revert.revert", disabled=authorized === true ? undefined : "true") Revert + button.btn.btn-primary#revert-button(data-toggle="coco-modal", data-target="modal/revert", data-i18n="editor.revert", disabled=authorized === true ? undefined : "true") Revert h3 Edit Thang Type: "#{thangType.attributes.name}" diff --git a/app/templates/modal/revert.jade b/app/templates/modal/revert.jade index adfd7688a..f20edd7d2 100644 --- a/app/templates/modal/revert.jade +++ b/app/templates/modal/revert.jade @@ -1,7 +1,7 @@ extends /templates/modal/modal_base block modal-header-content - h3(data-i18n="revert.revert_models") Revert Models + h3(data-i18n="editor.revert_models") Revert Models block modal-body-content table.table.table-striped#changed-models From f6d68055345f3e2abb2f8eb2920b49a418987814 Mon Sep 17 00:00:00 2001 From: Alexei Nikitin <mr-a1@yandex.ru> Date: Sun, 9 Mar 2014 04:31:15 +0400 Subject: [PATCH 066/100] Add password i18n --- app/locale/ru.coffee | 1 + app/templates/modal/login.jade | 2 +- app/templates/modal/signup.jade | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/locale/ru.coffee b/app/locale/ru.coffee index 1cb8f0c53..55c0f2fbf 100644 --- a/app/locale/ru.coffee +++ b/app/locale/ru.coffee @@ -266,6 +266,7 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi description: "Описание" or: "или" email: "Email" + password: "Пароль" message: "Сообщение" about: diff --git a/app/templates/modal/login.jade b/app/templates/modal/login.jade index ae6b8b236..bd0307824 100644 --- a/app/templates/modal/login.jade +++ b/app/templates/modal/login.jade @@ -9,7 +9,7 @@ block modal-body-content label.control-label(for="login-email", data-i18n="general.email") Email input#login-email.input-large.form-control(name="email", type="email") .form-group - label.control-label(for="login-password", data-i18n="forms.password") Password + label.control-label(for="login-password", data-i18n="general.password") Password input#login-password.input-large.form-control(name="password", type="password") block modal-body-wait-content diff --git a/app/templates/modal/signup.jade b/app/templates/modal/signup.jade index d99fed9d6..2b27577d2 100644 --- a/app/templates/modal/signup.jade +++ b/app/templates/modal/signup.jade @@ -12,7 +12,7 @@ block modal-body-content label.control-label(for="signup-email", data-i18n="general.email") Email input#signup-email.form-control.input-large(name="email", type="email") .form-group - label.control-label(for="signup-password", data-i18n="forms.password") Password + label.control-label(for="signup-password", data-i18n="general.password") Password input#signup-password.input-large.form-control(name="password", type="password") hr .form-group.checkbox From 81084a24c63a8e8dbc6ebd837f60c2d57087333f Mon Sep 17 00:00:00 2001 From: Alexei Nikitin <mr-a1@yandex.ru> Date: Sun, 9 Mar 2014 04:36:48 +0400 Subject: [PATCH 067/100] Add admin i18n --- app/locale/ru.coffee | 1 + app/templates/account/settings.jade | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/locale/ru.coffee b/app/locale/ru.coffee index 55c0f2fbf..6c52002e5 100644 --- a/app/locale/ru.coffee +++ b/app/locale/ru.coffee @@ -122,6 +122,7 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi wizard_tab: "Волшебник" password_tab: "Пароль" emails_tab: "Email-адреса" + admin: "Админ" gravatar_select: "Выберите, какое фото с Gravatar использовать" gravatar_add_photos: "Чтобы выбрать изображение, добавьте фото и уменьшенные изображения в ваш Gravatar-аккаунт." gravatar_add_more_photos: "Добавьте больше фото к вашему аккаунту в Gravatar, чтобы использовать их здесь." diff --git a/app/templates/account/settings.jade b/app/templates/account/settings.jade index d64936645..91b533b1b 100644 --- a/app/templates/account/settings.jade +++ b/app/templates/account/settings.jade @@ -34,7 +34,7 @@ block content input#email.form-control(name="email", type="text", value="#{me.get('email')}") if !isProduction .form-group.checkbox - label(for="email", data-i18n="forms.admin") Admin + label(for="email", data-i18n="account_settings.admin") Admin input#admin(name="admin", type="checkbox", checked=me.get('permissions').indexOf('admin')>-1)) From bcaa801f880cb7004fdbb27f8e33873aced842f0 Mon Sep 17 00:00:00 2001 From: Alexei Nikitin <mr-a1@yandex.ru> Date: Sun, 9 Mar 2014 05:04:11 +0400 Subject: [PATCH 068/100] Sync en i18n --- app/locale/en.coffee | 6 +++++- app/locale/ru.coffee | 6 ++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/app/locale/en.coffee b/app/locale/en.coffee index 5ca5cf902..e3c694f82 100644 --- a/app/locale/en.coffee +++ b/app/locale/en.coffee @@ -122,6 +122,7 @@ module.exports = nativeDescription: "English", englishDescription: "English", tr wizard_tab: "Wizard" password_tab: "Password" emails_tab: "Emails" + admin: "Admin" gravatar_select: "Select which Gravatar photo to use" gravatar_add_photos: "Add thumbnails and photos to a Gravatar account for your email to choose an image." gravatar_add_more_photos: "Add more photos to your Gravatar account to access them here." @@ -226,6 +227,8 @@ module.exports = nativeDescription: "English", englishDescription: "English", tr contact_us: "contact us!" hipchat_prefix: "You can also find us in our" hipchat_url: "HipChat room." + revert: "Revert" + revert_models: "Revert Models" level_some_options: "Some Options?" level_tab_thangs: "Thangs" level_tab_scripts: "Scripts" @@ -270,6 +273,7 @@ module.exports = nativeDescription: "English", englishDescription: "English", tr description: "Description" or: "or" email: "Email" + password: "Password" message: "Message" about: @@ -407,7 +411,7 @@ module.exports = nativeDescription: "English", englishDescription: "English", tr more_about_adventurer: "Learn More About Becoming an Adventurer" adventurer_subscribe_desc: "Get emails when there are new levels to test." scribe_summary_pref: "CodeCombat is not just going to be a bunch of levels. It will also be a resource of programming knowledge that players can hook into. That way, each Artisan can link to a detailed article that for the player's edification: documentation akin to what the " - scribe_summary_sufx: " has built. If you enjoy explaining programming concepts, then this class is for you." + scribe_summary_suf: " has built. If you enjoy explaining programming concepts, then this class is for you." scribe_introduction_pref: "CodeCombat isn't just going to be a bunch of levels. It will also include a resource for knowledge, a wiki of programming concepts that levels can hook into. That way rather than each Artisan having to describe in detail what a comparison operator is, they can simply link their level to the Article describing them that is already written for the player's edification. Something along the lines of what the " scribe_introduction_url_mozilla: "Mozilla Developer Network" scribe_introduction_suf: " has built. If your idea of fun is articulating the concepts of programming in Markdown form, then this class might be for you." diff --git a/app/locale/ru.coffee b/app/locale/ru.coffee index 6c52002e5..24c129bf9 100644 --- a/app/locale/ru.coffee +++ b/app/locale/ru.coffee @@ -251,6 +251,12 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi create_system_title: "Создать новую систему" new_component_title: "Создать новый компонент" new_component_field_system: "Система" + new_article_title: "Создать новую статью" + new_thang_title: "Создать новый объект" + new_level_title: "Создать новый уровень" + article_search_title: "Искать статьи" + thang_search_title: "Искать типы объектов" + level_search_title: "Искать уровни" article: edit_btn_preview: "Предпросмотр" From 59318678bac69b6d5b276e7bc0c61cbd58513df2 Mon Sep 17 00:00:00 2001 From: Alexei Nikitin <mr-a1@yandex.ru> Date: Sun, 9 Mar 2014 05:06:53 +0400 Subject: [PATCH 069/100] Update ru --- app/locale/ru.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/locale/ru.coffee b/app/locale/ru.coffee index 24c129bf9..0296d95d4 100644 --- a/app/locale/ru.coffee +++ b/app/locale/ru.coffee @@ -252,7 +252,7 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi new_component_title: "Создать новый компонент" new_component_field_system: "Система" new_article_title: "Создать новую статью" - new_thang_title: "Создать новый объект" + new_thang_title: "Создать новый тип объектов" new_level_title: "Создать новый уровень" article_search_title: "Искать статьи" thang_search_title: "Искать типы объектов" From f05e8432a9eb53bfbeb8ddc62e8910438714783a Mon Sep 17 00:00:00 2001 From: Nick Winter <livelily@gmail.com> Date: Sat, 8 Mar 2014 18:49:09 -0800 Subject: [PATCH 070/100] Starting work on ladder update emails. --- server/routes/mail.coffee | 149 +++++++++++++++++++++++++++++++------- server/sendwithus.coffee | 3 +- 2 files changed, 123 insertions(+), 29 deletions(-) diff --git a/server/routes/mail.coffee b/server/routes/mail.coffee index d5306e16a..2e693da66 100644 --- a/server/routes/mail.coffee +++ b/server/routes/mail.coffee @@ -4,37 +4,130 @@ User = require '../users/User.coffee' errors = require '../commons/errors' #request = require 'request' config = require '../../server_config' +LevelSession = require '../levels/sessions/LevelSession.coffee' +log = require 'winston' +sendwithus = require '../sendwithus' #badLog = (text) -> # console.log text # request.post 'http://requestb.in/1brdpaz1', { form: {log: text} } - + module.exports.setup = (app) -> - app.all config.mail.mailchimpWebhook, (req, res) -> - post = req.body -# badLog("Got post data: #{JSON.stringify(post, null, '\t')}") - - unless post.type in ['unsubscribe', 'profile'] - res.send 'Bad post type' - return res.end() + app.all config.mail.mailchimpWebhook, handleMailchimpWebHook + app.get '/mail/cron/ladder-update', handleLadderUpdate - unless post.data.email - res.send 'No email provided' - return res.end() +handleLadderUpdate = (req, res) -> + emailDays = [1, 2, 4, 7, 30] + now = new Date() + getTimeFromDaysAgo = (daysAgo) -> + # 2 hours before the date + t = now - (86400 * daysAgo + 2 * 3600) * 1000 + for daysAgo in emailDays + startTime = getTimeFromDaysAgo daysAgo + endTime = startTime + 5 * 60 * 1000 + # Get every session that was submitted in a 5-minute window after the time. + findParameters = {submitted: true, submitDate: {$gt: startTime, $lte: endTime}} + # TODO: think about putting screenshots in the email + selectString = "creator team levelID totalScore matches" + query = LevelSession.find(findParameters) + .select(selectString) + .lean() + mongoose = require 'mongoose' + mongoose.set 'debug', true + query.exec (err, results) -> + log.info "Yooooo got results: #{results.length}" + if err + log.error "Couldn't fetch ladder updates for", findParameters, "\nError: ", err + return errors.serverError res, "Ladder update email query failed: #{JSON.stringify(err)}" + sendLadderUpdateEmail result, daysAgo for result in results + res.send('') + res.end() - query = {'mailChimp.leid':post.data.web_id} - User.findOne query, (err, user) -> +sendLadderUpdateEmail = (session, daysAgo) -> + User.findOne({_id: session.creator}).select("name email firstName lastName emailSubscriptions preferredLanguage").lean().exec (err, user) -> + if err + log.error "Couldn't find user for", session.creator, "from session", session._id + return + return unless user.email and 'notification' in user.emailSubscriptions + name = if user.firstName and user.lastName then "#{user.firstName} #{user.lastName}" else user.name + name = "Wizard" if not name or name is "Anoner" + + sendEmail = (defeatContext, victoryContext) -> + # TODO: do something with the preferredLanguage? + context = + email_id: sendwithus.templates.ladder_update_email + #recipient: + # address: user.email + recipient: + address: 'nick@codecombat.com' + days_ago: daysAgo + name: name + wins: session.numberOfWinsAndTies + losses: session.numberOfLosses + total_score: session.totalScore + team: session.team + level: id + defeat: defeatContext + victory: victoryContext + sendwithus.api.send context, (err, result) -> + log.error "Error sending ladder update email:", err, 'result', result + + defeats = _.filter session.matches, (match) -> match.metrics.rank is 1 and match.opponents[0].metrics.rank is 0 + victories = _.filter session.matches, (match) -> match.metrics.rank is 0 + defeat = _.sample defeats + victory = _.sample victories + urlForMatch = (match) -> + "http://codecombat.com/play/ladder/#{session.levelID}?team=#{session.team}&session=#{session._id}&opponent=#{match.opponents[0].sessionID}" + + onFetchedDefeatedOpponent = (err, defeatedOpponent) -> + if err + log.error "Couldn't find defeateded opponent: #{err}" + defeatedOpponent = null + victoryContext = {opponent_name: defeatedOpponent?.name ? "Anoner", url: urlForMatch(victory)} if victory + + onFetchedVictoriousOpponent = (err, victoriousOpponent) -> + if err + log.error "Couldn't find victorious opponent: #{err}" + victoriousOpponent = null + defeatContext = {opponent_name: victoriousOpponent?.name ? "Anoner", url: urlForMatch(defeat)} if defeat + sendEmail defeatContext, victoryContext + + if defeat + User.findOne({_id: defeat.opponents[0].userID}).select("name").lean().exec onFetchedVictoriousOpponent + else + onFetchedVictoriousOpponent null, null + + if victory + User.findOne({_id: victory.opponents[0].userID}).select("name").lean().exec onFetchedDefeatedOpponent + else + onFetchedDefeatedOpponent null, null + + +handleMailchimpWebHook = (req, res) -> + post = req.body + #badLog("Got post data: #{JSON.stringify(post, null, '\t')}") + + unless post.type in ['unsubscribe', 'profile'] + res.send 'Bad post type' + return res.end() + + unless post.data.email + res.send 'No email provided' + return res.end() + + query = {'mailChimp.leid':post.data.web_id} + User.findOne query, (err, user) -> + return errors.serverError(res) if err + if not user + return errors.notFound(res) + + handleProfileUpdate(user, post) if post.type is 'profile' + handleUnsubscribe(user) if post.type is 'unsubscribe' + + user.updatedMailChimp = true # so as not to echo back to mailchimp + user.save (err) -> return errors.serverError(res) if err - if not user - return errors.notFound(res) - - handleProfileUpdate(user, post) if post.type is 'profile' - handleUnsubscribe(user) if post.type is 'unsubscribe' - - user.updatedMailChimp = true # so as not to echo back to mailchimp - user.save (err) -> - return errors.serverError(res) if err - res.end('Success') + res.end('Success') handleProfileUpdate = (user, post) -> @@ -43,19 +136,19 @@ handleProfileUpdate = (user, post) -> otherSubscriptions = (g for g in user.get('emailSubscriptions') when not mail.MAILCHIMP_GROUP_MAP[g]) groups = groups.concat otherSubscriptions user.set 'emailSubscriptions', groups - + fname = post.data.merges.FNAME user.set('firstName', fname) if fname lname = post.data.merges.LNAME user.set('lastName', lname) if lname - + user.set 'mailChimp.email', post.data.email user.set 'mailChimp.euid', post.data.id - + # badLog("Updating user object to: #{JSON.stringify(user.toObject(), null, '\t')}") - + handleUnsubscribe = (user) -> user.set 'emailSubscriptions', [] -# badLog("Unsubscribing user object to: #{JSON.stringify(user.toObject(), null, '\t')}") \ No newline at end of file +# badLog("Unsubscribing user object to: #{JSON.stringify(user.toObject(), null, '\t')}") diff --git a/server/sendwithus.coffee b/server/sendwithus.coffee index a9bb41bf4..c481893bf 100644 --- a/server/sendwithus.coffee +++ b/server/sendwithus.coffee @@ -10,4 +10,5 @@ module.exports.setupRoutes = (app) -> options = { DEBUG: not config.isProduction } module.exports.api = new sendwithusAPI swuAPIKey, options module.exports.templates = - welcome_email: 'utnGaBHuSU4Hmsi7qrAypU' \ No newline at end of file + welcome_email: 'utnGaBHuSU4Hmsi7qrAypU' + ladder_update_email: 'Xq3vSbDHXcjXfje7n2e7Eb' From ed93b2bbe56ceab2ba2747fe228bd2a862de352f Mon Sep 17 00:00:00 2001 From: Darredevil <alex.darredevil@gmail.com> Date: Sun, 9 Mar 2014 05:53:22 +0200 Subject: [PATCH 071/100] More than half done --- app/locale/ro.coffee | 110 +++++++++++++++++++++---------------------- 1 file changed, 55 insertions(+), 55 deletions(-) diff --git a/app/locale/ro.coffee b/app/locale/ro.coffee index 1fee5fe47..e9d7e3db6 100644 --- a/app/locale/ro.coffee +++ b/app/locale/ro.coffee @@ -219,64 +219,64 @@ module.exports = nativeDescription: "limba română", englishDescription: "Roman thang_title: "Editor Thang" thang_description: "Construiește unități ,definește logica lor,grafica și sunetul.Momentan suportă numai importare de grafică vectorială exportată din Flash." level_title: "Editor Nivele" -# level_description: "Includes the tools for scripting, uploading audio, and constructing custom logic to create all sorts of levels. Everything we use ourselves!" -# security_notice: "Many major features in these editors are not currently enabled by default. As we improve the security of these systems, they will be made generally available. If you'd like to use these features sooner, " -# contact_us: "contact us!" -# hipchat_prefix: "You can also find us in our" -# hipchat_url: "HipChat room." -# level_some_options: "Some Options?" -# level_tab_thangs: "Thangs" -# level_tab_scripts: "Scripts" -# level_tab_settings: "Settings" -# level_tab_components: "Components" -# level_tab_systems: "Systems" -# level_tab_thangs_title: "Current Thangs" -# level_tab_thangs_conditions: "Starting Conditions" -# level_tab_thangs_add: "Add Thangs" -# level_settings_title: "Settings" -# level_component_tab_title: "Current Components" -# level_component_btn_new: "Create New Component" -# level_systems_tab_title: "Current Systems" -# level_systems_btn_new: "Create New System" -# level_systems_btn_add: "Add System" -# level_components_title: "Back to All Thangs" -# level_components_type: "Type" -# level_component_edit_title: "Edit Component" -# level_system_edit_title: "Edit System" -# create_system_title: "Create New System" -# new_component_title: "Create New Component" -# new_component_field_system: "System" + level_description: "Include uneltele pentru scriptare, upload audio, și construcție de logică costum pentru toate tipurile de nivele.Tot ce folosim noi înșine!" + security_notice: "Multe setări majore de securitate în aceste editoare nu sunt momentan disponibile.Pe măsură ce îmbunătățim securitatea acestor sisteme, ele vor deveni disponibile. Dacă doriți să folosiți aceste setări mai devrme, " + contact_us: "contactați-ne!" + hipchat_prefix: "Ne puteți de asemenea găsi la" + hipchat_url: "HipChat." + level_some_options: "Opțiuni?" + level_tab_thangs: "Thangs" + level_tab_scripts: "Script-uri" + level_tab_settings: "Setări" + level_tab_components: "Componente" + level_tab_systems: "Sisteme" + level_tab_thangs_title: "Thangs actuali" + level_tab_thangs_conditions: "Condiți inițiale" + level_tab_thangs_add: "Adaugă Thangs" + level_settings_title: "Setări" + level_component_tab_title: "Componente actuale" + level_component_btn_new: "Crează componentă nouă" + level_systems_tab_title: "Sisteme actuale" + level_systems_btn_new: "Crează sistem nou" + level_systems_btn_add: "Adaugă Sistem" + level_components_title: "Înapoi la toți Thangs" + level_components_type: "Tip" + level_component_edit_title: "Editează Componenta" + level_system_edit_title: "Editează Sistem" + create_system_title: "Crează sistem nou" + new_component_title: "Crează componentă nouă" + new_component_field_system: "Sistem" -# article: -# edit_btn_preview: "Preview" -# edit_article_title: "Edit Article" + article: + edit_btn_preview: "Preview" + edit_article_title: "Editează Articol" -# general: -# and: "and" -# name: "Name" -# body: "Body" -# version: "Version" -# commit_msg: "Commit Message" -# version_history_for: "Version History for: " -# results: "Results" -# description: "Description" -# or: "or" -# email: "Email" -# message: "Message" + general: + and: "și" + name: "Nume" + body: "Corp" + version: "Versiune" + commit_msg: "Înregistrează Mesajul" + version_history_for: "Versiune istorie pentru: " + results: "Resultate" + description: "Descriere" + or: "sau" + email: "Email" + message: "Mesaj" -# about: -# who_is_codecombat: "Who is CodeCombat?" -# why_codecombat: "Why CodeCombat?" -# who_description_prefix: "together started CodeCombat in 2013. We also created " -# who_description_suffix: "in 2008, growing it to the #1 web and iOS application for learning to write Chinese and Japanese characters." -# who_description_ending: "Now it's time to teach people to write code." -# why_paragraph_1: "When making Skritter, George didn't know how to program and was constantly frustrated by his inability to implement his ideas. Afterwards, he tried learning, but the lessons were too slow. His housemate, wanting to reskill and stop teaching, tried Codecademy, but \"got bored.\" Each week another friend started Codecademy, then dropped off. We realized it was the same problem we'd solved with Skritter: people learning a skill via slow, intensive lessons when what they need is fast, extensive practice. We know how to fix that." -# why_paragraph_2: "Need to learn to code? You don't need lessons. You need to write a lot of code and have a great time doing it." -# why_paragraph_3_prefix: "That's what programming is about. It's gotta be fun. Not fun like" -# why_paragraph_3_italic: "yay a badge" -# why_paragraph_3_center: "but fun like" -# why_paragraph_3_italic_caps: "NO MOM I HAVE TO FINISH THE LEVEL!" -# why_paragraph_3_suffix: "That's why CodeCombat is a multiplayer game, not a gamified lesson course. We won't stop until you can't stop--but this time, that's a good thing." + about: + who_is_codecombat: "Cine este CodeCombat?" # I assume you meant (what) + why_codecombat: "De ce CodeCombat?" + who_description_prefix: "au pornit împreuna CodeCombat în 2013. Tot noi am creat " + who_description_suffix: "în 2008, dezvoltând aplicația web si iOS #1 de învățat cum să scri caractere Japoneze si Chinezești." + who_description_ending: "Acum este timpul să învățăm oamenii să scrie cod." + why_paragraph_1: "Când am dezolvat Skritter, George nu știa cum să programeze și era mereu frustat de inabilitatea sa de a putea implementa ideile sale. După aceea, a încercat să învețe, dar lecțiile erau prea lente. Colegul său , vrând să se reprofilze și să se lase de predat,a încercat Codecademy, dar \"s-a plictisit.\" În fiecare săptămână un alt prieten a început Codecademy, iar apoi s-a lăsat. Am realizat că este aceeași problemă care am rezolvat-u cu Skritter: oameni încercând să învețe ceva nou prin lecții lente și intensive când defapt ceea ce le trebuie sunt lecții rapide și multă practică. Noi știm cum să rezolvăm asta." + why_paragraph_2: "Trebuie să înveți să programezi? Nu-ți trebuie lecții. Trebuie să scri mult cod și să te distrezi făcând asta." + why_paragraph_3_prefix: "Despre asta este programarea. Trebuie să fie distractiv. Nu precum" + why_paragraph_3_italic: "wow o insignă" + why_paragraph_3_center: "ci" + why_paragraph_3_italic_caps: "TREBUIE SĂ TERMIN ACEST NIVEL!" + why_paragraph_3_suffix: "De aceea CodeCombat este un joc multiplayer, nu un curs transfigurat în joc. Nu ne vom opri până când tu nu te poți opri--și de data asta, e de bine." # why_paragraph_4: "If you're going to get addicted to some game, get addicted to this one and become one of the wizards of the tech age." # why_ending: "And hey, it's free. " # why_ending_url: "Start wizarding now!" From 82857fd0809f4201b42d66678ea2b1d3967b9a60 Mon Sep 17 00:00:00 2001 From: Nick Winter <livelily@gmail.com> Date: Sat, 8 Mar 2014 21:29:41 -0800 Subject: [PATCH 072/100] Was able to send out some emails. --- server/routes/mail.coffee | 60 +++++++++++++++++++++------------------ 1 file changed, 32 insertions(+), 28 deletions(-) diff --git a/server/routes/mail.coffee b/server/routes/mail.coffee index 2e693da66..ed6b58795 100644 --- a/server/routes/mail.coffee +++ b/server/routes/mail.coffee @@ -17,31 +17,31 @@ module.exports.setup = (app) -> app.get '/mail/cron/ladder-update', handleLadderUpdate handleLadderUpdate = (req, res) -> + res.send('Great work, Captain Cron! I can take it from here.') + res.end() emailDays = [1, 2, 4, 7, 30] now = new Date() getTimeFromDaysAgo = (daysAgo) -> # 2 hours before the date t = now - (86400 * daysAgo + 2 * 3600) * 1000 for daysAgo in emailDays + # Get every session that was submitted in a 5-minute window after the time. startTime = getTimeFromDaysAgo daysAgo endTime = startTime + 5 * 60 * 1000 - # Get every session that was submitted in a 5-minute window after the time. - findParameters = {submitted: true, submitDate: {$gt: startTime, $lte: endTime}} + #endTime = startTime + 1 * 60 * 60 * 1000 + findParameters = {submitted: true, submitDate: {$gt: new Date(startTime), $lte: new Date(endTime)}} # TODO: think about putting screenshots in the email - selectString = "creator team levelID totalScore matches" + selectString = "creator team levelName levelID totalScore matches submitted submitDate numberOfWinsAndTies numberOfLosses" query = LevelSession.find(findParameters) .select(selectString) .lean() mongoose = require 'mongoose' - mongoose.set 'debug', true - query.exec (err, results) -> - log.info "Yooooo got results: #{results.length}" - if err - log.error "Couldn't fetch ladder updates for", findParameters, "\nError: ", err - return errors.serverError res, "Ladder update email query failed: #{JSON.stringify(err)}" - sendLadderUpdateEmail result, daysAgo for result in results - res.send('') - res.end() + do (daysAgo) -> + query.exec (err, results) -> + if err + log.error "Couldn't fetch ladder updates for", findParameters, "\nError: ", err + return errors.serverError res, "Ladder update email query failed: #{JSON.stringify(err)}" + sendLadderUpdateEmail result, daysAgo for result in results sendLadderUpdateEmail = (session, daysAgo) -> User.findOne({_id: session.creator}).select("name email firstName lastName emailSubscriptions preferredLanguage").lean().exec (err, user) -> @@ -56,28 +56,32 @@ sendLadderUpdateEmail = (session, daysAgo) -> # TODO: do something with the preferredLanguage? context = email_id: sendwithus.templates.ladder_update_email - #recipient: - # address: user.email recipient: - address: 'nick@codecombat.com' - days_ago: daysAgo - name: name - wins: session.numberOfWinsAndTies - losses: session.numberOfLosses - total_score: session.totalScore - team: session.team - level: id - defeat: defeatContext - victory: victoryContext + #address: user.email + address: 'nick@codecombat.com' + name: name + email_data: + name: name + days_ago: daysAgo + wins: session.numberOfWinsAndTies + losses: session.numberOfLosses + total_score: Math.round(session.totalScore * 100) + team: session.team + level_name: session.levelName + ladder_url: "http://codecombat.com/play/ladder/#{session.levelID}#my-matches" + defeat: defeatContext + victory: victoryContext sendwithus.api.send context, (err, result) -> - log.error "Error sending ladder update email:", err, 'result', result + log.error "Error sending ladder update email:", err, 'result', result if err + # Fetch the most recent defeat and victory, if there are any. + # (We could look at strongest/weakest, but we'd have to fetch everyone, or denormalize more.) defeats = _.filter session.matches, (match) -> match.metrics.rank is 1 and match.opponents[0].metrics.rank is 0 victories = _.filter session.matches, (match) -> match.metrics.rank is 0 - defeat = _.sample defeats - victory = _.sample victories + defeat = _.last defeats + victory = _.last victories urlForMatch = (match) -> - "http://codecombat.com/play/ladder/#{session.levelID}?team=#{session.team}&session=#{session._id}&opponent=#{match.opponents[0].sessionID}" + "http://codecombat.com/play/level/#{session.levelID}?team=#{session.team}&session=#{session._id}&opponent=#{match.opponents[0].sessionID}" onFetchedDefeatedOpponent = (err, defeatedOpponent) -> if err From af2b43a3097514241a73a67b418f793c1d3c2f55 Mon Sep 17 00:00:00 2001 From: Darredevil <alex.darredevil@gmail.com> Date: Sun, 9 Mar 2014 15:09:22 +0200 Subject: [PATCH 073/100] Changed description of "Get to Locations" --- server/levels/level_schema.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/levels/level_schema.coffee b/server/levels/level_schema.coffee index cbfb84497..86f774ad0 100644 --- a/server/levels/level_schema.coffee +++ b/server/levels/level_schema.coffee @@ -33,7 +33,7 @@ GoalSchema = c.object {title: "Goal", description: "A goal that the player can a team: c.shortString(title: 'Team', description: 'Name of the team this goal is for, if it is not for all of the playable teams.') killThangs: c.array {title: "Kill Thangs", description: "A list of Thang IDs the player should kill, or team names.", uniqueItems: true, minItems: 1, "default": ["ogres"]}, thang saveThangs: c.array {title: "Save Thangs", description: "A list of Thang IDs the player should save, or team names", uniqueItems: true, minItems: 1, "default": ["humans"]}, thang - getToLocations: c.object {title: "Get To Locations", description: "TODO: explain", required: ["who", "targets"]}, + getToLocations: c.object {title: "Get To Locations", description: "Will be set off when any of the \"who\" touch any of the \"targets\" ", required: ["who", "targets"]}, who: c.array {title: "Who", description: "The Thangs who must get to the target locations.", minItems: 1}, thang targets: c.array {title: "Targets", description: "The target locations to which the Thangs must get.", minItems: 1}, thang keepFromLocations: c.object {title: "Keep From Locations", description: "TODO: explain", required: ["who", "targets"]}, From adc8c8eddf72711ede377be47c8e636feaeba122 Mon Sep 17 00:00:00 2001 From: gorodsb <gorodsb@gmail.com> Date: Sun, 9 Mar 2014 16:47:48 +0200 Subject: [PATCH 074/100] Update uk.coffee --- app/locale/uk.coffee | 92 ++++++++++++++++++++++---------------------- 1 file changed, 46 insertions(+), 46 deletions(-) diff --git a/app/locale/uk.coffee b/app/locale/uk.coffee index 52d4fd3b4..4dede7181 100644 --- a/app/locale/uk.coffee +++ b/app/locale/uk.coffee @@ -1,16 +1,16 @@ module.exports = nativeDescription: "українська мова", englishDescription: "Ukranian", translation: common: loading: "Завантаження..." -# saving: "Saving..." -# sending: "Sending..." -# cancel: "Cancel" -# save: "Save" -# delay_1_sec: "1 second" -# delay_3_sec: "3 seconds" -# delay_5_sec: "5 seconds" -# manual: "Manual" -# fork: "Fork" -# play: "Play" +# saving: "Збереження..." +# sending: "Відправлення..." +# cancel: "Відміна" +# save: "Зберегти" +# delay_1_sec: "1 секунда" +# delay_3_sec: "3 секунди" +# delay_5_sec: "5 секунд" +# manual: "Інструкція" +# fork: "Форк" +# play: "Грати" modal: close: "Закрити" @@ -31,28 +31,28 @@ module.exports = nativeDescription: "українська мова", englishDesc about: "Про нас" contact: "Контакти" twitter_follow: "Фоловити" -# employers: "Employers" +# employers: "Зайняті" # versions: -# save_version_title: "Save New Version" -# new_major_version: "New Major Version" -# cla_prefix: "To save changes, first you must agree to our" +# save_version_title: "Зберегти нову версію" +# new_major_version: "Зберегти основну версію" +# cla_prefix: "Для збереження змін, спочатку треба погодитись з нашим" # cla_url: "CLA" # cla_suffix: "." -# cla_agree: "I AGREE" +# cla_agree: "Я Згоден" login: sign_up: "створити акаунт" log_in: "Увійти" -# log_out: "Log Out" +# log_out: "Вийти" recover: "відновити акаунт" # recover: -# recover_account_title: "Recover Account" -# send_password: "Send Recovery Password" +# recover_account_title: "Відновити акаунт" +# send_password: "Вислати пароль відновлення" signup: -# create_account_title: "Create Account to Save Progress" +# create_account_title: "Створити акаунт, щоб зберегти прогрес" description: "Це безкоштовно. Просто зробіть кілька простих кроків, щоб бути готовим до гри:" email_announcements: "Отримувати анонси на email" coppa: "Ви старші 13 років або живете не в США" @@ -102,13 +102,13 @@ module.exports = nativeDescription: "українська мова", englishDesc subscribe_as_diplomat: "Записатися у Дипломати" # wizard_settings: -# title: "Wizard Settings" -# customize_avatar: "Customize Your Avatar" -# clothes: "Clothes" +# title: "Налаштування" +# customize_avatar: "Налаштувати аватар" +# clothes: "Одяг" # trim: "Trim" # cloud: "Cloud" # spell: "Spell" -# boots: "Boots" +# boots: "Черевики" # hue: "Hue" # saturation: "Saturation" # lightness: "Lightness" @@ -130,7 +130,7 @@ module.exports = nativeDescription: "українська мова", englishDesc new_password_verify: "Підтвердження паролю" email_subscriptions: "Email-підписки" email_announcements: "Оголошення" -# email_notifications_description: "Get periodic notifications for your account." +# email_notifications_description: "Отримувати періодичні нагадування для Вашого акаунта." email_announcements_description: "Отримувати електронні листи про останні новини CodeCombat." contributor_emails: "Підписки за класами учасників" contribute_prefix: "Нам потрібні люди, які приєднаються до нашої команди! Зайдіть на " @@ -199,18 +199,18 @@ module.exports = nativeDescription: "українська мова", englishDesc tome_select_a_thang: "Оберіть когось для " tome_available_spells: "Доступні закляття" hud_continue: "Продовжити (натисніть shift-space)" -# spell_saved: "Spell Saved" -# skip_tutorial: "Skip (esc)" +# spell_saved: "Заклинання збережено" +# skip_tutorial: "Пропустити (esc)" # admin: # av_title: "Admin Views" # av_entities_sub_title: "Entities" -# av_entities_users_url: "Users" +# av_entities_users_url: "користувачы" # av_entities_active_instances_url: "Active Instances" -# av_other_sub_title: "Other" +# av_other_sub_title: "Ынше" # av_other_debug_base_url: "Base (for debugging base.jade)" -# u_title: "User List" -# lg_title: "Latest Games" +# u_title: "Список користувачів" +# lg_title: "Останні ігри" # editor: # main_title: "CodeCombat Editors" @@ -222,8 +222,8 @@ module.exports = nativeDescription: "українська мова", englishDesc # level_title: "Level Editor" # level_description: "Includes the tools for scripting, uploading audio, and constructing custom logic to create all sorts of levels. Everything we use ourselves!" # security_notice: "Many major features in these editors are not currently enabled by default. As we improve the security of these systems, they will be made generally available. If you'd like to use these features sooner, " -# contact_us: "contact us!" -# hipchat_prefix: "You can also find us in our" +# contact_us: "Зв’язатися з нами!" +# hipchat_prefix: "Ви можете також знайти нас в нашому" # hipchat_url: "HipChat room." # level_some_options: "Some Options?" # level_tab_thangs: "Thangs" @@ -253,30 +253,30 @@ module.exports = nativeDescription: "українська мова", englishDesc # edit_article_title: "Edit Article" # general: -# and: "and" -# name: "Name" +# and: "та" +# name: "Ім’я" # body: "Body" -# version: "Version" +# version: "Версія" # commit_msg: "Commit Message" # version_history_for: "Version History for: " -# results: "Results" -# description: "Description" -# or: "or" +# results: "Результати" +# description: "Опис" +# or: "чи" # email: "Email" -# message: "Message" +# message: "Повідомлення" # about: -# who_is_codecombat: "Who is CodeCombat?" -# why_codecombat: "Why CodeCombat?" -# who_description_prefix: "together started CodeCombat in 2013. We also created " +# who_is_codecombat: "Хто є CodeCombat?" +# why_codecombat: "Чому CodeCombat?" +# who_description_prefix: "Взагалом розпочався CodeCombat у 2013. Ми також створили " # who_description_suffix: "in 2008, growing it to the #1 web and iOS application for learning to write Chinese and Japanese characters." -# who_description_ending: "Now it's time to teach people to write code." +# who_description_ending: "Зараз час вчити людей аисати код." # why_paragraph_1: "When making Skritter, George didn't know how to program and was constantly frustrated by his inability to implement his ideas. Afterwards, he tried learning, but the lessons were too slow. His housemate, wanting to reskill and stop teaching, tried Codecademy, but \"got bored.\" Each week another friend started Codecademy, then dropped off. We realized it was the same problem we'd solved with Skritter: people learning a skill via slow, intensive lessons when what they need is fast, extensive practice. We know how to fix that." # why_paragraph_2: "Need to learn to code? You don't need lessons. You need to write a lot of code and have a great time doing it." # why_paragraph_3_prefix: "That's what programming is about. It's gotta be fun. Not fun like" # why_paragraph_3_italic: "yay a badge" # why_paragraph_3_center: "but fun like" -# why_paragraph_3_italic_caps: "NO MOM I HAVE TO FINISH THE LEVEL!" +# why_paragraph_3_italic_caps: "НІ, МАМО, Я МУШУ ПРОЙТИ РІВЕНЬ!" # why_paragraph_3_suffix: "That's why CodeCombat is a multiplayer game, not a gamified lesson course. We won't stop until you can't stop--but this time, that's a good thing." # why_paragraph_4: "If you're going to get addicted to some game, get addicted to this one and become one of the wizards of the tech age." # why_ending: "And hey, it's free. " @@ -288,10 +288,10 @@ module.exports = nativeDescription: "українська мова", englishDesc # michael_description: "Programmer, sys-admin, and undergrad technical wunderkind, Michael is the person keeping our servers online." # legal: -# page_title: "Legal" +# page_title: "Юридичні нотатки" # opensource_intro: "CodeCombat is free to play and completely open source." # opensource_description_prefix: "Check out " -# github_url: "our GitHub" +# github_url: "наш GitHub" # opensource_description_center: "and help out if you like! CodeCombat is built on dozens of open source projects, and we love them. See " # archmage_wiki_url: "our Archmage wiki" # opensource_description_suffix: "for a list of the software that makes this game possible." From 6be9ff2bb7933ecae7800c497f5704f718368d25 Mon Sep 17 00:00:00 2001 From: gorodsb <gorodsb@users.noreply.github.com> Date: Sun, 9 Mar 2014 17:54:08 +0200 Subject: [PATCH 075/100] Update uk.coffee --- app/locale/uk.coffee | 94 ++++++++++++++++++++++---------------------- 1 file changed, 47 insertions(+), 47 deletions(-) diff --git a/app/locale/uk.coffee b/app/locale/uk.coffee index 4dede7181..b7e1bcf27 100644 --- a/app/locale/uk.coffee +++ b/app/locale/uk.coffee @@ -1,16 +1,16 @@ module.exports = nativeDescription: "українська мова", englishDescription: "Ukranian", translation: common: loading: "Завантаження..." -# saving: "Збереження..." -# sending: "Відправлення..." -# cancel: "Відміна" -# save: "Зберегти" -# delay_1_sec: "1 секунда" -# delay_3_sec: "3 секунди" -# delay_5_sec: "5 секунд" -# manual: "Інструкція" -# fork: "Форк" -# play: "Грати" + saving: "Збереження..." + sending: "Відправлення..." + cancel: "Відміна" + save: "Зберегти" + delay_1_sec: "1 секунда" + delay_3_sec: "3 секунди" + delay_5_sec: "5 секунд" + manual: "Інструкція" + fork: "Форк" + play: "Грати" modal: close: "Закрити" @@ -31,28 +31,28 @@ module.exports = nativeDescription: "українська мова", englishDesc about: "Про нас" contact: "Контакти" twitter_follow: "Фоловити" -# employers: "Зайняті" + employers: "Зайняті" # versions: -# save_version_title: "Зберегти нову версію" -# new_major_version: "Зберегти основну версію" -# cla_prefix: "Для збереження змін, спочатку треба погодитись з нашим" + save_version_title: "Зберегти нову версію" + new_major_version: "Зберегти основну версію" + cla_prefix: "Для збереження змін, спочатку треба погодитись з нашим" # cla_url: "CLA" # cla_suffix: "." -# cla_agree: "Я Згоден" + cla_agree: "Я Згоден" login: sign_up: "створити акаунт" log_in: "Увійти" -# log_out: "Вийти" + log_out: "Вийти" recover: "відновити акаунт" # recover: -# recover_account_title: "Відновити акаунт" -# send_password: "Вислати пароль відновлення" + recover_account_title: "Відновити акаунт" + send_password: "Вислати пароль відновлення" signup: -# create_account_title: "Створити акаунт, щоб зберегти прогрес" + create_account_title: "Створити акаунт, щоб зберегти прогрес" description: "Це безкоштовно. Просто зробіть кілька простих кроків, щоб бути готовим до гри:" email_announcements: "Отримувати анонси на email" coppa: "Ви старші 13 років або живете не в США" @@ -102,13 +102,13 @@ module.exports = nativeDescription: "українська мова", englishDesc subscribe_as_diplomat: "Записатися у Дипломати" # wizard_settings: -# title: "Налаштування" -# customize_avatar: "Налаштувати аватар" -# clothes: "Одяг" + title: "Налаштування" + customize_avatar: "Налаштувати аватар" + clothes: "Одяг" # trim: "Trim" # cloud: "Cloud" -# spell: "Spell" -# boots: "Черевики" + spell: "аклинанняЗ" + boots: "Черевики" # hue: "Hue" # saturation: "Saturation" # lightness: "Lightness" @@ -130,7 +130,7 @@ module.exports = nativeDescription: "українська мова", englishDesc new_password_verify: "Підтвердження паролю" email_subscriptions: "Email-підписки" email_announcements: "Оголошення" -# email_notifications_description: "Отримувати періодичні нагадування для Вашого акаунта." + email_notifications_description: "Отримувати періодичні нагадування для Вашого акаунта." email_announcements_description: "Отримувати електронні листи про останні новини CodeCombat." contributor_emails: "Підписки за класами учасників" contribute_prefix: "Нам потрібні люди, які приєднаються до нашої команди! Зайдіть на " @@ -199,18 +199,18 @@ module.exports = nativeDescription: "українська мова", englishDesc tome_select_a_thang: "Оберіть когось для " tome_available_spells: "Доступні закляття" hud_continue: "Продовжити (натисніть shift-space)" -# spell_saved: "Заклинання збережено" -# skip_tutorial: "Пропустити (esc)" + spell_saved: "Заклинання збережено" + skip_tutorial: "Пропустити (esc)" # admin: # av_title: "Admin Views" # av_entities_sub_title: "Entities" -# av_entities_users_url: "користувачы" + av_entities_users_url: "користувачі" # av_entities_active_instances_url: "Active Instances" # av_other_sub_title: "Ынше" # av_other_debug_base_url: "Base (for debugging base.jade)" -# u_title: "Список користувачів" -# lg_title: "Останні ігри" + u_title: "Список користувачів" + lg_title: "Останні ігри" # editor: # main_title: "CodeCombat Editors" @@ -222,8 +222,8 @@ module.exports = nativeDescription: "українська мова", englishDesc # level_title: "Level Editor" # level_description: "Includes the tools for scripting, uploading audio, and constructing custom logic to create all sorts of levels. Everything we use ourselves!" # security_notice: "Many major features in these editors are not currently enabled by default. As we improve the security of these systems, they will be made generally available. If you'd like to use these features sooner, " -# contact_us: "Зв’язатися з нами!" -# hipchat_prefix: "Ви можете також знайти нас в нашому" + contact_us: "Зв’язатися з нами!" + hipchat_prefix: "Ви можете також знайти нас в нашому" # hipchat_url: "HipChat room." # level_some_options: "Some Options?" # level_tab_thangs: "Thangs" @@ -253,30 +253,30 @@ module.exports = nativeDescription: "українська мова", englishDesc # edit_article_title: "Edit Article" # general: -# and: "та" -# name: "Ім’я" + and: "та" + name: "Ім’я" # body: "Body" -# version: "Версія" + version: "Версія" # commit_msg: "Commit Message" # version_history_for: "Version History for: " -# results: "Результати" -# description: "Опис" -# or: "чи" -# email: "Email" -# message: "Повідомлення" + results: "Результати" + description: "Опис" + or: "чи" + email: "Email" + message: "Повідомлення" # about: -# who_is_codecombat: "Хто є CodeCombat?" -# why_codecombat: "Чому CodeCombat?" -# who_description_prefix: "Взагалом розпочався CodeCombat у 2013. Ми також створили " + who_is_codecombat: "Хто є CodeCombat?" + why_codecombat: "Чому CodeCombat?" + who_description_prefix: "Взагалом розпочався CodeCombat у 2013. Ми також створили " # who_description_suffix: "in 2008, growing it to the #1 web and iOS application for learning to write Chinese and Japanese characters." -# who_description_ending: "Зараз час вчити людей аисати код." + who_description_ending: "Зараз час вчити людей писати код." # why_paragraph_1: "When making Skritter, George didn't know how to program and was constantly frustrated by his inability to implement his ideas. Afterwards, he tried learning, but the lessons were too slow. His housemate, wanting to reskill and stop teaching, tried Codecademy, but \"got bored.\" Each week another friend started Codecademy, then dropped off. We realized it was the same problem we'd solved with Skritter: people learning a skill via slow, intensive lessons when what they need is fast, extensive practice. We know how to fix that." # why_paragraph_2: "Need to learn to code? You don't need lessons. You need to write a lot of code and have a great time doing it." # why_paragraph_3_prefix: "That's what programming is about. It's gotta be fun. Not fun like" # why_paragraph_3_italic: "yay a badge" # why_paragraph_3_center: "but fun like" -# why_paragraph_3_italic_caps: "НІ, МАМО, Я МУШУ ПРОЙТИ РІВЕНЬ!" + why_paragraph_3_italic_caps: "НІ, МАМО, Я МАЮ ПРОЙТИ РІВЕНЬ!" # why_paragraph_3_suffix: "That's why CodeCombat is a multiplayer game, not a gamified lesson course. We won't stop until you can't stop--but this time, that's a good thing." # why_paragraph_4: "If you're going to get addicted to some game, get addicted to this one and become one of the wizards of the tech age." # why_ending: "And hey, it's free. " @@ -288,10 +288,10 @@ module.exports = nativeDescription: "українська мова", englishDesc # michael_description: "Programmer, sys-admin, and undergrad technical wunderkind, Michael is the person keeping our servers online." # legal: -# page_title: "Юридичні нотатки" + page_title: "Юридичні нотатки" # opensource_intro: "CodeCombat is free to play and completely open source." # opensource_description_prefix: "Check out " -# github_url: "наш GitHub" + github_url: "наш GitHub" # opensource_description_center: "and help out if you like! CodeCombat is built on dozens of open source projects, and we love them. See " # archmage_wiki_url: "our Archmage wiki" # opensource_description_suffix: "for a list of the software that makes this game possible." From 9e56540cee71a041dbc8ef25800ec54d27e40f49 Mon Sep 17 00:00:00 2001 From: Nick Winter <livelily@gmail.com> Date: Sun, 9 Mar 2014 11:03:21 -0700 Subject: [PATCH 076/100] Uncommented out some headers in uk.coffee localization. --- app/locale/uk.coffee | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/app/locale/uk.coffee b/app/locale/uk.coffee index b7e1bcf27..e93dae8d9 100644 --- a/app/locale/uk.coffee +++ b/app/locale/uk.coffee @@ -33,12 +33,12 @@ module.exports = nativeDescription: "українська мова", englishDesc twitter_follow: "Фоловити" employers: "Зайняті" -# versions: + versions: save_version_title: "Зберегти нову версію" new_major_version: "Зберегти основну версію" cla_prefix: "Для збереження змін, спочатку треба погодитись з нашим" -# cla_url: "CLA" -# cla_suffix: "." + cla_url: "CLA" + cla_suffix: "." cla_agree: "Я Згоден" login: @@ -47,7 +47,7 @@ module.exports = nativeDescription: "українська мова", englishDesc log_out: "Вийти" recover: "відновити акаунт" -# recover: + recover: recover_account_title: "Відновити акаунт" send_password: "Вислати пароль відновлення" @@ -101,7 +101,7 @@ module.exports = nativeDescription: "українська мова", englishDesc learn_more: "Узнати, як стати Дипломатом" subscribe_as_diplomat: "Записатися у Дипломати" -# wizard_settings: + wizard_settings: title: "Налаштування" customize_avatar: "Налаштувати аватар" clothes: "Одяг" @@ -144,11 +144,11 @@ module.exports = nativeDescription: "українська мова", englishDesc account_profile: edit_settings: "Змінити налаштування" profile_for_prefix: "Профіль для " -# profile_for_suffix: "" + profile_for_suffix: "" profile: "Профіль" user_not_found: "Користувача не знайдено. Будь ласка, перевірте URL." gravatar_not_found_mine: "Ми не можемо знайти ваш профіль, пов'язаний з:" -# gravatar_not_found_email_suffix: "." + gravatar_not_found_email_suffix: "." gravatar_signup_prefix: "Зареєструйтеся на " gravatar_signup_suffix: " щоб продовжувати" gravatar_not_found_other: "Нажаль, немає профіля, що пов'язаний з електронною адресою цієї людини." @@ -172,7 +172,7 @@ module.exports = nativeDescription: "українська мова", englishDesc reload_title: "Перезавантажити весь код?" reload_really: "Ви впевнені, що хочете перезавантажити цей рівень і почати спочатку?" reload_confirm: "Перезавантажити все" -# victory_title_prefix: "" + victory_title_prefix: "" victory_title_suffix: " закінчено" victory_sign_up: "Підписатися на оновлення" victory_sign_up_poke: "Хочете отримувати останні новини на email? Створіть безкоштовний акаунт, і ми будемо тримати вас в курсі!" @@ -202,17 +202,17 @@ module.exports = nativeDescription: "українська мова", englishDesc spell_saved: "Заклинання збережено" skip_tutorial: "Пропустити (esc)" -# admin: + admin: # av_title: "Admin Views" # av_entities_sub_title: "Entities" av_entities_users_url: "користувачі" # av_entities_active_instances_url: "Active Instances" -# av_other_sub_title: "Ынше" + av_other_sub_title: "Ынше" # av_other_debug_base_url: "Base (for debugging base.jade)" u_title: "Список користувачів" lg_title: "Останні ігри" -# editor: + editor: # main_title: "CodeCombat Editors" # main_description: "Build your own levels, campaigns, units and educational content. We provide all the tools you need!" # article_title: "Article Editor" @@ -252,7 +252,7 @@ module.exports = nativeDescription: "українська мова", englishDesc # edit_btn_preview: "Preview" # edit_article_title: "Edit Article" -# general: + general: and: "та" name: "Ім’я" # body: "Body" @@ -265,7 +265,7 @@ module.exports = nativeDescription: "українська мова", englishDesc email: "Email" message: "Повідомлення" -# about: + about: who_is_codecombat: "Хто є CodeCombat?" why_codecombat: "Чому CodeCombat?" who_description_prefix: "Взагалом розпочався CodeCombat у 2013. Ми також створили " @@ -287,7 +287,7 @@ module.exports = nativeDescription: "українська мова", englishDesc # jeremy_description: "Customer support mage, usability tester, and community organizer; you've probably already spoken with Jeremy." # michael_description: "Programmer, sys-admin, and undergrad technical wunderkind, Michael is the person keeping our servers online." -# legal: + legal: page_title: "Юридичні нотатки" # opensource_intro: "CodeCombat is free to play and completely open source." # opensource_description_prefix: "Check out " From 03abbc44cb913a6398482e204e83933242fbdd7c Mon Sep 17 00:00:00 2001 From: Nick Winter <livelily@gmail.com> Date: Sun, 9 Mar 2014 11:46:53 -0700 Subject: [PATCH 077/100] Better error messages for missing Component dependencies. --- app/models/Level.coffee | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/models/Level.coffee b/app/models/Level.coffee index 176b09aa6..c260a8178 100644 --- a/app/models/Level.coffee +++ b/app/models/Level.coffee @@ -52,13 +52,14 @@ module.exports = class Level extends CocoModel visit = (c) -> return if c in sorted lc = _.find levelComponents, {original: c.original} - console.error "Couldn't find lc for", c unless lc + console.error thang.id, "couldn't find lc for", c unless lc if lc.name is "Programmable" # Programmable always comes last visit c2 for c2 in _.without thang.components, c else for d in lc.dependencies or [] c2 = _.find thang.components, {original: d.original} + console.error thang.id, "couldn't find dependent Component", d.original, "from", lc.name unless c2 visit c2 if lc.name is "Collides" allied = _.find levelComponents, {name: "Allied"} From 34983059aaa9a3aa5fc7ce5394163abc6a11891c Mon Sep 17 00:00:00 2001 From: Nick Winter <livelily@gmail.com> Date: Sun, 9 Mar 2014 11:52:05 -0700 Subject: [PATCH 078/100] Fixed bug with trying to show challenger modals with no hash to indicate we should. --- app/views/play/ladder_view.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/play/ladder_view.coffee b/app/views/play/ladder_view.coffee index 7ed43660e..11283af13 100644 --- a/app/views/play/ladder_view.coffee +++ b/app/views/play/ladder_view.coffee @@ -67,7 +67,7 @@ module.exports = class LadderView extends RootView @insertSubView(@myMatchesTab = new MyMatchesTabView({}, @level, @sessions)) @refreshInterval = setInterval(@fetchSessionsAndRefreshViews.bind(@), 10000) hash = document.location.hash[1..] if document.location.hash - unless hash in ['my-matches', 'simulate', 'ladder'] + if hash and not (hash in ['my-matches', 'simulate', 'ladder']) @showPlayModal(hash) if @sessions.loaded fetchSessionsAndRefreshViews: -> From 3e82e0b599305ee8e99c0dbbb1a45243ac39a4ef Mon Sep 17 00:00:00 2001 From: Nick Winter <livelily@gmail.com> Date: Sun, 9 Mar 2014 12:53:11 -0700 Subject: [PATCH 079/100] Disallow submission of non-denormalized sessions to ladders. --- app/views/play/ladder/my_matches_tab.coffee | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/views/play/ladder/my_matches_tab.coffee b/app/views/play/ladder/my_matches_tab.coffee index b08a15083..f0f8ecc79 100644 --- a/app/views/play/ladder/my_matches_tab.coffee +++ b/app/views/play/ladder/my_matches_tab.coffee @@ -15,7 +15,7 @@ module.exports = class MyMatchesTabView extends CocoView constructor: (options, @level, @sessions) -> super(options) @refreshMatches() - + refreshMatches: -> @teams = teamDataFromLevel @level @nameMap = {} @@ -68,7 +68,7 @@ module.exports = class MyMatchesTabView extends CocoView team.matches = (convertMatch(match) for match in team.session?.get('matches') or []) team.matches.reverse() team.score = (team.session?.get('totalScore') or 10).toFixed(2) - + ctx afterRender: -> @@ -80,6 +80,7 @@ module.exports = class MyMatchesTabView extends CocoView @setRankingButtonText button, if @readyToRank(session) then 'rank' else 'unavailable' readyToRank: (session) -> + return false unless session?.get('levelID') # If it hasn't been denormalized, then it's not ready. c1 = session?.get('code') c2 = session?.get('submittedCode') c1 and not _.isEqual(c1, c2) @@ -94,7 +95,7 @@ module.exports = class MyMatchesTabView extends CocoView @setRankingButtonText(button, 'ranking') success = => @setRankingButtonText(button, 'ranked') failure = => @setRankingButtonText(button, 'failed') - + ajaxData = { session: sessionID, levelID: @level.id, originalLevelID: @level.attributes.original, levelMajorVersion: @level.attributes.version.major } $.ajax '/queue/scoring', { type: 'POST' From 2d32bc1ac42ca23dd28ed7614305d955ab8046b0 Mon Sep 17 00:00:00 2001 From: Nick Winter <livelily@gmail.com> Date: Sun, 9 Mar 2014 13:22:22 -0700 Subject: [PATCH 080/100] Reduced unnecessary name fetching. --- app/views/play/ladder/ladder_tab.coffee | 4 ++++ app/views/play/ladder/my_matches_tab.coffee | 13 +++++++++---- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/app/views/play/ladder/ladder_tab.coffee b/app/views/play/ladder/ladder_tab.coffee index b8b2d3615..466b0a402 100644 --- a/app/views/play/ladder/ladder_tab.coffee +++ b/app/views/play/ladder/ladder_tab.coffee @@ -74,6 +74,10 @@ class LeaderboardData # @playersBelow.once 'sync', @leaderboardPartLoaded, @ leaderboardPartLoaded: -> + # Forget loading the up-to-date names, that's way too slow for something that refreshes all the time, we learned. + @loaded = true + @trigger 'sync' + return if @session if @topPlayers.loaded # and @playersAbove.loaded and @playersBelow.loaded @loaded = true diff --git a/app/views/play/ladder/my_matches_tab.coffee b/app/views/play/ladder/my_matches_tab.coffee index f0f8ecc79..998b63bfa 100644 --- a/app/views/play/ladder/my_matches_tab.coffee +++ b/app/views/play/ladder/my_matches_tab.coffee @@ -14,23 +14,28 @@ module.exports = class MyMatchesTabView extends CocoView constructor: (options, @level, @sessions) -> super(options) + @nameMap = {} @refreshMatches() refreshMatches: -> @teams = teamDataFromLevel @level - @nameMap = {} @loadNames() loadNames: -> + # Only fetch the names for the userIDs we don't already have in @nameMap ids = [] for session in @sessions.models - ids.push match.opponents[0].userID for match in session.get('matches') or [] + for match in (session.get('matches') or []) + id = match.opponents[0].userID + ids.push id unless @nameMap[id] - success = (@nameMap) => + return @finishRendering() unless ids.length + + success = (nameMap) => for session in @sessions.models for match in session.get('matches') or [] opponent = match.opponents[0] - opponent.userName = @nameMap[opponent.userID] + @nameMap[opponent.userID] = nameMap[opponent.userID] @finishRendering() $.ajax('/db/user/-/names', { From e55a43b396afc8e489ac8c77273bc0f1750ee367 Mon Sep 17 00:00:00 2001 From: Rahazan <guido.reaver@gmail.com> Date: Sun, 9 Mar 2014 23:37:25 +0100 Subject: [PATCH 081/100] Fixed some formatting of sentences --- app/locale/nl.coffee | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/locale/nl.coffee b/app/locale/nl.coffee index f4c45edc5..eacff6de7 100644 --- a/app/locale/nl.coffee +++ b/app/locale/nl.coffee @@ -103,7 +103,7 @@ module.exports = nativeDescription: "Nederlands", englishDescription: "Dutch", t wizard_settings: title: "Tovenaar instellingen" - customize_avatar: "Bewerk jouw avatar" + customize_avatar: "Bewerk je avatar" clothes: "Kleren" trim: "Trim" cloud: "Wolk" @@ -357,7 +357,7 @@ module.exports = nativeDescription: "Nederlands", englishDescription: "Dutch", t contribute: page_title: "Bijdragen" - character_classes_title: "Karakter Klassen" + character_classes_title: "Karakterklassen" introduction_desc_intro: "We hebben hoge verwachtingen over CodeCombat." introduction_desc_pref: "We willen zijn waar programmeurs van alle niveaus komen om te leren en samen te spelen, anderen introduceren aan de wondere wereld van code, en de beste delen van de gemeenschap te reflecteren. We kunnen en willen dit niet alleen doen; wat projecten zoals GitHub, Stack Overflow en Linux groots en succesvol maken, zijn de mensen die deze software gebruiken en verbeteren. Daartoe, " introduction_desc_github_url: "CodeCombat is volledig open source" @@ -422,7 +422,7 @@ module.exports = nativeDescription: "Nederlands", englishDescription: "Dutch", t diplomat_launch_url: "release in oktober" diplomat_introduction_suf: "dan is het wel dat er een significante interesse is in CodeCombat in andere landen, vooral Brazilië! We zijn een corps aan vertalers aan het creëren dat ijverig de ene set woorden in een andere omzet om CodeCombat zo toegankelijk te maken als mogelijk in heel de wereld. Als jij het leuk vindt glimpsen op te vangen van aankomende content en deze levels zo snel mogelijk naar je landgenoten te krijgen, dan is dit de klasse voor jou." diplomat_attribute_1: "Vloeiend Engels en de taal waar naar je wilt vertalen kunnen spreken. Wanneer je moeilijke ideeën wilt overbrengen, is het belangrijk beide goed te kunnen!" - diplomat_join_pref_github: "Vind jouw taal haar locale bestand " + diplomat_join_pref_github: "Vind van jouw taal het locale bestand " diplomat_github_url: "op GitHub" diplomat_join_suf_github: ", edit het online, en submit een pull request. Daarnaast kun je hieronder aanvinken als je up-to-date wilt worden gehouden met nieuwe internationalisatie-ontwikkelingen." more_about_diplomat: "Leer meer over het worden van een geweldige Diplomaat" From 2daa70bef4f5bd9db264f10e87b1dfa5a18d5a89 Mon Sep 17 00:00:00 2001 From: Rahazan <guido.reaver@gmail.com> Date: Sun, 9 Mar 2014 23:45:04 +0100 Subject: [PATCH 082/100] Changed manual Manual is ambiguous, is it manual as in "manual instead of automatic" or manual as in a handbook/guide. I thought it was the latter, so I changed it here. --- app/locale/nl.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/locale/nl.coffee b/app/locale/nl.coffee index eacff6de7..99a0a54dc 100644 --- a/app/locale/nl.coffee +++ b/app/locale/nl.coffee @@ -8,7 +8,7 @@ module.exports = nativeDescription: "Nederlands", englishDescription: "Dutch", t delay_1_sec: "1 seconde" delay_3_sec: "3 secondes" delay_5_sec: "5 secondes" - manual: "Handmatig" + manual: "Handleiding" fork: "Fork" play: "Spelen" From 66b7ba40e84550e81553c0c46904f08349d49e72 Mon Sep 17 00:00:00 2001 From: Rahazan <guido.reaver@gmail.com> Date: Sun, 9 Mar 2014 23:54:06 +0100 Subject: [PATCH 083/100] Added missing Dutch diplomats --- app/templates/contribute/diplomat.jade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/templates/contribute/diplomat.jade b/app/templates/contribute/diplomat.jade index 06743761c..9f75161de 100644 --- a/app/templates/contribute/diplomat.jade +++ b/app/templates/contribute/diplomat.jade @@ -73,7 +73,7 @@ block content li German - Dirk, faabsen, HiroP0, Anon li Thai - Kamolchanok Jittrepit li Vietnamese - An Nguyen Hoang Thien - li Dutch - Glen De Cauwsemaecker + li Dutch - Glen De Cauwsemaecker, Guido Zuidhof, Ruben Vereecken li Greek - Stergios li Latin American Spanish - Jesús Ruppel, Matthew Burt, Mariano Luzza li Spain Spanish - Matthew Burt, DanielRodriguezRivero, Anon From 3d71fcbc708bee9fd0d62530ab491f79f865307e Mon Sep 17 00:00:00 2001 From: Nick Winter <livelily@gmail.com> Date: Sun, 9 Mar 2014 18:46:11 -0700 Subject: [PATCH 084/100] Wins, losses, and realizing that I'm not doing server code right. --- app/templates/play/ladder/my_matches_tab.jade | 6 +----- app/views/play/ladder/my_matches_tab.coffee | 3 +++ server/routes/mail.coffee | 16 +++++++++++++++- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/app/templates/play/ladder/my_matches_tab.jade b/app/templates/play/ladder/my_matches_tab.jade index d2b543478..c70192221 100644 --- a/app/templates/play/ladder/my_matches_tab.jade +++ b/app/templates/play/ladder/my_matches_tab.jade @@ -11,11 +11,7 @@ div#columns.row tr th(colspan=4, style="color: #{team.primaryColor}") - span Your - span - span= team.name - span - span Matches + span Your #{team.name} Matches - #{team.wins} Wins, #{team.losses} Losses if team.session button.btn.btn-sm.btn-warning.pull-right.rank-button(data-session-id=team.session.id) diff --git a/app/views/play/ladder/my_matches_tab.coffee b/app/views/play/ladder/my_matches_tab.coffee index 998b63bfa..a5e679f1b 100644 --- a/app/views/play/ladder/my_matches_tab.coffee +++ b/app/views/play/ladder/my_matches_tab.coffee @@ -73,6 +73,9 @@ module.exports = class MyMatchesTabView extends CocoView team.matches = (convertMatch(match) for match in team.session?.get('matches') or []) team.matches.reverse() team.score = (team.session?.get('totalScore') or 10).toFixed(2) + team.wins = _.filter(team.matches, {state: 'win'}).length + team.ties = _.filter(team.matches, {state: 'tie'}).length + team.losses = _.filter(team.matches, {state: 'loss'}).length ctx diff --git a/server/routes/mail.coffee b/server/routes/mail.coffee index ed6b58795..d629883d7 100644 --- a/server/routes/mail.coffee +++ b/server/routes/mail.coffee @@ -5,6 +5,7 @@ errors = require '../commons/errors' #request = require 'request' config = require '../../server_config' LevelSession = require '../levels/sessions/LevelSession.coffee' +Level = require '../levels/Level.coffee' log = require 'winston' sendwithus = require '../sendwithus' @@ -16,9 +17,23 @@ module.exports.setup = (app) -> app.all config.mail.mailchimpWebhook, handleMailchimpWebHook app.get '/mail/cron/ladder-update', handleLadderUpdate +getAllLadderScores = (next) -> + query = Level.find({type: 'ladder'}) + .select('levelID') + .lean() + query.exec (err, levels) -> + if err + log.error "Couldn't fetch ladder levels. Error: ", err + return next [] + for level in levels + for team in ['humans', 'ogres'] + 'I ... am not doing this.' + handleLadderUpdate = (req, res) -> + log.info("Going to see about sending ladder update emails.") res.send('Great work, Captain Cron! I can take it from here.') res.end() + # TODO: somehow fetch the histograms emailDays = [1, 2, 4, 7, 30] now = new Date() getTimeFromDaysAgo = (daysAgo) -> @@ -35,7 +50,6 @@ handleLadderUpdate = (req, res) -> query = LevelSession.find(findParameters) .select(selectString) .lean() - mongoose = require 'mongoose' do (daysAgo) -> query.exec (err, results) -> if err From 5e634f9915bfc980a59bcd4d824ef45f37ca0448 Mon Sep 17 00:00:00 2001 From: Darredevil <alex.darredevil@gmail.com> Date: Mon, 10 Mar 2014 04:10:29 +0200 Subject: [PATCH 085/100] Update ro.coffee Almost done. The legal stuff is the most boring thing i ever had to translate... --- app/locale/ro.coffee | 140 +++++++++++++++++++++---------------------- 1 file changed, 70 insertions(+), 70 deletions(-) diff --git a/app/locale/ro.coffee b/app/locale/ro.coffee index e9d7e3db6..531e3825f 100644 --- a/app/locale/ro.coffee +++ b/app/locale/ro.coffee @@ -265,7 +265,7 @@ module.exports = nativeDescription: "limba română", englishDescription: "Roman message: "Mesaj" about: - who_is_codecombat: "Cine este CodeCombat?" # I assume you meant (what) + who_is_codecombat: "Cine este CodeCombat?" why_codecombat: "De ce CodeCombat?" who_description_prefix: "au pornit împreuna CodeCombat în 2013. Tot noi am creat " who_description_suffix: "în 2008, dezvoltând aplicația web si iOS #1 de învățat cum să scri caractere Japoneze si Chinezești." @@ -277,77 +277,77 @@ module.exports = nativeDescription: "limba română", englishDescription: "Roman why_paragraph_3_center: "ci" why_paragraph_3_italic_caps: "TREBUIE SĂ TERMIN ACEST NIVEL!" why_paragraph_3_suffix: "De aceea CodeCombat este un joc multiplayer, nu un curs transfigurat în joc. Nu ne vom opri până când tu nu te poți opri--și de data asta, e de bine." -# why_paragraph_4: "If you're going to get addicted to some game, get addicted to this one and become one of the wizards of the tech age." -# why_ending: "And hey, it's free. " -# why_ending_url: "Start wizarding now!" -# george_description: "CEO, business guy, web designer, game designer, and champion of beginning programmers everywhere." -# scott_description: "Programmer extraordinaire, software architect, kitchen wizard, and master of finances. Scott is the reasonable one." -# nick_description: "Programming wizard, eccentric motivation mage, and upside-down experimenter. Nick can do anything and chooses to build CodeCombat." -# jeremy_description: "Customer support mage, usability tester, and community organizer; you've probably already spoken with Jeremy." -# michael_description: "Programmer, sys-admin, and undergrad technical wunderkind, Michael is the person keeping our servers online." + why_paragraph_4: "Dacă e să devi dependent de vreun joc, devino dependent de acesta și fi un vrăjitor al noii ere tehnologice." + why_ending: "Nu uita, este totul gratis. " + why_ending_url: "Devino un vrăjitor acum!" + george_description: "CEO, business guy, web designer, game designer, și campion al programatorilor începători." + scott_description: "Programmer extraordinaire, software architect, kitchen wizard, și maestru al finanțelor. Scott este cel rezonabil." + nick_description: "Programming wizard, eccentric motivation mage, and upside-down experimenter. Nick poate să facă orice si a ales să dezvolte CodeCombat." + jeremy_description: "Customer support mage, usability tester, and community organizer; probabil ca ați vorbit deja cu Jeremy." + michael_description: "Programmer, sys-admin, and undergrad technical wunderkind, Michael este cel care ține serverele in picioare." -# legal: -# page_title: "Legal" -# opensource_intro: "CodeCombat is free to play and completely open source." -# opensource_description_prefix: "Check out " -# github_url: "our GitHub" -# opensource_description_center: "and help out if you like! CodeCombat is built on dozens of open source projects, and we love them. See " -# archmage_wiki_url: "our Archmage wiki" -# opensource_description_suffix: "for a list of the software that makes this game possible." -# practices_title: "Respectful Best Practices" -# practices_description: "These are our promises to you, the player, in slightly less legalese." -# privacy_title: "Privacy" -# privacy_description: "We will not sell any of your personal information. We intend to make money through recruitment eventually, but rest assured we will not distribute your personal information to interested companies without your explicit consent." -# security_title: "Security" -# security_description: "We strive to keep your personal information safe. As an open source project, our site is freely open to anyone to review and improve our security systems." -# email_title: "Email" -# email_description_prefix: "We will not inundate you with spam. Through" -# email_settings_url: "your email settings" -# email_description_suffix: "or through links in the emails we send, you can change your preferences and easily unsubscribe at any time." -# cost_title: "Cost" -# cost_description: "Currently, CodeCombat is 100% free! One of our main goals is to keep it that way, so that as many people can play as possible, regardless of place in life. If the sky darkens, we might have to charge subscriptions or for some content, but we'd rather not. With any luck, we'll be able to sustain the company with:" -# recruitment_title: "Recruitment" -# recruitment_description_prefix: "Here on CodeCombat, you're going to become a powerful wizard–not just in the game, but also in real life." -# url_hire_programmers: "No one can hire programmers fast enough" -# recruitment_description_suffix: "so once you've sharpened your skills and if you agree, we will demo your best coding accomplishments to the thousands of employers who are drooling for the chance to hire you. They pay us a little, they pay you" -# recruitment_description_italic: "a lot" -# recruitment_description_ending: "the site remains free and everybody's happy. That's the plan." -# copyrights_title: "Copyrights and Licenses" -# contributor_title: "Contributor License Agreement" -# contributor_description_prefix: "All contributions, both on the site and on our GitHub repository, are subject to our" -# cla_url: "CLA" -# contributor_description_suffix: "to which you should agree before contributing." -# code_title: "Code - MIT" -# code_description_prefix: "All code owned by CodeCombat or hosted on codecombat.com, both in the GitHub repository or in the codecombat.com database, is licensed under the" -# mit_license_url: "MIT license" -# code_description_suffix: "This includes all code in Systems and Components that are made available by CodeCombat for the purpose of creating levels." -# art_title: "Art/Music - Creative Commons " -# art_description_prefix: "All common content is available under the" -# cc_license_url: "Creative Commons Attribution 4.0 International License" -# art_description_suffix: "Common content is anything made generally available by CodeCombat for the purpose of creating Levels. This includes:" -# art_music: "Music" -# art_sound: "Sound" -# art_artwork: "Artwork" -# art_sprites: "Sprites" -# art_other: "Any and all other non-code creative works that are made available when creating Levels." -# art_access: "Currently there is no universal, easy system for fetching these assets. In general, fetch them from the URLs as used by the site, contact us for assistance, or help us in extending the site to make these assets more easily accessible." -# art_paragraph_1: "For attribution, please name and link to codecombat.com near where the source is used or where appropriate for the medium. For example:" -# use_list_1: "If used in a movie or another game, include codecombat.com in the credits." -# use_list_2: "If used on a website, include a link near the usage, for example underneath an image, or in a general attributions page where you might also mention other Creative Commons works and open source software being used on the site. Something that's already clearly referencing CodeCombat, such as a blog post mentioning CodeCombat, does not need some separate attribution." -# art_paragraph_2: "If the content being used is created not by CodeCombat but instead by a user of codecombat.com, attribute them instead, and follow attribution directions provided in that resource's description if there are any." -# rights_title: "Rights Reserved" -# rights_desc: "All rights are reserved for Levels themselves. This includes" -# rights_scripts: "Scripts" -# rights_unit: "Unit configuration" -# rights_description: "Description" -# rights_writings: "Writings" -# rights_media: "Media (sounds, music) and any other creative content made specifically for that Level and not made generally available when creating Levels." -# rights_clarification: "To clarify, anything that is made available in the Level Editor for the purpose of making levels is under CC, whereas the content created with the Level Editor or uploaded in the course of creation of Levels is not." -# nutshell_title: "In a Nutshell" -# nutshell_description: "Any resources we provide in the Level Editor are free to use as you like for creating Levels. But we reserve the right to restrict distribution of the Levels themselves (that are created on codecombat.com) so that they may be charged for in the future, if that's what ends up happening." -# canonical: "The English version of this document is the definitive, canonical version. If there are any discrepencies between translations, the English document takes precedence." + legal: + page_title: "Aspecte Legale" + opensource_intro: "CodeCombat este free-to-play și complet open source." + opensource_description_prefix: "Vizitează " + github_url: "pagina noastră de GitHub" + opensource_description_center: "și ajută-ne dacă îți place! CodeCombat este construit peste o mulțime de proiecte open source, care noi le iubim. Vizitați" + archmage_wiki_url: "Archmage wiki" + opensource_description_suffix: "pentru o listă cu software-ul care face acest joc posibil." +# practices_title: "Respectful Best Practices" #not sure what you mean here? other word for /practices/? + practices_description: "Acestea sunt promisiunile noastre către tine, jucătorul, fără așa mulți termeni legali." + privacy_title: "Confidenţialitate şi termeni" + privacy_description: "Noi nu vom vinde nici o informație personală. Intenționăm să obținem profit prin recrutare eventual, dar stați liniștiți , nu vă vom vinde informațiile personale companiilor interesate fără consimțământul vostru explicit." + security_title: "Securitate" + security_description: "Ne străduim să vă protejăm informațiile personale. Fiind un proiect open-source, site-ul nostru oferă oricui posibilitatea de a ne revizui și îmbunătăți sistemul de securitate." + email_title: "Email" + email_description_prefix: "Noi nu vă vom inunda cu spam. Prin" + email_settings_url: "setările tale de email" + email_description_suffix: " sau prin link-urile din email-urile care vi le trimitem, puteți să schimbați preferințele și să vâ dezabonați oricând." + cost_title: "Cost" + cost_description: "Momentan, CodeCombat este 100% gratis! Unul dintre obiectele noastre principale este să îl menținem așa, astfel încât să poată juca cât mai mulți oameni. Dacă va fi nevoie , s-ar putea să percepem o plată pentru o pentru anumite servici,dar am prefera să nu o facem. Cu puțin noroc, vom putea susține compania cu:" + recruitment_title: "Recrutare" + recruitment_description_prefix: "Aici la CodeCombat, vei deveni un vrăjitor puternic nu doar în joc , ci și în viața reală." + url_hire_programmers: "Nimeni nu poate angaja programatori destul de rapid" + recruitment_description_suffix: "așa că odată ce ți-ai dezvoltat abilitățile și esti de acord, noi vom trimite un demo cu cele mai bune realizări ale tale către miile de angajatori care se omoară să pună mâna pe tine. Pe noi ne plătesc puțin, pe tine te vor plăti" + recruitment_description_italic: "mult" + recruitment_description_ending: "site-ul rămâne gratis și toată lumea este fericită. Acesta este planul." + copyrights_title: "Drepturi de autor și licențe" + contributor_title: "Acord de licență Contributor" + contributor_description_prefix: "Toți contribuitorii, atât pe site cât și pe GitHub-ul nostru, sunt supuși la" + cla_url: "ALC" + contributor_description_suffix: "la care trebuie să fi de accord înainte să poți contribui." + code_title: "Code - MIT" + code_description_prefix: "Tot codul deținut de CodeCombat sau hostat pe codecombat.com, atât pe GitHub cât și în baza de date codecombat.com, este licențiată sub" + mit_license_url: "MIT license" + code_description_suffix: "Asta include tot codul din Systems și Components care este oferit de către CodeCombat cu scopul de a crea nivele." + art_title: "Artă/Muzică - Conținut Comun " + art_description_prefix: "Tot conținutul creativ/artistic este valabil sub" + cc_license_url: "Creative Commons Attribution 4.0 International License" + art_description_suffix: "Conținut comun este orice făcut general valabil de către CodeCombat cu scopul de a crea nivele. Asta include:" + art_music: "Muzică" + art_sound: "Sunet" + art_artwork: "Artwork" + art_sprites: "Sprites" #can t be translated, either suggest alternative name or must be left like this + art_other: "Orice si toate celelalte creații non-cod care sunt disponibile când se crează nivele." + art_access: "Momentan nu există nici un sistem universal,ușor pentru preluarea acestor bunuri. În general, preluați-le precum site-ul din URL-urile folosite, contactați-ne pentru asistență, sau ajutați-ne sa extindem site-ul pentru a face aceste bunuri mai ușor accesibile." + art_paragraph_1: "Pentru atribuire, vă rugăm numiți și lăsați referire link la codecombat.com unde este folosită sursa sau unde este adecvat pentru mediu. De exemplu:" + use_list_1: "Dacă este folosit într-un film sau alt joc, includeți codecombat.com la credite." + use_list_2: "Dacă este folosit pe un site, includeți un link in apropiere, de exemplu sub o imagine, sau in pagina generală de atribuiri unde menționați și alte Bunuri Creative și software open source folosit pe site. Ceva care face referință explicit la CodeCombat, precum o postare pe un blog care menționează CodeCombat, nu trebuie să facă o atribuire separată." + art_paragraph_2: "Dacă conținutul folosit nu este creat de către CodeCombat ci de către un utilizator al codecombat.com,atunci faceți referință către ei, și urmăriți indicațiile de atribuire prevăzute în descrierea resursei dacă există." + rights_title: "Drepturi rezervate" + rights_desc: "Toate drepturile sunt rezervate pentru Nivele în sine. Asta include" + rights_scripts: "Script-uri" + rights_unit: "Configurații de unități" + rights_description: "Descriere" + rights_writings: "Scrieri" + rights_media: "Media (sunete, muzică) și orice alt conținut creativ dezvoltat special pentru acel nivel care nu este valabil în mod normal pentru creat nivele." + rights_clarification: "Pentru a clarifica, orice este valabil in Editorul de Nivele pentru scopul de a crea nivele se află sub CC,pe când conținutul creat cu Editorul de Nivele sau încărcat pentru a face nivelul nu se află." #CC stands for...? + nutshell_title: "Pe scurt" + nutshell_description: "Orice resurse vă punem la dispoziție în Editorul de Nivele puteți folosi liber cum vreți pentru a crea nivele. Dar ne rezervăm dreptul de a rezerva distribuția de nivele în sine (care sunt create pe codecombat.com) astfel încât să se poată percepe o taxă pentru ele pe vitor, dacă se va ajunge la așa ceva." + canonical: "The English version of this document is the definitive, canonical version. If there are any discrepencies between translations, the English document takes precedence." -# contribute: + contribute: # page_title: "Contributing" # character_classes_title: "Character Classes" # introduction_desc_intro: "We have high hopes for CodeCombat." From 69b902f62ac8a7ad6cb2cfb01a3cacad559175b4 Mon Sep 17 00:00:00 2001 From: Michael Schmatz <schmatz@umich.edu> Date: Mon, 10 Mar 2014 08:14:28 -0700 Subject: [PATCH 086/100] Added support for score history --- server/levels/sessions/level_session_schema.coffee | 12 ++++++++++++ server/queues/scoring.coffee | 7 ++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/server/levels/sessions/level_session_schema.coffee b/server/levels/sessions/level_session_schema.coffee index 290422c10..ca8df4c05 100644 --- a/server/levels/sessions/level_session_schema.coffee +++ b/server/levels/sessions/level_session_schema.coffee @@ -146,6 +146,18 @@ _.extend LevelSessionSchema.properties, numberOfLosses: type: 'number' default: 0 + + scoreHistory: + type: 'array' + title: 'Score History' + description: 'A list of objects representing the score history of a session' + items: + title: 'Score History Point' + description: 'An array with the format [unix timestamp, totalScore]' + type: 'array' + items: + type: 'number' + matches: type: 'array' diff --git a/server/queues/scoring.coffee b/server/queues/scoring.coffee index 1e2cd3f29..0462cd503 100644 --- a/server/queues/scoring.coffee +++ b/server/queues/scoring.coffee @@ -443,10 +443,15 @@ updateScoreInSession = (scoreObject,callback) -> if err? then return callback err, null session = session.toObject() + newTotalScore = scoreObject.meanStrength - 1.8 * scoreObject.standardDeviation + scoreHistoryAddition = [Date.now(), newTotalScore] updateObject = meanStrength: scoreObject.meanStrength standardDeviation: scoreObject.standardDeviation - totalScore: scoreObject.meanStrength - 1.8 * scoreObject.standardDeviation + totalScore: newTotalScore + $push: + scoreHistory: scoreHistoryAddition + LevelSession.update {"_id": scoreObject.id}, updateObject, callback log.info "New total score for session #{scoreObject.id} is #{updateObject.totalScore}" From c6b1d9089cd5b7802291757168adfa823030603a Mon Sep 17 00:00:00 2001 From: Nick Winter <livelily@gmail.com> Date: Mon, 10 Mar 2014 08:45:36 -0700 Subject: [PATCH 087/100] Fixed sounds spamming on last frame. --- app/lib/surface/Surface.coffee | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/app/lib/surface/Surface.coffee b/app/lib/surface/Surface.coffee index feb87fc93..5f8ae5f49 100644 --- a/app/lib/surface/Surface.coffee +++ b/app/lib/surface/Surface.coffee @@ -497,13 +497,15 @@ module.exports = Surface = class Surface extends CocoClass # seems to be a bug where only one object can register with the Ticker... oldFrame = @currentFrame oldWorldFrame = Math.floor oldFrame + lastFrame = @world.totalFrames - 1 while true Dropper.tick() @trailmaster.tick() if @trailmaster # Skip some frame updates unless we're playing and not at end (or we haven't drawn much yet) - frameAdvanced = (@playing and @currentFrame < @world.totalFrames) or @totalFramesDrawn < 2 - @currentFrame += @world.frameRate / @options.frameRate if frameAdvanced and @playing - @currentFrame = Math.min(@currentFrame, @world.totalFrames - 1) + frameAdvanced = (@playing and @currentFrame < lastFrame) or @totalFramesDrawn < 2 + if frameAdvanced and @playing + @currentFrame += @world.frameRate / @options.frameRate + @currentFrame = Math.min @currentFrame, lastFrame newWorldFrame = Math.floor @currentFrame worldFrameAdvanced = newWorldFrame isnt oldWorldFrame if worldFrameAdvanced @@ -513,6 +515,7 @@ module.exports = Surface = class Surface extends CocoClass break unless Dropper.drop() if frameAdvanced and not worldFrameAdvanced # We didn't end the above loop on an integer frame, so do the world state update. + console.log "Restore world state" @restoreWorldState() # these are skipped for dropped frames From 6a71e97204743c279a8e0b8fb672659a99871688 Mon Sep 17 00:00:00 2001 From: Nick Winter <livelily@gmail.com> Date: Mon, 10 Mar 2014 09:37:05 -0700 Subject: [PATCH 088/100] Improvements to HUD actions. --- app/styles/play/level/hud.sass | 6 +++--- app/views/play/level/hud_view.coffee | 7 ++++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/app/styles/play/level/hud.sass b/app/styles/play/level/hud.sass index 0379fc151..c83b20933 100644 --- a/app/styles/play/level/hud.sass +++ b/app/styles/play/level/hud.sass @@ -208,11 +208,11 @@ height: 19px div - @include box-sizing(border-box) + border-radius: 1px background-color: #6BA1C8 height: 100% - border-bottom: 2px groove #201B15 - border-right: 1px solid #201B15 + border-bottom: 2px groove darken(#6BA1C8, 30%) + border-right: 1px solid darken(#6BA1C8, 10%) position: absolute top: 0 diff --git a/app/views/play/level/hud_view.coffee b/app/views/play/level/hud_view.coffee index 662c9fc8f..9abd6d726 100644 --- a/app/views/play/level/hud_view.coffee +++ b/app/views/play/level/hud_view.coffee @@ -135,7 +135,7 @@ module.exports = class HUDView extends View props = @$el.find('.thang-props') props.find(":not(.thang-name)").remove() props.find('.thang-name').text(if @thang.type then "#{@thang.id} - #{@thang.type}" else @thang.id) - propNames = @thang.hudProperties ? [] + propNames = _.without @thang.hudProperties ? [], 'action' nColumns = Math.ceil propNames.length / 5 columns = ($('<div class="thang-props-column"></div>').appendTo(props) for i in [0 ... nColumns]) for prop, i in propNames @@ -316,11 +316,12 @@ module.exports = class HUDView extends View @timespans = {} dt = @thang.world.dt actionHistory = @thang.world.actionsForThang @thang.id, true - [lastFrame, lastAction] = [0, 'idle'] + console.log "got actionHistory", actionHistory + [lastFrame, lastAction] = [0, null] for hist in actionHistory.concat {frame: @thang.world.totalFrames, name: 'END'} [newFrame, newAction] = [hist.frame, hist.name] continue if newAction is lastAction - if newFrame > lastFrame + if newFrame > lastFrame and lastAction # TODO: don't push it if it didn't exist until then (@timespans[lastAction] ?= []).push [lastFrame * dt, newFrame * dt] [lastFrame, lastAction] = [newFrame, newAction] From 234b3f105d8ea2f74fad3fa83b8db7d360575baf Mon Sep 17 00:00:00 2001 From: Nick Winter <livelily@gmail.com> Date: Mon, 10 Mar 2014 10:36:28 -0700 Subject: [PATCH 089/100] Reworked ladder game victory modal main call to action to actually submit game for ranking. --- app/lib/surface/Surface.coffee | 1 - app/locale/en.coffee | 3 +++ app/templates/play/level/modal/victory.jade | 6 ++++-- app/views/play/ladder/my_matches_tab.coffee | 1 - app/views/play/level/hud_view.coffee | 1 - .../play/level/modal/victory_modal.coffee | 20 +++++++++++++++++++ 6 files changed, 27 insertions(+), 5 deletions(-) diff --git a/app/lib/surface/Surface.coffee b/app/lib/surface/Surface.coffee index 5f8ae5f49..fd278b8cc 100644 --- a/app/lib/surface/Surface.coffee +++ b/app/lib/surface/Surface.coffee @@ -515,7 +515,6 @@ module.exports = Surface = class Surface extends CocoClass break unless Dropper.drop() if frameAdvanced and not worldFrameAdvanced # We didn't end the above loop on an integer frame, so do the world state update. - console.log "Restore world state" @restoreWorldState() # these are skipped for dropped frames diff --git a/app/locale/en.coffee b/app/locale/en.coffee index e3c694f82..2766dda9c 100644 --- a/app/locale/en.coffee +++ b/app/locale/en.coffee @@ -179,6 +179,9 @@ module.exports = nativeDescription: "English", englishDescription: "English", tr victory_sign_up: "Sign Up to Save Progress" victory_sign_up_poke: "Want to save your code? Create a free account!" victory_rate_the_level: "Rate the level: " + victory_rank_my_game: "Rank My Game" + victory_ranking_game: "Submitting..." + victory_return_to_ladder: "Return to Ladder" victory_play_next_level: "Play Next Level" victory_go_home: "Go Home" victory_review: "Tell us more!" diff --git a/app/templates/play/level/modal/victory.jade b/app/templates/play/level/modal/victory.jade index 45c7f567f..6329f0bbe 100644 --- a/app/templates/play/level/modal/victory.jade +++ b/app/templates/play/level/modal/victory.jade @@ -14,8 +14,10 @@ div!= body .modal-footer - if level.get('type') === 'ladder' - a.btn.btn-primary(href="/play/ladder/#{level.get('slug')}#my-matches", data-dismiss="modal", data-i18n="play_level.victory_go_home") Go Home + if readyToRank + button.btn.btn-success.rank-game-button(data-i18n="play_level.victory_rank_my_game") Rank My Game + else if level.get('type') === 'ladder' + a.btn.btn-primary(href="/play/ladder/#{level.get('slug')}#my-matches", data-dismiss="modal", data-i18n="play_level.victory_go_ladder") Return to Ladder else if hasNextLevel button.btn.btn-primary.next-level-button(data-dismiss="modal", data-i18n="play_level.victory_play_next_level") Play Next Level else diff --git a/app/views/play/ladder/my_matches_tab.coffee b/app/views/play/ladder/my_matches_tab.coffee index a5e679f1b..a094aef82 100644 --- a/app/views/play/ladder/my_matches_tab.coffee +++ b/app/views/play/ladder/my_matches_tab.coffee @@ -94,7 +94,6 @@ module.exports = class MyMatchesTabView extends CocoView c1 and not _.isEqual(c1, c2) rankSession: (e) -> - console.log "Clicked" button = $(e.target).closest('.rank-button') sessionID = button.data('session-id') session = _.find @sessions.models, { id: sessionID } diff --git a/app/views/play/level/hud_view.coffee b/app/views/play/level/hud_view.coffee index 9abd6d726..324c5e68b 100644 --- a/app/views/play/level/hud_view.coffee +++ b/app/views/play/level/hud_view.coffee @@ -316,7 +316,6 @@ module.exports = class HUDView extends View @timespans = {} dt = @thang.world.dt actionHistory = @thang.world.actionsForThang @thang.id, true - console.log "got actionHistory", actionHistory [lastFrame, lastAction] = [0, null] for hist in actionHistory.concat {frame: @thang.world.totalFrames, name: 'END'} [newFrame, newAction] = [hist.frame, hist.name] diff --git a/app/views/play/level/modal/victory_modal.coffee b/app/views/play/level/modal/victory_modal.coffee index 6773982ad..c71b2bcac 100644 --- a/app/views/play/level/modal/victory_modal.coffee +++ b/app/views/play/level/modal/victory_modal.coffee @@ -11,6 +11,7 @@ module.exports = class VictoryModal extends View events: 'click .next-level-button': 'onPlayNextLevel' + 'click .rank-game-button': 'onRankGame' # review events 'mouseover .rating i': (e) -> @showStars(@starNum($(e.target))) @@ -58,6 +59,21 @@ module.exports = class VictoryModal extends View @saveReview() if @$el.find('.review textarea').val() Backbone.Mediator.publish('play-next-level') + onRankGame: (e) -> + button = @$el.find('.rank-game-button') + button.text($.i18n.t('play_level.victory_ranking_game', defaultValue: 'Submitting...')) + button.prop 'disabled', true + ajaxData = session: @session.id, levelID: @level.id, originalLevelID: @level.get('original'), levelMajorVersion: @level.get('version').major + ladderURL = "/play/ladder/#{@level.get('slug')}#my-matches" + goToLadder = -> Backbone.Mediator.publish 'router:navigate', route: ladderURL + $.ajax '/queue/scoring', + type: 'POST' + data: ajaxData + success: goToLadder + failure: (response) -> + console.error "Couldn't submit game for ranking:", response + goToLadder() + getRenderData: -> c = super() c.body = @body @@ -65,6 +81,10 @@ module.exports = class VictoryModal extends View c.hasNextLevel = _.isObject(@level.get('nextLevel')) and (@level.get('name') isnt "Mobile Artillery") c.levelName = @level.get('i18n')?[me.lang()]?.name ? @level.get('name') c.level = @level + if c.level.get('type') is 'ladder' + c1 = @session?.get('code') + c2 = @session?.get('submittedCode') + c.readyToRank = @session.get('levelID') and c1 and not _.isEqual(c1, c2) if me.get 'hourOfCode' # Show the Hour of Code "I'm Done" tracking pixel after they played for 30 minutes elapsed = (new Date() - new Date(me.get('dateCreated'))) From 2ed83f4b40023edff4fd189915f521df331b521b Mon Sep 17 00:00:00 2001 From: Michael Schmatz <schmatz@umich.edu> Date: Mon, 10 Mar 2014 10:56:33 -0700 Subject: [PATCH 090/100] Sped up my_sessions using projection, bypassing auth --- server/levels/level_handler.coffee | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/server/levels/level_handler.coffee b/server/levels/level_handler.coffee index 94bf33dd8..01b7eec40 100644 --- a/server/levels/level_handler.coffee +++ b/server/levels/level_handler.coffee @@ -89,15 +89,27 @@ LevelHandler = class LevelHandler extends Handler # associated with the handler, because the handler might return a different type # of model, like in this case. Refactor to move that logic to the model instead. - getMySessions: (req, res, id) -> - @fetchLevelByIDAndHandleErrors id, req, res, (err, level) => + getMySessions: (req, res, slugOrID) -> + findParameters = {} + if Handler.isID slugOrID + findParameters["_id"] = slugOrID + else + findParameters["slug"] = slugOrID + selectString = 'original version.major permissions' + query = Level.findOne(findParameters) + .select(selectString) + .lean() + + query.exec (err, level) => + return @sendDatabaseError(res, err) if err + return @sendNotFoundError(res) unless level? sessionQuery = level: original: level.original.toString() majorVersion: level.version.major creator: req.user._id+'' - - query = Session.find(sessionQuery) + + query = Session.find(sessionQuery).select('-screenshot') query.exec (err, results) => if err then @sendDatabaseError(res, err) else @sendSuccess res, results From d4abad88a2f479525bdf124bc8ea78f12ac713f2 Mon Sep 17 00:00:00 2001 From: Michael Schmatz <schmatz@umich.edu> Date: Mon, 10 Mar 2014 12:57:25 -0700 Subject: [PATCH 091/100] Removed leaderboard mongo sort --- server/levels/level_handler.coffee | 1 - 1 file changed, 1 deletion(-) diff --git a/server/levels/level_handler.coffee b/server/levels/level_handler.coffee index 01b7eec40..4a2bdd24a 100644 --- a/server/levels/level_handler.coffee +++ b/server/levels/level_handler.coffee @@ -140,7 +140,6 @@ LevelHandler = class LevelHandler extends Handler query = Session .find(sessionsQueryParameters) .limit(req.query.limit) - .sort(sortParameters) .select(selectProperties.join ' ') query.exec (err, resultSessions) => From 6055512ac7bc57be4add9e633e0f90656a6555c2 Mon Sep 17 00:00:00 2001 From: Nick Winter <livelily@gmail.com> Date: Mon, 10 Mar 2014 13:20:00 -0700 Subject: [PATCH 092/100] Working on ladder updates. --- app/views/account/settings_view.coffee | 4 ++-- app/views/modal/signup_modal.coffee | 3 ++- .../sessions/level_session_handler.coffee | 2 +- .../sessions/level_session_schema.coffee | 14 +++++++++----- server/routes/mail.coffee | 18 +++++++++++------- server/sendwithus.coffee | 2 +- server/users/user_schema.coffee | 6 +++--- 7 files changed, 29 insertions(+), 20 deletions(-) diff --git a/app/views/account/settings_view.coffee b/app/views/account/settings_view.coffee index 2f609eebd..27edf27e1 100644 --- a/app/views/account/settings_view.coffee +++ b/app/views/account/settings_view.coffee @@ -66,7 +66,7 @@ module.exports = class SettingsView extends View c.photos = me.gravatarPhotoURLs() c.chosenPhoto = me.getPhotoURL() c.subs = {} - c.subs[sub] = 1 for sub in c.me.get('emailSubscriptions') or ['announcement', 'tester', 'level_creator', 'developer'] + c.subs[sub] = 1 for sub in c.me.get('emailSubscriptions') or ['announcement', 'notification', 'tester', 'level_creator', 'developer'] c getSubscriptions: -> @@ -88,7 +88,7 @@ module.exports = class SettingsView extends View if res? forms.applyErrorsToForm(@$el, res) return - + return unless me.hasLocalChanges() res = me.save() diff --git a/app/views/modal/signup_modal.coffee b/app/views/modal/signup_modal.coffee index 579a938cb..d1c76e3a1 100644 --- a/app/views/modal/signup_modal.coffee +++ b/app/views/modal/signup_modal.coffee @@ -49,8 +49,9 @@ module.exports = class SignupModalView extends View userObject.emailSubscriptions ?= [] if subscribe userObject.emailSubscriptions.push 'announcement' unless 'announcement' in userObject.emailSubscriptions + userObject.emailSubscriptions.push 'notification' unless 'notification' in userObject.emailSubscriptions else - userObject.emailSubscriptions = _.without (userObject.emailSubscriptions ? []), 'announcement' + userObject.emailSubscriptions = _.without (userObject.emailSubscriptions ? []), 'announcement', 'notification' res = tv4.validateMultiple userObject, User.schema.attributes return forms.applyErrorsToForm(@$el, res.errors) unless res.valid window.tracker?.trackEvent 'Finished Signup' diff --git a/server/levels/sessions/level_session_handler.coffee b/server/levels/sessions/level_session_handler.coffee index dbb3324f4..d3ab07830 100644 --- a/server/levels/sessions/level_session_handler.coffee +++ b/server/levels/sessions/level_session_handler.coffee @@ -7,7 +7,7 @@ class LevelSessionHandler extends Handler modelClass: LevelSession editableProperties: ['multiplayer', 'players', 'code', 'completed', 'state', 'levelName', 'creatorName', 'levelID', 'screenshot', - 'chat', 'teamSpells','submitted'] + 'chat', 'teamSpells', 'submitted', 'unsubscribed'] getByRelationship: (req, res, args...) -> return @sendNotFoundError(res) unless args.length is 2 and args[1] is 'active' diff --git a/server/levels/sessions/level_session_schema.coffee b/server/levels/sessions/level_session_schema.coffee index ca8df4c05..da4395dec 100644 --- a/server/levels/sessions/level_session_schema.coffee +++ b/server/levels/sessions/level_session_schema.coffee @@ -122,7 +122,7 @@ _.extend LevelSessionSchema.properties, standardDeviation: type:'number' - default:25/3 + default: 25/3 minimum: 0 totalScore: @@ -139,25 +139,29 @@ _.extend LevelSessionSchema.properties, submittedCode: type: 'object' - + + unsubscribed: + type: 'boolean' + description: 'Whether the player has opted out of receiving email updates about ladder rankings for this session.' + numberOfWinsAndTies: type: 'number' default: 0 numberOfLosses: type: 'number' default: 0 - + scoreHistory: type: 'array' title: 'Score History' description: 'A list of objects representing the score history of a session' - items: + items: title: 'Score History Point' description: 'An array with the format [unix timestamp, totalScore]' type: 'array' items: type: 'number' - + matches: type: 'array' diff --git a/server/routes/mail.coffee b/server/routes/mail.coffee index d629883d7..4ef488b67 100644 --- a/server/routes/mail.coffee +++ b/server/routes/mail.coffee @@ -42,8 +42,8 @@ handleLadderUpdate = (req, res) -> for daysAgo in emailDays # Get every session that was submitted in a 5-minute window after the time. startTime = getTimeFromDaysAgo daysAgo - endTime = startTime + 5 * 60 * 1000 - #endTime = startTime + 1 * 60 * 60 * 1000 + #endTime = startTime + 5 * 60 * 1000 + endTime = startTime + 1 * 60 * 60 * 1000 # Debugging: make sure there's something to send findParameters = {submitted: true, submitDate: {$gt: new Date(startTime), $lte: new Date(endTime)}} # TODO: think about putting screenshots in the email selectString = "creator team levelName levelID totalScore matches submitted submitDate numberOfWinsAndTies numberOfLosses" @@ -53,16 +53,19 @@ handleLadderUpdate = (req, res) -> do (daysAgo) -> query.exec (err, results) -> if err - log.error "Couldn't fetch ladder updates for", findParameters, "\nError: ", err + log.error "Couldn't fetch ladder updates for #{findParameters}\nError: #{err}" return errors.serverError res, "Ladder update email query failed: #{JSON.stringify(err)}" + log.info "Found #{results.length} ladder sessions to email updates about for #{daysAgo} day(s) ago." sendLadderUpdateEmail result, daysAgo for result in results sendLadderUpdateEmail = (session, daysAgo) -> User.findOne({_id: session.creator}).select("name email firstName lastName emailSubscriptions preferredLanguage").lean().exec (err, user) -> if err - log.error "Couldn't find user for", session.creator, "from session", session._id + log.error "Couldn't find user for #{session.creator} from session #{session._id}" + return + if not user.email or not ('notification' in user.emailSubscriptions) + log.info "Not sending email to #{user.email} #{user.name} because they only want emails about #{user.emailSubscriptions}" return - return unless user.email and 'notification' in user.emailSubscriptions name = if user.firstName and user.lastName then "#{user.firstName} #{user.lastName}" else user.name name = "Wizard" if not name or name is "Anoner" @@ -72,7 +75,7 @@ sendLadderUpdateEmail = (session, daysAgo) -> email_id: sendwithus.templates.ladder_update_email recipient: #address: user.email - address: 'nick@codecombat.com' + address: 'nick@codecombat.com' # Debugging name: name email_data: name: name @@ -85,8 +88,9 @@ sendLadderUpdateEmail = (session, daysAgo) -> ladder_url: "http://codecombat.com/play/ladder/#{session.levelID}#my-matches" defeat: defeatContext victory: victoryContext + log.info "Sending ladder update email to #{context.recipient.address} with #{context.email_data.wins} wins and #{context.email_data.losses} since #{daysAgo} day(s) ago." sendwithus.api.send context, (err, result) -> - log.error "Error sending ladder update email:", err, 'result', result if err + log.error "Error sending ladder update email: #{err} with result #{result}" if err # Fetch the most recent defeat and victory, if there are any. # (We could look at strongest/weakest, but we'd have to fetch everyone, or denormalize more.) diff --git a/server/sendwithus.coffee b/server/sendwithus.coffee index c481893bf..659ce5ec8 100644 --- a/server/sendwithus.coffee +++ b/server/sendwithus.coffee @@ -11,4 +11,4 @@ options = { DEBUG: not config.isProduction } module.exports.api = new sendwithusAPI swuAPIKey, options module.exports.templates = welcome_email: 'utnGaBHuSU4Hmsi7qrAypU' - ladder_update_email: 'Xq3vSbDHXcjXfje7n2e7Eb' + ladder_update_email: 'JzaZxf39A4cKMxpPZUfWy4' diff --git a/server/users/user_schema.coffee b/server/users/user_schema.coffee index 7ae34c4af..d43a8d6b6 100644 --- a/server/users/user_schema.coffee +++ b/server/users/user_schema.coffee @@ -19,7 +19,7 @@ UserSchema = c.object {}, music: {type: 'boolean', default: true} #autocastDelay, or more complex autocast options? I guess I'll see what I need when trying to hook up Scott's suggested autocast behavior - emailSubscriptions: c.array {uniqueItems: true, 'default': ['announcement']}, {'enum': emailSubscriptions} + emailSubscriptions: c.array {uniqueItems: true, 'default': ['announcement', 'notification']}, {'enum': emailSubscriptions} # server controlled permissions: c.array {'default': []}, c.shortString() @@ -29,7 +29,7 @@ UserSchema = c.object {}, mailChimp: {type: 'object'} hourOfCode: {type: 'boolean'} hourOfCodeComplete: {type: 'boolean'} - + emailLower: c.shortString() nameLower: c.shortString() passwordHash: {type: 'string', maxLength: 256} @@ -40,7 +40,7 @@ UserSchema = c.object {}, #Internationalization stuff preferredLanguage: {type: 'string', default: 'en', 'enum': c.getLanguageCodeArray()} - + signedCLA: c.date({title: 'Date Signed the CLA'}) wizard: c.object {}, colorConfig: c.object {additionalProperties: c.colorConfig()} From c88c973fa714b14c51764e5669ea2c95c2aad34d Mon Sep 17 00:00:00 2001 From: Michael Schmatz <michaelschmatz@gmail.com> Date: Mon, 10 Mar 2014 14:18:34 -0700 Subject: [PATCH 093/100] Added 2.6.0-rc1 to allowed Mongo versions --- bin/coco-mongodb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/coco-mongodb b/bin/coco-mongodb index be4f285ed..4ff889493 100755 --- a/bin/coco-mongodb +++ b/bin/coco-mongodb @@ -71,7 +71,7 @@ def which(cmd, mode=os.F_OK | os.X_OK, path=None): current_directory = os.path.dirname(os.path.realpath(sys.argv[0])) -allowedMongoVersions = ["v2.5.4","v2.5.5"] +allowedMongoVersions = ["v2.5.4","v2.5.5","v2.6.0-rc1"] if which("mongod") and any(i in subprocess.check_output("mongod --version",shell=True) for i in allowedMongoVersions): mongo_executable = "mongod" else: From 6b48577e56aed5eb3a71f6f80b781d9522c6a827 Mon Sep 17 00:00:00 2001 From: Darredevil <alex.darredevil@gmail.com> Date: Tue, 11 Mar 2014 01:03:20 +0200 Subject: [PATCH 094/100] Update ro.coffee --- app/locale/ro.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/locale/ro.coffee b/app/locale/ro.coffee index 531e3825f..5038a79d8 100644 --- a/app/locale/ro.coffee +++ b/app/locale/ro.coffee @@ -347,7 +347,7 @@ module.exports = nativeDescription: "limba română", englishDescription: "Roman nutshell_description: "Orice resurse vă punem la dispoziție în Editorul de Nivele puteți folosi liber cum vreți pentru a crea nivele. Dar ne rezervăm dreptul de a rezerva distribuția de nivele în sine (care sunt create pe codecombat.com) astfel încât să se poată percepe o taxă pentru ele pe vitor, dacă se va ajunge la așa ceva." canonical: "The English version of this document is the definitive, canonical version. If there are any discrepencies between translations, the English document takes precedence." - contribute: +# contribute: # page_title: "Contributing" # character_classes_title: "Character Classes" # introduction_desc_intro: "We have high hopes for CodeCombat." From 6ce65488aa727899336cff7585a893b37cee97f5 Mon Sep 17 00:00:00 2001 From: Nick Winter <livelily@gmail.com> Date: Mon, 10 Mar 2014 20:22:25 -0700 Subject: [PATCH 095/100] Added simple rank history graph. Improving ladder update mail. --- app/templates/play/ladder/my_matches_tab.jade | 4 ++++ app/views/play/ladder/my_matches_tab.coffee | 9 +++++++++ app/views/play/ladder_view.coffee | 2 +- server/commons/Handler.coffee | 4 +++- server/routes/mail.coffee | 10 +++++++--- 5 files changed, 24 insertions(+), 5 deletions(-) diff --git a/app/templates/play/ladder/my_matches_tab.jade b/app/templates/play/ladder/my_matches_tab.jade index c70192221..e9bc0dd21 100644 --- a/app/templates/play/ladder/my_matches_tab.jade +++ b/app/templates/play/ladder/my_matches_tab.jade @@ -21,6 +21,10 @@ div#columns.row span.ranked.hidden Submitted for Ranking span.failed.hidden Failed to Rank + tr + th(colspan=4, style="color: #{team.primaryColor}") + img(src="https://chart.googleapis.com/chart?chs=450x125&cht=lxy&chco=#{team.chartColor}&chtt=Score+History&chts=#{team.chartColor},16,c&chf=a,s,000000FF&chls=2&chm=o,#{team.chartColor},0,4&chd=t:#{team.chartData}") + tr th Result th Opponent diff --git a/app/views/play/ladder/my_matches_tab.coffee b/app/views/play/ladder/my_matches_tab.coffee index a094aef82..b7708c8df 100644 --- a/app/views/play/ladder/my_matches_tab.coffee +++ b/app/views/play/ladder/my_matches_tab.coffee @@ -76,6 +76,15 @@ module.exports = class MyMatchesTabView extends CocoView team.wins = _.filter(team.matches, {state: 'win'}).length team.ties = _.filter(team.matches, {state: 'tie'}).length team.losses = _.filter(team.matches, {state: 'loss'}).length + team.scoreHistory = team.session.get('scoreHistory') + team.chartColor = team.primaryColor.replace '#', '' + times = (s[0] for s in team.scoreHistory) + times = (100 * (t - times[0]) / (times[times.length - 1] - times[0]) for t in times) + scores = (s[1] for s in team.scoreHistory) + lowest = _.min scores + highest = _.max scores + scores = (100 * (s - lowest) / highest for s in scores) + team.chartData = times.join(',') + '|' + scores.join(',') ctx diff --git a/app/views/play/ladder_view.coffee b/app/views/play/ladder_view.coffee index 11283af13..b7cf49599 100644 --- a/app/views/play/ladder_view.coffee +++ b/app/views/play/ladder_view.coffee @@ -65,7 +65,7 @@ module.exports = class LadderView extends RootView return if @startsLoading @insertSubView(@ladderTab = new LadderTabView({}, @level, @sessions)) @insertSubView(@myMatchesTab = new MyMatchesTabView({}, @level, @sessions)) - @refreshInterval = setInterval(@fetchSessionsAndRefreshViews.bind(@), 10000) + @refreshInterval = setInterval(@fetchSessionsAndRefreshViews.bind(@), 10 * 1000) hash = document.location.hash[1..] if document.location.hash if hash and not (hash in ['my-matches', 'simulate', 'ladder']) @showPlayModal(hash) if @sessions.loaded diff --git a/server/commons/Handler.coffee b/server/commons/Handler.coffee index ada7acad3..4460b2d22 100644 --- a/server/commons/Handler.coffee +++ b/server/commons/Handler.coffee @@ -123,7 +123,9 @@ module.exports = class Handler # Keeping it simple for now and just allowing access to the first FETCH_LIMIT results. query = {'original': mongoose.Types.ObjectId(id)} sort = {'created': -1} - @modelClass.find(query).limit(FETCH_LIMIT).sort(sort).exec (err, results) => + selectString = 'slug name version commitMessage created' # Is this even working? + @modelClass.find(query).select(selectString).lean().limit(FETCH_LIMIT).sort(sort).exec (err, results) => + return @sendDatabaseError(res, err) if err for doc in results return @sendUnauthorizedError(res) unless @hasAccessToDocument(req, doc) res.send(results) diff --git a/server/routes/mail.coffee b/server/routes/mail.coffee index 4ef488b67..5ed1fad22 100644 --- a/server/routes/mail.coffee +++ b/server/routes/mail.coffee @@ -42,8 +42,8 @@ handleLadderUpdate = (req, res) -> for daysAgo in emailDays # Get every session that was submitted in a 5-minute window after the time. startTime = getTimeFromDaysAgo daysAgo - #endTime = startTime + 5 * 60 * 1000 - endTime = startTime + 1 * 60 * 60 * 1000 # Debugging: make sure there's something to send + endTime = startTime + 5 * 60 * 1000 + #endTime = startTime + 1.5 * 60 * 60 * 1000 # Debugging: make sure there's something to send findParameters = {submitted: true, submitDate: {$gt: new Date(startTime), $lte: new Date(endTime)}} # TODO: think about putting screenshots in the email selectString = "creator team levelName levelID totalScore matches submitted submitDate numberOfWinsAndTies numberOfLosses" @@ -63,9 +63,12 @@ sendLadderUpdateEmail = (session, daysAgo) -> if err log.error "Couldn't find user for #{session.creator} from session #{session._id}" return - if not user.email or not ('notification' in user.emailSubscriptions) + unless user.email and ('notification' in user.emailSubscriptions) log.info "Not sending email to #{user.email} #{user.name} because they only want emails about #{user.emailSubscriptions}" return + unless session.levelName + log.info "Not sending email to #{user.email} #{user.name} because the session had no levelName in it." + return name = if user.firstName and user.lastName then "#{user.firstName} #{user.lastName}" else user.name name = "Wizard" if not name or name is "Anoner" @@ -84,6 +87,7 @@ sendLadderUpdateEmail = (session, daysAgo) -> losses: session.numberOfLosses total_score: Math.round(session.totalScore * 100) team: session.team + team_name: session.team[0].toUpperCase() + session.team.substr(1) level_name: session.levelName ladder_url: "http://codecombat.com/play/ladder/#{session.levelID}#my-matches" defeat: defeatContext From 32baf2ae7988685baeb3585a5a63b5dc1b186fa6 Mon Sep 17 00:00:00 2001 From: Nick Winter <livelily@gmail.com> Date: Mon, 10 Mar 2014 21:30:46 -0700 Subject: [PATCH 096/100] Improved my-matches rank history and name fetching, ladder update emails, ladder update unsubscribes. --- app/templates/play/ladder/my_matches_tab.jade | 7 ++-- app/views/play/ladder/my_matches_tab.coffee | 21 ++++++----- server/routes/auth.coffee | 37 ++++++++++++------- server/routes/mail.coffee | 29 ++++++++------- 4 files changed, 55 insertions(+), 39 deletions(-) diff --git a/app/templates/play/ladder/my_matches_tab.jade b/app/templates/play/ladder/my_matches_tab.jade index e9bc0dd21..f40b4a04d 100644 --- a/app/templates/play/ladder/my_matches_tab.jade +++ b/app/templates/play/ladder/my_matches_tab.jade @@ -21,9 +21,10 @@ div#columns.row span.ranked.hidden Submitted for Ranking span.failed.hidden Failed to Rank - tr - th(colspan=4, style="color: #{team.primaryColor}") - img(src="https://chart.googleapis.com/chart?chs=450x125&cht=lxy&chco=#{team.chartColor}&chtt=Score+History&chts=#{team.chartColor},16,c&chf=a,s,000000FF&chls=2&chm=o,#{team.chartColor},0,4&chd=t:#{team.chartData}") + if team.chartData + tr + th(colspan=4, style="color: #{team.primaryColor}") + img(src="https://chart.googleapis.com/chart?chs=450x125&cht=lxy&chco=#{team.chartColor}&chtt=Score+History&chts=#{team.chartColor},16,c&chf=a,s,000000FF&chls=2&chm=o,#{team.chartColor},0,4&chd=t:#{team.chartData}") tr th Result diff --git a/app/views/play/ladder/my_matches_tab.coffee b/app/views/play/ladder/my_matches_tab.coffee index b7708c8df..24df7fbf1 100644 --- a/app/views/play/ladder/my_matches_tab.coffee +++ b/app/views/play/ladder/my_matches_tab.coffee @@ -35,7 +35,7 @@ module.exports = class MyMatchesTabView extends CocoView for session in @sessions.models for match in session.get('matches') or [] opponent = match.opponents[0] - @nameMap[opponent.userID] = nameMap[opponent.userID] + @nameMap[opponent.userID] ?= nameMap[opponent.userID] @finishRendering() $.ajax('/db/user/-/names', { @@ -76,15 +76,16 @@ module.exports = class MyMatchesTabView extends CocoView team.wins = _.filter(team.matches, {state: 'win'}).length team.ties = _.filter(team.matches, {state: 'tie'}).length team.losses = _.filter(team.matches, {state: 'loss'}).length - team.scoreHistory = team.session.get('scoreHistory') - team.chartColor = team.primaryColor.replace '#', '' - times = (s[0] for s in team.scoreHistory) - times = (100 * (t - times[0]) / (times[times.length - 1] - times[0]) for t in times) - scores = (s[1] for s in team.scoreHistory) - lowest = _.min scores - highest = _.max scores - scores = (100 * (s - lowest) / highest for s in scores) - team.chartData = times.join(',') + '|' + scores.join(',') + team.scoreHistory = team.session?.get('scoreHistory') + if team.scoreHistory?.length > 1 + team.chartColor = team.primaryColor.replace '#', '' + times = (s[0] for s in team.scoreHistory) + times = ((100 * (t - times[0]) / (times[times.length - 1] - times[0])).toFixed(1) for t in times) + scores = (s[1] for s in team.scoreHistory) + lowest = _.min scores + highest = _.max scores + scores = (Math.round(100 * (s - lowest) / (highest - lowest)) for s in scores) + team.chartData = times.join(',') + '|' + scores.join(',') ctx diff --git a/server/routes/auth.coffee b/server/routes/auth.coffee index 2e6dbf72d..c845b28c2 100644 --- a/server/routes/auth.coffee +++ b/server/routes/auth.coffee @@ -2,6 +2,7 @@ authentication = require('passport') LocalStrategy = require('passport-local').Strategy User = require('../users/User') UserHandler = require('../users/user_handler') +LevelSession = require '../levels/sessions/LevelSession' config = require '../../server_config' errors = require '../commons/errors' mail = require '../commons/mail' @@ -21,16 +22,16 @@ module.exports.setup = (app) -> if passwordReset and password.toLowerCase() is passwordReset User.update {_id: user.get('_id')}, {passwordReset: ''}, {}, -> return done(null, user) - + hash = User.hashPassword(password) unless user.get('passwordHash') is hash - return done(null, false, {message:'is wrong, wrong, wrong', property:'password'}) + return done(null, false, {message:'is wrong, wrong, wrong', property:'password'}) return done(null, user) ) )) app.post '/auth/spy', (req, res, next) -> if req?.user?.isAdmin() - + username = req.body.usernameLower emailLower = req.body.emailLower if emailLower @@ -39,19 +40,19 @@ module.exports.setup = (app) -> query = {"nameLower":username} else return errors.badInput res, "You need to supply one of emailLower or username" - + User.findOne query, (err, user) -> if err? then return errors.serverError res, "There was an error finding the specified user" - + unless user then return errors.badInput res, "The specified user couldn't be found" - + req.logIn user, (err) -> if err? then return errors.serverError res, "There was an error logging in with the specified" res.send(UserHandler.formatEntity(req, user)) return res.end() else return errors.unauthorized res, "You must be an admin to enter espionage mode" - + app.post('/auth/login', (req, res, next) -> authentication.authenticate('local', (err, user, info) -> return next(err) if err @@ -87,11 +88,11 @@ module.exports.setup = (app) -> user.save((err) -> if err return @sendDatabaseError(res, err) - + req.logIn(user, (err) -> if err return @sendDatabaseError(res, err) - + if send return @sendSuccess(res, user) next() if next @@ -110,7 +111,7 @@ module.exports.setup = (app) -> User.findOne({emailLower:req.body.email.toLowerCase()}).exec((err, user) -> if not user return errors.notFound(res, [{message:'not found.', property:'email'}]) - + user.set('passwordReset', Math.random().toString(36).slice(2,7).toUpperCase()) user.save (err) => return errors.serverError(res) if err @@ -127,12 +128,22 @@ module.exports.setup = (app) -> return res.end() ) ) - + app.get '/auth/unsubscribe', (req, res) -> email = req.query.email unless req.query.email return errors.badInput res, 'No email provided to unsubscribe.' - + + if req.query.session + # Unsubscribe from just one session's notifications instead. + return LevelSession.findOne({_id: req.query.session}).exec (err, session) -> + return errors.serverError res, 'Could not unsubscribe: #{req.query.session}, #{req.query.email}: #{err}' if err + session.set 'unsubscribed', true + session.save (err) -> + return errors.serverError res, 'Database failure.' if err + res.send "Unsubscribed #{req.query.email} from CodeCombat emails for #{session.levelName} #{session.team} ladder updates. Sorry to see you go! <p><a href='/play/ladder/#{session.levelID}#my-matches'>Ladder preferences</a></p>" + res.end() + User.findOne({emailLower:req.query.email.toLowerCase()}).exec (err, user) -> if not user return errors.notFound res, "No user found with email '#{req.query.email}'" @@ -152,4 +163,4 @@ createMailOptions = (receiver, password) -> replyTo: config.mail.username subject: "[CodeCombat] Password Reset" text: "You can log into your account with: #{password}" -# \ No newline at end of file +# diff --git a/server/routes/mail.coffee b/server/routes/mail.coffee index 5ed1fad22..a9430115b 100644 --- a/server/routes/mail.coffee +++ b/server/routes/mail.coffee @@ -46,7 +46,7 @@ handleLadderUpdate = (req, res) -> #endTime = startTime + 1.5 * 60 * 60 * 1000 # Debugging: make sure there's something to send findParameters = {submitted: true, submitDate: {$gt: new Date(startTime), $lte: new Date(endTime)}} # TODO: think about putting screenshots in the email - selectString = "creator team levelName levelID totalScore matches submitted submitDate numberOfWinsAndTies numberOfLosses" + selectString = "creator team levelName levelID totalScore matches submitted submitDate" query = LevelSession.find(findParameters) .select(selectString) .lean() @@ -63,8 +63,8 @@ sendLadderUpdateEmail = (session, daysAgo) -> if err log.error "Couldn't find user for #{session.creator} from session #{session._id}" return - unless user.email and ('notification' in user.emailSubscriptions) - log.info "Not sending email to #{user.email} #{user.name} because they only want emails about #{user.emailSubscriptions}" + unless user.email and ('notification' in user.emailSubscriptions) and not session.unsubscribed + log.info "Not sending email to #{user.email} #{user.name} because they only want emails about #{user.emailSubscriptions} - session unsubscribed: #{session.unsubscribed}" return unless session.levelName log.info "Not sending email to #{user.email} #{user.name} because the session had no levelName in it." @@ -72,23 +72,32 @@ sendLadderUpdateEmail = (session, daysAgo) -> name = if user.firstName and user.lastName then "#{user.firstName} #{user.lastName}" else user.name name = "Wizard" if not name or name is "Anoner" + # Fetch the most recent defeat and victory, if there are any. + # (We could look at strongest/weakest, but we'd have to fetch everyone, or denormalize more.) + matches = _.filter session.matches, (match) -> match.date >= (new Date() - 86400 * 1000 * daysAgo) + defeats = _.filter matches, (match) -> match.metrics.rank is 1 and match.opponents[0].metrics.rank is 0 + victories = _.filter matches, (match) -> match.metrics.rank is 0 + defeat = _.last defeats + victory = _.last victories + sendEmail = (defeatContext, victoryContext) -> # TODO: do something with the preferredLanguage? context = email_id: sendwithus.templates.ladder_update_email recipient: - #address: user.email - address: 'nick@codecombat.com' # Debugging + address: user.email + #address: 'nick@codecombat.com' # Debugging name: name email_data: name: name days_ago: daysAgo - wins: session.numberOfWinsAndTies - losses: session.numberOfLosses + wins: victories.length + losses: defeats.length total_score: Math.round(session.totalScore * 100) team: session.team team_name: session.team[0].toUpperCase() + session.team.substr(1) level_name: session.levelName + session_id: session._id ladder_url: "http://codecombat.com/play/ladder/#{session.levelID}#my-matches" defeat: defeatContext victory: victoryContext @@ -96,12 +105,6 @@ sendLadderUpdateEmail = (session, daysAgo) -> sendwithus.api.send context, (err, result) -> log.error "Error sending ladder update email: #{err} with result #{result}" if err - # Fetch the most recent defeat and victory, if there are any. - # (We could look at strongest/weakest, but we'd have to fetch everyone, or denormalize more.) - defeats = _.filter session.matches, (match) -> match.metrics.rank is 1 and match.opponents[0].metrics.rank is 0 - victories = _.filter session.matches, (match) -> match.metrics.rank is 0 - defeat = _.last defeats - victory = _.last victories urlForMatch = (match) -> "http://codecombat.com/play/level/#{session.levelID}?team=#{session.team}&session=#{session._id}&opponent=#{match.opponents[0].sessionID}" From eac219a2be8b7277d2985629d2abad769bfbdb1b Mon Sep 17 00:00:00 2001 From: Nick Winter <livelily@gmail.com> Date: Mon, 10 Mar 2014 22:03:33 -0700 Subject: [PATCH 097/100] Inserted score history graph into ladder update emails. --- app/templates/play/ladder/my_matches_tab.jade | 2 +- app/views/play/ladder/my_matches_tab.coffee | 1 + server/routes/mail.coffee | 19 +++++++++++++++++-- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/app/templates/play/ladder/my_matches_tab.jade b/app/templates/play/ladder/my_matches_tab.jade index f40b4a04d..0eb83dddb 100644 --- a/app/templates/play/ladder/my_matches_tab.jade +++ b/app/templates/play/ladder/my_matches_tab.jade @@ -24,7 +24,7 @@ div#columns.row if team.chartData tr th(colspan=4, style="color: #{team.primaryColor}") - img(src="https://chart.googleapis.com/chart?chs=450x125&cht=lxy&chco=#{team.chartColor}&chtt=Score+History&chts=#{team.chartColor},16,c&chf=a,s,000000FF&chls=2&chm=o,#{team.chartColor},0,4&chd=t:#{team.chartData}") + img(src="https://chart.googleapis.com/chart?chs=450x125&cht=lxy&chco=#{team.chartColor}&chtt=Score%3A+#{team.currentScore}&chts=#{team.chartColor},16,c&chf=a,s,000000FF&chls=2&chm=o,#{team.chartColor},0,4&chd=t:#{team.chartData}") tr th Result diff --git a/app/views/play/ladder/my_matches_tab.coffee b/app/views/play/ladder/my_matches_tab.coffee index 24df7fbf1..660114111 100644 --- a/app/views/play/ladder/my_matches_tab.coffee +++ b/app/views/play/ladder/my_matches_tab.coffee @@ -78,6 +78,7 @@ module.exports = class MyMatchesTabView extends CocoView team.losses = _.filter(team.matches, {state: 'loss'}).length team.scoreHistory = team.session?.get('scoreHistory') if team.scoreHistory?.length > 1 + team.currentScore = Math.round team.scoreHistory[team.scoreHistory.length - 1][1] * 100 team.chartColor = team.primaryColor.replace '#', '' times = (s[0] for s in team.scoreHistory) times = ((100 * (t - times[0]) / (times[times.length - 1] - times[0])).toFixed(1) for t in times) diff --git a/server/routes/mail.coffee b/server/routes/mail.coffee index a9430115b..5ad2dfcd4 100644 --- a/server/routes/mail.coffee +++ b/server/routes/mail.coffee @@ -46,7 +46,7 @@ handleLadderUpdate = (req, res) -> #endTime = startTime + 1.5 * 60 * 60 * 1000 # Debugging: make sure there's something to send findParameters = {submitted: true, submitDate: {$gt: new Date(startTime), $lte: new Date(endTime)}} # TODO: think about putting screenshots in the email - selectString = "creator team levelName levelID totalScore matches submitted submitDate" + selectString = "creator team levelName levelID totalScore matches submitted submitDate scoreHistory" query = LevelSession.find(findParameters) .select(selectString) .lean() @@ -69,7 +69,7 @@ sendLadderUpdateEmail = (session, daysAgo) -> unless session.levelName log.info "Not sending email to #{user.email} #{user.name} because the session had no levelName in it." return - name = if user.firstName and user.lastName then "#{user.firstName} #{user.lastName}" else user.name + name = if user.firstName and user.lastName then "#{user.firstName}" else user.name name = "Wizard" if not name or name is "Anoner" # Fetch the most recent defeat and victory, if there are any. @@ -99,6 +99,7 @@ sendLadderUpdateEmail = (session, daysAgo) -> level_name: session.levelName session_id: session._id ladder_url: "http://codecombat.com/play/ladder/#{session.levelID}#my-matches" + score_history_graph_url: getScoreHistoryGraphURL session, daysAgo defeat: defeatContext victory: victoryContext log.info "Sending ladder update email to #{context.recipient.address} with #{context.email_data.wins} wins and #{context.email_data.losses} since #{daysAgo} day(s) ago." @@ -131,6 +132,20 @@ sendLadderUpdateEmail = (session, daysAgo) -> else onFetchedDefeatedOpponent null, null +getScoreHistoryGraphURL = (session, daysAgo) -> + # Totally duplicated in My Matches tab for now until we figure out what we're doing. + since = new Date() - 86400 * 1000 * daysAgo + scoreHistory = (s for s in session.scoreHistory ? [] when s[0] >= since) + return '' unless scoreHistory.length > 1 + times = (s[0] for s in scoreHistory) + times = ((100 * (t - times[0]) / (times[times.length - 1] - times[0])).toFixed(1) for t in times) + scores = (s[1] for s in scoreHistory) + lowest = _.min scores + highest = _.max scores + scores = (Math.round(100 * (s - lowest) / (highest - lowest)) for s in scores) + currentScore = Math.round scoreHistory[scoreHistory.length - 1][1] * 100 + chartData = times.join(',') + '|' + scores.join(',') + "https://chart.googleapis.com/chart?chs=600x75&cht=lxy&chtt=Score%3A+#{currentScore}&chts=222222,12,r&chf=a,s,000000FF&chls=2&chd=t:#{chartData}" handleMailchimpWebHook = (req, res) -> post = req.body From 4062bba245829b185827eda47d2835bcbaa11e35 Mon Sep 17 00:00:00 2001 From: Michael Schmatz <schmatz@umich.edu> Date: Tue, 11 Mar 2014 13:12:40 -0700 Subject: [PATCH 098/100] Created initial spectate level view --- app/styles/play/spectate.sass | 152 +++++++++++++++++++++ app/templates/play/spectate.jade | 16 ++- app/views/play/spectate_view.coffee | 199 ++++++++++++++++++++++------ 3 files changed, 323 insertions(+), 44 deletions(-) create mode 100644 app/styles/play/spectate.sass diff --git a/app/styles/play/spectate.sass b/app/styles/play/spectate.sass new file mode 100644 index 000000000..9de885caa --- /dev/null +++ b/app/styles/play/spectate.sass @@ -0,0 +1,152 @@ +@import "app/styles/bootstrap/mixins" +@import "app/styles/mixins" + +#spectate-level-view + margin: 0 auto + @include user-select(none) + + .level-content + position: relative + + #canvas-wrapper + width: 55% + position: relative + + canvas#surface + background-color: #ddd + width: 100% + display: block + z-index: 1 + + + //max-width: 1680px // guideline, but for now let's let it stretch out + min-width: 1024px + position: relative + + #code-area + @include box-sizing(border-box) + padding: 10px 1% + width: 45% + background: transparent url(/images/level/wood_texture.png) + background-size: 100% 100% + position: absolute + right: 0 + top: 0px + bottom: 0 + + #pointer + position: absolute + left: 0 + top: 0 + height: 100px + opacity: 0.0 + pointer-events: none + z-index: 10 + + // Level Docs + .ui-effects-transfer + border: 2px dotted gray + + .modal + img + float: right + + img.diagram + float: none + + #multiplayer-join-link + font-size: 12px + + #level-done-button + position: absolute + right: 46% + top: 43px + @include box-shadow(4px 4px 15px black) + + // Custom Buttons + .btn.banner + @include banner-button(#FFF, #333) + @include box-shadow(2px 2px 2px rgba(0, 0, 0, 0.5)) + border: 1px solid black + text-shadow: none + + $buttonConfig: 'primary' #6CA8EA, 'info' #71AACC, 'success' #90B236, 'warning' #CD6800, 'danger' #B43C20, 'inverse' #3A537F + @each $tuple in $buttonConfig + &.btn-#{nth($tuple, 1)} + @include banner-button(nth($tuple, 2), #FFF) + + .footer .footer-link-text a + @include opacity(0.75) + @include transition(opacity .10s linear) + + &:hover, &:active + @include opacity(1) + + $GI: 0.5 // gradient intensity; can tweak this 0-1 + + .gradient + position: absolute + z-index: 10 + + #code-area-gradient + top: 0px + width: 3px + background: linear-gradient(to right, rgba(0,0,0,0) 0%,rgba(0,0,0,$GI) 100%) + left: -3px + bottom: 0 + + #hud-top-gradient + top: -32px + background: linear-gradient(to bottom, rgba(0,0,0,0) 0%,rgba(0,0,0,0.8*$GI) 100%) + left: 0 + right: 0 + bottom: 0 + height: 3px + + #canvas-left-gradient + left: 0px + width: 5px + background: linear-gradient(to left, rgba(0,0,0,0) 0%,rgba(0,0,0,0.8*$GI) 100%) + bottom: -30px + top: 0 + + #canvas-top-gradient + top: 0 + height: 5px + left: 0 + right: 0 + background: linear-gradient(to top, rgba(0,0,0,0) 0%,rgba(0,0,0,0.8*$GI) 100%) + + #hud-left-gradient + background: linear-gradient(to right, rgba(0,0,0,$GI) 0%,rgba(0,0,0,0) 100%) + left: 0 + top: 0 + height: 100% + width: 2% + + #hud-right-gradient + background: linear-gradient(to right, rgba(0,0,0,0) 0%,rgba(0,0,0,$GI) 100%) + right: 0 + position: absolute + top: 0 + height: 100% + width: 2% + + .footer + @media screen and (min-aspect-ratio: 17/10) + display: none + + &:not(:hover) + @include opacity(0.6) + + .hour-of-code-explanation + margin-top: 5px + color: white + font-size: 12px + + &:not(:hover) + @include opacity(0.75) + + a + color: white + text-decoration: underline diff --git a/app/templates/play/spectate.jade b/app/templates/play/spectate.jade index 9be37b46a..cfaba9234 100644 --- a/app/templates/play/spectate.jade +++ b/app/templates/play/spectate.jade @@ -1,16 +1,22 @@ .level-content #control-bar-view - #canvas-wrapper - canvas(width=924, height=589)#surface + canvas(width=1848, height=1178)#surface #canvas-left-gradient.gradient #canvas-top-gradient.gradient - #goals-view.hide - #gold-view.hide.expanded + #gold-view.secret.expanded #level-chat-view #playback-view #thang-hud .footer .content p(class='footer-link-text') - a(title='Contact', tabindex=-1, data-toggle="coco-modal", data-target="modal/contact", data-i18n="nav.contact") Contact + a(title='Send CodeCombat a message', tabindex=-1, data-toggle="coco-modal", data-target="modal/contact", data-i18n="nav.contact") Contact + if explainHourOfCode + // Does not show up unless lang is en-US. + div.hour-of-code-explanation + | The 'Hour of Code' is a nationwide initiative by + a(href="http://csedweek.org") Computer Science Education Week + | and + a(href="http://code.org") Code.org + | to introduce millions of students to one hour of computer science and computer programming. \ No newline at end of file diff --git a/app/views/play/spectate_view.coffee b/app/views/play/spectate_view.coffee index e2686e90d..17dbcf6d3 100644 --- a/app/views/play/spectate_view.coffee +++ b/app/views/play/spectate_view.coffee @@ -5,7 +5,6 @@ ThangType = require 'models/ThangType' # temp hard coded data World = require 'lib/world/world' -docs = require 'lib/world/docs' # tools Surface = require 'lib/surface/Surface' @@ -17,7 +16,9 @@ LevelLoader = require 'lib/LevelLoader' LevelSession = require 'models/LevelSession' Level = require 'models/Level' LevelComponent = require 'models/LevelComponent' +Article = require 'models/Article' Camera = require 'lib/surface/Camera' +AudioPlayer = require 'lib/AudioPlayer' # subviews TomeView = require './level/tome/tome_view' @@ -34,8 +35,6 @@ LoadingScreen = require 'lib/LoadingScreen' PROFILE_ME = false -PlayLevelView = require './level_view' - module.exports = class SpectateLevelView extends View id: 'spectate-level-view' template: template @@ -46,6 +45,8 @@ module.exports = class SpectateLevelView extends View subscriptions: 'level-set-volume': (e) -> createjs.Sound.setVolume(e.volume) + 'level-show-victory': 'onShowVictory' + 'restart-level': 'onRestartLevel' 'level-highlight-dom': 'onHighlightDom' 'end-level-highlight-dom': 'onEndHighlight' 'level-focus-dom': 'onFocusDom' @@ -53,33 +54,33 @@ module.exports = class SpectateLevelView extends View 'level-enable-controls': 'onEnableControls' 'god:new-world-created': 'onNewWorld' 'god:infinite-loop': 'onInfiniteLoop' + 'level-reload-from-data': 'onLevelReloadFromData' + 'play-next-level': 'onPlayNextLevel' 'edit-wizard-settings': 'showWizardSettingsModal' 'surface:world-set-up': 'onSurfaceSetUpNewWorld' 'level:session-will-save': 'onSessionWillSave' 'level:set-team': 'setTeam' + 'god:new-world-created': 'loadSoundsForWorld' events: 'click #level-done-button': 'onDonePressed' + shortcuts: + 'ctrl+s': 'onCtrlS' constructor: (options, @levelID) -> console.profile?() if PROFILE_ME super options - console.log @levelID - - @ogreSessionID = @getQueryVariable 'ogres' - @humanSessionID = @getQueryVariable 'humans' + @sessionID = @getQueryVariable 'session' $(window).on('resize', @onWindowResize) - @supermodel.once 'error', => - msg = $.i18n.t('play_level.level_load_error', defaultValue: "Level could not be loaded.") - @$el.html('<div class="alert">' + msg + '</div>') - + @supermodel.once 'error', @onLevelLoadError @load() - + onLevelLoadError: (e) => + application.router.navigate "/play?not_found=#{@levelID}", {trigger: true} setLevel: (@level, @supermodel) -> @god?.level = @level.serialize @supermodel @@ -91,7 +92,8 @@ module.exports = class SpectateLevelView extends View load: -> @levelLoader = new LevelLoader supermodel: @supermodel, levelID: @levelID, sessionID: @sessionID, opponentSessionID: @getQueryVariable('opponent'), team: @getQueryVariable("team") - @levelLoader.once 'loaded-all', @onLevelLoaderLoaded + @levelLoader.once 'loaded-all', @onLevelLoaderLoaded, @ + @levelLoader.on 'progress', @onLevelLoaderProgressChanged, @ @god = new God() getRenderData: -> @@ -103,30 +105,83 @@ module.exports = class SpectateLevelView extends View window.onPlayLevelViewLoaded? @ # still a hack @loadingScreen = new LoadingScreen(@$el.find('canvas')[0]) @loadingScreen.show() + @$el.find('#level-done-button').hide() super() - onLevelLoaderLoaded: => - #needs editing - @session = @levelLoader.session - @world = @levelLoader.world - @level = @levelLoader.level - @levelLoader.destroy() - @levelLoader = null + onLevelLoaderProgressChanged: -> + return if @seenDocs + return unless showFrequency = @levelLoader.level.get('showGuide') + session = @levelLoader.session + diff = new Date().getTime() - new Date(session.get('created')).getTime() + return if showFrequency is 'first-time' and diff > (5 * 60 * 1000) + return unless @levelLoader.level.loaded + articles = @levelLoader.supermodel.getModels Article + for article in articles + return unless article.loaded + @showGuide() + + showGuide: -> + @seenDocs = true + DocsModal = require './level/modal/docs_modal' + options = {docs: @levelLoader.level.get('documentation'), supermodel: @supermodel} + @openModalView(new DocsModal(options), true) + Backbone.Mediator.subscribeOnce 'modal-closed', @onLevelLoaderLoaded, @ + return true + + onLevelLoaderLoaded: -> + return unless @levelLoader.progress() is 1 # double check, since closing the guide may trigger this early + # Save latest level played in local storage + if window.currentModal and not window.currentModal.destroyed + @loadingScreen.showReady() + return Backbone.Mediator.subscribeOnce 'modal-closed', @onLevelLoaderLoaded, @ + + localStorage["lastLevel"] = @levelID if localStorage? + @grabLevelLoaderData() + team = @getQueryVariable("team") ? @world.teamForPlayer(0) + @loadOpponentTeam(team) @loadingScreen.destroy() @god.level = @level.serialize @supermodel @god.worldClassMap = @world.classMap - #@setTeam @world.teamForPlayer _.size @session.get 'players' # TODO: players aren't initialized yet? - @setTeam @getQueryVariable("team") ? @world.teamForPlayer(0) + @setTeam team @initSurface() @initGoalManager() @initScriptManager() - @insertSubviews() + @insertSubviews ladderGame: @otherSession? @initVolume() - @session.on 'change:multiplayer', @onMultiplayerChanged, @ @originalSessionState = _.cloneDeep(@session.get('state')) @register() @controlBar.setBus(@bus) @surface.showLevel() + if @otherSession + # TODO: colorize name and cloud by team, colorize wizard by user's color config + @surface.createOpponentWizard id: @otherSession.get('creator'), name: @otherSession.get('creatorName'), team: @otherSession.get('team') + + grabLevelLoaderData: -> + @session = @levelLoader.session + @world = @levelLoader.world + @level = @levelLoader.level + @otherSession = @levelLoader.opponentSession + @levelLoader.destroy() + @levelLoader = null + + loadOpponentTeam: (myTeam) -> + opponentSpells = [] + for spellTeam, spells of @session.get('teamSpells') ? @otherSession?.get('teamSpells') ? {} + continue if spellTeam is myTeam or not myTeam + opponentSpells = opponentSpells.concat spells + + opponentCode = @otherSession?.get('submittedCode') or {} + myCode = @session.get('code') or {} + for spell in opponentSpells + [thang, spell] = spell.split '/' + c = opponentCode[thang]?[spell] + myCode[thang] ?= {} + if c then myCode[thang][spell] = c else delete myCode[thang][spell] + @session.set('code', myCode) + if @session.get('multiplayer') and @otherSession? + # For now, ladderGame will disallow multiplayer, because session code combining doesn't play nice yet. + @session.set 'multiplayer', false + onSupermodelLoadedOne: => @modelsLoaded ?= 0 @@ -142,38 +197,66 @@ module.exports = class SpectateLevelView extends View ctx.clearRect(0, 0, canvas.width, canvas.height) ctx.fillText("Loaded #{@modelsLoaded} thingies",50,50) - insertSubviews: -> - #needs editing - @insertSubView @tome = new TomeView levelID: @levelID, session: @session, thangs: @world.thangs, supermodel: @supermodel + insertSubviews: (subviewOptions) -> + @insertSubView @tome = new TomeView levelID: @levelID, session: @session, thangs: @world.thangs, supermodel: @supermodel, ladderGame: subviewOptions.ladderGame @insertSubView new PlaybackView {} @insertSubView new GoalsView {} @insertSubView new GoldView {} @insertSubView new HUDView {} @insertSubView new ChatView levelID: @levelID, sessionID: @session.id, session: @session worldName = @level.get('i18n')?[me.lang()]?.name ? @level.get('name') - @controlBar = @insertSubView new ControlBarView {worldName: worldName, session: @session, level: @level, supermodel: @supermodel, playableTeams: @world.playableTeams} + @controlBar = @insertSubView new ControlBarView {worldName: worldName, session: @session, level: @level, supermodel: @supermodel, playableTeams: @world.playableTeams, ladderGame: subviewOptions.ladderGame} #Backbone.Mediator.publish('level-set-debug', debug: true) if me.displayName() is 'Nick!' afterInsert: -> super() + @showWizardSettingsModal() if not me.get('name') + # callbacks + + onCtrlS: (e) -> + e.preventDefault() + + onLevelReloadFromData: (e) -> + isReload = Boolean @world + @setLevel e.level, e.supermodel + if isReload + @scriptManager.setScripts(e.level.get('scripts')) + Backbone.Mediator.publish 'tome:cast-spell' # a bit hacky onWindowResize: (s...) -> $('#pointer').css('opacity', 0.0) - onDisableControls: (e) => + onDisableControls: (e) -> return if e.controls and not ('level' in e.controls) @shortcutsEnabled = false @wasFocusedOn = document.activeElement $('body').focus() - onEnableControls: (e) => + onEnableControls: (e) -> return if e.controls? and not ('level' in e.controls) @shortcutsEnabled = true $(@wasFocusedOn).focus() if @wasFocusedOn @wasFocusedOn = null - onDonePressed: => @showVictory() + onDonePressed: -> @showVictory() + + onShowVictory: (e) -> + $('#level-done-button').show() + @showVictory() if e.showModal + setTimeout(@preloadNextLevel, 3000) + + showVictory: -> + options = {level: @level, supermodel: @supermodel, session:@session} + docs = new VictoryModal(options) + @openModalView(docs) + window.tracker?.trackEvent 'Saw Victory', level: @world.name, label: @world.name + + onRestartLevel: -> + @tome.reloadAllCode() + Backbone.Mediator.publish 'level:restarted' + $('#level-done-button', @$el).hide() + window.tracker?.trackEvent 'Confirmed Restart', level: @world.name, label: @world.name onNewWorld: (e) -> @world = e.world @@ -183,13 +266,21 @@ module.exports = class SpectateLevelView extends View @openModalView new InfiniteLoopModal() window.tracker?.trackEvent 'Saw Initial Infinite Loop', level: @world.name, label: @world.name + onPlayNextLevel: -> + nextLevel = @getNextLevel() + nextLevelID = nextLevel.get('slug') or nextLevel.id + url = "/play/level/#{nextLevelID}" + Backbone.Mediator.publish 'router:navigate', { + route: url, + viewClass: PlayLevelView, + viewArgs: [{supermodel:@supermodel}, nextLevelID]} getNextLevel: -> nextLevelOriginal = @level.get('nextLevel')?.original levels = @supermodel.getModels(Level) return l for l in levels when l.get('original') is nextLevelOriginal - onHighlightDom: (e) => + onHighlightDom: (e) -> if e.delay delay = e.delay delete e.delay @@ -243,19 +334,25 @@ module.exports = class SpectateLevelView extends View ), 1) - animatePointer: => + animatePointer: -> pointer = $('#pointer') pointer.css('transition', 'all 0.6s ease-out') pointer.css('transform', "rotate(#{@pointerRotation}rad) translate(-3px, #{@pointerRadialDistance-50}px)") setTimeout((=> pointer.css('transform', "rotate(#{@pointerRotation}rad) translate(-3px, #{@pointerRadialDistance}px)").css('transition', 'all 0.4s ease-in')), 800) - onFocusDom: (e) => $(e.selector).focus() + onFocusDom: (e) -> $(e.selector).focus() - onEndHighlight: => + onEndHighlight: -> $('#pointer').css('opacity', 0.0) clearInterval(@pointerInterval) + onMultiplayerChanged: (e) -> + if @session.get('multiplayer') + @bus.connect() + else + @bus.removeFirebaseData => + @bus.disconnect() # initialization @@ -273,7 +370,7 @@ module.exports = class SpectateLevelView extends View @surface.camera.zoomTo({x:0, y:0}, 0.1, 0) initGoalManager: -> - @goalManager = new GoalManager(@world) + @goalManager = new GoalManager(@world, @level.get('goals')) @god.goalManager = @goalManager initScriptManager: -> @@ -297,11 +394,18 @@ module.exports = class SpectateLevelView extends View if state.playing? Backbone.Mediator.publish 'level-set-playing', { playing: state.playing } + preloadNextLevel: => + # TODO: Loading models in the middle of gameplay causes stuttering. Most of the improvement in loading time is simply from passing the supermodel from this level to the next, but if we can find a way to populate the level early without it being noticeable, that would be even better. +# return if @destroyed +# return if @preloaded +# nextLevel = @getNextLevel() +# @supermodel.populateModel nextLevel +# @preloaded = true register: -> @bus = LevelBus.get(@levelID, @session.id) @bus.setSession(@session) - @bus.setTeamSpellMap @tome.teamSpellMap + @bus.setSpells @tome.spells @bus.connect() if @session.get('multiplayer') onSessionWillSave: (e) -> @@ -319,7 +423,20 @@ module.exports = class SpectateLevelView extends View me.team = team Backbone.Mediator.publish 'level:team-set', team: team + # Dynamic sound loading + + loadSoundsForWorld: (e) -> + return if @headless + world = e.world + thangTypes = @supermodel.getModels(ThangType) + for [spriteName, message] in world.thangDialogueSounds() + continue unless thangType = _.find thangTypes, (m) -> m.get('name') is spriteName + continue unless sound = AudioPlayer.soundForDialogue message, thangType.get('soundTriggers') + AudioPlayer.preloadSoundReference sound + destroy: -> + @supermodel?.off 'error', @onLevelLoadError + @levelLoader?.off 'loaded-all', @onLevelLoaderLoaded @levelLoader?.destroy() @surface?.destroy() @god?.destroy() @@ -327,10 +444,14 @@ module.exports = class SpectateLevelView extends View @scriptManager?.destroy() $(window).off('resize', @onWindowResize) delete window.world # not sure where this is set, but this is one way to clean it up - clearInterval(@pointerInterval) @bus?.destroy() #@instance.save() unless @instance.loading console.profileEnd?() if PROFILE_ME - @session.off 'change:multiplayer', @onMultiplayerChanged, @ + @session?.off 'change:multiplayer', @onMultiplayerChanged, @ + @onLevelLoadError = null + @onLevelLoaderLoaded = null + @onSupermodelLoadedOne = null + @preloadNextLevel = null + @saveScreenshot = null super() From bba3c78107c7bc09633d013b95ee4aaae833e882 Mon Sep 17 00:00:00 2001 From: Nick Winter <livelily@gmail.com> Date: Tue, 11 Mar 2014 13:20:52 -0700 Subject: [PATCH 099/100] Ties are not victories in the emails. --- app/templates/play/ladder/my_matches_tab.jade | 2 +- server/routes/mail.coffee | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/templates/play/ladder/my_matches_tab.jade b/app/templates/play/ladder/my_matches_tab.jade index 0eb83dddb..3827bd3b1 100644 --- a/app/templates/play/ladder/my_matches_tab.jade +++ b/app/templates/play/ladder/my_matches_tab.jade @@ -24,7 +24,7 @@ div#columns.row if team.chartData tr th(colspan=4, style="color: #{team.primaryColor}") - img(src="https://chart.googleapis.com/chart?chs=450x125&cht=lxy&chco=#{team.chartColor}&chtt=Score%3A+#{team.currentScore}&chts=#{team.chartColor},16,c&chf=a,s,000000FF&chls=2&chm=o,#{team.chartColor},0,4&chd=t:#{team.chartData}") + img(src="https://chart.googleapis.com/chart?chs=450x125&cht=lxy&chco=#{team.chartColor}&chtt=Score%3A+#{team.currentScore}&chts=#{team.chartColor},16,r&chf=a,s,000000FF&chls=2&chm=o,#{team.chartColor},0,4&chd=t:#{team.chartData}") tr th Result diff --git a/server/routes/mail.coffee b/server/routes/mail.coffee index 5ad2dfcd4..5a46a9006 100644 --- a/server/routes/mail.coffee +++ b/server/routes/mail.coffee @@ -76,7 +76,7 @@ sendLadderUpdateEmail = (session, daysAgo) -> # (We could look at strongest/weakest, but we'd have to fetch everyone, or denormalize more.) matches = _.filter session.matches, (match) -> match.date >= (new Date() - 86400 * 1000 * daysAgo) defeats = _.filter matches, (match) -> match.metrics.rank is 1 and match.opponents[0].metrics.rank is 0 - victories = _.filter matches, (match) -> match.metrics.rank is 0 + victories = _.filter matches, (match) -> match.metrics.rank is 0 and match.opponents[0].metrics.rank is 1 defeat = _.last defeats victory = _.last victories From 852b1c97aca6655f24799695200617f6345da280 Mon Sep 17 00:00:00 2001 From: Nick Winter <livelily@gmail.com> Date: Tue, 11 Mar 2014 13:59:12 -0700 Subject: [PATCH 100/100] Added idle.js so we can stop refreshing the page forever on ladder views when they're not looking. --- app/application.coffee | 9 ++- app/views/play/ladder_view.coffee | 4 +- vendor/scripts/idle.js | 126 ++++++++++++++++++++++++++++++ 3 files changed, 135 insertions(+), 4 deletions(-) create mode 100644 vendor/scripts/idle.js diff --git a/app/application.coffee b/app/application.coffee index 6b0c7f80b..860aee96e 100644 --- a/app/application.coffee +++ b/app/application.coffee @@ -44,8 +44,13 @@ Application = initialize: -> }, (t) => @router = new Router() @router.subscribe() - Object.freeze this if typeof Object.freeze is 'function' - @router = Router + @idleTracker = new Idle + onAway: => @userIsIdle = true + onAwayBack: => @userIsIdle = false + onHidden: => @userIsIdle = true + onVisible: => @userIsIdle = false + awayTimeout: 5 * 60 * 1000 + @idleTracker.start() module.exports = Application window.application = Application diff --git a/app/views/play/ladder_view.coffee b/app/views/play/ladder_view.coffee index b7cf49599..e12304f96 100644 --- a/app/views/play/ladder_view.coffee +++ b/app/views/play/ladder_view.coffee @@ -4,6 +4,7 @@ Simulator = require 'lib/simulator/Simulator' LevelSession = require 'models/LevelSession' CocoCollection = require 'models/CocoCollection' {teamDataFromLevel} = require './ladder/utils' +application = require 'application' LadderTabView = require './ladder/ladder_tab' MyMatchesTabView = require './ladder/my_matches_tab' @@ -74,12 +75,11 @@ module.exports = class LadderView extends RootView @sessions.fetch({"success": @refreshViews}) refreshViews: => - return if @destroyed + return if @destroyed or application.userIsIdle @ladderTab.refreshLadder() @myMatchesTab.refreshMatches() console.log "refreshed views!" - # Simulations onSimulateAllButtonClick: (e) -> diff --git a/vendor/scripts/idle.js b/vendor/scripts/idle.js new file mode 100644 index 000000000..702c8ecd2 --- /dev/null +++ b/vendor/scripts/idle.js @@ -0,0 +1,126 @@ +// https://github.com/shawnmclean/Idle.js +(function() { + "use strict"; + var Idle; + + Idle = {}; + + Idle = (function() { + Idle.isAway = false; + + Idle.awayTimeout = 3000; + + Idle.awayTimestamp = 0; + + Idle.awayTimer = null; + + Idle.onAway = null; + + Idle.onAwayBack = null; + + Idle.onVisible = null; + + Idle.onHidden = null; + + function Idle(options) { + var activeMethod, activity; + + if (options) { + this.awayTimeout = parseInt(options.awayTimeout, 10); + this.onAway = options.onAway; + this.onAwayBack = options.onAwayBack; + this.onVisible = options.onVisible; + this.onHidden = options.onHidden; + } + activity = this; + activeMethod = function() { + return activity.onActive(); + }; + window.onclick = activeMethod; + window.onmousemove = activeMethod; + window.onmouseenter = activeMethod; + window.onkeydown = activeMethod; + window.onscroll = activeMethod; + window.onmousewheel = activeMethod; + document.addEventListener("visibilitychange", (function() { + return activity.handleVisibilityChange(); + }), false); + document.addEventListener("webkitvisibilitychange", (function() { + return activity.handleVisibilityChange(); + }), false); + document.addEventListener("msvisibilitychange", (function() { + return activity.handleVisibilityChange(); + }), false); + } + + Idle.prototype.onActive = function() { + this.awayTimestamp = new Date().getTime() + this.awayTimeout; + if (this.isAway) { + if (this.onAwayBack) { + this.onAwayBack(); + } + this.start(); + } + this.isAway = false; + return true; + }; + + Idle.prototype.start = function() { + var activity; + + this.awayTimestamp = new Date().getTime() + this.awayTimeout; + if (this.awayTimer !== null) { + clearTimeout(this.awayTimer); + } + activity = this; + this.awayTimer = setTimeout((function() { + return activity.checkAway(); + }), this.awayTimeout + 100); + return this; + }; + + Idle.prototype.setAwayTimeout = function(ms) { + this.awayTimeout = parseInt(ms, 10); + return this; + }; + + Idle.prototype.checkAway = function() { + var activity, t; + + t = new Date().getTime(); + if (t < this.awayTimestamp) { + this.isAway = false; + activity = this; + this.awayTimer = setTimeout((function() { + return activity.checkAway(); + }), this.awayTimestamp - t + 100); + return; + } + if (this.awayTimer !== null) { + clearTimeout(this.awayTimer); + } + this.isAway = true; + if (this.onAway) { + return this.onAway(); + } + }; + + Idle.prototype.handleVisibilityChange = function() { + if (document.hidden || document.msHidden || document.webkitHidden) { + if (this.onHidden) { + return this.onHidden(); + } + } else { + if (this.onVisible) { + return this.onVisible(); + } + } + }; + + return Idle; + + })(); + + window.Idle = Idle; + +}).call(this);