Merge branch 'master' into production

This commit is contained in:
Nick Winter 2015-01-05 20:50:09 -08:00
commit 5b704201bb
45 changed files with 622 additions and 353 deletions

View file

@ -311,6 +311,7 @@ self.setupDebugWorldToRunUntilFrame = function (args) {
self.debugWorld.levelSessionIDs = args.levelSessionIDs; self.debugWorld.levelSessionIDs = args.levelSessionIDs;
self.debugWorld.submissionCount = args.submissionCount; self.debugWorld.submissionCount = args.submissionCount;
self.debugWorld.flagHistory = args.flagHistory; self.debugWorld.flagHistory = args.flagHistory;
self.debugWorld.difficulty = args.difficulty;
if (args.level) if (args.level)
self.debugWorld.loadFromLevel(args.level, true); self.debugWorld.loadFromLevel(args.level, true);
self.debugWorld.debugging = true; self.debugWorld.debugging = true;
@ -371,6 +372,7 @@ self.runWorld = function runWorld(args) {
self.world.levelSessionIDs = args.levelSessionIDs; self.world.levelSessionIDs = args.levelSessionIDs;
self.world.submissionCount = args.submissionCount; self.world.submissionCount = args.submissionCount;
self.world.flagHistory = args.flagHistory || []; self.world.flagHistory = args.flagHistory || [];
self.world.difficulty = args.difficulty || 0;
if(args.level) if(args.level)
self.world.loadFromLevel(args.level, true); self.world.loadFromLevel(args.level, true);
self.world.preloading = args.preload; self.world.preloading = args.preload;

View file

@ -1,5 +1,5 @@
Backbone.Mediator.setValidationEnabled false Backbone.Mediator.setValidationEnabled false
app = require 'core/application' app = null
channelSchemas = channelSchemas =
'auth': require 'schemas/subscriptions/auth' 'auth': require 'schemas/subscriptions/auth'
@ -21,6 +21,15 @@ definitionSchemas =
'misc': require 'schemas/definitions/misc' 'misc': require 'schemas/definitions/misc'
init = -> init = ->
return if app
if not window.userObject._id
$.ajax('/auth/whoami', {success: (res) ->
window.userObject = res
init()
})
return
app = require 'core/application'
setupConsoleLogging() setupConsoleLogging()
watchForErrors() watchForErrors()
setUpIOSLogging() setUpIOSLogging()
@ -34,8 +43,6 @@ init = ->
handleNormalUrls() handleNormalUrls()
setUpMoment() # Set up i18n for moment setUpMoment() # Set up i18n for moment
module.exports.init = init = _.once init
handleNormalUrls = -> handleNormalUrls = ->
# http://artsy.github.com/blog/2012/06/25/replacing-hashbang-routes-with-pushstate/ # http://artsy.github.com/blog/2012/06/25/replacing-hashbang-routes-with-pushstate/
$(document).on 'click', "a[href^='/']", (event) -> $(document).on 'click', "a[href^='/']", (event) ->

View file

@ -39,7 +39,7 @@ module.exports = class Angel extends CocoClass
# say: debugging stuff, usually off; log: important performance indicators, keep on # say: debugging stuff, usually off; log: important performance indicators, keep on
say: (args...) -> #@log args... say: (args...) -> #@log args...
log: -> log: ->
# console.info.apply is undefined in IE9, CofeeScript splats invocation won't work. # console.info.apply is undefined in IE9, CofeeScript splats invocation won't work.
# http://stackoverflow.com/questions/5472938/does-ie9-support-console-log-and-is-it-a-real-function # http://stackoverflow.com/questions/5472938/does-ie9-support-console-log-and-is-it-a-real-function
message = "|#{@shared.godNick}'s #{@nick}|" message = "|#{@shared.godNick}'s #{@nick}|"
@ -246,6 +246,7 @@ module.exports = class Angel extends CocoClass
work.testWorld.levelSessionIDs = work.levelSessionIDs work.testWorld.levelSessionIDs = work.levelSessionIDs
work.testWorld.submissionCount = work.submissionCount work.testWorld.submissionCount = work.submissionCount
work.testWorld.flagHistory = work.flagHistory ? [] work.testWorld.flagHistory = work.flagHistory ? []
work.testWorld.difficulty = work.difficulty
testWorld.loadFromLevel work.level testWorld.loadFromLevel work.level
work.testWorld.preloading = work.preload work.testWorld.preloading = work.preload
work.testWorld.headless = work.headless work.testWorld.headless = work.headless

View file

@ -63,6 +63,7 @@ module.exports = class God extends CocoClass
onTomeCast: (e) -> onTomeCast: (e) ->
@lastSubmissionCount = e.submissionCount @lastSubmissionCount = e.submissionCount
@lastFlagHistory = (flag for flag in e.flagHistory when flag.source isnt 'code') @lastFlagHistory = (flag for flag in e.flagHistory when flag.source isnt 'code')
@lastDifficulty = e.difficulty
@createWorld e.spells, e.preload, e.realTime @createWorld e.spells, e.preload, e.realTime
createWorld: (spells, preload, realTime) -> createWorld: (spells, preload, realTime) ->
@ -92,6 +93,7 @@ module.exports = class God extends CocoClass
levelSessionIDs: @levelSessionIDs levelSessionIDs: @levelSessionIDs
submissionCount: @lastSubmissionCount submissionCount: @lastSubmissionCount
flagHistory: @lastFlagHistory flagHistory: @lastFlagHistory
difficulty: @lastDifficulty
goals: @angelsShare.goalManager?.getGoals() goals: @angelsShare.goalManager?.getGoals()
headless: @angelsShare.headless headless: @angelsShare.headless
preload: preload preload: preload
@ -123,6 +125,7 @@ module.exports = class God extends CocoClass
levelSessionIDs: @levelSessionIDs levelSessionIDs: @levelSessionIDs
submissionCount: @lastSubmissionCount submissionCount: @lastSubmissionCount
flagHistory: @lastFlagHistory flagHistory: @lastFlagHistory
difficulty: @lastDifficulty
goals: @goalManager?.getGoals() goals: @goalManager?.getGoals()
frame: args.frame frame: args.frame
currentThangID: args.thangID currentThangID: args.thangID

View file

@ -374,6 +374,7 @@ module.exports = class LevelLoader extends CocoClass
@world.levelSessionIDs = if @opponentSessionID then [@sessionID, @opponentSessionID] else [@sessionID] @world.levelSessionIDs = if @opponentSessionID then [@sessionID, @opponentSessionID] else [@sessionID]
@world.submissionCount = @session?.get('state')?.submissionCount ? 0 @world.submissionCount = @session?.get('state')?.submissionCount ? 0
@world.flagHistory = @session?.get('state')?.flagHistory ? [] @world.flagHistory = @session?.get('state')?.flagHistory ? []
@world.difficulty = @session?.get('state')?.difficulty ? 0
serializedLevel = @level.serialize(@supermodel, @session, @opponentSession) serializedLevel = @level.serialize(@supermodel, @session, @opponentSession)
@world.loadFromLevel serializedLevel, false @world.loadFromLevel serializedLevel, false
console.log 'World has been initialized from level loader.' console.log 'World has been initialized from level loader.'

View file

@ -360,7 +360,7 @@ module.exports = class World
#console.log "... world serializing frames from", startFrame, "to", endFrame, "of", @totalFrames #console.log "... world serializing frames from", startFrame, "to", endFrame, "of", @totalFrames
[transferableObjects, nontransferableObjects] = [0, 0] [transferableObjects, nontransferableObjects] = [0, 0]
delete flag.processed for flag in @flagHistory delete flag.processed for flag in @flagHistory
o = {totalFrames: @totalFrames, maxTotalFrames: @maxTotalFrames, frameRate: @frameRate, dt: @dt, victory: @victory, userCodeMap: {}, trackedProperties: {}, flagHistory: @flagHistory} o = {totalFrames: @totalFrames, maxTotalFrames: @maxTotalFrames, frameRate: @frameRate, dt: @dt, victory: @victory, userCodeMap: {}, trackedProperties: {}, flagHistory: @flagHistory, difficulty: @difficulty}
o.trackedProperties[prop] = @[prop] for prop in @trackedProperties or [] o.trackedProperties[prop] = @[prop] for prop in @trackedProperties or []
for thangID, methods of @userCodeMap for thangID, methods of @userCodeMap
@ -467,7 +467,7 @@ module.exports = class World
w.userCodeMap[thangID][methodName][aetherStateKey] = serializedAether[aetherStateKey] w.userCodeMap[thangID][methodName][aetherStateKey] = serializedAether[aetherStateKey]
else else
w = new World o.userCodeMap, classMap w = new World o.userCodeMap, classMap
[w.totalFrames, w.maxTotalFrames, w.frameRate, w.dt, w.scriptNotes, w.victory, w.flagHistory] = [o.totalFrames, o.maxTotalFrames, o.frameRate, o.dt, o.scriptNotes ? [], o.victory, o.flagHistory] [w.totalFrames, w.maxTotalFrames, w.frameRate, w.dt, w.scriptNotes, w.victory, w.flagHistory, w.difficulty] = [o.totalFrames, o.maxTotalFrames, o.frameRate, o.dt, o.scriptNotes ? [], o.victory, o.flagHistory, o.difficulty]
w[prop] = val for prop, val of o.trackedProperties w[prop] = val for prop, val of o.trackedProperties
perf.t1 = now() perf.t1 = now()

View file

@ -146,13 +146,13 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription:
fork: "Fork" fork: "Fork"
play: "Spiel starten" # When used as an action verb, like "Play next level" play: "Spiel starten" # When used as an action verb, like "Play next level"
retry: "Erneut versuchen" retry: "Erneut versuchen"
# actions: "Actions" actions: "Aktionen"
# info: "Info" info: "Info"
# help: "Help" help: "Hilfe"
watch: "Beobachten" watch: "Beobachten"
unwatch: "Nicht beobachten" unwatch: "Nicht beobachten"
submit_patch: "Patch einreichen" submit_patch: "Patch einreichen"
# submit_changes: "Submit Changes" submit_changes: "Änderungen einreichen"
general: general:
and: "und" and: "und"
@ -160,16 +160,16 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription:
date: "Datum" date: "Datum"
body: "Inhalt" body: "Inhalt"
version: "Version" version: "Version"
# submitter: "Submitter" submitter: "Übermittler"
# submitted: "Submitted" submitted: "Übermittelt"
commit_msg: "Übertrage Nachricht" commit_msg: "Übertrage Nachricht"
# review: "Review" review: "Prüfen"
version_history: "Versionshistorie" version_history: "Versionshistorie"
version_history_for: "Versionsgeschichte für: " version_history_for: "Versionsgeschichte für: "
# select_changes: "Select two changes below to see the difference." select_changes: "Wähle zwei Änderungen unten um den Unterschied sehen zu können."
# undo: "Undo (Ctrl+Z)" undo: "Rückgängig (Ctrl+Z)"
# redo: "Redo (Ctrl+Shift+Z)" redo: "Wiederholen (Ctrl+Shift+Z)"
# play_preview: "Play preview of current level" play_preview: "Spiele eine Vorschau des momentanen Levels"
result: "Ergebnis" result: "Ergebnis"
results: "Ergebnisse" results: "Ergebnisse"
description: "Beschreibung" description: "Beschreibung"
@ -191,7 +191,7 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription:
medium: "Mittel" medium: "Mittel"
hard: "Schwer" hard: "Schwer"
player: "Spieler" player: "Spieler"
player_level: "Level" # Like player level 5, not like level: Dungeons of Kithgard player_level: "Stufe" # Like player level 5, not like level: Dungeons of Kithgard
units: units:
second: "Sekunde" second: "Sekunde"
@ -270,7 +270,7 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription:
loading_ready: "Bereit!" loading_ready: "Bereit!"
loading_start: "Starte Level" loading_start: "Starte Level"
problem_alert_title: "Repariere deinen Code" problem_alert_title: "Repariere deinen Code"
# problem_alert_help: "Help" problem_alert_help: "Hilfe"
time_current: "Aktuell" time_current: "Aktuell"
time_total: "Total" time_total: "Total"
time_goto: "Gehe zu" time_goto: "Gehe zu"
@ -295,10 +295,10 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription:
tip_great_responsibility: "Mit großen Programmierfähigkeiten kommt große Verantwortung." tip_great_responsibility: "Mit großen Programmierfähigkeiten kommt große Verantwortung."
tip_munchkin: "Wenn du dein Gemüse nicht isst, besucht dich ein Zwerg während du schläfst." tip_munchkin: "Wenn du dein Gemüse nicht isst, besucht dich ein Zwerg während du schläfst."
tip_binary: "Es gibt auf der Welt nur 10 Arten von Menschen: die die Binär verstehen und die die nicht." tip_binary: "Es gibt auf der Welt nur 10 Arten von Menschen: die die Binär verstehen und die die nicht."
tip_commitment_yoda: "Ein Programmier muss die größte Hingabe haben, den ernstesten Verstand. ~ Yoda" tip_commitment_yoda: "Ein Programmier muss die größte Hingabe haben, den ernstesten Verstand. - Yoda"
tip_no_try: "Tu. Oder tu nicht. Es gibt kein Versuchen. - Yoda" tip_no_try: "Tun oder nicht tun. Es gibt kein Versuchen. - Yoda"
tip_patience: "Geduld du haben musst, junger Padawan. - Yoda" tip_patience: "Geduld du haben musst, junger Padawan. - Yoda"
tip_documented_bug: "Ein dokumentierter Fehler ist kein Fehler; er ist ein Merkmal." tip_documented_bug: "Ein dokumentierter Fehler ist kein Fehler; er ist ein Besonderheit."
tip_impossible: "Es wirkt immer unmöglich bis es vollbracht ist. - Nelson Mandela" tip_impossible: "Es wirkt immer unmöglich bis es vollbracht ist. - Nelson Mandela"
tip_talk_is_cheap: "Reden ist billig. Zeig mir den Code. - Linus Torvalds" tip_talk_is_cheap: "Reden ist billig. Zeig mir den Code. - Linus Torvalds"
tip_first_language: "Das Schwierigste, das du jemals lernen wirst, ist die erste Programmiersprache. - Alan Kay" tip_first_language: "Das Schwierigste, das du jemals lernen wirst, ist die erste Programmiersprache. - Alan Kay"
@ -307,15 +307,15 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription:
tip_premature_optimization: "Vorzeitige Optimierung ist die Wurzel allen Übels (oder mindestens des meisten) bei der Programmierung - Donald Knuth" tip_premature_optimization: "Vorzeitige Optimierung ist die Wurzel allen Übels (oder mindestens des meisten) bei der Programmierung - Donald Knuth"
tip_brute_force: "Verwende im Zweifelsfall rohe Gewalt. - Ken Thompson" tip_brute_force: "Verwende im Zweifelsfall rohe Gewalt. - Ken Thompson"
tip_extrapolation: "Es gibt nur zwei Sorten Menschen, diejenigen die aus unvollständigen Informationen Schlüsse ziehen können, ..." tip_extrapolation: "Es gibt nur zwei Sorten Menschen, diejenigen die aus unvollständigen Informationen Schlüsse ziehen können, ..."
# tip_superpower: "Coding is the closest thing we have to a superpower." tip_superpower: "Programmieren ist das näheste zu einer Superkraft was wir haben."
game_menu: game_menu:
inventory_tab: "Inventar" inventory_tab: "Inventar"
save_load_tab: "Speicher/Lade" save_load_tab: "Speicher/Lade"
options_tab: "Einstellungen" options_tab: "Einstellungen"
guide_tab: "Handbuch" guide_tab: "Handbuch"
# guide_video_tutorial: "Video Tutorial" guide_video_tutorial: "Video Anleitung"
# guide_tips: "Tips" guide_tips: "Hinweise"
multiplayer_tab: "Mehrspieler" multiplayer_tab: "Mehrspieler"
auth_tab: "Registrieren" auth_tab: "Registrieren"
inventory_caption: "Rüste deinen Helden aus" inventory_caption: "Rüste deinen Helden aus"
@ -329,7 +329,7 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription:
inventory: inventory:
choose_inventory: "Gegenstände ausrüsten" choose_inventory: "Gegenstände ausrüsten"
equipped_item: "Hinzugefügt" equipped_item: "Hinzugefügt"
# required_purchase_title: "Required" required_purchase_title: "Benötigt"
available_item: "Verfügbar" available_item: "Verfügbar"
restricted_title: "Eingeschränkt" restricted_title: "Eingeschränkt"
should_equip: "(Doppelklick zum Hinzufügen)" should_equip: "(Doppelklick zum Hinzufügen)"
@ -349,7 +349,7 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription:
prompt_title: "Nicht genug Edelsteine" prompt_title: "Nicht genug Edelsteine"
prompt_body: "Benötigst du mehr?" prompt_body: "Benötigst du mehr?"
prompt_button: "Laden betreten" prompt_button: "Laden betreten"
# recovered: "Previous gems purchase recovered. Please refresh the page." recovered: "Vorhergegangener Edelsteinkauf rückgängig gemacht. Aktualisiere bitte die Seite."
subscribe: subscribe:
subscribe_title: "Abonnieren" subscribe_title: "Abonnieren"
@ -411,7 +411,7 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription:
current_value: "Aktueller Wert" current_value: "Aktueller Wert"
default_value: "Standardwert" default_value: "Standardwert"
parameters: "Parameter" parameters: "Parameter"
# returns: "Returns" returns: "Gibt zurück"
granted_by: "Gewährt durch" granted_by: "Gewährt durch"
save_load: save_load:
@ -449,7 +449,7 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription:
why_paragraph_2_prefix: "Darum geht's beim Programmieren. Es soll Spaß machen. Nicht so einen Spaß wie" why_paragraph_2_prefix: "Darum geht's beim Programmieren. Es soll Spaß machen. Nicht so einen Spaß wie"
why_paragraph_2_italic: "jau, 'ne Plakette" why_paragraph_2_italic: "jau, 'ne Plakette"
why_paragraph_2_center: "sondern Spaß wie" why_paragraph_2_center: "sondern Spaß wie"
why_paragraph_2_italic_caps: "NEIN MUTTI ICH MUSS NOCH DEN LEVEL BEENDEN !" why_paragraph_2_italic_caps: "NEIN MUTTI ICH MUSS NOCH DAS LEVEL BEENDEN !"
why_paragraph_2_suffix: "Deshalb ist CodeCombat ein Multiplayerspiel und kein spielähnlicher Kurs. Wir werden nicht aufhören bis du nicht mehr aufhören kannst -- nur diesmal ist das eine gute Sache." why_paragraph_2_suffix: "Deshalb ist CodeCombat ein Multiplayerspiel und kein spielähnlicher Kurs. Wir werden nicht aufhören bis du nicht mehr aufhören kannst -- nur diesmal ist das eine gute Sache."
why_paragraph_3: "Wenn dich Spiele süchtig machen, dann lass dich von diesem süchtig machen und werde ein Zauberer des Technologiezeitalters." why_paragraph_3: "Wenn dich Spiele süchtig machen, dann lass dich von diesem süchtig machen und werde ein Zauberer des Technologiezeitalters."
press_title: "Blogger/Presse" press_title: "Blogger/Presse"
@ -482,14 +482,14 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription:
forum_prefix: "Für alle öffentlichen Themen, benutze stattdessen " forum_prefix: "Für alle öffentlichen Themen, benutze stattdessen "
forum_page: "unser Forum" forum_page: "unser Forum"
forum_suffix: "." forum_suffix: "."
# faq_prefix: "There's also a" faq_prefix: "Es gibt auch ein"
# faq: "FAQ" faq: "FAQ"
# subscribe_prefix: "If you need help figuring out a level, please" subscribe_prefix: "Wenn du hilfe brauchst ein Level zu lösen, bitte"
# subscribe: "buy a CodeCombat subscription" subscribe: "kaufe ein CodeCombat Abonnement"
# subscribe_suffix: "and we'll be happy to help you with your code." subscribe_suffix: "und wir werden dir gerne bei deinem Code helfen."
# subscriber_support: "Since you're a CodeCombat subscriber, your email will get our priority support." subscriber_support: "Da du ein CodeCombat Abonnent bist, bekommt deine E-Mail Priorität."
# screenshot_included: "Screenshot included." screenshot_included: "Bildschirmfoto hinzugefügt."
# where_reply: "Where should we reply?" where_reply: "Wohin sollen wir antworten?"
send: "Sende Feedback" send: "Sende Feedback"
contact_candidate: "Kontaktiere Kandidaten" # Deprecated contact_candidate: "Kontaktiere Kandidaten" # Deprecated
recruitment_reminder: "Benutzen Sie dieses Formular um Kontakt zu Kandidaten aufzunehmen, an denen Sie interessiert sind. Bedenken Sie das CodeCombat 15% des ersten Jahresgehaltes berechnet. Diese Gebühr wird fällig wenn Sie den Kandidaten einstellen und ist für 90 Tage rückerstattungsfähig, sollte der Mitarbeiter nicht eingestellt bleiben. Mitarbeiter die für Teilzeit, Remote oder eine Auftragsarbeit eingestellt werden sind kostenlos, das gilt auch für Praktikanten." # Deprecated recruitment_reminder: "Benutzen Sie dieses Formular um Kontakt zu Kandidaten aufzunehmen, an denen Sie interessiert sind. Bedenken Sie das CodeCombat 15% des ersten Jahresgehaltes berechnet. Diese Gebühr wird fällig wenn Sie den Kandidaten einstellen und ist für 90 Tage rückerstattungsfähig, sollte der Mitarbeiter nicht eingestellt bleiben. Mitarbeiter die für Teilzeit, Remote oder eine Auftragsarbeit eingestellt werden sind kostenlos, das gilt auch für Praktikanten." # Deprecated
@ -573,22 +573,22 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription:
classes: classes:
archmage_title: "Erzmagier" archmage_title: "Erzmagier"
archmage_title_description: "(Programmierer)" archmage_title_description: "(Programmierer)"
# archmage_summary: "If you are a developer interested in coding educational games, become an archmage to help us build CodeCombat!" archmage_summary: "Wenn du ein Entwickler bist der daran interessiert ist Lernspiele zu programmieren, werde ein Erzmagier um uns zu helfen CodeCombat zu erschaffen!"
artisan_title: "Handwerker" artisan_title: "Handwerker"
artisan_title_description: "(Level Entwickler)" artisan_title_description: "(Level Entwickler)"
# artisan_summary: "Build and share levels for you and your friends to play. Become an Artisan to learn the art of teaching others to program." artisan_summary: "Erschaffe und teile Level zum spielen für dich und deine Freunde. Werde ein Handwerker um die Kunst zu lernen anderen Programmieren zu lehren."
adventurer_title: "Abenteurer" adventurer_title: "Abenteurer"
adventurer_title_description: "(Level Spieltester)" adventurer_title_description: "(Level Spieltester)"
# adventurer_summary: "Get our new levels (even our subscriber content) for free one week early and help us work out bugs before our public release." adventurer_summary: "Bekomme unsere neuen Level (sogar unser Abonnement Inhalt) kostenlos eine Woche früher und hilf uns Fehler vor der Veröffentlichung zu finden."
scribe_title: "Schreiber" scribe_title: "Schreiber"
scribe_title_description: "(Artikel Editor)" scribe_title_description: "(Artikel Editor)"
# scribe_summary: "Good code needs good documentation. Write, edit, and improve the docs read by millions of players across the globe." scribe_summary: "Guter Code braucht gute Dokumentation. Schreibe, bearbeite and verbessere die, von weltweit Millionen von Spielern, gelesenen Dokumentationen."
diplomat_title: "Diplomat" diplomat_title: "Diplomat"
diplomat_title_description: "(Übersetzer)" diplomat_title_description: "(Übersetzer)"
# diplomat_summary: "CodeCombat is localized in 45+ languages by our Diplomats. Help us out and contribute translations." diplomat_summary: "CodeCombat wird in 45+ Sprachen von unseren Diplomaten übersetzt. Hilf uns und steuere Übersetzungen bei."
ambassador_title: "Botschafter" ambassador_title: "Botschafter"
ambassador_title_description: "(Support)" ambassador_title_description: "(Support)"
# ambassador_summary: "Tame our forum users and provide direction for those with questions. Our ambassadors represent CodeCombat to the world." ambassador_summary: "Zähme unsere Forum Benutzer und weise denen mit Fragen die Richtung. Unsere Botschafter repräsentieren CodeCombat vor der Welt."
editor: editor:
main_title: "CodeCombat Editoren" main_title: "CodeCombat Editoren"
@ -608,9 +608,9 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription:
more: "Mehr" more: "Mehr"
wiki: "Wiki" wiki: "Wiki"
live_chat: "Live Chat" live_chat: "Live Chat"
# thang_main: "Main" thang_main: "Main" # I'd keep it this way, everyone should get what it means
# thang_spritesheets: "Spritesheets" thang_spritesheets: "Sprite Palette"
# thang_colors: "Colors" thang_colors: "Farben"
level_some_options: "Einige Einstellungsmöglichkeiten?" level_some_options: "Einige Einstellungsmöglichkeiten?"
level_tab_thangs: "Thangs" level_tab_thangs: "Thangs"
level_tab_scripts: "Skripte" level_tab_scripts: "Skripte"
@ -657,7 +657,7 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription:
achievement_query_misc: "Sonstige Schlüsselerfolge" achievement_query_misc: "Sonstige Schlüsselerfolge"
achievement_query_goals: "Level Erfolge" achievement_query_goals: "Level Erfolge"
level_completion: "abgeschlossene Level" level_completion: "abgeschlossene Level"
# pop_i18n: "Populate I18N" pop_i18n: "Bevölkere I18N"
article: article:
edit_btn_preview: "Vorschau" edit_btn_preview: "Vorschau"
@ -665,7 +665,7 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription:
contribute: contribute:
page_title: "Mitwirken" page_title: "Mitwirken"
# intro_blurb: "CodeCombat is 100% open source! Hundreds of dedicated players have helped us build the game into what it is today. Join us and write the next chapter in CodeCombat's quest to teach the world to code!" intro_blurb: "CodeCombat ist zu 100% Open Source! Hunderte hingebungsvolle Spieler haben uns geholfen das Spiel zu dem zu machen was es heute ist. Trete uns bei und schreibe das nächste Kapitel in der Aufgabe von CodeCombat der Welt Programmieren zu lehren!"
alert_account_message_intro: "Hey du!" alert_account_message_intro: "Hey du!"
alert_account_message: "Um Klassen-Emails abonnieren zu können, musst du dich zuerst anmelden." alert_account_message: "Um Klassen-Emails abonnieren zu können, musst du dich zuerst anmelden."
archmage_introduction: "Einer der größten Vorteile daran ein Spiel aufzubauen, ist es, dass so viele verschiedene Aspekte mit reinspielen. Grafiken, Sound, echtzeit Networking, Social Networking und natürlich viele der gewöhnlichen Aspekte des Programmierens, von low-level Datenbankmanagement und Server Administration bis hin zum Aufbau von Design und Interface. Es gibt viel zu tun und wenn du ein erfahrener Programmierer bist, mit einer Veranlagung dazu, wirklich knallhart bei CodeCombat einzutauchen, dann könnte diese Klasse etwas für dich sein. Wir würden uns wahnsinnig über deine Hilfe dabei freuen, das beste Programmierspiel der Welt aufzubauen." archmage_introduction: "Einer der größten Vorteile daran ein Spiel aufzubauen, ist es, dass so viele verschiedene Aspekte mit reinspielen. Grafiken, Sound, echtzeit Networking, Social Networking und natürlich viele der gewöhnlichen Aspekte des Programmierens, von low-level Datenbankmanagement und Server Administration bis hin zum Aufbau von Design und Interface. Es gibt viel zu tun und wenn du ein erfahrener Programmierer bist, mit einer Veranlagung dazu, wirklich knallhart bei CodeCombat einzutauchen, dann könnte diese Klasse etwas für dich sein. Wir würden uns wahnsinnig über deine Hilfe dabei freuen, das beste Programmierspiel der Welt aufzubauen."
@ -963,7 +963,7 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription:
art_paragraph_1: "Für den Verweis auf CodeCombat, nenne und verlinke bitte die Website codecombat.com nahe der Quelle oder an der Stelle, wo es für das Medium angemessen ist. Zum Beispiel:" art_paragraph_1: "Für den Verweis auf CodeCombat, nenne und verlinke bitte die Website codecombat.com nahe der Quelle oder an der Stelle, wo es für das Medium angemessen ist. Zum Beispiel:"
use_list_1: "Wenn in einem Film verwendet, nenne codecombat.com in den Credits/Abspann" use_list_1: "Wenn in einem Film verwendet, nenne codecombat.com in den Credits/Abspann"
use_list_2: "Wenn auf einer Webseite verwendet, füge einen Link nahe bei der Verwendung ein, z.B. unter einem Bild oder auf der generellen Beitragsseite, wo auch andere Creative Commons Werke und Open Source Software genannt wird, die auf der Seite verwendet wird. Wenn deutlich auf CodeCombat Bezug genommen wird, wie z.B. in einem Blogeintrag, in dem CodeCombat erwähnt wird, dann muss CodeCombat nicht separat belegt werden." use_list_2: "Wenn auf einer Webseite verwendet, füge einen Link nahe bei der Verwendung ein, z.B. unter einem Bild oder auf der generellen Beitragsseite, wo auch andere Creative Commons Werke und Open Source Software genannt wird, die auf der Seite verwendet wird. Wenn deutlich auf CodeCombat Bezug genommen wird, wie z.B. in einem Blogeintrag, in dem CodeCombat erwähnt wird, dann muss CodeCombat nicht separat belegt werden."
# 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_paragraph_2: "Wenn der benutzte Inhalt nicht von CodeCombat sonder einem Benutzer von codecombat.com geschaffen wurde, schreibe es diesem stattdessen zu und folge den Anweisungen, wenn es welche gibt, in der Beschreibung dieses Inhalts."
rights_title: "Rechte vorbehalten" rights_title: "Rechte vorbehalten"
rights_desc: "Alle Rechte vorbehalten für die Level selbst. Dies beinhaltet" rights_desc: "Alle Rechte vorbehalten für die Level selbst. Dies beinhaltet"
rights_scripts: "Skripte" rights_scripts: "Skripte"
@ -1002,8 +1002,8 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription:
done_editing: "Editierung beenden" done_editing: "Editierung beenden"
profile_for_prefix: "Profil von " profile_for_prefix: "Profil von "
profile_for_suffix: "" profile_for_suffix: ""
# featured: "Featured" featured: "Featured" # I'd keep it that way, should be understandable by everyone
# not_featured: "Not Featured" not_featured: "Not Featured" # Ref. Above
looking_for: "Suche nach:" looking_for: "Suche nach:"
last_updated: "zuletzt geändert:" last_updated: "zuletzt geändert:"
contact: "Kontakt" contact: "Kontakt"
@ -1143,7 +1143,7 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription:
candidate_years_experience: "Erfahrung (Jahre)" candidate_years_experience: "Erfahrung (Jahre)"
candidate_last_updated: "Zuletzt aktualisiert" candidate_last_updated: "Zuletzt aktualisiert"
candidate_who: "Wer" candidate_who: "Wer"
# featured_developers: "Featured Developers" featured_developers: "Mitwirkende Entwickler" # Meaning: Mitwirken -> Contribute, play a part
other_developers: "Andere Entwickler" other_developers: "Andere Entwickler"
inactive_developers: "Inaktive Etwickler" inactive_developers: "Inaktive Etwickler"

View file

@ -166,8 +166,10 @@
version_history: "Version History" version_history: "Version History"
version_history_for: "Version History for: " version_history_for: "Version History for: "
select_changes: "Select two changes below to see the difference." select_changes: "Select two changes below to see the difference."
undo: "Undo (Ctrl+Z)" undo_prefix: "Undo"
redo: "Redo (Ctrl+Shift+Z)" undo_shortcut: "(Ctrl+Z)"
redo_prefix: "Redo"
redo_shortcut: "(Ctrl+Shift+Z)"
play_preview: "Play preview of current level" play_preview: "Play preview of current level"
result: "Result" result: "Result"
results: "Results" results: "Results"

View file

@ -56,9 +56,9 @@ module.exports = nativeDescription: "español (ES)", englishDescription: "Spanis
items: "Objetos" # Tooltip on item shop button from /play items: "Objetos" # Tooltip on item shop button from /play
unlock: "Desbloquear" # For purchasing items and heroes unlock: "Desbloquear" # For purchasing items and heroes
confirm: "Confirmar" confirm: "Confirmar"
# owned: "Owned" # For items you own owned: "Lo Posees" # For items you own
# locked: "Locked" locked: "Bloqueado"
# purchasable: "Purchasable" # For a hero you unlocked but haven't purchased purchasable: "Comprable" # For a hero you unlocked but haven't purchased
available: "Disponible" available: "Disponible"
skills_granted: "Habilidades concedidas" # Property documentation details skills_granted: "Habilidades concedidas" # Property documentation details
heroes: "Heroes" # Tooltip on hero shop button from /play heroes: "Heroes" # Tooltip on hero shop button from /play
@ -69,7 +69,7 @@ module.exports = nativeDescription: "español (ES)", englishDescription: "Spanis
change_hero: "Seleccionar Heroe" # Go back from choose inventory to choose hero change_hero: "Seleccionar Heroe" # Go back from choose inventory to choose hero
choose_inventory: "Equipar Objetos" choose_inventory: "Equipar Objetos"
buy_gems: "Comprar Joyas" buy_gems: "Comprar Joyas"
# campaign_desert: "Desert Campaign" campaign_desert: "Campaña del Desierto"
campaign_forest: "Campaña del Bosque" campaign_forest: "Campaña del Bosque"
campaign_dungeon: "Campaña del Calabozo" campaign_dungeon: "Campaña del Calabozo"
subscription_required: "Suscripción requerida" subscription_required: "Suscripción requerida"
@ -80,7 +80,7 @@ module.exports = nativeDescription: "español (ES)", englishDescription: "Spanis
level_difficulty: "Dificultad: " level_difficulty: "Dificultad: "
campaign_beginner: "Campaña de Principiante" campaign_beginner: "Campaña de Principiante"
awaiting_levels_adventurer_prefix: "Liberamos cinco niveles cada semana." awaiting_levels_adventurer_prefix: "Liberamos cinco niveles cada semana."
# awaiting_levels_adventurer: "Sign up as an Adventurer" awaiting_levels_adventurer: "Regístrate como Aventurero"
awaiting_levels_adventurer_suffix: "para ser el primero en jugar nuevos niveles." awaiting_levels_adventurer_suffix: "para ser el primero en jugar nuevos niveles."
choose_your_level: "Elige tu nivel" # The rest of this section is the old play view at /play-old and isn't very important. choose_your_level: "Elige tu nivel" # The rest of this section is the old play view at /play-old and isn't very important.
adventurer_prefix: "Puedes elegir cualquier pantalla o charlar en " adventurer_prefix: "Puedes elegir cualquier pantalla o charlar en "
@ -102,14 +102,14 @@ module.exports = nativeDescription: "español (ES)", englishDescription: "Spanis
log_in: "Entrar" log_in: "Entrar"
logging_in: "Entrando..." logging_in: "Entrando..."
log_out: "Salir" log_out: "Salir"
# forgot_password: "Forgot your password?" forgot_password: "¿Olvidaste tu contraseña?"
authenticate_gplus: "Autenticar G+" authenticate_gplus: "Autenticar G+"
load_profile: "Cargar perfil G+" load_profile: "Cargar perfil G+"
load_email: "Cargar correo G+" load_email: "Cargar correo G+"
finishing: "Finalizando" finishing: "Finalizando"
# sign_in_with_facebook: "Sign in with Facebook" sign_in_with_facebook: "Accede usando Facebook"
# sign_in_with_gplus: "Sign in with G+" sign_in_with_gplus: "Accede usando G+"
# signup_switch: "Want to create an account?" signup_switch: "¿Quieres crear una cuenta?"
signup: signup:
email_announcements: "Recibir noticias por correo electrónico" email_announcements: "Recibir noticias por correo electrónico"
@ -118,7 +118,7 @@ module.exports = nativeDescription: "español (ES)", englishDescription: "Spanis
log_in: "Iniciar sesión con contraseña" log_in: "Iniciar sesión con contraseña"
social_signup: "O, puedes acceder a través de tu cuenta de Facebook o G+:" social_signup: "O, puedes acceder a través de tu cuenta de Facebook o G+:"
required: "Tienes que estar reginstrado antes de poder seguir por aquí." required: "Tienes que estar reginstrado antes de poder seguir por aquí."
# login_switch: "Already have an account?" login_switch: "¿Ya tienes una cuenta?"
recover: recover:
recover_account_title: "Recuperar Cuenta" recover_account_title: "Recuperar Cuenta"

View file

@ -169,7 +169,7 @@ module.exports = nativeDescription: "magyar", englishDescription: "Hungarian", t
select_changes: "Válassz két lehetőséget alul, hogy lásd a különbséget." select_changes: "Válassz két lehetőséget alul, hogy lásd a különbséget."
undo: "Vissza (Ctrl+Z)" undo: "Vissza (Ctrl+Z)"
redo: "Újra (Ctrl+Shift+Z)" redo: "Újra (Ctrl+Shift+Z)"
# play_preview: "Play preview of current level" play_preview: "Aktuális szint előnézete"
result: "Eredmény" result: "Eredmény"
results: "Eredmények" results: "Eredmények"
description: "Leírás" description: "Leírás"
@ -269,8 +269,8 @@ module.exports = nativeDescription: "magyar", englishDescription: "Hungarian", t
keyboard_shortcuts: "Billentyűparancsok" keyboard_shortcuts: "Billentyűparancsok"
loading_ready: "Kész!" loading_ready: "Kész!"
loading_start: "Szint kezdése" loading_start: "Szint kezdése"
problem_alert_title: "igazítsd ki a Kódod" problem_alert_title: "Igazítsd ki a Kódod"
# problem_alert_help: "Help" problem_alert_help: "Segítség"
time_current: "Most:" time_current: "Most:"
time_total: "Maximum:" time_total: "Maximum:"
time_goto: "Menj" time_goto: "Menj"
@ -307,15 +307,15 @@ module.exports = nativeDescription: "magyar", englishDescription: "Hungarian", t
tip_premature_optimization: "Minden rossz gyökere a korai optimizáció. - Donald Knuth" tip_premature_optimization: "Minden rossz gyökere a korai optimizáció. - Donald Knuth"
tip_brute_force: "Ha kérdésesa helyzet, használj nyers erőt. - Ken Thompson" tip_brute_force: "Ha kérdésesa helyzet, használj nyers erőt. - Ken Thompson"
tip_extrapolation: "Csak két fajta ember létezik. Az egyik, aki extrapolál hiányos adatokból..." tip_extrapolation: "Csak két fajta ember létezik. Az egyik, aki extrapolál hiányos adatokból..."
# tip_superpower: "Coding is the closest thing we have to a superpower." tip_superpower: "A programozás képessége van legközelebb a szuperképességekhez."
game_menu: game_menu:
inventory_tab: "Raktár" inventory_tab: "Raktár"
save_load_tab: "Ment/Betölt" save_load_tab: "Ment/Betölt"
options_tab: "Beállítások" options_tab: "Beállítások"
guide_tab: "Vezérfonal" guide_tab: "Vezérfonal"
# guide_video_tutorial: "Video Tutorial" guide_video_tutorial: "Bevezető videó"
# guide_tips: "Tips" guide_tips: "Tippek"
multiplayer_tab: "Többjátékos" multiplayer_tab: "Többjátékos"
auth_tab: "Iratkozz fel!" auth_tab: "Iratkozz fel!"
inventory_caption: "Szereld fel a hősöd!" inventory_caption: "Szereld fel a hősöd!"
@ -329,7 +329,7 @@ module.exports = nativeDescription: "magyar", englishDescription: "Hungarian", t
inventory: inventory:
choose_inventory: "felszerelési tárgyak" choose_inventory: "felszerelési tárgyak"
equipped_item: "Választott" equipped_item: "Választott"
# required_purchase_title: "Required" required_purchase_title: "Szükséges"
available_item: "Elérhető" available_item: "Elérhető"
restricted_title: "Limitált" restricted_title: "Limitált"
should_equip: "(felszereléshez dupla katt)" should_equip: "(felszereléshez dupla katt)"
@ -351,57 +351,57 @@ module.exports = nativeDescription: "magyar", englishDescription: "Hungarian", t
prompt_button: "Lépj be a boltba" prompt_button: "Lépj be a boltba"
# recovered: "Previous gems purchase recovered. Please refresh the page." # recovered: "Previous gems purchase recovered. Please refresh the page."
# subscribe: subscribe:
# subscribe_title: "Subscribe" subscribe_title: "Feliratkozás"
# unsubscribe: "Unsubscribe" unsubscribe: "Leiratkozás"
# levels: "Get more practice with bonus levels!" levels: "Gyakorolj a bónusz szinteken!"
# heroes: "More powerful heroes!" heroes: "Még erősebb hősök!"
# gems: "3500 bonus gems every month!" gems: "3500 búnusz drágakő havonta!"
# items: "Over 250 bonus items!" items: "Több mint 250 bónusz tárgy!"
# parents: "For Parents" parents: "Szülőknek"
# parents_title: "Your child will learn to code." parents_title: "A gyereke programozni tanul majd."
# parents_blurb1: "With CodeCombat, your child learns by writing real code. They start by learning simple commands, and progress to more advanced topics." parents_blurb1: "A CodeCombattal a gyereke valódi programozási feladatokon keresztül tanul. Egyszerű utasításokkal kezdenek, aztán további témákba is betekintést kapnak."
# parents_blurb2: "For $9.99 USD/mo, they get new challenges every week and personal email support from professional programmers." parents_blurb2: "Havonta 9,99 USD-ért, minden héten új kihívások elé állítjuk őket és személyre szóló emailes támogatást nyújtanak enkik profi programozók."
# parents_blurb3: "No Risk: 100% money back guarantee, easy 1-click unsubscribe." parents_blurb3: "100%-os pénzvisszafizetés garancia: 1-kattintásossal leiratkozhat."
# subscribe_button: "Subscribe Now" subscribe_button: "Iratkozzon fel most"
# stripe_description: "Monthly Subscription" stripe_description: "Havi feliratkozás"
# subscription_required_to_play: "You'll need a subscription to play this level." subscription_required_to_play: "Ehhez a szinthez fel kell iratkoznod."
# choose_hero: choose_hero:
# choose_hero: "Choose Your Hero" choose_hero: "Válassz hőst."
# programming_language: "Programming Language" programming_language: "Programnyelv"
# programming_language_description: "Which programming language do you want to use?" programming_language_description: "Melyik programnyelvet akarod használni?"
# default: "Default" default: "Alapbeállítás"
# experimental: "Experimental" experimental: "Kísérleti"
# python_blurb: "Simple yet powerful, great for beginners and experts." python_blurb: "Egyszerű és mégis hatékony, kezdőknek és szakértőknek is."
# javascript_blurb: "The language of the web. (Not the same as Java.)" javascript_blurb: "Az internet nyelve. (Nem azonos a Javaval.)"
# coffeescript_blurb: "Nicer JavaScript syntax." coffeescript_blurb: "Szerethetőbb JavaScript szintaxis."
# clojure_blurb: "A modern Lisp." clojure_blurb: "A modern Lisp."
# lua_blurb: "Game scripting language." lua_blurb: "Játék programozó nyelv"
# io_blurb: "Simple but obscure." io_blurb: "Egyszerű, de különleges."
# status: "Status" status: "Státusz"
# weapons: "Weapons" weapons: "Fegyverek"
# weapons_warrior: "Swords - Short Range, No Magic" weapons_warrior: "Kardok - Rövid hatótávolság, mágikus erő nélkül."
# weapons_ranger: "Crossbows, Guns - Long Range, No Magic" weapons_ranger: "Számszeríj, Fegyverek - Nagy hatótávolság, mágikus erő nélkül."
# weapons_wizard: "Wands, Staffs - Long Range, Magic" weapons_wizard: "Pálcák és Botok - Nagy hatótávolság és mágikus erő."
# attack: "Damage" # Can also translate as "Attack" attack: "Támadóérték" # Can also translate as "Attack"
# health: "Health" health: "Élet"
# speed: "Speed" speed: "Sebesség"
# regeneration: "Regeneration" regeneration: "Gyógyulás"
# range: "Range" # As in "attack or visual range" range: "Hatótávolság" # As in "attack or visual range"
# blocks: "Blocks" # As in "this shield blocks this much damage" blocks: "Blokkolás értéke" # As in "this shield blocks this much damage"
# backstab: "Backstab" # As in "this dagger does this much backstab damage" backstab: "Visszavágás" # As in "this dagger does this much backstab damage"
# skills: "Skills" skills: "Képességek"
# available_for_purchase: "Available for Purchase" # Shows up when you have unlocked, but not purchased, a hero in the hero store available_for_purchase: "Megvehető" # Shows up when you have unlocked, but not purchased, a hero in the hero store
# level_to_unlock: "Level to unlock:" # Label for which level you have to beat to unlock a particular hero (click a locked hero in the store to see) level_to_unlock: "Szükséges szintek:" # Label for which level you have to beat to unlock a particular hero (click a locked hero in the store to see)
# restricted_to_certain_heroes: "Only certain heroes can play this level." restricted_to_certain_heroes: "Csak bizonyos hős játszhatja ezt a szintet."
skill_docs: skill_docs:
writable: "írható" # Hover over "attack" in Your Skills while playing a level to see most of this writable: "írható" # Hover over "attack" in Your Skills while playing a level to see most of this
read_only: "csak olvasható" read_only: "csak olvasható"
action_name: "név" action_name: "név"
action_cooldown: "Kitart" action_cooldown: "Kitart"
# action_specific_cooldown: "Cooldown" action_specific_cooldown: "Újratöltés"
action_damage: "Sebzés" action_damage: "Sebzés"
action_range: "Lőtávolság" action_range: "Lőtávolság"
action_radius: "Körzet" action_radius: "Körzet"
@ -447,7 +447,7 @@ module.exports = nativeDescription: "magyar", englishDescription: "Hungarian", t
why_codecombat: "CodeCombat, de miért?" why_codecombat: "CodeCombat, de miért?"
why_paragraph_1: "Ha programozni akarsz tanulni, nem kellenek hozzá tanórák. Csak írnod kell egy csomó kódot és jól érezned magad közben." why_paragraph_1: "Ha programozni akarsz tanulni, nem kellenek hozzá tanórák. Csak írnod kell egy csomó kódot és jól érezned magad közben."
why_paragraph_2_prefix: "Erről szól a programozás. Buli lesz. Nem viccelek." why_paragraph_2_prefix: "Erről szól a programozás. Buli lesz. Nem viccelek."
# why_paragraph_2_italic: "yay a badge" why_paragraph_2_italic: "ezaz, kitüntetés"
# why_paragraph_2_center: "but fun like" # why_paragraph_2_center: "but fun like"
why_paragraph_2_italic_caps: "NE ANYA, BE KELL FEJEZNEM A SZINTET!" why_paragraph_2_italic_caps: "NE ANYA, BE KELL FEJEZNEM A SZINTET!"
# why_paragraph_2_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_2_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."
@ -573,51 +573,51 @@ module.exports = nativeDescription: "magyar", englishDescription: "Hungarian", t
classes: classes:
archmage_title: "Főmágus" archmage_title: "Főmágus"
archmage_title_description: "(Kódoló)" archmage_title_description: "(Kódoló)"
# archmage_summary: "If you are a developer interested in coding educational games, become an archmage to help us build CodeCombat!" archmage_summary: "Ha oktatási célú játékokban érdekelt fejlesztő vagy csatlakozz főmágusként a CodeCombat csapatához!"
artisan_title: "Alkotóművész" artisan_title: "Alkotóművész"
artisan_title_description: "(Szint Építő)" artisan_title_description: "(Szint Építő)"
# artisan_summary: "Build and share levels for you and your friends to play. Become an Artisan to learn the art of teaching others to program." artisan_summary: "Építs és ossz meg szinteket a barátaiddal. Legyél alkotóművész, aki másokat tanít programozni."
adventurer_title: "Kalandor" adventurer_title: "Kalandor"
adventurer_title_description: "(Játékteszter)" adventurer_title_description: "(Játékteszter)"
# adventurer_summary: "Get our new levels (even our subscriber content) for free one week early and help us work out bugs before our public release." adventurer_summary: "Szerezd meg az új szinteket, meg a feliratkozással elérhetőeket is egy héttel korábban és ingyen, és segíts a debugging-ban a hivatalos kiadás előtt."
scribe_title: "Írnok" scribe_title: "Írnok"
scribe_title_description: "(Cikk Szerkesztő)" scribe_title_description: "(Cikk Szerkesztő)"
# scribe_summary: "Good code needs good documentation. Write, edit, and improve the docs read by millions of players across the globe." scribe_summary: "A jó kód jó dokumentációt igényel. Írd, szekeszd és fejleszd a fájlokat, amelyeket milliók használnak a Föld minden pontjáról."
diplomat_title: "Diplomata" diplomat_title: "Diplomata"
diplomat_title_description: "(Fordító)" diplomat_title_description: "(Fordító)"
# diplomat_summary: "CodeCombat is localized in 45+ languages by our Diplomats. Help us out and contribute translations." diplomat_summary: "CodeCombat fordítása már több mint 45 nyelvre elkezdődött. Segítsd a fordításoddal te is a munkát."
ambassador_title: "Nagykövet" ambassador_title: "Nagykövet"
ambassador_title_description: "(Támogató)" ambassador_title_description: "(Támogató)"
# ambassador_summary: "Tame our forum users and provide direction for those with questions. Our ambassadors represent CodeCombat to the world." ambassador_summary: "Vezesd a fórumozókat és mutass utat a kérdezőknek. A CodeCombatot a Nagykövetek reprezentálják a világban."
editor: editor:
main_title: "CodeCombat Szerkesztők" main_title: "CodeCombat Szerkesztők"
article_title: "Cikk Szerkesztő" article_title: "Cikk Szerkesztő"
# thang_title: "Thang Editor" thang_title: "Dolog szerkesztő"
level_title: "Szint Szerkesztő" level_title: "Szint Szerkesztő"
# achievement_title: "Achievement Editor" achievement_title: "Eredmény szerkesztő"
back: "Vissza" back: "Vissza"
# revert: "Revert" # revert: "Revert"
# revert_models: "Revert Models" # revert_models: "Revert Models"
# pick_a_terrain: "Pick A Terrain" # pick_a_terrain: "Pick A Terrain"
# small: "Small" small: "Kicsi"
# grassy: "Grassy" grassy: "Füves"
# fork_title: "Fork New Version" fork_title: "Új Verzió villára vétele"
# fork_creating: "Creating Fork..." # fork_creating: "Creating Fork..."
# generate_terrain: "Generate Terrain" generate_terrain: "Terület generálása"
more: "Több" more: "Több"
wiki: "Tudásbázis" wiki: "Tudásbázis"
live_chat: "Élő cset" live_chat: "Élő cset"
# thang_main: "Main" thang_main: "Főoldal"
# thang_spritesheets: "Spritesheets" # thang_spritesheets: "Spritesheets"
# thang_colors: "Colors" # thang_colors: "Colors"
# level_some_options: "Some Options?" # level_some_options: "Some Options?"
# level_tab_thangs: "Thangs" # level_tab_thangs: "Thangs"
# level_tab_scripts: "Scripts" # level_tab_scripts: "Scripts"
# level_tab_settings: "Settings" level_tab_settings: "Beállítások"
# level_tab_components: "Components" # level_tab_components: "Components"
# level_tab_systems: "Systems" # level_tab_systems: "Systems"
# level_tab_docs: "Documentation" level_tab_docs: "Dokumentáció"
# level_tab_thangs_title: "Current Thangs" # level_tab_thangs_title: "Current Thangs"
# level_tab_thangs_all: "All" # level_tab_thangs_all: "All"
# level_tab_thangs_conditions: "Starting Conditions" # level_tab_thangs_conditions: "Starting Conditions"

View file

@ -53,7 +53,7 @@ module.exports = nativeDescription: "Македонски", englishDescription:
spectate: "Набљудувај" # Ladder page spectate: "Набљудувај" # Ladder page
players: "играчи" # Hover over a level on /play players: "играчи" # Hover over a level on /play
hours_played: "изиграни часови" # Hover over a level on /play hours_played: "изиграни часови" # Hover over a level on /play
items: "Предмети" # Tooltip on item shop button from /play items: "Опрема" # Tooltip on item shop button from /play
unlock: "Отклучи" # For purchasing items and heroes unlock: "Отклучи" # For purchasing items and heroes
confirm: "Потврди" confirm: "Потврди"
owned: "Имаш" # For items you own owned: "Имаш" # For items you own
@ -64,7 +64,7 @@ module.exports = nativeDescription: "Македонски", englishDescription:
heroes: "Херои" # Tooltip on hero shop button from /play heroes: "Херои" # Tooltip on hero shop button from /play
achievements: "Постигнувања" # Tooltip on achievement list button from /play achievements: "Постигнувања" # Tooltip on achievement list button from /play
account: "Сметка" # Tooltip on account button from /play account: "Сметка" # Tooltip on account button from /play
settings: "Поставки" # Tooltip on settings button from /play settings: "Подесувања" # Tooltip on settings button from /play
next: "Следно" # Go from choose hero to choose inventory before playing a level next: "Следно" # Go from choose hero to choose inventory before playing a level
change_hero: "Смени херој" # Go back from choose inventory to choose hero change_hero: "Смени херој" # Go back from choose inventory to choose hero
choose_inventory: "Опреми се" choose_inventory: "Опреми се"
@ -102,7 +102,7 @@ module.exports = nativeDescription: "Македонски", englishDescription:
log_in: "Најави се" log_in: "Најави се"
logging_in: "Најавувањето е во тек" logging_in: "Најавувањето е во тек"
log_out: "Одјави се" log_out: "Одјави се"
forgot_password: "Ја заборави својата лозинка?" forgot_password: "Ја заборави твојата лозинка?"
authenticate_gplus: "Провери G+ најава" authenticate_gplus: "Провери G+ најава"
load_profile: "Вчитај G+ профил" load_profile: "Вчитај G+ профил"
load_email: "Вчитај G+ e-mail" load_email: "Вчитај G+ e-mail"
@ -287,9 +287,9 @@ module.exports = nativeDescription: "Македонски", englishDescription:
tip_error_free: "Постојат два начина за пишување програми без грешки; само третиот работи. - Alan Perlis" tip_error_free: "Постојат два начина за пишување програми без грешки; само третиот работи. - Alan Perlis"
tip_debugging_program: "Ако дебагирање е процесот на отстранување грешки, тогаш програмирање мора да е процесот при кој тие настануваат. - Edsger W. Dijkstra" tip_debugging_program: "Ако дебагирање е процесот на отстранување грешки, тогаш програмирање мора да е процесот при кој тие настануваат. - Edsger W. Dijkstra"
tip_forums: "Упати се кон форумите и кажи ни што мислиш!" tip_forums: "Упати се кон форумите и кажи ни што мислиш!"
# tip_baby_coders: "In the future, even babies will be Archmages." tip_baby_coders: "Во иднината, дури и бебињата ќе бидат Веле-Волшебници."
tip_morale_improves: "Вчитувањето ќе продолжи се додека моралот не се подобри." tip_morale_improves: "Вчитувањето ќе продолжи се додека моралот не се подобри."
# tip_all_species: "We believe in equal opportunities to learn programming for all species." tip_all_species: "Веруваме во еднакви можности за изучување на програмирањето за сите видови."
# tip_reticulating: "Reticulating spines." # tip_reticulating: "Reticulating spines."
# tip_harry: "Yer a Wizard, " # tip_harry: "Yer a Wizard, "
tip_great_responsibility: "Со големи програмерски вештини доаѓа и голема одговорност за дебагирање." tip_great_responsibility: "Со големи програмерски вештини доаѓа и голема одговорност за дебагирање."
@ -297,7 +297,7 @@ module.exports = nativeDescription: "Македонски", englishDescription:
tip_binary: "Има само 10 вида на луѓе во светот: оние кои што разбираат бинарно, и оние кои не разбираат." tip_binary: "Има само 10 вида на луѓе во светот: оние кои што разбираат бинарно, и оние кои не разбираат."
tip_commitment_yoda: "Програмер мора да ја има најдлабоката обврзаност, најсериозниот ум. ~ Yoda" tip_commitment_yoda: "Програмер мора да ја има најдлабоката обврзаност, најсериозниот ум. ~ Yoda"
tip_no_try: "Направи. Или не прави. Нема обидување. - Yoda" tip_no_try: "Направи. Или не прави. Нема обидување. - Yoda"
# tip_patience: "Patience you must have, young Padawan. - Yoda" tip_patience: "Трпение мора да имаш, млад Padawan. - Yoda"
tip_documented_bug: "Документирана грешка не е грешка; тоа е едноставно нешто што програмот го прави." tip_documented_bug: "Документирана грешка не е грешка; тоа е едноставно нешто што програмот го прави."
tip_impossible: "Секогаш изледа невозможно, се додека некој не го направи. - Nelson Mandela" tip_impossible: "Секогаш изледа невозможно, се додека некој не го направи. - Nelson Mandela"
tip_talk_is_cheap: "Зборувањето е евтино. Покажи ми го кодот. - Linus Torvalds" tip_talk_is_cheap: "Зборувањето е евтино. Покажи ми го кодот. - Linus Torvalds"
@ -305,26 +305,26 @@ module.exports = nativeDescription: "Македонски", englishDescription:
tip_hardware_problem: "Прашање: Колку програмери се потребни за да се смени сијалица? Одговор: Ниту еден, тоа е хардверски проблем." tip_hardware_problem: "Прашање: Колку програмери се потребни за да се смени сијалица? Одговор: Ниту еден, тоа е хардверски проблем."
tip_hofstadters_law: "Законот на Hofstadter: Секогаш треба повеќе време отколку што очекуваш, дури и кога ќе го земеш во предвид законот на Hofstadter." tip_hofstadters_law: "Законот на Hofstadter: Секогаш треба повеќе време отколку што очекуваш, дури и кога ќе го земеш во предвид законот на Hofstadter."
tip_premature_optimization: "Предвремената оптимизација е коренот на сето зло. - Donald Knuth" tip_premature_optimization: "Предвремената оптимизација е коренот на сето зло. - Donald Knuth"
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson" tip_brute_force: "Кога не си сигурен, користи 'brute force'. - Ken Thompson"
tip_extrapolation: "Има само два вида на луѓе: оние кои можат да екстраполираат од некомплетни податоци..." tip_extrapolation: "Има само два вида на луѓе: оние кои можат да екстраполираат од некомплетни податоци..."
tip_superpower: "Програмирањето е способност, најблиска до супермоќ, која ја имаме." tip_superpower: "Програмирањето е способност, најблиска до супермоќ, која ја имаме."
# game_menu: game_menu:
# inventory_tab: "Inventory" # inventory_tab: "Inventory"
# save_load_tab: "Save/Load" save_load_tab: "Зачувај/Вчитај"
# options_tab: "Options" # options_tab: "Options"
# guide_tab: "Guide" guide_tab: "Водич"
# guide_video_tutorial: "Video Tutorial" guide_video_tutorial: "Видео водич"
# guide_tips: "Tips" guide_tips: "Совети"
# multiplayer_tab: "Multiplayer" multiplayer_tab: "Повеќе играчи"
# auth_tab: "Sign Up" auth_tab: "Направи сметка"
# inventory_caption: "Equip your hero" inventory_caption: "Опреми го твојот херој"
# choose_hero_caption: "Choose hero, language" choose_hero_caption: "Избери херој, јазик"
# save_load_caption: "... and view history" save_load_caption: "... и види историја"
# options_caption: "Configure settings" options_caption: "Промени подесувања"
# guide_caption: "Docs and tips" guide_caption: "Документи и совети"
# multiplayer_caption: "Play with friends!" multiplayer_caption: "Играј со пријатели!"
# auth_caption: "Save your progress." auth_caption: "Зачувај го твојот напредок."
inventory: inventory:
# choose_inventory: "Equip Items" # choose_inventory: "Equip Items"
@ -349,70 +349,70 @@ module.exports = nativeDescription: "Македонски", englishDescription:
prompt_title: "Немаш доволно скапоцени камења" prompt_title: "Немаш доволно скапоцени камења"
prompt_body: "Дали сакаш да земеш повеќе?" prompt_body: "Дали сакаш да земеш повеќе?"
prompt_button: "Влези во продавницата" prompt_button: "Влези во продавницата"
# recovered: "Previous gems purchase recovered. Please refresh the page." recovered: "Претходното купување на скапоцени камења е вратено од загуба. Те молам 'освежи' ја страната."
# subscribe: subscribe:
# subscribe_title: "Subscribe" subscribe_title: "Зачлени се"
# unsubscribe: "Unsubscribe" unsubscribe: "Откажи членство"
# levels: "Get more practice with bonus levels!" levels: "Вежбај повеќе со дополнителни нивоа!"
# heroes: "More powerful heroes!" heroes: "Помоќни херои!"
# gems: "3500 bonus gems every month!" gems: "3500 скапоцени камења секој месец!"
# items: "Over 250 bonus items!" items: "Над 250 дополнителни предмети и опрема!"
# parents: "For Parents" parents: "За родители"
# parents_title: "Your child will learn to code." parents_title: "Вашето дете ќе научи да програмира."
# parents_blurb1: "With CodeCombat, your child learns by writing real code. They start by learning simple commands, and progress to more advanced topics." parents_blurb1: "Со CodeCombat, вашите деца учат преку пишување на вистински програмски код. Почнуваат со учење на едноставни команди, по што се продолжува на понапредни теми."
# parents_blurb2: "For $9.99 USD/mo, they get new challenges every week and personal email support from professional programmers." parents_blurb2: "За $9.99 американски долари месечно, добиваат нови предизвици секоја недела и лична поддршка преку e-mail, од страна на професионални програмери."
# parents_blurb3: "No Risk: 100% money back guarantee, easy 1-click unsubscribe." parents_blurb3: "Без ризик: 100% гаранција за враќање на парите, лесно откажување на членството со еден клик."
# subscribe_button: "Subscribe Now" subscribe_button: "Зачлени се сега"
# stripe_description: "Monthly Subscription" stripe_description: "Месечна членарина"
# subscription_required_to_play: "You'll need a subscription to play this level." subscription_required_to_play: "Треба да бидеш зачленет за да го играш ова ниво."
# choose_hero: choose_hero:
# choose_hero: "Choose Your Hero" choose_hero: "Избери го твојот херој"
# programming_language: "Programming Language" programming_language: "Програмски јазик"
# programming_language_description: "Which programming language do you want to use?" programming_language_description: "Кој програмски јазик сакаш да го користиш?"
# default: "Default" # default: "Default"
# experimental: "Experimental" experimental: "Експериментално"
# python_blurb: "Simple yet powerful, great for beginners and experts." python_blurb: "Едноставен, но моќен, одличен за почетници и експерти."
# javascript_blurb: "The language of the web. (Not the same as Java.)" javascript_blurb: "Јазикот на веб-от. (Не е исто што и Java.)"
# coffeescript_blurb: "Nicer JavaScript syntax." coffeescript_blurb: "Поубава JavaScript синтакса."
# clojure_blurb: "A modern Lisp." clojure_blurb: "Модерен Lisp."
# lua_blurb: "Game scripting language." lua_blurb: "Јазик за скриптирање на игри."
# io_blurb: "Simple but obscure." io_blurb: "Едноставен, но не така очигледен."
# status: "Status" status: "Статус"
# weapons: "Weapons" weapons: "Оружја"
# weapons_warrior: "Swords - Short Range, No Magic" weapons_warrior: "Мечеви - Краток досег, нема магија"
# weapons_ranger: "Crossbows, Guns - Long Range, No Magic" weapons_ranger: "Самострели, Пушки - Долг досег, нема магија"
# weapons_wizard: "Wands, Staffs - Long Range, Magic" weapons_wizard: "Волшебни стапчиња, стапови - долг досег, има магија"
# attack: "Damage" # Can also translate as "Attack" attack: "Напад" # Can also translate as "Attack"
# health: "Health" health: "Здравје"
# speed: "Speed" speed: "Брзина"
# regeneration: "Regeneration" regeneration: "Заздравување"
# range: "Range" # As in "attack or visual range" range: "Досег" # As in "attack or visual range"
# blocks: "Blocks" # As in "this shield blocks this much damage" blocks: "Блокира" # As in "this shield blocks this much damage"
# backstab: "Backstab" # As in "this dagger does this much backstab damage" backstab: "Од зад грб" # As in "this dagger does this much backstab damage"
# skills: "Skills" skills: "Вештини"
# available_for_purchase: "Available for Purchase" # Shows up when you have unlocked, but not purchased, a hero in the hero store available_for_purchase: "Достапно за купување" # Shows up when you have unlocked, but not purchased, a hero in the hero store
# level_to_unlock: "Level to unlock:" # Label for which level you have to beat to unlock a particular hero (click a locked hero in the store to see) level_to_unlock: "Ниво за да се отклучи:" # Label for which level you have to beat to unlock a particular hero (click a locked hero in the store to see)
# restricted_to_certain_heroes: "Only certain heroes can play this level." restricted_to_certain_heroes: "Само одредени херои можат да го играат ова ниво."
# skill_docs: skill_docs:
# writable: "writable" # Hover over "attack" in Your Skills while playing a level to see most of this # writable: "writable" # Hover over "attack" in Your Skills while playing a level to see most of this
# read_only: "read-only" # read_only: "read-only"
# action_name: "name" action_name: "име"
# action_cooldown: "Takes" # action_cooldown: "Takes"
# action_specific_cooldown: "Cooldown" # action_specific_cooldown: "Cooldown"
# action_damage: "Damage" # action_damage: "Damage"
# action_range: "Range" action_range: "Досег"
# action_radius: "Radius" action_radius: "Радиус"
# action_duration: "Duration" action_duration: "Времетраење"
# example: "Example" example: "Пример"
# ex: "ex" # Abbreviation of "example" ex: "пр" # Abbreviation of "example"
# current_value: "Current Value" current_value: "Моментална вредност"
# default_value: "Default value" default_value: "Стандардна вредност"
# parameters: "Parameters" parameters: "Параметри"
# returns: "Returns" returns: "Враќа"
# granted_by: "Granted by" granted_by: "Овозможено од"
# save_load: # save_load:
# granularity_saved_games: "Saved" # granularity_saved_games: "Saved"

View file

@ -4,7 +4,7 @@ module.exports = nativeDescription: "Nederlands (Nederland)", englishDescription
no_ie: "CodeCombat werkt niet in IE8 of ouder. Sorry!" # Warning that only shows up in IE8 and older no_ie: "CodeCombat werkt niet in IE8 of ouder. Sorry!" # Warning that only shows up in IE8 and older
no_mobile: "CodeCombat is niet gemaakt voor mobiele apparaten en werkt misschien niet!" # Warning that shows up on mobile devices no_mobile: "CodeCombat is niet gemaakt voor mobiele apparaten en werkt misschien niet!" # Warning that shows up on mobile devices
play: "Speel" # The big play button that just starts playing a level play: "Speel" # The big play button that just starts playing a level
# try_it: "Try It" # Alternate wording for Play button try_it: "Probeer nu" # Alternate wording for Play button
old_browser: "Uh oh, jouw browser is te oud om CodeCombat te kunnen spelen, Sorry!" # Warning that shows up on really old Firefox/Chrome/Safari old_browser: "Uh oh, jouw browser is te oud om CodeCombat te kunnen spelen, Sorry!" # Warning that shows up on really old Firefox/Chrome/Safari
old_browser_suffix: "Je kan toch proberen, maar het zal waarschijnlijk niet werken!" old_browser_suffix: "Je kan toch proberen, maar het zal waarschijnlijk niet werken!"
ipad_browser: "Slecht nieuws: CodeCombat draait niet in je browser op iPad. Goed nieuws: onze iPad-app wordt op het moment beoordeeld door Apple." ipad_browser: "Slecht nieuws: CodeCombat draait niet in je browser op iPad. Goed nieuws: onze iPad-app wordt op het moment beoordeeld door Apple."
@ -22,7 +22,7 @@ module.exports = nativeDescription: "Nederlands (Nederland)", englishDescription
forum: "Forum" forum: "Forum"
account: "Lidmaatschap" account: "Lidmaatschap"
profile: "Profiel" profile: "Profiel"
# stats: "Stats" stats: "Statistieken"
# code: "Code" # code: "Code"
admin: "Administrator" # Only shows up when you are an admin admin: "Administrator" # Only shows up when you are an admin
home: "Home" home: "Home"
@ -31,7 +31,7 @@ module.exports = nativeDescription: "Nederlands (Nederland)", englishDescription
about: "Over Ons" about: "Over Ons"
contact: "Contact" contact: "Contact"
twitter_follow: "Volgen" twitter_follow: "Volgen"
# teachers: "Teachers" teachers: "Docenten"
modal: modal:
close: "Sluiten" close: "Sluiten"
@ -52,36 +52,36 @@ module.exports = nativeDescription: "Nederlands (Nederland)", englishDescription
play_as: "Speel als " # Ladder page play_as: "Speel als " # Ladder page
spectate: "Toeschouwen" # Ladder page spectate: "Toeschouwen" # Ladder page
players: "Spelers" # Hover over a level on /play players: "Spelers" # Hover over a level on /play
# hours_played: "hours played" # Hover over a level on /play hours_played: "Speeltijd" # Hover over a level on /play
# items: "Items" # Tooltip on item shop button from /play # items: "Items" # Tooltip on item shop button from /play
unlock: "Ontgrendel" # For purchasing items and heroes unlock: "Koop" # For purchasing items and heroes
confirm: "Bevestigen" confirm: "Bevestigen"
# owned: "Owned" # For items you own owned: "In bezit" # For items you own
locked: "Vergrendeld" locked: "Vergrendeld"
# purchasable: "Purchasable" # For a hero you unlocked but haven't purchased purchasable: "Te koop" # For a hero you unlocked but haven't purchased
# available: "Available" available: "Beschikbaar"
# skills_granted: "Skills Granted" # Property documentation details # skills_granted: "Skills Granted" # Property documentation details
# heroes: "Heroes" # Tooltip on hero shop button from /play heroes: "Helden" # Tooltip on hero shop button from /play
# achievements: "Achievements" # Tooltip on achievement list button from /play achievements: "Prestaties" # Tooltip on achievement list button from /play
# account: "Account" # Tooltip on account button from /play # account: "Account" # Tooltip on account button from /play
settings: "Instellingen" # Tooltip on settings button from /play settings: "Instellingen" # Tooltip on settings button from /play
# next: "Next" # Go from choose hero to choose inventory before playing a level next: "Volgende" # Go from choose hero to choose inventory before playing a level
# change_hero: "Change Hero" # Go back from choose inventory to choose hero change_hero: "Verander held" # Go back from choose inventory to choose hero
# choose_inventory: "Equip Items" # choose_inventory: "Equip Items"
buy_gems: "Edelstenen kopen" buy_gems: "Edelstenen kopen"
# campaign_desert: "Desert Campaign" campaign_desert: "Woestijncampagne"
# campaign_forest: "Forest Campaign" campaign_forest: "Boscampagne"
# campaign_dungeon: "Dungeon Campaign" campaign_dungeon: "Kerkercampagne"
# subscription_required: "Subscription Required" subscription_required: "Abonnement nodig"
free: "Gratis" free: "Gratis"
subscribed: "Geabbonneerd" subscribed: "Geabbonneerd"
# older_campaigns: "Older Campaigns" # older_campaigns: "Older Campaigns"
anonymous: "Anonieme Speler" anonymous: "Anonieme Speler"
level_difficulty: "Moeilijkheidsgraad: " level_difficulty: "Moeilijkheidsgraad: "
campaign_beginner: "Beginnercampagne" campaign_beginner: "Beginnercampagne"
# awaiting_levels_adventurer_prefix: "We release five levels per week." awaiting_levels_adventurer_prefix: "We brengen 5 nieuwe levels per week uit."
# awaiting_levels_adventurer: "Sign up as an Adventurer" awaiting_levels_adventurer: "Schrijf je in als Avonturier"
# awaiting_levels_adventurer_suffix: "to be the first to play new levels." awaiting_levels_adventurer_suffix: "om de eerste te zijn die nieuwe levels speelt."
choose_your_level: "Kies Je Level" # The rest of this section is the old play view at /play-old and isn't very important. choose_your_level: "Kies Je Level" # The rest of this section is the old play view at /play-old and isn't very important.
adventurer_prefix: "Je kunt meteen naar een van de levels hieronder springen, of de levels bespreken op " adventurer_prefix: "Je kunt meteen naar een van de levels hieronder springen, of de levels bespreken op "
adventurer_forum: "het Avonturiersforum" adventurer_forum: "het Avonturiersforum"
@ -106,7 +106,7 @@ module.exports = nativeDescription: "Nederlands (Nederland)", englishDescription
authenticate_gplus: "G+ verifiëren" authenticate_gplus: "G+ verifiëren"
load_profile: "G+ profiel laden" load_profile: "G+ profiel laden"
load_email: "G+ e-mail laden" load_email: "G+ e-mail laden"
# finishing: "Finishing" finishing: "Aan het afmaken"
sign_in_with_facebook: "Inloggen met Facebook" sign_in_with_facebook: "Inloggen met Facebook"
sign_in_with_gplus: "Inloggen met G+" sign_in_with_gplus: "Inloggen met G+"
signup_switch: "Wil je een account maken?" signup_switch: "Wil je een account maken?"
@ -117,7 +117,7 @@ module.exports = nativeDescription: "Nederlands (Nederland)", englishDescription
sign_up: "Aanmelden" sign_up: "Aanmelden"
log_in: "inloggen met wachtwoord" log_in: "inloggen met wachtwoord"
social_signup: "Of je kunt je registreren met Facebook of G+:" social_signup: "Of je kunt je registreren met Facebook of G+:"
# required: "You need to log in before you can go that way." required: "Je moet inloggen om daarheen te gaan."
login_switch: "Heb je al een account?" login_switch: "Heb je al een account?"
recover: recover:
@ -125,13 +125,13 @@ module.exports = nativeDescription: "Nederlands (Nederland)", englishDescription
send_password: "Verzend nieuw wachtwoord" send_password: "Verzend nieuw wachtwoord"
recovery_sent: "Herstel e-mail verzonden." recovery_sent: "Herstel e-mail verzonden."
# items: items:
# primary: "Primary" primary: "Primair"
# secondary: "Secondary" secondary: "Secundair"
# armor: "Armor" armor: "Harnas"
# accessories: "Accessories" accessories: "Accessoires"
# misc: "Misc" # misc: "Misc"
# books: "Books" books: "Boeken"
common: common:
loading: "Bezig met laden..." loading: "Bezig met laden..."
@ -146,18 +146,18 @@ module.exports = nativeDescription: "Nederlands (Nederland)", englishDescription
fork: "Fork" fork: "Fork"
play: "Spelen" # When used as an action verb, like "Play next level" play: "Spelen" # When used as an action verb, like "Play next level"
retry: "Probeer opnieuw" retry: "Probeer opnieuw"
# actions: "Actions" actions: "Acties"
# info: "Info" # info: "Info"
# help: "Help" # help: "Help"
watch: "Volgen" watch: "Volgen"
unwatch: "Ontvolgen" unwatch: "Ontvolgen"
submit_patch: "Correctie Opsturen" submit_patch: "Correctie Opsturen"
# submit_changes: "Submit Changes" submit_changes: "Veranderingen indienen"
general: general:
and: "en" and: "en"
name: "Naam" name: "Naam"
# date: "Date" date: "Datum"
body: "Inhoud" body: "Inhoud"
version: "Versie" version: "Versie"
# submitter: "Submitter" # submitter: "Submitter"
@ -166,10 +166,10 @@ module.exports = nativeDescription: "Nederlands (Nederland)", englishDescription
# review: "Review" # review: "Review"
version_history: "Versie geschiedenis" version_history: "Versie geschiedenis"
version_history_for: "Versie geschiedenis voor: " version_history_for: "Versie geschiedenis voor: "
# select_changes: "Select two changes below to see the difference." select_changes: "Selecteer hieronder twee veranderingen om het verschil te zien."
# undo: "Undo (Ctrl+Z)" # undo: "Undo (Ctrl+Z)"
# redo: "Redo (Ctrl+Shift+Z)" # redo: "Redo (Ctrl+Shift+Z)"
# play_preview: "Play preview of current level" play_preview: "Speel voorproefje van dit level"
result: "Resultaat" result: "Resultaat"
results: "Resultaten" results: "Resultaten"
description: "Beschrijving" description: "Beschrijving"
@ -191,7 +191,7 @@ module.exports = nativeDescription: "Nederlands (Nederland)", englishDescription
medium: "Medium" medium: "Medium"
hard: "Moeilijk" hard: "Moeilijk"
player: "Speler" player: "Speler"
# player_level: "Level" # Like player level 5, not like level: Dungeons of Kithgard player_level: "Niveau" # Like player level 5, not like level: Dungeons of Kithgard
units: units:
second: "seconde" second: "seconde"
@ -200,53 +200,53 @@ module.exports = nativeDescription: "Nederlands (Nederland)", englishDescription
minutes: "minuten" minutes: "minuten"
hour: "uur" hour: "uur"
hours: "uren" hours: "uren"
# day: "day" day: "dag"
# days: "days" days: "dagen"
# week: "week" week: "week"
# weeks: "weeks" weeks: "weken"
# month: "month" month: "maand"
# months: "months" months: "maanden"
# year: "year" year: "jaar"
# years: "years" years: "jaren"
play_level: play_level:
done: "Klaar" done: "Klaar"
home: "Home" # Not used any more, will be removed soon. home: "Home" # Not used any more, will be removed soon.
# level: "Level" # Like "Level: Dungeons of Kithgard" # level: "Level" # Like "Level: Dungeons of Kithgard"
# skip: "Skip" skip: "Overslaan"
# game_menu: "Game Menu" # game_menu: "Game Menu"
guide: "Handleiding" guide: "Handleiding"
restart: "Herstarten" restart: "Herstarten"
goals: "Doelen" goals: "Doelen"
# goal: "Goal" goal: "Doel"
# running: "Running..." # running: "Running..."
# success: "Success!" success: "Gelukt!"
# incomplete: "Incomplete" incomplete: "Incompleet"
# timed_out: "Ran out of time" timed_out: "De tijd is op"
# failing: "Failing" # failing: "Failing"
action_timeline: "Actie tijdlijn" action_timeline: "Actie tijdlijn"
click_to_select: "Klik op een eenheid om deze te selecteren." click_to_select: "Klik op een eenheid om deze te selecteren."
# control_bar_multiplayer: "Multiplayer" # control_bar_multiplayer: "Multiplayer"
# control_bar_join_game: "Join Game" control_bar_join_game: "Meespelen"
# reload: "Reload" reload: "Herlaad"
reload_title: "Alle Code Herladen?" reload_title: "Alle Code Herladen?"
reload_really: "Weet je zeker dat je dit level tot het begin wilt herladen?" reload_really: "Weet je zeker dat je dit level tot het begin wilt herladen?"
reload_confirm: "Herlaad Alles" reload_confirm: "Herlaad Alles"
# victory: "Victory" victory: "Gewonnen"
victory_title_prefix: "" victory_title_prefix: ""
victory_title_suffix: " Compleet" victory_title_suffix: " Compleet"
victory_sign_up: "Schrijf je in om je vooruitgang op te slaan" victory_sign_up: "Schrijf je in om je vooruitgang op te slaan"
victory_sign_up_poke: "Wil je jouw code opslaan? Maak een gratis account aan!" victory_sign_up_poke: "Wil je jouw code opslaan? Maak een gratis account aan!"
victory_rate_the_level: "Beoordeel het level: " # Only in old-style levels. victory_rate_the_level: "Beoordeel het level: " # Only in old-style levels.
victory_return_to_ladder: "Keer terug naar de ladder" victory_return_to_ladder: "Keer terug naar de ladder"
# victory_play_continue: "Continue" victory_play_continue: "Ga door"
# victory_saving_progress: "Saving Progress" victory_saving_progress: "Voortgang opslaan"
victory_go_home: "Ga naar Home" # Only in old-style levels. victory_go_home: "Ga naar Home" # Only in old-style levels.
victory_review: "Vertel ons meer!" # Only in old-style levels. victory_review: "Vertel ons meer!" # Only in old-style levels.
victory_hour_of_code_done: "Ben Je Klaar?" victory_hour_of_code_done: "Ben Je Klaar?"
victory_hour_of_code_done_yes: "Ja, ik ben klaar met mijn Hour of Code!" victory_hour_of_code_done_yes: "Ja, ik ben klaar met mijn Hour of Code!"
# victory_experience_gained: "XP Gained" victory_experience_gained: "XP verdient"
# victory_gems_gained: "Gems Gained" victory_gems_gained: "Edelstenen verdient"
guide_title: "Handleiding" guide_title: "Handleiding"
tome_minion_spells: "Jouw Minions' Spreuken" # Only in old-style levels. tome_minion_spells: "Jouw Minions' Spreuken" # Only in old-style levels.
tome_read_only_spells: "Read-Only Spreuken" # Only in old-style levels. tome_read_only_spells: "Read-Only Spreuken" # Only in old-style levels.

View file

@ -102,7 +102,7 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese
log_in: "登录" log_in: "登录"
logging_in: "正在登录" logging_in: "正在登录"
log_out: "登出" log_out: "登出"
# forgot_password: "Forgot your password?" forgot_password: "忘记密码?"
authenticate_gplus: "使用 G+ 授权" authenticate_gplus: "使用 G+ 授权"
load_profile: "载入 G+ 档案" load_profile: "载入 G+ 档案"
load_email: "载入 G+ 电子邮件" load_email: "载入 G+ 电子邮件"
@ -148,7 +148,7 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese
retry: "重试" retry: "重试"
# actions: "Actions" # actions: "Actions"
# info: "Info" # info: "Info"
# help: "Help" help: "帮助"
watch: "关注" watch: "关注"
unwatch: "取消关注" unwatch: "取消关注"
submit_patch: "提交补丁" submit_patch: "提交补丁"
@ -353,7 +353,7 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese
subscribe: subscribe:
subscribe_title: "订阅" subscribe_title: "订阅"
# unsubscribe: "Unsubscribe" unsubscribe: "取消订阅"
levels: "多解锁17个关卡每周解锁5个新关卡" levels: "多解锁17个关卡每周解锁5个新关卡"
heroes: "更多强大的英雄!" heroes: "更多强大的英雄!"
gems: "每月多3500宝石奖励" gems: "每月多3500宝石奖励"
@ -483,7 +483,7 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese
forum_page: "我们的论坛" forum_page: "我们的论坛"
forum_suffix: "" forum_suffix: ""
# faq_prefix: "There's also a" # faq_prefix: "There's also a"
# faq: "FAQ" faq: "FAQ"
# subscribe_prefix: "If you need help figuring out a level, please" # subscribe_prefix: "If you need help figuring out a level, please"
# subscribe: "buy a CodeCombat subscription" # subscribe: "buy a CodeCombat subscription"
# subscribe_suffix: "and we'll be happy to help you with your code." # subscribe_suffix: "and we'll be happy to help you with your code."

View file

@ -94,7 +94,7 @@ class CocoModel extends Backbone.Model
loadFromBackup: -> loadFromBackup: ->
return unless @saveBackups return unless @saveBackups
existing = storage.load @id existing = storage.load @id # + @attributes.__v # TODO: try and test this, also only use __v for non-versioned, otherwise just id
if existing if existing
@set(existing, {silent: true}) @set(existing, {silent: true})
CocoModel.backedUp[@id] = @ CocoModel.backedUp[@id] = @
@ -102,8 +102,8 @@ class CocoModel extends Backbone.Model
saveBackup: -> @saveBackupNow() saveBackup: -> @saveBackupNow()
saveBackupNow: -> saveBackupNow: ->
storage.save(@id, @attributes) storage.save(@id, @attributes) # TODO: use __v
CocoModel.backedUp[@id] = @ CocoModel.backedUp[@id] = @ # TODO
@backedUp = {} @backedUp = {}
schema: -> return @constructor.schema schema: -> return @constructor.schema
@ -377,22 +377,37 @@ class CocoModel extends Backbone.Model
#- Internationalization #- Internationalization
updateI18NCoverage: -> updateI18NCoverage: ->
i18nObjects = @findI18NObjects() langCodeArrays = []
return unless i18nObjects.length pathToData = {}
langCodeArrays = (_.keys(i18n) for i18n in i18nObjects)
@set('i18nCoverage', _.intersection(langCodeArrays...))
findI18NObjects: (data, results) -> TreemaUtils.walk(@attributes, @schema(), null, (path, data, workingSchema) ->
data ?= @attributes # Store parent data for the next block...
results ?= [] if data?.i18n
pathToData[path] = data
if _.string.endsWith path, 'i18n'
i18n = data
# grab the parent data
parentPath = path[0...-5]
parentData = pathToData[parentPath]
# use it to determine what properties actually need to be translated
props = workingSchema.props or []
props = (prop for prop in props when parentData[prop])
# get a list of lang codes where its object has keys for every prop to be translated
coverage = _.filter(_.keys(i18n), (langCode) ->
translations = i18n[langCode]
_.all((translations[prop] for prop in props))
)
langCodeArrays.push coverage
)
return unless langCodeArrays.length
# language codes that are covered for every i18n object are fully covered
overallCoverage = _.intersection(langCodeArrays...)
@set('i18nCoverage', overallCoverage)
if _.isPlainObject(data) or _.isArray(data)
for [key, value] in _.pairs data
if key is 'i18n'
results.push value
else if _.isPlainObject(value) or _.isArray(value)
@findI18NObjects(value, results)
return results
module.exports = CocoModel module.exports = CocoModel

View file

@ -53,3 +53,16 @@ module.exports = class LevelSession extends CocoModel
save: (attrs, options) -> save: (attrs, options) ->
return if @shouldAvoidCorruptData attrs return if @shouldAvoidCorruptData attrs
super attrs, options super attrs, options
increaseDifficulty: ->
state = @get('state') ? {}
state.difficulty = (state.difficulty ? 0) + 1
delete state.lastUnsuccessfulSubmissionTime
@set 'state', state
timeUntilResubmit: ->
state = @get('state') ? {}
return 0 unless last = state.lastUnsuccessfulSubmissionTime
last = new Date(last) if _.isString last
# Wait at least this long before allowing submit button active again.
(last - new Date()) + 22 * 60 * 60 * 1000

View file

@ -299,6 +299,7 @@ _.extend LevelSchema.properties,
style: c.shortString title: 'Style', description: 'Like: original, eccentric, scripted, edited, etc.' style: c.shortString title: 'Style', description: 'Like: original, eccentric, scripted, edited, etc.'
free: {type: 'boolean', title: 'Free', description: 'Whether this video is freely available to all players without a subscription.'} free: {type: 'boolean', title: 'Free', description: 'Whether this video is freely available to all players without a subscription.'}
url: c.url {title: 'URL', description: 'Link to the video on Vimeo.'} url: c.url {title: 'URL', description: 'Link to the video on Vimeo.'}
replayable: {type: 'boolean', title: 'Replayable', description: 'Whether this (hero) level infinitely scales up its difficulty and can be beaten over and over for greater rewards.'}
# Admin flags # Admin flags
adventurer: { type: 'boolean' } adventurer: { type: 'boolean' }

View file

@ -114,6 +114,12 @@ _.extend LevelSessionSchema.properties,
description: 'How many times the session has been submitted for real-time playback (can affect the random seed).' description: 'How many times the session has been submitted for real-time playback (can affect the random seed).'
type: 'integer' type: 'integer'
minimum: 0 minimum: 0
difficulty:
description: 'The highest difficulty level beaten, for use in increasing-difficulty replayable levels.'
type: 'integer'
minimum: 0
lastUnsuccessfulSubmissionTime: c.date
description: 'The last time that real-time submission was started without resulting in a win.'
flagHistory: flagHistory:
description: 'The history of flag events during the last real-time playback submission.' description: 'The history of flag events during the last real-time playback submission.'
type: 'array' type: 'array'

View file

@ -7,16 +7,20 @@ module.exports =
preload: {type: 'boolean'} preload: {type: 'boolean'}
realTime: {type: 'boolean'} realTime: {type: 'boolean'}
'tome:cast-spells': c.object {title: 'Cast Spells', description: 'Published when spells are cast', required: ['spells', 'preload', 'realTime', 'submissionCount', 'flagHistory']}, 'tome:cast-spells': c.object {title: 'Cast Spells', description: 'Published when spells are cast', required: ['spells', 'preload', 'realTime', 'submissionCount', 'flagHistory', 'difficulty']},
spells: [type: 'object'] spells: [type: 'object']
preload: [type: 'boolean'] preload: [type: 'boolean']
realTime: [type: 'boolean'] realTime: [type: 'boolean']
submissionCount: [type: 'integer'] submissionCount: [type: 'integer']
flagHistory: [type: 'array'] flagHistory: [type: 'array']
difficulty: [type: 'integer']
'tome:manual-cast': c.object {title: 'Manually Cast Spells', description: 'Published when you wish to manually recast all spells', required: []}, 'tome:manual-cast': c.object {title: 'Manually Cast Spells', description: 'Published when you wish to manually recast all spells', required: []},
realTime: {type: 'boolean'} realTime: {type: 'boolean'}
'tome:manual-cast-denied': c.object {title: 'Manual Cast Denied', description: 'Published when player attempts to submit for real-time playback, but must wait after a replayable level failure.', required: ['timeUntilResubmit']},
timeUntilResubmit: {type: 'number'}
'tome:spell-created': c.object {title: 'Spell Created', description: 'Published after a new spell has been created', required: ['spell']}, 'tome:spell-created': c.object {title: 'Spell Created', description: 'Published after a new spell has been created', required: ['spell']},
spell: {type: 'object'} spell: {type: 'object'}

View file

@ -28,5 +28,8 @@
top: 1% top: 1%
padding: 3px 8px padding: 3px 8px
#analytics-modal .modal-content #analytics-modal
background-color: white .modal-dialog
width: 75%
.modal-content
background-color: white

View file

@ -137,6 +137,8 @@
.treema-root .treema-root
background-color: white background-color: white
border-radius: 4px border-radius: 4px
&:focus
box-shadow: 0 0 10px blue
.editor-nano-container .editor-nano-container
position: static position: static

View file

@ -125,3 +125,8 @@
#contextmenu #contextmenu
text-align: left text-align: left
#thang-components-edit-view
#thang-component-configs
// Get these away from the bottom of the screen so we can have dropdowns.
padding-bottom: 400px

View file

@ -65,6 +65,7 @@ block outer_content
td Finished td Finished
td Dropped td Dropped
td Drop % td Drop %
td Completion %
tbody tbody
- for (var i = 0; i < campaignDropOffs.levels.length; i++) - for (var i = 0; i < campaignDropOffs.levels.length; i++)
tr tr
@ -75,6 +76,7 @@ block outer_content
td= campaignDropOffs.levels[i].finished td= campaignDropOffs.levels[i].finished
td= campaignDropOffs.levels[i].finishDropped td= campaignDropOffs.levels[i].finishDropped
td= campaignDropOffs.levels[i].finishDropRate td= campaignDropOffs.levels[i].finishDropRate
td= campaignDropOffs.levels[i].completionRate
else else
button.btn.btn-default.disabled#analytics-button Analytics Loading... button.btn.btn-default.disabled#analytics-button Analytics Loading...

View file

@ -38,9 +38,59 @@
td= levelPlaytimes[i].average.toFixed(2) td= levelPlaytimes[i].average.toFixed(2)
else else
div Loading... div Loading...
h4 Common Problems
if commonProblems
if commonProblems.startDay
if commonProblems.endDay
div(style='font-size:10pt') #{commonProblems.startDay} to #{commonProblems.endDay}
else
div(style='font-size:10pt') #{commonProblems.startDay} to today
table.table.table-bordered.table-condensed.table-hover(style='font-size:10pt')
thead
tr
td Language
td Error Message
td Error Hint
td Count
tbody
- for (var i = 0; i < commonProblems.length && i < 20; i++)
tr
td= commonProblems[i].language
td= commonProblems[i].message
td= commonProblems[i].hint
td= commonProblems[i].count
else
div Loading...
h4 Recent Sessions
if recentSessions
div(style='font-size:10pt') Latest 10 sessions for this level
div(style='font-size:10pt') Double-click row to open player and session
table.table.table-bordered.table-condensed.table-hover(style='font-size:10pt')
thead
tr
td Session ID
td Player ID
td Code Language
td Playtime
td Complete
td Changed
tbody
- for (var i = 0; i < recentSessions.length; i++)
tr.recent-session(data-player-id=recentSessions[i].creator, data-session-id=recentSessions[i]._id)
td= recentSessions[i]._id
td= recentSessions[i].creator
td= recentSessions[i].codeLanguage
td= recentSessions[i].playtime
if recentSessions[i].state && recentSessions[i].state.complete
td= recentSessions[i].state.complete
else
td false
td= recentSessions[i].changed
else
div Loading...
if level.get('tasks') if level.get('tasks')
.tasks .tasks
h3 Tasks (read only) h3 Tasks (read only)

View file

@ -33,7 +33,7 @@ nav.navbar.navbar-default(role='navigation')
span.spl(data-i18n="common.unwatch") Unwatch span.spl(data-i18n="common.unwatch") Unwatch
if !me.get('anonymous') if !me.get('anonymous')
li#create-new-component-button li#create-new-component-button
a(data-i18n="editor.level_component_b_new") Create New Component a(data-i18n="editor.level_component_btn_new") Create New Component
li li
a(data-i18n="editor.pop_i18n")#pop-component-i18n-button Populate i18n a(data-i18n="editor.pop_i18n")#pop-component-i18n-button Populate i18n

View file

@ -44,10 +44,10 @@ block header
span.navbar-brand #{level.attributes.name} span.navbar-brand #{level.attributes.name}
ul.nav.navbar-nav.navbar-right ul.nav.navbar-nav.navbar-right
li#undo-button(data-i18n="[title]general.undo", title="Undo (Ctrl+Z)") li#undo-button
a a
span.glyphicon-arrow-left.glyphicon span.glyphicon-arrow-left.glyphicon
li#redo-button(data-i18n="[title]general.redo", title="Redo (Ctrl+Shift+Z)") li#redo-button
a a
span.glyphicon-arrow-right.glyphicon span.glyphicon-arrow-right.glyphicon
if authorized if authorized

View file

@ -1,7 +1,9 @@
button.btn.btn-lg.btn-illustrated.cast-button(title=castVerbose) button.btn.btn-lg.btn-illustrated.cast-button(title=castVerbose)
span(data-i18n="play_level.tome_run_button_ran") Ran span(data-i18n="play_level.tome_run_button_ran") Ran
button.btn.btn-lg.btn-illustrated.submit-button(title=castRealTimeVerbose, data-i18n="play_level.tome_submit_button") Submit button.btn.btn-lg.btn-illustrated.submit-button(title=castRealTimeVerbose)
span(data-i18n="play_level.tome_submit_button") Submit
span.spl.secret.submit-again-time
button.btn.btn-lg.btn-illustrated.done-button.secret button.btn.btn-lg.btn-illustrated.done-button.secret
span(data-i18n="play_level.done") Done span(data-i18n="play_level.done") Done

View file

@ -63,7 +63,7 @@ module.exports = class AchievementEditView extends RootView
@$el.find('#achievement-view').empty() @$el.find('#achievement-view').empty()
for key, value of @treema.data for key, value of @treema.data
@achievement.set key, value @achievement.set key, value
earned = earnedPoints: @achievement.get 'worth' earned = get: (key) => {earnedPoints: @achievement.get('worth'), previouslyAchievedAmount: 0}[key]
popup = new AchievementPopup achievement: @achievement, earnedAchievement: earned, popup: false, container: $('#achievement-view') popup = new AchievementPopup achievement: @achievement, earnedAchievement: earned, popup: false, container: $('#achievement-view')
openSaveModal: -> openSaveModal: ->

View file

@ -47,7 +47,7 @@ module.exports = class CampaignEditorView extends RootView
@listenToOnce @levels, 'sync', @onFundamentalLoaded @listenToOnce @levels, 'sync', @onFundamentalLoaded
@listenToOnce @achievements, 'sync', @onFundamentalLoaded @listenToOnce @achievements, 'sync', @onFundamentalLoaded
_.delay @getCampaignDropOffs, 1000 _.delay @getCampaignCompletions, 1000
loadThangTypeNames: -> loadThangTypeNames: ->
# Load the names of the ThangTypes that this level's Treema nodes might want to display. # Load the names of the ThangTypes that this level's Treema nodes might want to display.
@ -240,7 +240,7 @@ module.exports = class CampaignEditorView extends RootView
if achievement.hasLocalChanges() if achievement.hasLocalChanges()
@toSave.add achievement @toSave.add achievement
getCampaignDropOffs: => getCampaignCompletions: =>
# Fetch last 7 days of campaign drop-off rates # Fetch last 7 days of campaign drop-off rates
startDay = new Date() startDay = new Date()
@ -254,14 +254,15 @@ module.exports = class CampaignEditorView extends RootView
mapFn = (item) -> mapFn = (item) ->
item.startDropRate = (item.startDropped / item.started * 100).toFixed(2) item.startDropRate = (item.startDropped / item.started * 100).toFixed(2)
item.finishDropRate = (item.finishDropped / item.finished * 100).toFixed(2) item.finishDropRate = (item.finishDropped / item.finished * 100).toFixed(2)
item.completionRate = (item.finished / item.started * 100).toFixed(2)
item item
@campaignDropOffs.levels = _.map @campaignDropOffs.levels, mapFn, @ @campaignDropOffs.levels = _.map @campaignDropOffs.levels, mapFn, @
@campaignDropOffs.startDay = startDay @campaignDropOffs.startDay = startDay
@render() @render()
# TODO: Why do we need this url dash? # TODO: Why do we need this url dash?
request = @supermodel.addRequestResource 'campaign_drop_offs', { request = @supermodel.addRequestResource 'campaign_completions', {
url: '/db/analytics_log_event/-/campaign_drop_offs' url: '/db/analytics_log_event/-/campaign_completions'
data: {startDay: startDay, slug: @campaignHandle} data: {startDay: startDay, slug: @campaignHandle}
method: 'POST' method: 'POST'
success: success success: success

View file

@ -1,5 +1,8 @@
CocoView = require 'views/core/CocoView' CocoView = require 'views/core/CocoView'
Level = require 'models/Level' Level = require 'models/Level'
LevelSession = require 'models/LevelSession'
ModelModal = require 'views/modal/ModelModal'
User = require 'models/User'
module.exports = class CampaignLevelView extends CocoView module.exports = class CampaignLevelView extends CocoView
id: 'campaign-level-view' id: 'campaign-level-view'
@ -7,6 +10,7 @@ module.exports = class CampaignLevelView extends CocoView
events: events:
'click .close': 'onClickClose' 'click .close': 'onClickClose'
'dblclick .recent-session': 'onDblClickRecentSession'
constructor: (options, @level) -> constructor: (options, @level) ->
super(options) super(options)
@ -15,20 +19,53 @@ module.exports = class CampaignLevelView extends CocoView
@listenToOnce @fullLevel, 'sync', => @render?() @listenToOnce @fullLevel, 'sync', => @render?()
@levelSlug = @level.get('slug') @levelSlug = @level.get('slug')
@getCommonLevelProblems()
@getLevelCompletions() @getLevelCompletions()
@getLevelPlaytimes() @getLevelPlaytimes()
@getRecentSessions()
getRenderData: -> getRenderData: ->
c = super() c = super()
c.level = if @fullLevel.loaded then @fullLevel else @level c.level = if @fullLevel.loaded then @fullLevel else @level
c.commonProblems = @commonProblems
c.levelCompletions = @levelCompletions c.levelCompletions = @levelCompletions
c.levelPlaytimes = @levelPlaytimes c.levelPlaytimes = @levelPlaytimes
c.recentSessions = @recentSessions
c c
onClickClose: -> onClickClose: ->
@$el.addClass('hidden') @$el.addClass('hidden')
@trigger 'hidden' @trigger 'hidden'
onDblClickRecentSession: (e) ->
# Admin view of players' code
return unless me.isAdmin()
row = $(e.target).parent()
player = new User _id: row.data 'player-id'
session = new LevelSession _id: row.data 'session-id'
@openModalView new ModelModal models: [session, player]
getCommonLevelProblems: ->
# Fetch last 30 days of common level problems
startDay = new Date()
startDay.setDate(startDay.getUTCDate() - 29)
startDay = startDay.getUTCFullYear() + '-' + (startDay.getUTCMonth() + 1) + '-' + startDay.getUTCDate()
success = (data) =>
return if @destroyed
@commonProblems = data
@commonProblems.startDay = startDay
@render()
# TODO: Why do we need this url dash?
request = @supermodel.addRequestResource 'common_problems', {
url: '/db/user_code_problem/-/common_problems'
data: {startDay: startDay, slug: @levelSlug}
method: 'POST'
success: success
}, 0
request.load()
getLevelCompletions: -> getLevelCompletions: ->
# Fetch last 7 days of level completion counts # Fetch last 7 days of level completion counts
success = (data) => success = (data) =>
@ -72,3 +109,20 @@ module.exports = class CampaignLevelView extends CocoView
success: success success: success
}, 0 }, 0
request.load() request.load()
getRecentSessions: ->
limit = 10
success = (data) =>
return if @destroyed
@recentSessions = data
@render()
# TODO: Why do we need this url dash?
request = @supermodel.addRequestResource 'level_sessions_recent', {
url: "/db/level_session/-/recent"
data: {slug: @levelSlug, limit: limit}
method: 'POST'
success: success
}, 0
request.load()

View file

@ -137,11 +137,11 @@ module.exports = class LevelEditView extends RootView
showUndoDescription: -> showUndoDescription: ->
undoDescription = TreemaNode.getLastTreemaWithFocus().getUndoDescription() undoDescription = TreemaNode.getLastTreemaWithFocus().getUndoDescription()
@$el.find('#undo-button').attr('title', 'Undo ' + undoDescription + ' (Ctrl+Z)') @$el.find('#undo-button').attr('title', $.i18n.t("general.undo_prefix") + " " + undoDescription + " " + $.i18n.t("general.undo_shortcut"))
showRedoDescription: -> showRedoDescription: ->
redoDescription = TreemaNode.getLastTreemaWithFocus().getRedoDescription() redoDescription = TreemaNode.getLastTreemaWithFocus().getRedoDescription()
@$el.find('#redo-button').attr('title', 'Redo ' + redoDescription + ' (Ctrl+Shift+Z)') @$el.find('#redo-button').attr('title', $.i18n.t("general.redo_prefix") + " " + redoDescription + " " + $.i18n.t("general.redo_shortcut"))
getCurrentView: -> getCurrentView: ->
currentViewID = @$el.find('.tab-pane.active').attr('id') currentViewID = @$el.find('.tab-pane.active').attr('id')

View file

@ -15,7 +15,7 @@ module.exports = class SettingsTabView extends CocoView
editableSettings: [ editableSettings: [
'name', 'description', 'documentation', 'nextLevel', 'background', 'victory', 'i18n', 'icon', 'goals', 'name', 'description', 'documentation', 'nextLevel', 'background', 'victory', 'i18n', 'icon', 'goals',
'type', 'terrain', 'showsGuide', 'banner', 'employerDescription', 'loadingTip', 'requiresSubscription', 'type', 'terrain', 'showsGuide', 'banner', 'employerDescription', 'loadingTip', 'requiresSubscription',
'tasks', 'helpVideos' 'tasks', 'helpVideos', 'replayable'
] ]
subscriptions: subscriptions:

View file

@ -520,6 +520,7 @@ module.exports = class PlayLevelView extends RootView
return if @destroyed return if @destroyed
# TODO: Show a victory dialog specific to hero-ladder level # TODO: Show a victory dialog specific to hero-ladder level
if @goalManager.checkOverallStatus() is 'success' and not @options.realTimeMultiplayerSessionID? if @goalManager.checkOverallStatus() is 'success' and not @options.realTimeMultiplayerSessionID?
@session.increaseDifficulty() if @level.get 'replayable'
Backbone.Mediator.publish 'level:show-victory', showModal: true Backbone.Mediator.publish 'level:show-victory', showModal: true
destroy: -> destroy: ->

View file

@ -14,6 +14,7 @@ module.exports = class CastButtonView extends CocoView
subscriptions: subscriptions:
'tome:spell-changed': 'onSpellChanged' 'tome:spell-changed': 'onSpellChanged'
'tome:cast-spells': 'onCastSpells' 'tome:cast-spells': 'onCastSpells'
'tome:manual-cast-denied': 'onManualCastDenied'
'god:new-world-created': 'onNewWorld' 'god:new-world-created': 'onNewWorld'
'real-time-multiplayer:created-game': 'onJoinedRealTimeMultiplayerGame' 'real-time-multiplayer:created-game': 'onJoinedRealTimeMultiplayerGame'
'real-time-multiplayer:joined-game': 'onJoinedRealTimeMultiplayerGame' 'real-time-multiplayer:joined-game': 'onJoinedRealTimeMultiplayerGame'
@ -25,6 +26,11 @@ module.exports = class CastButtonView extends CocoView
super options super options
@spells = options.spells @spells = options.spells
@castShortcut = '⇧↵' @castShortcut = '⇧↵'
@updateReplayabilityInterval = setInterval @updateReplayability, 1000
destroy: ->
clearInterval @updateReplayabilityInterval
super()
getRenderData: (context={}) -> getRenderData: (context={}) ->
context = super context context = super context
@ -49,6 +55,7 @@ module.exports = class CastButtonView extends CocoView
@$el.find('.done-button').show() @$el.find('.done-button').show()
if @options.level.get('slug') is 'thornbush-farm'# and not @options.session.get('state')?.complete if @options.level.get('slug') is 'thornbush-farm'# and not @options.session.get('state')?.complete
@$el.find('.submit-button').hide() # Hide submit until first win so that script can explain it. @$el.find('.submit-button').hide() # Hide submit until first win so that script can explain it.
@updateReplayability()
attachTo: (spellView) -> attachTo: (spellView) ->
@$el.detach().prependTo(spellView.toolbarView.$el).show() @$el.detach().prependTo(spellView.toolbarView.$el).show()
@ -59,8 +66,11 @@ module.exports = class CastButtonView extends CocoView
onCastRealTimeButtonClick: (e) -> onCastRealTimeButtonClick: (e) ->
if @inRealTimeMultiplayerSession if @inRealTimeMultiplayerSession
Backbone.Mediator.publish 'real-time-multiplayer:manual-cast', {} Backbone.Mediator.publish 'real-time-multiplayer:manual-cast', {}
else if @options.level.get('replayable') and (timeUntilResubmit = @options.session.timeUntilResubmit()) > 0
Backbone.Mediator.publish 'tome:manual-cast-denied', timeUntilResubmit: timeUntilResubmit
else else
Backbone.Mediator.publish 'tome:manual-cast', {realTime: true} Backbone.Mediator.publish 'tome:manual-cast', {realTime: true}
@updateReplayability()
onDoneButtonClick: (e) -> onDoneButtonClick: (e) ->
Backbone.Mediator.publish 'level:show-victory', showModal: true Backbone.Mediator.publish 'level:show-victory', showModal: true
@ -72,14 +82,19 @@ module.exports = class CastButtonView extends CocoView
return if e.preload return if e.preload
@casting = true @casting = true
if @hasStartedCastingOnce # Don't play this sound the first time if @hasStartedCastingOnce # Don't play this sound the first time
Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'cast', volume: 0.5 @playSound 'cast', 0.5
@hasStartedCastingOnce = true @hasStartedCastingOnce = true
@updateCastButton() @updateCastButton()
onManualCastDenied: (e) ->
wait = moment().add(e.timeUntilResubmit, 'ms').fromNow()
#@playSound 'manual-cast-denied', 1.0 # find some sound for this?
noty text: "You can try again #{wait}.", layout: 'center', type: 'warning', killer: false, timeout: 6000
onNewWorld: (e) -> onNewWorld: (e) ->
@casting = false @casting = false
if @hasCastOnce # Don't play this sound the first time if @hasCastOnce # Don't play this sound the first time
Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'cast-end', volume: 0.5 @playSound 'cast-end', 0.5
@hasCastOnce = true @hasCastOnce = true
@updateCastButton() @updateCastButton()
@ -120,6 +135,17 @@ module.exports = class CastButtonView extends CocoView
@castButton.text castText @castButton.text castText
#@castButton.prop 'disabled', not castable #@castButton.prop 'disabled', not castable
updateReplayability: =>
return if @destroyed
return unless @options.level.get 'replayable'
timeUntilResubmit = @options.session.timeUntilResubmit()
disabled = timeUntilResubmit > 0
submitButton = @$el.find('.submit-button').toggleClass('disabled', disabled)
submitAgainLabel = submitButton.find('.submit-again-time').toggleClass('secret', not disabled)
if disabled
waitTime = moment().add(timeUntilResubmit, 'ms').fromNow()
submitAgainLabel.text waitTime
setAutocastDelay: (delay) -> setAutocastDelay: (delay) ->
#console.log 'Set autocast delay to', delay #console.log 'Set autocast delay to', delay
return unless delay return unless delay

View file

@ -122,7 +122,11 @@ module.exports = class SpellView extends CocoView
addCommand addCommand
name: 'run-code-real-time' name: 'run-code-real-time'
bindKey: {win: 'Ctrl-Shift-Enter', mac: 'Command-Shift-Enter|Ctrl-Shift-Enter'} bindKey: {win: 'Ctrl-Shift-Enter', mac: 'Command-Shift-Enter|Ctrl-Shift-Enter'}
exec: -> Backbone.Mediator.publish 'tome:manual-cast', {realTime: true} exec: =>
if @options.level.get('replayable') and (timeUntilResubmit = @session.timeUntilResubmit()) > 0
Backbone.Mediator.publish 'tome:manual-cast-denied', timeUntilResubmit: timeUntilResubmit
else
Backbone.Mediator.publish 'tome:manual-cast', {realTime: true}
addCommand addCommand
name: 'no-op' name: 'no-op'
bindKey: {win: 'Ctrl-S', mac: 'Command-S|Ctrl-S'} bindKey: {win: 'Ctrl-S', mac: 'Command-S|Ctrl-S'}
@ -616,7 +620,7 @@ module.exports = class SpellView extends CocoView
onSignificantChange.push _.debounce @checkSuspectCode, 750 if @options.level.get 'suspectCode' onSignificantChange.push _.debounce @checkSuspectCode, 750 if @options.level.get 'suspectCode'
@onCodeChangeMetaHandler = => @onCodeChangeMetaHandler = =>
return if @eventsSuppressed return if @eventsSuppressed
Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'code-change', volume: 0.5 #@playSound 'code-change', volume: 0.5 # Currently not using this sound.
if @spellThang if @spellThang
@spell.hasChangedSignificantly @getSource(), @spellThang.aether.raw, (hasChanged) => @spell.hasChangedSignificantly @getSource(), @spellThang.aether.raw, (hasChanged) =>
if not @spellThang or hasChanged if not @spellThang or hasChanged

View file

@ -163,8 +163,9 @@ module.exports = class TomeView extends CocoView
if realTime if realTime
sessionState.submissionCount = (sessionState.submissionCount ? 0) + 1 sessionState.submissionCount = (sessionState.submissionCount ? 0) + 1
sessionState.flagHistory = _.filter sessionState.flagHistory ? [], (event) => event.team isnt (@options.session.get('team') ? 'humans') sessionState.flagHistory = _.filter sessionState.flagHistory ? [], (event) => event.team isnt (@options.session.get('team') ? 'humans')
sessionState.lastUnsuccessfulSubmissionTime = new Date() if @options.level.get 'replayable'
@options.session.set 'state', sessionState @options.session.set 'state', sessionState
Backbone.Mediator.publish 'tome:cast-spells', spells: @spells, preload: preload, realTime: realTime, submissionCount: sessionState.submissionCount ? 0, flagHistory: sessionState.flagHistory ? [] Backbone.Mediator.publish 'tome:cast-spells', spells: @spells, preload: preload, realTime: realTime, submissionCount: sessionState.submissionCount ? 0, flagHistory: sessionState.flagHistory ? [], difficulty: sessionState.difficulty ? 0
onToggleSpellList: (e) -> onToggleSpellList: (e) ->
@spellList.rerenderEntries() @spellList.rerenderEntries()

View file

@ -80,6 +80,7 @@ work = () ->
self.world.levelSessionIDs = args.levelSessionIDs self.world.levelSessionIDs = args.levelSessionIDs
self.world.submissionCount = args.submissionCount self.world.submissionCount = args.submissionCount
self.world.flagHistory = args.flagHistory self.world.flagHistory = args.flagHistory
self.world.difficulty = args.difficulty
self.world.loadFromLevel args.level, true if args.level self.world.loadFromLevel args.level, true if args.level
self.world.headless = args.headless self.world.headless = args.headless
self.goalManager = new GoalManager(self.world) self.goalManager = new GoalManager(self.world)

View file

@ -181,8 +181,8 @@ if __name__ == '__main__':
api_key = sys.argv[1] api_key = sys.argv[1]
api_secret = sys.argv[2] api_secret = sys.argv[2]
# HoC # HoC
printPriceConversionRates(api_key, api_secret, '2014-12-08', '2014-12-13') printPriceConversionRates(api_key, api_secret, '2014-12-08', '2014-12-19')
# Use these to feed numbers into Stripe parsing script, since Stripe knows better about conversions than Mixpanel # Use these to feed numbers into Stripe parsing script, since Stripe knows better about conversions than Mixpanel
print 'Pre-HoC shown', getShownSubModal(api_key, api_secret, '2014-12-06', '2014-12-07') print 'Pre-HoC shown', getShownSubModal(api_key, api_secret, '2014-12-06', '2014-12-07')
print 'Post-HoC shown', getShownSubModal(api_key, api_secret, '2014-12-14', '2014-12-14') print 'Post-HoC shown', getShownSubModal(api_key, api_secret, '2014-12-20', '2015-01-04')

View file

@ -74,7 +74,7 @@ def getHoCPriceConversionRates(paymentsFile):
# 'start2': datetime(2014, 12, 11, 0, 34), # 'start2': datetime(2014, 12, 11, 0, 34),
# 'end2': datetime(2014, 12, 12, 3, 21), # 'end2': datetime(2014, 12, 12, 3, 21),
# 'start3': datetime(2014, 12, 13, 17, 30), # 'start3': datetime(2014, 12, 13, 17, 30),
'Show subscription modal': 45343, 'Show subscription modal': 86883,
'Finished subscription purchase': 0 'Finished subscription purchase': 0
}, },
'1499': { '1499': {
@ -94,7 +94,7 @@ def getHoCPriceConversionRates(paymentsFile):
# Find 'Finished subscription purchase' event from Stripe data # Find 'Finished subscription purchase' event from Stripe data
startDate = datetime(2014, 12, 8) startDate = datetime(2014, 12, 8)
endDate = datetime(2014, 12, 14) endDate = datetime(2014, 12, 20)
print startDate, 'to', endDate print startDate, 'to', endDate
with open(paymentsFile) as f: with open(paymentsFile) as f:
first = True first = True
@ -180,14 +180,14 @@ def getPostHoCPriceConversionRates(paymentsFile):
# Show count from Mixpanel # Show count from Mixpanel
prices = { prices = {
'999': { '999': {
'Show subscription modal': 2935, 'Show subscription modal': 13339,
'Finished subscription purchase': 0 'Finished subscription purchase': 0
} }
} }
# Find 'Finished subscription purchase' event from Stripe data # Find 'Finished subscription purchase' event from Stripe data
startDate = datetime(2014, 12, 14) startDate = datetime(2014, 12, 20)
endDate = datetime(2014, 12, 15) endDate = datetime(2015, 1, 4)
print startDate, 'to', endDate print startDate, 'to', endDate
with open(paymentsFile) as f: with open(paymentsFile) as f:
first = True first = True

View file

@ -6,5 +6,6 @@ AnalyticsLogEventSchema = new mongoose.Schema({
type: Date type: Date
'default': Date.now 'default': Date.now
}, {strict: false}) }, {strict: false})
AnalyticsLogEventSchema.index({event: 1, created: -1})
module.exports = AnalyticsLogEvent = mongoose.model('analytics.log.event', AnalyticsLogEventSchema) module.exports = AnalyticsLogEvent = mongoose.model('analytics.log.event', AnalyticsLogEventSchema)

View file

@ -21,11 +21,11 @@ class AnalyticsLogEventHandler extends Handler
instance instance
getByRelationship: (req, res, args...) -> getByRelationship: (req, res, args...) ->
return @getLevelCompletionsBySlugs(req, res) if args[1] is 'level_completions' return @getLevelCompletionsBySlug(req, res) if args[1] is 'level_completions'
return @getCampaignDropOffs(req, res) if args[1] is 'campaign_drop_offs' return @getCampaignCompletionsBySlug(req, res) if args[1] is 'campaign_completions'
super(arguments...) super(arguments...)
getLevelCompletionsBySlugs: (req, res) -> getLevelCompletionsBySlug: (req, res) ->
# Returns an array of per-day level starts and finishes # Returns an array of per-day level starts and finishes
# Parameters: # Parameters:
# slug - level slug # slug - level slug
@ -97,8 +97,8 @@ class AnalyticsLogEventHandler extends Handler
@levelCompletionsCache[cacheKey] = completions[level] @levelCompletionsCache[cacheKey] = completions[level]
@sendSuccess res, completions[levelSlug] @sendSuccess res, completions[levelSlug]
getCampaignDropOffs: (req, res) -> getCampaignCompletionsBySlug: (req, res) ->
# Returns a dictionary of per-campaign level start and finish drop-offs # Returns a dictionary of per-campaign level starts, finishes, and drop-offs
# Drop-off: last started or finished level event # Drop-off: last started or finished level event
# Parameters: # Parameters:
# slugs - array of campaign slugs # slugs - array of campaign slugs
@ -128,7 +128,7 @@ class AnalyticsLogEventHandler extends Handler
cacheKey += 'e' + endDay if endDay? cacheKey += 'e' + endDay if endDay?
return @sendSuccess res, campaignDropOffs if campaignDropOffs = @campaignDropOffsCache[cacheKey] return @sendSuccess res, campaignDropOffs if campaignDropOffs = @campaignDropOffsCache[cacheKey]
calculateDropOffs = (campaigns) => getCompletions = (campaigns) =>
# Calculate campaign drop off rates # Calculate campaign drop off rates
# Input: # Input:
# campaigns - per-campaign dictionary of ordered level slugs # campaigns - per-campaign dictionary of ordered level slugs
@ -180,7 +180,7 @@ class AnalyticsLogEventHandler extends Handler
levelProgression[level].finishDropped++ if i is userProgression[user].length - 1 levelProgression[level].finishDropped++ if i is userProgression[user].length - 1
# Put in campaign order # Put in campaign order
campaignRates = {} completions = {}
for level of levelProgression for level of levelProgression
for campaign of campaigns for campaign of campaigns
if level in campaigns[campaign] if level in campaigns[campaign]
@ -188,14 +188,14 @@ class AnalyticsLogEventHandler extends Handler
startDropped = levelProgression[level].startDropped startDropped = levelProgression[level].startDropped
finished = levelProgression[level].finished finished = levelProgression[level].finished
finishDropped = levelProgression[level].finishDropped finishDropped = levelProgression[level].finishDropped
campaignRates[campaign] ?= completions[campaign] ?=
levels: [] levels: []
# overall: # overall:
# started: 0, # started: 0,
# startDropped: 0, # startDropped: 0,
# finished: 0, # finished: 0,
# finishDropped: 0 # finishDropped: 0
campaignRates[campaign].levels.push completions[campaign].levels.push
level: level level: level
started: started started: started
startDropped: startDropped startDropped: startDropped
@ -204,19 +204,19 @@ class AnalyticsLogEventHandler extends Handler
break break
# Sort level data by campaign order # Sort level data by campaign order
for campaign of campaignRates for campaign of completions
campaignRates[campaign].levels.sort (a, b) -> completions[campaign].levels.sort (a, b) ->
if campaigns[campaign].indexOf(a.level) < campaigns[campaign].indexOf(b.level) then return -1 else 1 if campaigns[campaign].indexOf(a.level) < campaigns[campaign].indexOf(b.level) then return -1 else 1
# Return all campaign data for simplicity # Return all campaign data for simplicity
# Cache other individual campaigns too, since we have them # Cache other individual campaigns too, since we have them
@campaignDropOffsCache[cacheKey] = campaignRates @campaignDropOffsCache[cacheKey] = completions
for campaign of campaignRates for campaign of completions
cacheKey = campaign cacheKey = campaign
cacheKey += 's' + startDay if startDay? cacheKey += 's' + startDay if startDay?
cacheKey += 'e' + endDay if endDay? cacheKey += 'e' + endDay if endDay?
@campaignDropOffsCache[cacheKey] = campaignRates @campaignDropOffsCache[cacheKey] = completions
@sendSuccess res, campaignRates @sendSuccess res, completions
getLevelData = (campaigns, campaignLevelIDs) => getLevelData = (campaigns, campaignLevelIDs) =>
# Get level data and replace levelIDs with level slugs in campaigns # Get level data and replace levelIDs with level slugs in campaigns
@ -239,10 +239,8 @@ class AnalyticsLogEventHandler extends Handler
for campaign of campaigns for campaign of campaigns
mapFn = (item) -> levelSlugMap[item] mapFn = (item) -> levelSlugMap[item]
campaigns[campaign] = _.map campaigns[campaign], mapFn, @ campaigns[campaign] = _.map campaigns[campaign], mapFn, @
# Forest campaign levels are reversed for some reason
campaigns[campaign].reverse() if campaign is 'forest'
calculateDropOffs campaigns getCompletions campaigns
getCampaignData = () => getCampaignData = () =>
# Get campaign data # Get campaign data

View file

@ -55,6 +55,7 @@ LevelHandler = class LevelHandler extends Handler
'tasks' 'tasks'
'helpVideos' 'helpVideos'
'campaign' 'campaign'
'replayable'
] ]
postEditableProperties: ['name'] postEditableProperties: ['name']
@ -383,7 +384,7 @@ LevelHandler = class LevelHandler extends Handler
# Build list of level average playtimes # Build list of level average playtimes
playtimes = [] playtimes = []
for item in data for item in data
playtimes.push playtimes.push
level: item._id.level level: item._id.level
created: item._id.created created: item._id.created
average: item.average average: item.average

View file

@ -9,6 +9,7 @@ class LevelSessionHandler extends Handler
getByRelationship: (req, res, args...) -> getByRelationship: (req, res, args...) ->
return @getActiveSessions req, res if args.length is 2 and args[1] is 'active' return @getActiveSessions req, res if args.length is 2 and args[1] is 'active'
return @getRecentSessions req, res if args.length is 2 and args[1] is 'recent'
return @getCodeLanguageCounts req, res if args[1] is 'code_language_counts' return @getCodeLanguageCounts req, res if args[1] is 'code_language_counts'
super(arguments...) super(arguments...)
@ -29,6 +30,19 @@ class LevelSessionHandler extends Handler
documents = (@formatEntity(req, doc) for doc in documents) documents = (@formatEntity(req, doc) for doc in documents)
@sendSuccess(res, documents) @sendSuccess(res, documents)
getRecentSessions: (req, res) ->
return @sendForbiddenError(res) unless req.user?.isAdmin()
levelSlug = req.query.slug or req.body.slug
limit = req.query.limit or req.body.limit or 7
return @sendSuccess res, [] unless levelSlug?
query = @modelClass.find({"levelID": levelSlug}).sort({changed: -1}).limit(limit)
query.exec (err, documents) =>
return @sendDatabaseError(res, err) if err
@sendSuccess res, documents
hasAccessToDocument: (req, document, method=null) -> hasAccessToDocument: (req, document, method=null) ->
return true if req.method is 'GET' and document.get('submitted') return true if req.method is 'GET' and document.get('submitted')
return true if ('employer' in (req.user?.get('permissions') ? [])) and (method ? req.method).toLowerCase() is 'get' return true if ('employer' in (req.user?.get('permissions') ? [])) and (method ? req.method).toLowerCase() is 'get'

View file

@ -16,7 +16,8 @@ AchievablePlugin = (schema, options) ->
return return
# Keep track the document before it's saved # Keep track the document before it's saved
schema.post 'init', (doc) -> schema.post 'init', (doc) ->
before[doc.id] = doc.toObject() #doc.beforeDoc = doc.toObject() # TODO: switch to this
before[doc.id] = doc.toObject() # TODO: switch from this, run the testzzz
# TODO check out how many objects go unreleased # TODO check out how many objects go unreleased
# Check if an achievement has been earned # Check if an achievement has been earned
@ -43,6 +44,6 @@ AchievablePlugin = (schema, options) ->
return unless newlyAchieved and (not alreadyAchieved or isRepeatable) return unless newlyAchieved and (not alreadyAchieved or isRepeatable)
EarnedAchievement.createForAchievement(achievement, doc, originalDocObj) EarnedAchievement.createForAchievement(achievement, doc, originalDocObj)
delete before[doc.id] if doc.id of before delete before[doc.id] if doc.id of before # TODO: don't do it!
module.exports = AchievablePlugin module.exports = AchievablePlugin

View file

@ -23,4 +23,62 @@ class UserCodeProblemHandler extends Handler
ucp.set('creator', req.user._id) ucp.set('creator', req.user._id)
ucp ucp
getByRelationship: (req, res, args...) ->
return @getCommonLevelProblemsBySlug(req, res) if args[1] is 'common_problems'
super(arguments...)
getCommonLevelProblemsBySlug: (req, res) ->
# Returns an ordered array of common user code problems with: language, message, hint, count
# Parameters:
# slug - level slug
# startDay - Inclusive, optional, e.g. '2014-12-14'
# endDay - Exclusive, optional, e.g. '2014-12-16'
levelSlug = req.query.slug or req.body.slug
startDay = req.query.startDay or req.body.startDay
endDay = req.query.endDay or req.body.endDay
return @sendSuccess res, [] unless levelSlug?
# Cache results for 1 day
@commonLevelProblemsCache ?= {}
@commonLevelProblemsCachedSince ?= new Date()
if (new Date()) - @commonLevelProblemsCachedSince > 86400 * 1000 # Dumb cache expiration
@commonLevelProblemsCache = {}
@commonLevelProblemsCachedSince = new Date()
cacheKey = levelSlug
cacheKey += 's' + startDay if startDay?
cacheKey += 'e' + endDay if endDay?
return @sendSuccess res, commonProblems if commonProblems = @commonLevelProblemsCache[cacheKey]
# Build query
match = if startDay? or endDay? then {$match: {$and: []}} else {$match: {}}
match["$match"]["$and"].push created: {$gte: new Date(startDay + "T00:00:00.000Z")} if startDay?
match["$match"]["$and"].push created: {$lt: new Date(endDay + "T00:00:00.000Z")} if endDay?
group = {"$group": {"_id": {"errMessage": "$errMessageNoLineInfo", "errHint": "$errHint", "language": "$language", "levelID": "$levelID"}, "count": {"$sum": 1}}}
sort = { $sort : { "_id.levelID": 1, count : -1, "_id.language": 1 } }
query = UserCodeProblem.aggregate match, group, sort
query.exec (err, data) =>
if err? then return @sendDatabaseError res, err
# Build per-level common problem lists
commonProblems = {}
for item in data
levelID = item._id.levelID
commonProblems[levelID] ?= []
commonProblems[levelID].push
language: item._id.language
message: item._id.errMessage
hint: item._id.errHint
count: item.count
# Cache all the levels
for levelID of commonProblems
cacheKey = levelID
cacheKey += 's' + startDay if startDay?
cacheKey += 'e' + endDay if endDay?
@commonLevelProblemsCache[cacheKey] = commonProblems[levelID]
@sendSuccess res, commonProblems[levelSlug]
module.exports = new UserCodeProblemHandler() module.exports = new UserCodeProblemHandler()

View file

@ -127,26 +127,15 @@ setupJavascript404s = (app) ->
setupFallbackRouteToIndex = (app) -> setupFallbackRouteToIndex = (app) ->
app.all '*', (req, res) -> app.all '*', (req, res) ->
if req.user fs.readFile path.join(__dirname, 'public', 'main.html'), 'utf8', (err, data) ->
sendMain(req, res) log.error "Error modifying main.html: #{err}" if err
# Disabling for HoC # insert the user object directly into the html so the application can have it immediately. Sanitize </script>
# req.user.set('lastIP', req.connection.remoteAddress) user = if req.user then JSON.stringify(UserHandler.formatEntity(req, req.user)).replace(/\//g, '\\/') else '{}'
# req.user.save() data = data.replace('"userObjectTag"', user)
else res.header 'Cache-Control', 'no-cache, no-store, must-revalidate'
user = auth.makeNewUser(req) res.header 'Pragma', 'no-cache'
makeNext = (req, res) -> -> sendMain(req, res) res.header 'Expires', 0
next = makeNext(req, res) res.send 200, data
auth.loginUser(req, res, user, false, next)
sendMain = (req, res) ->
fs.readFile path.join(__dirname, 'public', 'main.html'), 'utf8', (err, data) ->
log.error "Error modifying main.html: #{err}" if err
# insert the user object directly into the html so the application can have it immediately. Sanitize </script>
data = data.replace('"userObjectTag"', JSON.stringify(UserHandler.formatEntity(req, req.user)).replace(/\//g, '\\/'))
res.header 'Cache-Control', 'no-cache, no-store, must-revalidate'
res.header 'Pragma', 'no-cache'
res.header 'Expires', 0
res.send 200, data
setupFacebookCrossDomainCommunicationRoute = (app) -> setupFacebookCrossDomainCommunicationRoute = (app) ->
app.get '/channel.html', (req, res) -> app.get '/channel.html', (req, res) ->