Merge branch 'master' into production
This commit is contained in:
commit
5da7244f57
36 changed files with 1080 additions and 205 deletions
app
assets/images/pages/play
lib
locale
models
schemas/models
styles/editor/campaign
templates
core
editor
play
views
bin
scripts
analytics/mongodb/queries
devSetup
windows/coco-dev-setup/batch
server
analytics
levels
test/server/unit
Binary file not shown.
Before ![]() (image error) Size: 17 KiB After ![]() (image error) Size: 18 KiB ![]() ![]() |
|
@ -8,7 +8,7 @@ module.exports = [
|
||||||
focus:
|
focus:
|
||||||
bounds: [{x: 0, y: 0}, {x: 80, y: 68}]
|
bounds: [{x: 0, y: 0}, {x: 80, y: 68}]
|
||||||
target: "Hero Placeholder"
|
target: "Hero Placeholder"
|
||||||
zoom: 2
|
zoom: 0.5
|
||||||
sound:
|
sound:
|
||||||
music:
|
music:
|
||||||
file: "/music/music_level_2"
|
file: "/music/music_level_2"
|
||||||
|
|
|
@ -307,7 +307,7 @@ module.exports = nativeDescription: "čeština", englishDescription: "Czech", tr
|
||||||
tip_premature_optimization: "Předčasná optimalizace je původce všeho zla. - Donald Knuth"
|
tip_premature_optimization: "Předčasná optimalizace je původce všeho zla. - Donald Knuth"
|
||||||
tip_brute_force: "V případě pochybností, použijte brute force. - Ken Thompson"
|
tip_brute_force: "V případě pochybností, použijte brute force. - Ken Thompson"
|
||||||
tip_extrapolation: "Jsou jenom dva druhy lidí: ti, kteří mohou extrapolovat z nekompletních dat..."
|
tip_extrapolation: "Jsou jenom dva druhy lidí: ti, kteří mohou extrapolovat z nekompletních dat..."
|
||||||
# tip_superpower: "Coding is the closest thing we have to a superpower."
|
tip_superpower: "Kódování by se téměř dalo srovnávat se superschopnostmi."
|
||||||
|
|
||||||
game_menu:
|
game_menu:
|
||||||
inventory_tab: "Inventář"
|
inventory_tab: "Inventář"
|
||||||
|
@ -329,7 +329,7 @@ module.exports = nativeDescription: "čeština", englishDescription: "Czech", tr
|
||||||
inventory:
|
inventory:
|
||||||
choose_inventory: "Nasadit předměty"
|
choose_inventory: "Nasadit předměty"
|
||||||
equipped_item: "Nasazeno"
|
equipped_item: "Nasazeno"
|
||||||
# required_purchase_title: "Required"
|
required_purchase_title: "Vyžadováno"
|
||||||
available_item: "Dostupné"
|
available_item: "Dostupné"
|
||||||
restricted_title: "Omezeno"
|
restricted_title: "Omezeno"
|
||||||
should_equip: "(dvojklik pro nasazení)"
|
should_equip: "(dvojklik pro nasazení)"
|
||||||
|
@ -482,13 +482,13 @@ module.exports = nativeDescription: "čeština", englishDescription: "Czech", tr
|
||||||
forum_prefix: "Pro ostatní veřejné věci, prosím zkuste "
|
forum_prefix: "Pro ostatní veřejné věci, prosím zkuste "
|
||||||
forum_page: "naše fórum"
|
forum_page: "naše fórum"
|
||||||
forum_suffix: "."
|
forum_suffix: "."
|
||||||
# faq_prefix: "There's also a"
|
faq_prefix: "Také máme "
|
||||||
# faq: "FAQ"
|
faq: "FAQ"
|
||||||
subscribe_prefix: "Pokud potřebujete pomoc s nějakou úrovní, prosím"
|
subscribe_prefix: "Pokud potřebujete pomoc s nějakou úrovní, prosím"
|
||||||
subscribe: "zakupte si CodeCombat předplatné"
|
subscribe: "zakupte si CodeCombat předplatné"
|
||||||
subscribe_suffix: "a rádi vám pomůžeme s vaším kódem."
|
subscribe_suffix: "a rádi vám pomůžeme s vaším kódem."
|
||||||
subscriber_support: "Již jste CodeCombat předplatitel, takže vaše emaily budou vyřízeny dříve."
|
subscriber_support: "Již jste CodeCombat předplatitel, takže vaše emaily budou vyřízeny dříve."
|
||||||
# screenshot_included: "Screenshot included."
|
screenshot_included: "Snímek obrazovky zahrnut."
|
||||||
where_reply: "Kam máme odpovědět?"
|
where_reply: "Kam máme odpovědět?"
|
||||||
send: "Odeslat připomínku"
|
send: "Odeslat připomínku"
|
||||||
contact_candidate: "Kontaktovat kandidáta" # Deprecated
|
contact_candidate: "Kontaktovat kandidáta" # Deprecated
|
||||||
|
|
|
@ -3,8 +3,7 @@
|
||||||
slogan: "Learn to Code by Playing a Game"
|
slogan: "Learn to Code by Playing a Game"
|
||||||
no_ie: "CodeCombat does not run in Internet Explorer 8 or older. Sorry!" # Warning that only shows up in IE8 and older
|
no_ie: "CodeCombat does not run in Internet Explorer 8 or older. Sorry!" # Warning that only shows up in IE8 and older
|
||||||
no_mobile: "CodeCombat wasn't designed for mobile devices and may not work!" # Warning that shows up on mobile devices
|
no_mobile: "CodeCombat wasn't designed for mobile devices and may not work!" # Warning that shows up on mobile devices
|
||||||
play: "Play" # The big play button that just starts playing a level
|
play: "Play" # The big play button that opens up the campaign view.
|
||||||
try_it: "Try It" # Alternate wording for Play button
|
|
||||||
old_browser: "Uh oh, your browser is too old to run CodeCombat. Sorry!" # Warning that shows up on really old Firefox/Chrome/Safari
|
old_browser: "Uh oh, your browser is too old to run CodeCombat. Sorry!" # Warning that shows up on really old Firefox/Chrome/Safari
|
||||||
old_browser_suffix: "You can try anyway, but it probably won't work."
|
old_browser_suffix: "You can try anyway, but it probably won't work."
|
||||||
ipad_browser: "Bad news: CodeCombat doesn't run on iPad in the browser. Good news: our native iPad app is awaiting Apple approval."
|
ipad_browser: "Bad news: CodeCombat doesn't run on iPad in the browser. Good news: our native iPad app is awaiting Apple approval."
|
||||||
|
@ -658,6 +657,7 @@
|
||||||
achievement_query_goals: "Key achievement off of level goals"
|
achievement_query_goals: "Key achievement off of level goals"
|
||||||
level_completion: "Level Completion"
|
level_completion: "Level Completion"
|
||||||
pop_i18n: "Populate I18N"
|
pop_i18n: "Populate I18N"
|
||||||
|
tasks: "Tasks"
|
||||||
|
|
||||||
article:
|
article:
|
||||||
edit_btn_preview: "Preview"
|
edit_btn_preview: "Preview"
|
||||||
|
|
|
@ -4,7 +4,6 @@ module.exports = nativeDescription: "Español (América Latina)", englishDescrip
|
||||||
no_ie: "¡Lo sentimos! CodeCombat no funciona en Internet Explorer 8 o versiones anteriores." # Warning that only shows up in IE8 and older
|
no_ie: "¡Lo sentimos! CodeCombat no funciona en Internet Explorer 8 o versiones anteriores." # Warning that only shows up in IE8 and older
|
||||||
no_mobile: "¡CodeCombat no fue diseñado para dispositivos móviles y quizás no funcione!" # Warning that shows up on mobile devices
|
no_mobile: "¡CodeCombat no fue diseñado para dispositivos móviles y quizás no funcione!" # Warning that shows up on mobile devices
|
||||||
play: "Jugar" # The big play button that just starts playing a level
|
play: "Jugar" # The big play button that just starts playing a level
|
||||||
try_it: "Pruébalo" # Alternate wording for Play button
|
|
||||||
old_browser: "¡Oh! ¡Oh! Tu navegador es muy antiguo para correr CodeCombat. ¡Lo sentimos!" # Warning that shows up on really old Firefox/Chrome/Safari
|
old_browser: "¡Oh! ¡Oh! Tu navegador es muy antiguo para correr CodeCombat. ¡Lo sentimos!" # Warning that shows up on really old Firefox/Chrome/Safari
|
||||||
old_browser_suffix: "Puedes probar de todas formas, pero probablemente no funcione."
|
old_browser_suffix: "Puedes probar de todas formas, pero probablemente no funcione."
|
||||||
ipad_browser: "Malas noticias: CodeCombat no funciona en el navegador de iPad. Buenas noticias: nuestra propia aplicación de iPad esta en espera para ser aprobada por Apple."
|
ipad_browser: "Malas noticias: CodeCombat no funciona en el navegador de iPad. Buenas noticias: nuestra propia aplicación de iPad esta en espera para ser aprobada por Apple."
|
||||||
|
@ -593,63 +592,63 @@ module.exports = nativeDescription: "Español (América Latina)", englishDescrip
|
||||||
editor:
|
editor:
|
||||||
main_title: "Editor de CodeCombat"
|
main_title: "Editor de CodeCombat"
|
||||||
article_title: "Editor de Artículo"
|
article_title: "Editor de Artículo"
|
||||||
# thang_title: "Thang Editor"
|
thang_title: "Editor de Thangs"
|
||||||
level_title: "Editor de Nivel"
|
level_title: "Editor de Nivel"
|
||||||
# achievement_title: "Achievement Editor"
|
achievement_title: "Editor de logros"
|
||||||
back: "Atrás"
|
back: "Atrás"
|
||||||
revert: "Revertir"
|
revert: "Revertir"
|
||||||
revert_models: "Revertir Modelos"
|
revert_models: "Revertir Modelos"
|
||||||
pick_a_terrain: "Elije un Terreno"
|
pick_a_terrain: "Elije un Terreno"
|
||||||
small: "Pequeño"
|
small: "Pequeño"
|
||||||
grassy: "Herboso"
|
grassy: "Herboso"
|
||||||
# fork_title: "Fork New Version"
|
fork_title: "Fork de Nueva Versión"
|
||||||
# fork_creating: "Creating Fork..."
|
fork_creating: "Creando Fork..."
|
||||||
generate_terrain: "Generar terreno"
|
generate_terrain: "Generar terreno"
|
||||||
more: "Más"
|
more: "Más"
|
||||||
wiki: "Wiki"
|
wiki: "Wiki"
|
||||||
live_chat: "Chat en vivo"
|
live_chat: "Chat en vivo"
|
||||||
# thang_main: "Main"
|
thang_main: "Principal"
|
||||||
# thang_spritesheets: "Spritesheets"
|
thang_spritesheets: "Spritesheets"
|
||||||
# thang_colors: "Colors"
|
thang_colors: "Colores"
|
||||||
# level_some_options: "Some Options?"
|
level_some_options: "¿Algunas opciones?"
|
||||||
# level_tab_thangs: "Thangs"
|
level_tab_thangs: "Thangs"
|
||||||
# level_tab_scripts: "Scripts"
|
level_tab_scripts: "Scripts"
|
||||||
level_tab_settings: "Opciones"
|
level_tab_settings: "Opciones"
|
||||||
level_tab_components: "Componentes"
|
level_tab_components: "Componentes"
|
||||||
level_tab_systems: "Sistemas"
|
level_tab_systems: "Sistemas"
|
||||||
level_tab_docs: "Documentación"
|
level_tab_docs: "Documentación"
|
||||||
# level_tab_thangs_title: "Current Thangs"
|
level_tab_thangs_title: "Thangs Actuales"
|
||||||
level_tab_thangs_all: "Todo"
|
level_tab_thangs_all: "Todo"
|
||||||
# level_tab_thangs_conditions: "Starting Conditions"
|
level_tab_thangs_conditions: "Condiciones Iniciales"
|
||||||
# level_tab_thangs_add: "Add Thangs"
|
level_tab_thangs_add: "Agregar Thangs"
|
||||||
delete: "Borrar"
|
delete: "Borrar"
|
||||||
duplicate: "Duplicar"
|
duplicate: "Duplicar"
|
||||||
rotate: "Rotar"
|
rotate: "Rotar"
|
||||||
level_settings_title: "Opciones"
|
level_settings_title: "Opciones"
|
||||||
level_component_tab_title: "Componentes Actuales"
|
level_component_tab_title: "Componentes Actuales"
|
||||||
level_component_btn_new: "Crear Nuevo Componente"
|
level_component_btn_new: "Crear Nuevo Componente"
|
||||||
level_systems_tab_title: "Sistemas Actuales Systems"
|
level_systems_tab_title: "Sistemas Actuales"
|
||||||
level_systems_btn_new: "Crear Nuevo Sistema New System"
|
level_systems_btn_new: "Crear Nuevo Sistema"
|
||||||
level_systems_btn_add: "Agregar Sistema"
|
level_systems_btn_add: "Agregar Sistema"
|
||||||
# level_components_title: "Back to All Thangs"
|
level_components_title: "Regresar a todos los Thangs"
|
||||||
level_components_type: "Tipo"
|
level_components_type: "Tipo"
|
||||||
level_component_edit_title: "Editar Componente"
|
level_component_edit_title: "Editar Componente"
|
||||||
# level_component_config_schema: "Config Schema"
|
level_component_config_schema: "Config Schema"
|
||||||
level_component_settings: "Opciones"
|
level_component_settings: "Opciones"
|
||||||
level_system_edit_title: "Editar Sistema"
|
level_system_edit_title: "Editar Sistema"
|
||||||
create_system_title: "Crear Nuevo Sistema"
|
create_system_title: "Crear Nuevo Sistema"
|
||||||
new_component_title: "Crear Nuevo Componente"
|
new_component_title: "Crear Nuevo Componente"
|
||||||
new_component_field_system: "Sistema"
|
new_component_field_system: "Sistema"
|
||||||
new_article_title: "Crear un Nuevo Artículo"
|
new_article_title: "Crear un Nuevo Artículo"
|
||||||
# new_thang_title: "Create a New Thang Type"
|
new_thang_title: "Crear un Nuevo tipo de Thang"
|
||||||
new_level_title: "Crear un Nuevo Nivel"
|
new_level_title: "Crear un Nuevo Nivel"
|
||||||
new_article_title_login: "Ingresa para Crear un Nuevo Artículo"
|
new_article_title_login: "Ingresa para Crear un Nuevo Artículo"
|
||||||
# new_thang_title_login: "Log In to Create a New Thang Type"
|
new_thang_title_login: "Ingresa para crear un nuevo tipo de Thang"
|
||||||
new_level_title_login: "Ingresa para Crear un Nuevo Nivel"
|
new_level_title_login: "Ingresa para Crear un Nuevo Nivel"
|
||||||
new_achievement_title: "Crear un Nuevo Logro"
|
new_achievement_title: "Crear un Nuevo Logro"
|
||||||
new_achievement_title_login: "Ingresa para Crear un Nuevo Logro"
|
new_achievement_title_login: "Ingresa para Crear un Nuevo Logro"
|
||||||
article_search_title: "Buscar Artículos aquí"
|
article_search_title: "Buscar Artículos aquí"
|
||||||
# thang_search_title: "Search Thang Types Here"
|
thang_search_title: "Buscar tipos de Thang aquí"
|
||||||
level_search_title: "Buscar Niveles aquí"
|
level_search_title: "Buscar Niveles aquí"
|
||||||
achievement_search_title: "Buscar logros"
|
achievement_search_title: "Buscar logros"
|
||||||
read_only_warning2: "Nota: no puedes guardar ediciones aquí, porque no estas logeado."
|
read_only_warning2: "Nota: no puedes guardar ediciones aquí, porque no estas logeado."
|
||||||
|
@ -663,24 +662,24 @@ module.exports = nativeDescription: "Español (América Latina)", englishDescrip
|
||||||
edit_btn_preview: "Vista previa"
|
edit_btn_preview: "Vista previa"
|
||||||
edit_article_title: "Editar Artículo"
|
edit_article_title: "Editar Artículo"
|
||||||
|
|
||||||
# contribute:
|
contribute:
|
||||||
# page_title: "Contributing"
|
page_title: "Contribuyendo"
|
||||||
# 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 es 100% open source! Cientos de jugadores dedicados nos han ayudado a contruir el juego. Únete y escribe el siguiente capítulo de la misión de CodeCombat de enseñar al mundo a programar!"
|
||||||
# alert_account_message_intro: "Hey there!"
|
alert_account_message_intro: "¡Hola!"
|
||||||
# alert_account_message: "To subscribe for class emails, you'll need to be logged in first."
|
alert_account_message: "Para suscribirte para los correos, necesitas ingresar primero."
|
||||||
# archmage_introduction: "One of the best parts about building games is they synthesize so many different things. Graphics, sound, real-time networking, social networking, and of course many of the more common aspects of programming, from low-level database management, and server administration to user facing design and interface building. There's a lot to do, and if you're an experienced programmer with a hankering to really dive into the nitty-gritty of CodeCombat, this class might be for you. We would love to have your help building the best programming game ever."
|
archmage_introduction: "Una de las mejores partes de hacer juegos es que sintetizan muchas cosas diferentes. Gráficas, sonido, redes, redes sociales y muchos aspectos comunes de programación, desde manejo de bases de datos y administración de servidores, hasta trabajar en el diseño y construcción de interfaces. Hay mucho para hacer, y si eres un programador con experiencia con el deseo de ingresar en el meollo del asunto de CodeCombat, esta clase puede ser para ti. Nos encantaría contar con tu ayuda para construir el mejor juego de programación."
|
||||||
# class_attributes: "Class Attributes"
|
class_attributes: "Atributos de Clase"
|
||||||
# archmage_attribute_1_pref: "Knowledge in "
|
archmage_attribute_1_pref: "Conocimiento en "
|
||||||
# archmage_attribute_1_suf: ", or a desire to learn. Most of our code is in this language. If you're a fan of Ruby or Python, you'll feel right at home. It's JavaScript, but with a nicer syntax."
|
archmage_attribute_1_suf: ", o un deseo de aprender. La mayor parte de nuestro código está en este lenguaje. Si eres un fan de Python o Ruby, te sentirás en casa. Es Javascript, pero con un mejor syntax."
|
||||||
# archmage_attribute_2: "Some experience in programming and personal initiative. We'll help you get oriented, but we can't spend much time training you."
|
archmage_attribute_2: "Alguna experiencia programando e iniciativa personal. Te ayudaremos a orientarte, pero no podemos perder mucho tiempo entrenando."
|
||||||
# how_to_join: "How To Join"
|
how_to_join: "Unirse:"
|
||||||
# join_desc_1: "Anyone can help out! Just check out our "
|
join_desc_1: "¡Cualquiera puede unirse! Sólo checa nuestro "
|
||||||
# join_desc_2: "to get started, and check the box below to mark yourself as a brave Archmage and get the latest news by email. Want to chat about what to do or how to get more deeply involved? "
|
join_desc_2: "para comenzar, y pon un check abajo para marcarte como un valiente Archimago y conseguir las últimas noticias por email. ¿Quieres chatear sobre qué hacer o cómo involucrarte más? "
|
||||||
# join_desc_3: ", or find us in our "
|
join_desc_3: ", o encuéntranos en "
|
||||||
# join_desc_4: "and we'll go from there!"
|
join_desc_4: "y ahí empezaremos!"
|
||||||
# join_url_email: "Email us"
|
join_url_email: "Escríbenos"
|
||||||
# join_url_hipchat: "public HipChat room"
|
join_url_hipchat: "chat público HipChat"
|
||||||
# archmage_subscribe_desc: "Get emails on new coding opportunities and announcements."
|
archmage_subscribe_desc: "Obten correos de nuevas oportunidades y anuncios."
|
||||||
# artisan_introduction_pref: "We must construct additional levels! People be clamoring for more content, and we can only build so many ourselves. Right now your workstation is level one; our level editor is barely usable even by its creators, so be wary. If you have visions of campaigns spanning for-loops to"
|
# artisan_introduction_pref: "We must construct additional levels! People be clamoring for more content, and we can only build so many ourselves. Right now your workstation is level one; our level editor is barely usable even by its creators, so be wary. If you have visions of campaigns spanning for-loops to"
|
||||||
# artisan_introduction_suf: ", then this class might be for you."
|
# artisan_introduction_suf: ", then this class might be for you."
|
||||||
# artisan_attribute_1: "Any experience in building content like this would be nice, such as using Blizzard's level editors. But not required!"
|
# artisan_attribute_1: "Any experience in building content like this would be nice, such as using Blizzard's level editors. But not required!"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
module.exports = nativeDescription: "Македонски", englishDescription: "Macedonian", translation:
|
module.exports = nativeDescription: "Македонски", englishDescription: "Macedonian", translation:
|
||||||
home:
|
home:
|
||||||
slogan: "Научи да Програмираш Преку Игра"
|
slogan: "Научи да програмираш преку игра"
|
||||||
no_ie: "CodeCombat не работи во Internet Explorer верзија 8 или постара. Извини!" # Warning that only shows up in IE8 and older
|
no_ie: "CodeCombat не работи во Internet Explorer верзија 8 или постара. Извини!" # Warning that only shows up in IE8 and older
|
||||||
no_mobile: "CodeCombat не е дизајнирана за мобилни уреди и може да не работи!" # Warning that shows up on mobile devices
|
no_mobile: "CodeCombat не е дизајнирана за мобилни уреди и може да не работи!" # Warning that shows up on mobile devices
|
||||||
play: "Играј" # The big play button that just starts playing a level
|
play: "Играј" # The big play button that just starts playing a level
|
||||||
|
@ -9,8 +9,8 @@ module.exports = nativeDescription: "Македонски", englishDescription:
|
||||||
old_browser_suffix: "Можеш да пробаш и покрај тоа, но најверојатно нема да работи."
|
old_browser_suffix: "Можеш да пробаш и покрај тоа, но најверојатно нема да работи."
|
||||||
ipad_browser: "Лоши вести: CodeCombat не работи во прелистувачот на iPad. Добри вести: Нашата апликација за iPad е готова и чека одобрение од Apple."
|
ipad_browser: "Лоши вести: CodeCombat не работи во прелистувачот на iPad. Добри вести: Нашата апликација за iPad е готова и чека одобрение од Apple."
|
||||||
campaign: "Кампања"
|
campaign: "Кампања"
|
||||||
for_beginners: "За Почетници"
|
for_beginners: "За почетници"
|
||||||
multiplayer: "Повеќе Играчи" # Not currently shown on home page
|
multiplayer: "Повеќе играчи" # Not currently shown on home page
|
||||||
for_developers: "За Developer-и" # Not currently shown on home page.
|
for_developers: "За Developer-и" # Not currently shown on home page.
|
||||||
or_ipad: "Или симни за iPad"
|
or_ipad: "Или симни за iPad"
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ module.exports = nativeDescription: "Македонски", englishDescription:
|
||||||
profile: "Профил"
|
profile: "Профил"
|
||||||
stats: "Статистики"
|
stats: "Статистики"
|
||||||
# code: "Code"
|
# code: "Code"
|
||||||
# admin: "Admin" # Only shows up when you are an admin
|
admin: "Админ" # Only shows up when you are an admin
|
||||||
home: "Дома"
|
home: "Дома"
|
||||||
contribute: "Допринеси"
|
contribute: "Допринеси"
|
||||||
legal: "Законски"
|
legal: "Законски"
|
||||||
|
@ -43,87 +43,87 @@ module.exports = nativeDescription: "Македонски", englishDescription:
|
||||||
diplomat_suggestion:
|
diplomat_suggestion:
|
||||||
title: "Помогни да се преведе CodeCombat!" # This shows up when a player switches to a non-English language using the language selector.
|
title: "Помогни да се преведе CodeCombat!" # This shows up when a player switches to a non-English language using the language selector.
|
||||||
sub_heading: "Ни требаат твоите јазични вештини."
|
sub_heading: "Ни требаат твоите јазични вештини."
|
||||||
pitch_body: "Ние ја развиваме CodeCombat на Англиски, но веќе имаме играчи низ целиот свет. Многу од нив сакаат да играат на Македонски, а не разбираат Англиски, па ако ги зборуваш и двата јазика, размисли дали би сакал/а да се зачлениш како Дипломат и да помогнеш да се преведат на Македонски CodeCombat веб сајтот и сите нивоа од играта."
|
pitch_body: "Ние ја развиваме CodeCombat на англиски, но веќе имаме играчи низ целиот свет. Многу од нив сакаат да играат на македонски, а не разбираат англиски, па ако ги зборуваш и двата јазика, размисли дали би сакал/а да се зачлениш како Дипломат и да помогнеш да се преведат на македонски CodeCombat веб сајтот и сите нивоа од играта."
|
||||||
missing_translations: "Додека не преведеме сè на Македонски, содржината ќе биде на Англиски каде што Македонскиот не е достапен."
|
missing_translations: "Додека не преведеме сè на македонски, содржината ќе биде на англиски каде што македонскиот не е достапен."
|
||||||
learn_more: "Научи повеќе за тоа како е да се биде Дипломат"
|
learn_more: "Научи повеќе за тоа како е да се биде Дипломат"
|
||||||
subscribe_as_diplomat: "Зачлени се како Дипломат"
|
subscribe_as_diplomat: "Зачлени се како Дипломат"
|
||||||
|
|
||||||
# play:
|
play:
|
||||||
# play_as: "Play As" # Ladder page
|
play_as: "Играј како" # Ladder page
|
||||||
# spectate: "Spectate" # Ladder page
|
spectate: "Набљудувај" # Ladder page
|
||||||
# players: "players" # Hover over a level on /play
|
players: "играчи" # Hover over a level on /play
|
||||||
# hours_played: "hours played" # Hover over a level on /play
|
hours_played: "часови изиграни" # Hover over a level on /play
|
||||||
# items: "Items" # Tooltip on item shop button from /play
|
items: "Предмети" # Tooltip on item shop button from /play
|
||||||
# unlock: "Unlock" # For purchasing items and heroes
|
unlock: "Отклучи" # For purchasing items and heroes
|
||||||
# confirm: "Confirm"
|
confirm: "Потврди"
|
||||||
# owned: "Owned" # For items you own
|
owned: "Во сопственост" # For items you own
|
||||||
# locked: "Locked"
|
locked: "Заклучено"
|
||||||
# purchasable: "Purchasable" # For a hero you unlocked but haven't purchased
|
purchasable: "Може да се купи" # For a hero you unlocked but haven't purchased
|
||||||
# available: "Available"
|
available: "Достапно"
|
||||||
# skills_granted: "Skills Granted" # Property documentation details
|
skills_granted: "Доделени вештини" # Property documentation details
|
||||||
# heroes: "Heroes" # Tooltip on hero shop button from /play
|
heroes: "Херои" # Tooltip on hero shop button from /play
|
||||||
# achievements: "Achievements" # Tooltip on achievement list button from /play
|
achievements: "Постигнувања" # Tooltip on achievement list button from /play
|
||||||
# account: "Account" # Tooltip on account button from /play
|
account: "Сметка" # Tooltip on account button from /play
|
||||||
# settings: "Settings" # Tooltip on settings button from /play
|
settings: "Поставки" # Tooltip on settings button from /play
|
||||||
# next: "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: "Change Hero" # Go back from choose inventory to choose hero
|
change_hero: "Смени херој" # Go back from choose inventory to choose hero
|
||||||
# choose_inventory: "Equip Items"
|
choose_inventory: "Опреми се"
|
||||||
# buy_gems: "Buy Gems"
|
buy_gems: "Купи скапоцени камења"
|
||||||
# campaign_desert: "Desert Campaign"
|
campaign_desert: "Пустинска кампања"
|
||||||
# campaign_forest: "Forest Campaign"
|
campaign_forest: "Шумска кампања"
|
||||||
# campaign_dungeon: "Dungeon Campaign"
|
campaign_dungeon: "Занданска кампања"
|
||||||
# subscription_required: "Subscription Required"
|
subscription_required: "Потребно е зачленување"
|
||||||
# free: "Free"
|
free: "Бесплатно"
|
||||||
# subscribed: "Subscribed"
|
subscribed: "Зачленет"
|
||||||
# older_campaigns: "Older Campaigns"
|
older_campaigns: "Постари кампањи"
|
||||||
# anonymous: "Anonymous Player"
|
anonymous: "Анонимен играч"
|
||||||
# level_difficulty: "Difficulty: "
|
level_difficulty: "Тешкотија: "
|
||||||
# campaign_beginner: "Beginner Campaign"
|
campaign_beginner: "Почетничка кампања"
|
||||||
# awaiting_levels_adventurer_prefix: "We release five levels per week."
|
awaiting_levels_adventurer_prefix: "Пуштаме пет нивоа неделно."
|
||||||
# awaiting_levels_adventurer: "Sign up as an Adventurer"
|
awaiting_levels_adventurer: "Зачлени се како Авантурист"
|
||||||
# awaiting_levels_adventurer_suffix: "to be the first to play new levels."
|
awaiting_levels_adventurer_suffix: "за да бидеш првиот кој ќе ги игра новите нивоа."
|
||||||
# choose_your_level: "Choose Your Level" # The rest of this section is the old play view at /play-old and isn't very important.
|
choose_your_level: "Избери го твоето ниво" # The rest of this section is the old play view at /play-old and isn't very important.
|
||||||
# adventurer_prefix: "You can jump to any level below, or discuss the levels on "
|
adventurer_prefix: "Можеш да отидеш на било кое од подолните нивоа, или да дискутираш за нивоата на "
|
||||||
# adventurer_forum: "the Adventurer forum"
|
adventurer_forum: "форумот на Авантуристите"
|
||||||
# adventurer_suffix: "."
|
adventurer_suffix: "."
|
||||||
# campaign_old_beginner: "Old Beginner Campaign"
|
campaign_old_beginner: "Стара почетничка кампања"
|
||||||
# campaign_old_beginner_description: "... in which you learn the wizardry of programming."
|
campaign_old_beginner_description: "... во која учиш за волшепството на програмирањето."
|
||||||
# campaign_dev: "Random Harder Levels"
|
campaign_dev: "Призволни потешки нивоа"
|
||||||
# campaign_dev_description: "... in which you learn the interface while doing something a little harder."
|
campaign_dev_description: "... во кои го учиш интерфејсот додека правиш нешто малку потешко."
|
||||||
# campaign_multiplayer: "Multiplayer Arenas"
|
campaign_multiplayer: "Арени за повеќе играчи"
|
||||||
# campaign_multiplayer_description: "... in which you code head-to-head against other players."
|
campaign_multiplayer_description: "... во кои кодираш лице-во-лице против други играчи."
|
||||||
# campaign_player_created: "Player-Created"
|
campaign_player_created: "Направено од играчи"
|
||||||
# campaign_player_created_description: "... in which you battle against the creativity of your fellow <a href=\"/contribute/artisan\">Artisan Wizards</a>."
|
campaign_player_created_description: "... се бориш наспроти креативноста на останатите играчи од <a href=\"/contribute/artisan\">Волшебничкиот занает</a>."
|
||||||
# campaign_classic_algorithms: "Classic Algorithms"
|
campaign_classic_algorithms: "Класични алгоритми"
|
||||||
# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science."
|
campaign_classic_algorithms_description: "... во кои ги учиш најпопуларните алгоритми во компјутерската наука."
|
||||||
|
|
||||||
login:
|
login:
|
||||||
sign_up: "Направи Сметка"
|
sign_up: "Направи сметка"
|
||||||
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+ Email"
|
load_email: "Вчитај G+ e-mail"
|
||||||
finishing: "Завршување"
|
finishing: "Завршување"
|
||||||
sign_in_with_facebook: "Најави се со Facebook"
|
sign_in_with_facebook: "Најави се со Facebook"
|
||||||
sign_in_with_gplus: "Најави се со G+"
|
sign_in_with_gplus: "Најави се со G+"
|
||||||
signup_switch: "Сакаш да направиш сметка?"
|
signup_switch: "Сакаш да направиш сметка?"
|
||||||
|
|
||||||
signup:
|
signup:
|
||||||
email_announcements: "Примај соопштенија преку email"
|
email_announcements: "Примај соопштенија преку e-mail"
|
||||||
creating: "Сметката се прави..."
|
creating: "Сметката се прави..."
|
||||||
sign_up: "Направи Сметка"
|
sign_up: "Направи сметка"
|
||||||
log_in: "најави се со лозинка"
|
log_in: "најави се со лозинка"
|
||||||
social_signup: "Или, можеш да се пријавиш преку Facebook или G+:"
|
social_signup: "Или, можеш да се пријавиш преку Facebook или G+:"
|
||||||
required: "Мораш да се најавиш за да имаш пристап таму."
|
required: "Мораш да се најавиш за да имаш пристап таму."
|
||||||
login_switch: "Веќе имаш сметка?"
|
login_switch: "Веќе имаш сметка?"
|
||||||
|
|
||||||
recover:
|
recover:
|
||||||
recover_account_title: "Врати Сметка"
|
recover_account_title: "Врати сметка"
|
||||||
send_password: "Испрати лозинка за враќање"
|
send_password: "Испрати лозинка за враќање"
|
||||||
recovery_sent: "Email-от за враќање на лозинката е испратен."
|
recovery_sent: "E-mail-от за враќање на лозинката е испратен."
|
||||||
|
|
||||||
items:
|
items:
|
||||||
primary: "Главно"
|
primary: "Главно"
|
||||||
|
@ -151,37 +151,37 @@ module.exports = nativeDescription: "Македонски", englishDescription:
|
||||||
help: "Помош"
|
help: "Помош"
|
||||||
# watch: "Watch"
|
# watch: "Watch"
|
||||||
# unwatch: "Unwatch"
|
# unwatch: "Unwatch"
|
||||||
submit_patch: "Поднеси Закрпа"
|
submit_patch: "Поднеси закрпа"
|
||||||
submit_changes: "Поднеси Промени"
|
submit_changes: "Поднеси промени"
|
||||||
|
|
||||||
# general:
|
general:
|
||||||
# and: "and"
|
and: "и"
|
||||||
# name: "Name"
|
name: "Име"
|
||||||
# date: "Date"
|
date: "Датум"
|
||||||
# body: "Body"
|
body: "Тело" # Original was 'Body'. Not sure if this is the best translation.
|
||||||
# version: "Version"
|
version: "Верзија"
|
||||||
# submitter: "Submitter"
|
submitter: "Подносител"
|
||||||
# submitted: "Submitted"
|
submitted: "Поднесено"
|
||||||
# commit_msg: "Commit Message"
|
# commit_msg: "Commit Message"
|
||||||
# review: "Review"
|
# review: "Review"
|
||||||
# version_history: "Version History"
|
version_history: "Историја на верзии"
|
||||||
# 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: "Undo (Ctrl+Z)"
|
||||||
# redo: "Redo (Ctrl+Shift+Z)"
|
# redo: "Redo (Ctrl+Shift+Z)"
|
||||||
# play_preview: "Play preview of current level"
|
# play_preview: "Play preview of current level"
|
||||||
# result: "Result"
|
result: "Резултат"
|
||||||
# results: "Results"
|
results: "Резултати"
|
||||||
# description: "Description"
|
description: "Опис"
|
||||||
# or: "or"
|
or: "или"
|
||||||
# subject: "Subject"
|
subject: "Предмет на пораката" # Original: 'Subject'. Translated as 'Message subject' because in macedonian the word 'Предмет' can have different meaning.
|
||||||
# email: "Email"
|
email: "E-mail"
|
||||||
# password: "Password"
|
password: "Лозинка"
|
||||||
# message: "Message"
|
message: "Порака"
|
||||||
# code: "Code"
|
# code: "Code"
|
||||||
# ladder: "Ladder"
|
# ladder: "Ladder"
|
||||||
# when: "When"
|
when: "Кога"
|
||||||
# opponent: "Opponent"
|
opponent: "Противник"
|
||||||
# rank: "Rank"
|
# rank: "Rank"
|
||||||
# score: "Score"
|
# score: "Score"
|
||||||
# win: "Win"
|
# win: "Win"
|
||||||
|
@ -190,7 +190,7 @@ module.exports = nativeDescription: "Македонски", englishDescription:
|
||||||
# easy: "Easy"
|
# easy: "Easy"
|
||||||
# medium: "Medium"
|
# medium: "Medium"
|
||||||
# hard: "Hard"
|
# hard: "Hard"
|
||||||
# player: "Player"
|
player: "Играч"
|
||||||
# player_level: "Level" # Like player level 5, not like level: Dungeons of Kithgard
|
# player_level: "Level" # Like player level 5, not like level: Dungeons of Kithgard
|
||||||
|
|
||||||
units:
|
units:
|
||||||
|
@ -209,45 +209,45 @@ module.exports = nativeDescription: "Македонски", englishDescription:
|
||||||
year: "година"
|
year: "година"
|
||||||
years: "години"
|
years: "години"
|
||||||
|
|
||||||
# play_level:
|
play_level:
|
||||||
# done: "Done"
|
done: "Готово"
|
||||||
# home: "Home" # Not used any more, will be removed soon.
|
home: "Дома" # Not used any more, will be removed soon.
|
||||||
# level: "Level" # Like "Level: Dungeons of Kithgard"
|
level: "Ниво" # Like "Level: Dungeons of Kithgard"
|
||||||
# skip: "Skip"
|
skip: "Прескокни"
|
||||||
# game_menu: "Game Menu"
|
game_menu: "Мени"
|
||||||
# guide: "Guide"
|
guide: "Водич"
|
||||||
# restart: "Restart"
|
# restart: "Restart"
|
||||||
# goals: "Goals"
|
goals: "Цели"
|
||||||
# goal: "Goal"
|
goal: "Цел"
|
||||||
# running: "Running..."
|
# running: "Running..."
|
||||||
# success: "Success!"
|
success: "Успешно!"
|
||||||
# incomplete: "Incomplete"
|
incomplete: "Некомплетирано"
|
||||||
# timed_out: "Ran out of time"
|
timed_out: "Истече времето"
|
||||||
# failing: "Failing"
|
# failing: "Failing"
|
||||||
# action_timeline: "Action Timeline"
|
# action_timeline: "Action Timeline"
|
||||||
# click_to_select: "Click on a unit to select it."
|
# click_to_select: "Click on a unit to select it."
|
||||||
# control_bar_multiplayer: "Multiplayer"
|
control_bar_multiplayer: "Повеќе играчи"
|
||||||
# control_bar_join_game: "Join Game"
|
control_bar_join_game: "Приклучи се во игра"
|
||||||
# reload: "Reload"
|
# reload: "Reload"
|
||||||
# reload_title: "Reload All Code?"
|
# reload_title: "Reload All Code?"
|
||||||
# reload_really: "Are you sure you want to reload this level back to the beginning?"
|
# reload_really: "Are you sure you want to reload this level back to the beginning?"
|
||||||
# reload_confirm: "Reload All"
|
# reload_confirm: "Reload All"
|
||||||
# victory: "Victory"
|
victory: "Победа"
|
||||||
# victory_title_prefix: ""
|
# victory_title_prefix: ""
|
||||||
# victory_title_suffix: " Complete"
|
# victory_title_suffix: " Complete"
|
||||||
# victory_sign_up: "Sign Up to Save Progress"
|
victory_sign_up: "Направи сметка за да го зачуваш напредокот"
|
||||||
# victory_sign_up_poke: "Want to save your code? Create a free account!"
|
victory_sign_up_poke: "Сакаш да го зачуваш твојот код? Направи бесплатна сметка!"
|
||||||
# victory_rate_the_level: "Rate the level: " # Only in old-style levels.
|
victory_rate_the_level: "Оцени го нивото: " # Only in old-style levels.
|
||||||
# victory_return_to_ladder: "Return to Ladder"
|
# victory_return_to_ladder: "Return to Ladder"
|
||||||
# victory_play_continue: "Continue"
|
victory_play_continue: "Продолжи"
|
||||||
# victory_saving_progress: "Saving Progress"
|
victory_saving_progress: "Напредокот се зачувува"
|
||||||
# victory_go_home: "Go Home" # Only in old-style levels.
|
victory_go_home: "Оди дома" # Only in old-style levels.
|
||||||
# victory_review: "Tell us more!" # Only in old-style levels.
|
victory_review: "Кажи ни повеќе!" # Only in old-style levels.
|
||||||
# victory_hour_of_code_done: "Are You Done?"
|
victory_hour_of_code_done: "Дали си готов?"
|
||||||
# victory_hour_of_code_done_yes: "Yes, I'm finished with my Hour of Code™!"
|
# victory_hour_of_code_done_yes: "Yes, I'm finished with my Hour of Code™!"
|
||||||
# victory_experience_gained: "XP Gained"
|
victory_experience_gained: "Добиено искуство"
|
||||||
# victory_gems_gained: "Gems Gained"
|
victory_gems_gained: "Добиени скапоцени камења"
|
||||||
# guide_title: "Guide"
|
guide_title: "Водич"
|
||||||
# tome_minion_spells: "Your Minions' Spells" # Only in old-style levels.
|
# tome_minion_spells: "Your Minions' Spells" # Only in old-style levels.
|
||||||
# tome_read_only_spells: "Read-Only Spells" # Only in old-style levels.
|
# tome_read_only_spells: "Read-Only Spells" # Only in old-style levels.
|
||||||
# tome_other_units: "Other Units" # Only in old-style levels.
|
# tome_other_units: "Other Units" # Only in old-style levels.
|
||||||
|
@ -326,16 +326,16 @@ module.exports = nativeDescription: "Македонски", englishDescription:
|
||||||
# multiplayer_caption: "Play with friends!"
|
# multiplayer_caption: "Play with friends!"
|
||||||
# auth_caption: "Save your progress."
|
# auth_caption: "Save your progress."
|
||||||
|
|
||||||
# inventory:
|
inventory:
|
||||||
# choose_inventory: "Equip Items"
|
# choose_inventory: "Equip Items"
|
||||||
# equipped_item: "Equipped"
|
# equipped_item: "Equipped"
|
||||||
# required_purchase_title: "Required"
|
required_purchase_title: "Задолжително"
|
||||||
# available_item: "Available"
|
available_item: "Достапно"
|
||||||
# restricted_title: "Restricted"
|
restricted_title: "Забрането"
|
||||||
# should_equip: "(double-click to equip)"
|
# should_equip: "(double-click to equip)"
|
||||||
# equipped: "(equipped)"
|
# equipped: "(equipped)"
|
||||||
# locked: "(locked)"
|
locked: "(заклучено)"
|
||||||
# restricted: "(restricted in this level)"
|
restricted: "(забрането во ова ниво)"
|
||||||
# equip: "Equip"
|
# equip: "Equip"
|
||||||
# unequip: "Unequip"
|
# unequip: "Unequip"
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,8 @@ module.exports = class LevelComponent extends CocoModel
|
||||||
@PlansID: '524b7b517fc0f6d51900000d'
|
@PlansID: '524b7b517fc0f6d51900000d'
|
||||||
@ProgrammableID: '524b7b5a7fc0f6d51900000e'
|
@ProgrammableID: '524b7b5a7fc0f6d51900000e'
|
||||||
@MovesID: '524b7b8c7fc0f6d519000013'
|
@MovesID: '524b7b8c7fc0f6d519000013'
|
||||||
|
@MissileID: '524cc2593ea855e0ab000142'
|
||||||
|
@FindsPaths: '52872b0ead92b98561000002'
|
||||||
urlRoot: '/db/level.component'
|
urlRoot: '/db/level.component'
|
||||||
|
|
||||||
set: (key, val, options) ->
|
set: (key, val, options) ->
|
||||||
|
|
|
@ -16,7 +16,7 @@ defaultTasks = [
|
||||||
'Choose music file in Introduction script.'
|
'Choose music file in Introduction script.'
|
||||||
|
|
||||||
'Add to a campaign.'
|
'Add to a campaign.'
|
||||||
'Publish for playtesting.'
|
'Publish.'
|
||||||
'Choose level options like required/restricted gear.'
|
'Choose level options like required/restricted gear.'
|
||||||
'Create achievements, including unlocking next level.'
|
'Create achievements, including unlocking next level.'
|
||||||
|
|
||||||
|
@ -25,17 +25,17 @@ defaultTasks = [
|
||||||
'Playtest with a couple random seeds.'
|
'Playtest with a couple random seeds.'
|
||||||
'Make sure the level ends promptly on success and failure.'
|
'Make sure the level ends promptly on success and failure.'
|
||||||
'Remove/simplify unnecessary doodad collision.'
|
'Remove/simplify unnecessary doodad collision.'
|
||||||
'Release to adventurers.'
|
'Release to adventurers via MailChimp.'
|
||||||
|
|
||||||
'Write the description.'
|
'Write the description.'
|
||||||
'Translate the sample code comments.'
|
'Translate the sample code comments.'
|
||||||
'Add Io/Clojure/Lua/CoffeeScript.'
|
'Add Io/Clojure/Lua/CoffeeScript.'
|
||||||
'Write the guide.'
|
'Write the guide.'
|
||||||
'Write a loading tip, if needed.'
|
'Write a loading tip, if needed.'
|
||||||
'Populate i18n.'
|
'Click the Populate i18n button.'
|
||||||
|
|
||||||
'Mark whether it requires a subscription (after adventurer week).'
|
'Mark whether it requires a subscription (after adventurer week).'
|
||||||
'Release to everyone.'
|
'Release to everyone via MailChimp.'
|
||||||
|
|
||||||
'Check completion/engagement/problem analytics.'
|
'Check completion/engagement/problem analytics.'
|
||||||
'Do any custom scripting, if needed.'
|
'Do any custom scripting, if needed.'
|
||||||
|
|
|
@ -168,6 +168,8 @@ _.extend ThangTypeSchema.properties,
|
||||||
i18n: {type: 'object', format: 'i18n', props: ['name', 'description', 'extendedName', 'unlockLevelName'], description: 'Help translate this ThangType\'s name and description.'}
|
i18n: {type: 'object', format: 'i18n', props: ['name', 'description', 'extendedName', 'unlockLevelName'], description: 'Help translate this ThangType\'s name and description.'}
|
||||||
extendedName: {type: 'string', title: 'Extended Hero Name', description: 'The long form of the hero\'s name. Ex.: "Captain Anya Weston".'}
|
extendedName: {type: 'string', title: 'Extended Hero Name', description: 'The long form of the hero\'s name. Ex.: "Captain Anya Weston".'}
|
||||||
unlockLevelName: {type: 'string', title: 'Unlock Level Name', description: 'The name of the level in which the hero is unlocked.'}
|
unlockLevelName: {type: 'string', title: 'Unlock Level Name', description: 'The name of the level in which the hero is unlocked.'}
|
||||||
|
tasks: c.array {title: 'Tasks', description: 'Tasks to be completed for this ThangType.'}, c.task
|
||||||
|
|
||||||
|
|
||||||
ThangTypeSchema.required = []
|
ThangTypeSchema.required = []
|
||||||
|
|
||||||
|
|
|
@ -21,3 +21,12 @@
|
||||||
bottom: 0
|
bottom: 0
|
||||||
right: 0
|
right: 0
|
||||||
width: 75%
|
width: 75%
|
||||||
|
|
||||||
|
#analytics-button
|
||||||
|
position: absolute
|
||||||
|
right: 1%
|
||||||
|
top: 1%
|
||||||
|
padding: 3px 8px
|
||||||
|
|
||||||
|
#analytics-modal .modal-content
|
||||||
|
background-color: white
|
||||||
|
|
|
@ -11,7 +11,7 @@ block modal-body-content
|
||||||
p(data-i18n="diplomat_suggestion.missing_translations") Until we can translate everything into {English}, you'll see English when {English} isn't available.
|
p(data-i18n="diplomat_suggestion.missing_translations") Until we can translate everything into {English}, you'll see English when {English} isn't available.
|
||||||
|
|
||||||
p
|
p
|
||||||
a(href="/contribute#diplomat", data-i18n="diplomat_suggestion.learn_more") Learn more about being a Diplomat
|
a(href="/contribute/diplomat", data-i18n="diplomat_suggestion.learn_more") Learn more about being a Diplomat
|
||||||
|
|
||||||
block modal-footer-content
|
block modal-footer-content
|
||||||
button.btn.btn-primary.btn-large#subscribe-button(data-i18n="diplomat_suggestion.subscribe_as_diplomat") Subscribe as a Diplomat
|
button.btn.btn-primary.btn-large#subscribe-button(data-i18n="diplomat_suggestion.subscribe_as_diplomat") Subscribe as a Diplomat
|
||||||
|
|
|
@ -40,5 +40,40 @@ block outer_content
|
||||||
#right-column
|
#right-column
|
||||||
#campaign-view
|
#campaign-view
|
||||||
#campaign-level-view.hidden
|
#campaign-level-view.hidden
|
||||||
|
if campaignDropOffs
|
||||||
|
button.btn.btn-default#analytics-button(title="Analytics", data-toggle="modal" data-target="#analytics-modal") Analytics
|
||||||
|
.modal.fade#analytics-modal(tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true")
|
||||||
|
.modal-dialog
|
||||||
|
.modal-content
|
||||||
|
.modal-header
|
||||||
|
button.close(type="button", data-dismiss="modal", aria-label="Close")
|
||||||
|
span(aria-hidden="true") ×
|
||||||
|
h4.modal-title Analytics
|
||||||
|
.modal-body
|
||||||
|
if campaignDropOffs.startDay
|
||||||
|
if campaignDropOffs.endDay
|
||||||
|
div #{campaignDropOffs.startDay} to #{campaignDropOffs.endDay}
|
||||||
|
else
|
||||||
|
div #{campaignDropOffs.startDay} to today
|
||||||
|
table.table.table-bordered.table-condensed.table-hover(style='font-size:10pt')
|
||||||
|
thead
|
||||||
|
tr
|
||||||
|
td Level
|
||||||
|
td Started
|
||||||
|
td Dropped
|
||||||
|
td Drop %
|
||||||
|
td Finished
|
||||||
|
td Dropped
|
||||||
|
td Drop %
|
||||||
|
tbody
|
||||||
|
- for (var i = 0; i < campaignDropOffs.levels.length; i++)
|
||||||
|
tr
|
||||||
|
td= campaignDropOffs.levels[i].level
|
||||||
|
td= campaignDropOffs.levels[i].started
|
||||||
|
td= campaignDropOffs.levels[i].startDropped
|
||||||
|
td= campaignDropOffs.levels[i].startDropRate
|
||||||
|
td= campaignDropOffs.levels[i].finished
|
||||||
|
td= campaignDropOffs.levels[i].finishDropped
|
||||||
|
td= campaignDropOffs.levels[i].finishDropRate
|
||||||
|
|
||||||
block footer
|
block footer
|
||||||
|
|
|
@ -5,8 +5,42 @@
|
||||||
a(href="/editor/level/#{level.get('slug')}", target="_blank") (edit)
|
a(href="/editor/level/#{level.get('slug')}", target="_blank") (edit)
|
||||||
p= level.get('description')
|
p= level.get('description')
|
||||||
|
|
||||||
h2 TODO: actually put useful stuff in here
|
h4 Completion Rates
|
||||||
|
if levelCompletions
|
||||||
|
table.table.table-bordered.table-condensed.table-hover(style='font-size:10pt')
|
||||||
|
thead
|
||||||
|
tr
|
||||||
|
td Date
|
||||||
|
td Started
|
||||||
|
td Finished
|
||||||
|
td Completion %
|
||||||
|
tbody
|
||||||
|
- for (var i = 0; i < levelCompletions.length; i++)
|
||||||
|
tr
|
||||||
|
td= levelCompletions[i].created
|
||||||
|
td= levelCompletions[i].started
|
||||||
|
td= levelCompletions[i].finished
|
||||||
|
td= levelCompletions[i].rate
|
||||||
|
else
|
||||||
|
div Loading...
|
||||||
|
|
||||||
|
h4 Average Playtimes
|
||||||
|
if levelPlaytimes
|
||||||
|
table.table.table-bordered.table-condensed.table-hover(style='font-size:10pt')
|
||||||
|
thead
|
||||||
|
tr
|
||||||
|
td Date
|
||||||
|
td Average (s)
|
||||||
|
tbody
|
||||||
|
- for (var i = 0; i < levelPlaytimes.length; i++)
|
||||||
|
tr
|
||||||
|
td= levelPlaytimes[i].created
|
||||||
|
td= levelPlaytimes[i].average.toFixed(2)
|
||||||
|
else
|
||||||
|
div Loading...
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if level.get('tasks')
|
if level.get('tasks')
|
||||||
.tasks
|
.tasks
|
||||||
h3 Tasks (read only)
|
h3 Tasks (read only)
|
||||||
|
|
|
@ -14,6 +14,7 @@ block tableHeader
|
||||||
th(data-i18n="general.name") Name
|
th(data-i18n="general.name") Name
|
||||||
th(data-i18n="general.description") Description
|
th(data-i18n="general.description") Description
|
||||||
th(data-i18n="general.version") Version
|
th(data-i18n="general.version") Version
|
||||||
|
th(data-i18n="editor.tasks") Tasks
|
||||||
|
|
||||||
block tableBody
|
block tableBody
|
||||||
for thang in documents
|
for thang in documents
|
||||||
|
@ -25,4 +26,10 @@ block tableBody
|
||||||
| #{thang.get('name')}
|
| #{thang.get('name')}
|
||||||
td.body-row #{thang.get('description')}
|
td.body-row #{thang.get('description')}
|
||||||
- var version = thang.get('version')
|
- var version = thang.get('version')
|
||||||
td #{version.major}.#{version.minor}
|
td #{version.major}.#{version.minor}
|
||||||
|
- var tasks = thang.get('tasks');
|
||||||
|
if tasks && tasks.length
|
||||||
|
- var completed = tasks.filter(function(t) { return t.complete; });
|
||||||
|
td #{completed.length}/#{tasks.length}
|
||||||
|
else
|
||||||
|
td
|
||||||
|
|
|
@ -24,4 +24,4 @@ block content
|
||||||
span.spl.spr - #{playCount.sessions}
|
span.spl.spr - #{playCount.sessions}
|
||||||
span(data-i18n="play.players") players
|
span(data-i18n="play.players") players
|
||||||
.play-text-container
|
.play-text-container
|
||||||
.overlay-text.play-text= playText
|
.overlay-text.play-text(data-i18n="home.play") Play
|
||||||
|
|
|
@ -32,9 +32,6 @@ module.exports = class HomeView extends RootView
|
||||||
c.explainsHourOfCode = @explainsHourOfCode
|
c.explainsHourOfCode = @explainsHourOfCode
|
||||||
c.isMobile = @isMobile()
|
c.isMobile = @isMobile()
|
||||||
c.isIPadBrowser = @isIPadBrowser()
|
c.isIPadBrowser = @isIPadBrowser()
|
||||||
c.playText = $.i18n.t('home.try_it', false)
|
|
||||||
if c.playText is 'home.try_it'
|
|
||||||
c.playText = $.i18n.t 'home.play' # Temporary fallback for not having many try_it translations yet.
|
|
||||||
c
|
c
|
||||||
|
|
||||||
onClickBeginnerCampaign: (e) ->
|
onClickBeginnerCampaign: (e) ->
|
||||||
|
|
|
@ -6,7 +6,7 @@ app = require 'core/application'
|
||||||
class SearchCollection extends Backbone.Collection
|
class SearchCollection extends Backbone.Collection
|
||||||
initialize: (modelURL, @model, @term, @projection) ->
|
initialize: (modelURL, @model, @term, @projection) ->
|
||||||
@url = "#{modelURL}?project="
|
@url = "#{modelURL}?project="
|
||||||
if @projection? and not (@projection == [])
|
if @projection?.length
|
||||||
@url += 'created,permissions'
|
@url += 'created,permissions'
|
||||||
@url += ',' + projected for projected in projection
|
@url += ',' + projected for projected in projection
|
||||||
else @url += 'true'
|
else @url += 'true'
|
||||||
|
|
|
@ -47,6 +47,8 @@ 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
|
||||||
|
|
||||||
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.
|
||||||
originals = []
|
originals = []
|
||||||
|
@ -130,6 +132,7 @@ module.exports = class CampaignEditorView extends RootView
|
||||||
getRenderData: ->
|
getRenderData: ->
|
||||||
c = super()
|
c = super()
|
||||||
c.campaign = @campaign
|
c.campaign = @campaign
|
||||||
|
c.campaignDropOffs = @campaignDropOffs
|
||||||
c
|
c
|
||||||
|
|
||||||
onClickSaveButton: ->
|
onClickSaveButton: ->
|
||||||
|
@ -236,6 +239,35 @@ module.exports = class CampaignEditorView extends RootView
|
||||||
achievement.set 'rewards', newRewards
|
achievement.set 'rewards', newRewards
|
||||||
if achievement.hasLocalChanges()
|
if achievement.hasLocalChanges()
|
||||||
@toSave.add achievement
|
@toSave.add achievement
|
||||||
|
|
||||||
|
getCampaignDropOffs: =>
|
||||||
|
# Fetch last 7 days of campaign drop-off rates
|
||||||
|
|
||||||
|
startDay = new Date()
|
||||||
|
startDay.setDate(startDay.getUTCDate() - 6)
|
||||||
|
startDay = startDay.getUTCFullYear() + '-' + (startDay.getUTCMonth() + 1) + '-' + startDay.getUTCDate()
|
||||||
|
|
||||||
|
success = (data) =>
|
||||||
|
return if @destroyed
|
||||||
|
# API returns all the campaign data currently
|
||||||
|
@campaignDropOffs = data[@campaignHandle]
|
||||||
|
mapFn = (item) ->
|
||||||
|
item.startDropRate = (item.startDropped / item.started * 100).toFixed(2)
|
||||||
|
item.finishDropRate = (item.finishDropped / item.finished * 100).toFixed(2)
|
||||||
|
item
|
||||||
|
@campaignDropOffs.levels = _.map @campaignDropOffs.levels, mapFn, @
|
||||||
|
@campaignDropOffs.startDay = startDay
|
||||||
|
@render()
|
||||||
|
|
||||||
|
# TODO: Why do we need this url dash?
|
||||||
|
request = @supermodel.addRequestResource 'campaign_drop_offs', {
|
||||||
|
url: '/db/analytics_log_event/-/campaign_drop_offs'
|
||||||
|
data: {startDay: startDay, slugs: [@campaignHandle]}
|
||||||
|
method: 'POST'
|
||||||
|
success: success
|
||||||
|
}, 0
|
||||||
|
request.load()
|
||||||
|
|
||||||
|
|
||||||
class LevelsNode extends TreemaObjectNode
|
class LevelsNode extends TreemaObjectNode
|
||||||
valueClass: 'treema-levels'
|
valueClass: 'treema-levels'
|
||||||
|
|
|
@ -14,11 +14,61 @@ module.exports = class CampaignLevelView extends CocoView
|
||||||
@fullLevel.fetch()
|
@fullLevel.fetch()
|
||||||
@listenToOnce @fullLevel, 'sync', => @render?()
|
@listenToOnce @fullLevel, 'sync', => @render?()
|
||||||
|
|
||||||
|
@levelSlug = @level.get('slug')
|
||||||
|
@getLevelCompletions()
|
||||||
|
@getLevelPlaytimes()
|
||||||
|
|
||||||
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.levelCompletions = @levelCompletions
|
||||||
|
c.levelPlaytimes = @levelPlaytimes
|
||||||
c
|
c
|
||||||
|
|
||||||
onClickClose: ->
|
onClickClose: ->
|
||||||
@$el.addClass('hidden')
|
@$el.addClass('hidden')
|
||||||
@trigger 'hidden'
|
@trigger 'hidden'
|
||||||
|
|
||||||
|
getLevelCompletions: ->
|
||||||
|
# Fetch last 7 days of level completion counts
|
||||||
|
success = (data) =>
|
||||||
|
return if @destroyed
|
||||||
|
data.sort (a, b) -> if a.created < b.created then 1 else -1
|
||||||
|
mapFn = (item) ->
|
||||||
|
item.rate = (item.finished / item.started * 100).toFixed(2)
|
||||||
|
item
|
||||||
|
@levelCompletions = _.map data, mapFn, @
|
||||||
|
@render()
|
||||||
|
|
||||||
|
startDay = new Date()
|
||||||
|
startDay.setDate(startDay.getUTCDate() - 6)
|
||||||
|
startDay = startDay.getUTCFullYear() + '-' + (startDay.getUTCMonth() + 1) + '-' + startDay.getUTCDate()
|
||||||
|
|
||||||
|
# TODO: Why do we need this url dash?
|
||||||
|
request = @supermodel.addRequestResource 'level_completions', {
|
||||||
|
url: '/db/analytics_log_event/-/level_completions'
|
||||||
|
data: {startDay: startDay, slug: @levelSlug}
|
||||||
|
method: 'POST'
|
||||||
|
success: success
|
||||||
|
}, 0
|
||||||
|
request.load()
|
||||||
|
|
||||||
|
getLevelPlaytimes: ->
|
||||||
|
# Fetch last 7 days of level average playtimes
|
||||||
|
success = (data) =>
|
||||||
|
return if @destroyed
|
||||||
|
@levelPlaytimes = data.sort (a, b) -> if a.created < b.created then 1 else -1
|
||||||
|
@render()
|
||||||
|
|
||||||
|
startDay = new Date()
|
||||||
|
startDay.setDate(startDay.getUTCDate() - 6)
|
||||||
|
startDay = startDay.getUTCFullYear() + '-' + (startDay.getUTCMonth() + 1) + '-' + startDay.getUTCDate()
|
||||||
|
|
||||||
|
# TODO: Why do we need this url dash?
|
||||||
|
request = @supermodel.addRequestResource 'playtime_averages', {
|
||||||
|
url: '/db/level/-/playtime_averages'
|
||||||
|
data: {startDay: startDay, slugs: [@levelSlug]}
|
||||||
|
method: 'POST'
|
||||||
|
success: success
|
||||||
|
}, 0
|
||||||
|
request.load()
|
||||||
|
|
|
@ -15,8 +15,8 @@ CocoCollection = require 'collections/CocoCollection'
|
||||||
|
|
||||||
LC = (componentName, config) -> original: LevelComponent[componentName + 'ID'], majorVersion: 0, config: config
|
LC = (componentName, config) -> original: LevelComponent[componentName + 'ID'], majorVersion: 0, config: config
|
||||||
DEFAULT_COMPONENTS =
|
DEFAULT_COMPONENTS =
|
||||||
Unit: [LC('Equips')]
|
Unit: [LC('Equips'), LC('FindsPaths')]
|
||||||
Hero: [LC('Equips')]
|
Hero: [LC('Equips'), LC('FindsPaths')]
|
||||||
Floor: [
|
Floor: [
|
||||||
LC('Exists', stateless: true)
|
LC('Exists', stateless: true)
|
||||||
LC('Physical', width: 20, height: 17, depth: 2, shape: 'sheet', pos: {x: 10, y: 8.5, z: 1})
|
LC('Physical', width: 20, height: 17, depth: 2, shape: 'sheet', pos: {x: 10, y: 8.5, z: 1})
|
||||||
|
@ -35,6 +35,7 @@ DEFAULT_COMPONENTS =
|
||||||
Misc: [LC('Exists'), LC('Physical')]
|
Misc: [LC('Exists'), LC('Physical')]
|
||||||
Mark: []
|
Mark: []
|
||||||
Item: [LC('Item')]
|
Item: [LC('Item')]
|
||||||
|
Missile: [LC('Missile')]
|
||||||
|
|
||||||
module.exports = class ThangComponentsEditView extends CocoView
|
module.exports = class ThangComponentsEditView extends CocoView
|
||||||
id: 'thang-components-edit-view'
|
id: 'thang-components-edit-view'
|
||||||
|
|
|
@ -23,6 +23,106 @@ storage = require 'core/storage'
|
||||||
|
|
||||||
CENTER = {x: 200, y: 300}
|
CENTER = {x: 200, y: 300}
|
||||||
|
|
||||||
|
commonTasks = [
|
||||||
|
'Upload the art.'
|
||||||
|
'Set up the vector icon.'
|
||||||
|
]
|
||||||
|
|
||||||
|
displayedThangTypeTasks = [
|
||||||
|
'Configure the idle action.'
|
||||||
|
'Configure the positions (registration point, etc.).'
|
||||||
|
'Set shadow diameter to 0 if needed.'
|
||||||
|
'Set scale to 0.3, 0.5, or whatever is appropriate.'
|
||||||
|
'Set rotation to isometric if needed.'
|
||||||
|
'Set accurate Physical size, shape, and default z.'
|
||||||
|
'Set accurate Collides collision information if needed.'
|
||||||
|
'Double-check that fixedRotation is accurate, if it collides.'
|
||||||
|
]
|
||||||
|
|
||||||
|
animatedThangTypeTasks = displayedThangTypeTasks.concat [
|
||||||
|
'Configure the non-idle actions.'
|
||||||
|
'Configure any per-action registration points needed.'
|
||||||
|
'Add flipX per action if needed to face to the right.'
|
||||||
|
'Make sure any death and attack actions do not loop.'
|
||||||
|
'Add defaultSimlish if needed.'
|
||||||
|
'Add selection sounds if needed.'
|
||||||
|
'Add per-action sound triggers.'
|
||||||
|
'Add team color groups.'
|
||||||
|
]
|
||||||
|
|
||||||
|
containerTasks = displayedThangTypeTasks.concat [
|
||||||
|
'Select viable terrains if not universal.'
|
||||||
|
'Set Exists stateless: true if needed.'
|
||||||
|
]
|
||||||
|
|
||||||
|
purchasableTasks = [
|
||||||
|
'Add a tier, or 10 + desired tier if not ready yet.'
|
||||||
|
'Add a gem cost.'
|
||||||
|
'Write a description.'
|
||||||
|
'Click the Populate i18n button.'
|
||||||
|
]
|
||||||
|
|
||||||
|
defaultTasks =
|
||||||
|
Unit: commonTasks.concat animatedThangTypeTasks.concat [
|
||||||
|
'Start a new name category in names.coffee if needed.'
|
||||||
|
'Set to Allied to correct team (ogres, humans, or neutral).'
|
||||||
|
'Add AutoTargetsNearest or FightsBack if needed.'
|
||||||
|
'Add other Components like Shoots or Casts if needed.'
|
||||||
|
'Configure other Components, like Moves, Attackable, Attacks, etc.'
|
||||||
|
'Override the HasAPI type if it will not be correctly inferred.'
|
||||||
|
]
|
||||||
|
Hero: commonTasks.concat animatedThangTypeTasks.concat purchasableTasks.concat [
|
||||||
|
'Set the hero class.'
|
||||||
|
'Add Extended Hero Name.'
|
||||||
|
'Upload Hero Doll Images.'
|
||||||
|
'Start a new name category in names.coffee.'
|
||||||
|
'Set up hero stats in Equips, Attackable, Moves.'
|
||||||
|
'Set Collects collectRange to 2, Sees visualRange to 60.'
|
||||||
|
'Add any custom hero abilities.'
|
||||||
|
'Add to ThangType model hard-coded hero ids/classes list.'
|
||||||
|
'Add to LevelHUDView hard-coded hero short names list.'
|
||||||
|
'Add to InventoryView hard-coded hero gender list.'
|
||||||
|
'Add to PlayHeroesModal hard-coded hero positioning logic.'
|
||||||
|
'Add as unlock to a level and add unlockLevelName here.'
|
||||||
|
]
|
||||||
|
Floor: commonTasks.concat containerTasks.concat [
|
||||||
|
'Add 10 x 8.5 snapping.'
|
||||||
|
'Set fixed rotation.'
|
||||||
|
'Make sure everything is scaled to tile perfectly.'
|
||||||
|
'Adjust SingularSprite floor scale list if necessary.'
|
||||||
|
]
|
||||||
|
Wall: commonTasks.concat containerTasks.concat [
|
||||||
|
'Add 4x4 snapping.'
|
||||||
|
'Set fixed rotation.'
|
||||||
|
'Set up and tune complicated wall-face actions.'
|
||||||
|
'Make sure everything is scaled to tile perfectly.'
|
||||||
|
]
|
||||||
|
Doodad: commonTasks.concat containerTasks.concat [
|
||||||
|
'Add to GenerateTerrainModal logic if needed.'
|
||||||
|
]
|
||||||
|
Misc: commonTasks.concat [
|
||||||
|
'Add any misc tasks for this misc ThangType.'
|
||||||
|
]
|
||||||
|
Mark: commonTasks.concat [
|
||||||
|
'Check the animation framerate.'
|
||||||
|
'Double-check that bottom of mark is just touching registration point.'
|
||||||
|
]
|
||||||
|
Item: commonTasks.concat purchasableTasks.concat [
|
||||||
|
'Set the hero class if class-specific.'
|
||||||
|
'Upload Paper Doll Images.'
|
||||||
|
]
|
||||||
|
Missile: commonTasks.concat animatedThangTypeTasks.concat [
|
||||||
|
'Make sure there is a launch sound trigger.'
|
||||||
|
'Make sure there is a hit sound trigger.'
|
||||||
|
'Make sure there is a die animation.'
|
||||||
|
'Add Arrow, Shell, Beam, or other missile Component.'
|
||||||
|
'Choose Missile.leadsShots and Missile.shootsAtGround.'
|
||||||
|
'Choose Moves.maxSpeed and other config.'
|
||||||
|
'Choose Expires.lifespan config if needed.'
|
||||||
|
'Set spriteType: singular if needed for proper rendering.'
|
||||||
|
'Add HasAPI if the missile should show up in findEnemyMissiles.'
|
||||||
|
]
|
||||||
|
|
||||||
module.exports = class ThangTypeEditView extends RootView
|
module.exports = class ThangTypeEditView extends RootView
|
||||||
id: 'thang-type-edit-view'
|
id: 'thang-type-edit-view'
|
||||||
className: 'editor'
|
className: 'editor'
|
||||||
|
@ -435,6 +535,8 @@ module.exports = class ThangTypeEditView extends RootView
|
||||||
Backbone.Mediator.publish 'editor:thang-type-kind-changed', kind: kind
|
Backbone.Mediator.publish 'editor:thang-type-kind-changed', kind: kind
|
||||||
if kind in ['Doodad', 'Floor', 'Wall'] and not @treema.data.terrains
|
if kind in ['Doodad', 'Floor', 'Wall'] and not @treema.data.terrains
|
||||||
@treema.set '/terrains', ['Grass', 'Dungeon', 'Indoor', 'Desert'] # So editors know to set them.
|
@treema.set '/terrains', ['Grass', 'Dungeon', 'Indoor', 'Desert'] # So editors know to set them.
|
||||||
|
if not @treema.data.tasks
|
||||||
|
@treema.set '/tasks', (name: t for t in defaultTasks[kind])
|
||||||
|
|
||||||
onSelectNode: (e, selected) =>
|
onSelectNode: (e, selected) =>
|
||||||
selected = selected[0]
|
selected = selected[0]
|
||||||
|
|
|
@ -6,7 +6,7 @@ module.exports = class ThangTypeSearchView extends SearchView
|
||||||
model: require 'models/ThangType'
|
model: require 'models/ThangType'
|
||||||
modelURL: '/db/thang.type'
|
modelURL: '/db/thang.type'
|
||||||
tableTemplate: require 'templates/editor/thang/table'
|
tableTemplate: require 'templates/editor/thang/table'
|
||||||
projection: ['original', 'name', 'version', 'description', 'slug', 'kind', 'rasterIcon']
|
projection: ['original', 'name', 'version', 'description', 'slug', 'kind', 'rasterIcon', 'tasks']
|
||||||
page: 'thang'
|
page: 'thang'
|
||||||
|
|
||||||
getRenderData: ->
|
getRenderData: ->
|
||||||
|
|
|
@ -71,7 +71,7 @@ def which(cmd, mode=os.F_OK | os.X_OK, path=None):
|
||||||
|
|
||||||
|
|
||||||
current_directory = os.path.dirname(os.path.realpath(sys.argv[0]))
|
current_directory = os.path.dirname(os.path.realpath(sys.argv[0]))
|
||||||
allowedMongoVersions = ["v2.6.0","v2.6.1","v2.6.4","v2.6.5"]
|
allowedMongoVersions = ["v2.6"]
|
||||||
if which("mongod") and any(i in subprocess.check_output("mongod --version",shell=True) for i in allowedMongoVersions):
|
if which("mongod") and any(i in subprocess.check_output("mongod --version",shell=True) for i in allowedMongoVersions):
|
||||||
mongo_executable = "mongod"
|
mongo_executable = "mongod"
|
||||||
else:
|
else:
|
||||||
|
|
244
scripts/analytics/mongodb/queries/campaignRates.js
Normal file
244
scripts/analytics/mongodb/queries/campaignRates.js
Normal file
|
@ -0,0 +1,244 @@
|
||||||
|
// Print out campaign drop-off rates
|
||||||
|
// Drop off: last started or finished level event
|
||||||
|
// Adjust startDate below for different timeframe than last 7 days.
|
||||||
|
|
||||||
|
// Usage:
|
||||||
|
// mongo <address>:<port>/<database> <script file> -u <username> -p <password>
|
||||||
|
|
||||||
|
// Ignores the order at which levels are completed
|
||||||
|
// Ignores level skipping
|
||||||
|
|
||||||
|
// TODO: Is our overall drop-off rate correct?
|
||||||
|
// TODO: What's the right time frame for this data?
|
||||||
|
|
||||||
|
// TODO: Calculate completion rates per-level, and campaign overall
|
||||||
|
|
||||||
|
var today = new Date();
|
||||||
|
today = today.toISOString().substr(0, 10);
|
||||||
|
print("Today is " + today);
|
||||||
|
|
||||||
|
var todayMinus6 = new Date();
|
||||||
|
todayMinus6.setDate(todayMinus6.getUTCDate() - 6);
|
||||||
|
var startDate = todayMinus6.toISOString().substr(0, 10) + "T00:00:00.000Z";
|
||||||
|
// startDate = "2014-12-01T00:00:00.000Z";
|
||||||
|
print("Start date is " + startDate)
|
||||||
|
|
||||||
|
var cursor = db['analytics.log.events'].find({
|
||||||
|
$and: [
|
||||||
|
{"created": { $gte: ISODate(startDate)}},
|
||||||
|
{$or: [ {"event" : 'Started Level'}, {"event" : 'Saw Victory'}]}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
var longestLevelName = -1;
|
||||||
|
|
||||||
|
|
||||||
|
// Copied from WorldMapView
|
||||||
|
var dungeonLevels = [
|
||||||
|
'dungeons-of-kithgard',
|
||||||
|
'gems-in-the-deep',
|
||||||
|
'shadow-guard',
|
||||||
|
'kounter-kithwise',
|
||||||
|
'crawlways-of-kithgard',
|
||||||
|
'forgetful-gemsmith',
|
||||||
|
'true-names',
|
||||||
|
'favorable-odds',
|
||||||
|
'the-raised-sword',
|
||||||
|
'haunted-kithmaze',
|
||||||
|
'riddling-kithmaze',
|
||||||
|
'descending-further',
|
||||||
|
'the-second-kithmaze',
|
||||||
|
'dread-door',
|
||||||
|
'known-enemy',
|
||||||
|
'master-of-names',
|
||||||
|
'lowly-kithmen',
|
||||||
|
'closing-the-distance',
|
||||||
|
'tactical-strike',
|
||||||
|
'the-final-kithmaze',
|
||||||
|
'the-gauntlet',
|
||||||
|
'kithgard-gates',
|
||||||
|
'cavern-survival'
|
||||||
|
];
|
||||||
|
|
||||||
|
var forestLevels = [
|
||||||
|
'defense-of-plainswood',
|
||||||
|
'winding-trail',
|
||||||
|
'patrol-buster',
|
||||||
|
'endangered-burl',
|
||||||
|
'village-guard',
|
||||||
|
'thornbush-farm',
|
||||||
|
'back-to-back',
|
||||||
|
'ogre-encampment',
|
||||||
|
'woodland-cleaver',
|
||||||
|
'shield-rush',
|
||||||
|
'peasant-protection',
|
||||||
|
'munchkin-swarm',
|
||||||
|
'munchkin-harvest',
|
||||||
|
'swift-dagger',
|
||||||
|
'shrapnel',
|
||||||
|
'arcane-ally',
|
||||||
|
'touch-of-death',
|
||||||
|
'bonemender',
|
||||||
|
'coinucopia',
|
||||||
|
'copper-meadows',
|
||||||
|
'drop-the-flag',
|
||||||
|
'deadly-pursuit',
|
||||||
|
'rich-forager',
|
||||||
|
'siege-of-stonehold',
|
||||||
|
'multiplayer-treasure-grove',
|
||||||
|
'dueling-grounds'
|
||||||
|
];
|
||||||
|
|
||||||
|
var desertLevels = [
|
||||||
|
'the-dunes',
|
||||||
|
'the-mighty-sand-yak',
|
||||||
|
'oasis',
|
||||||
|
'sarven-road',
|
||||||
|
'sarven-gaps',
|
||||||
|
'thunderhooves',
|
||||||
|
'medical-attention',
|
||||||
|
'minesweeper',
|
||||||
|
'sarven-sentry',
|
||||||
|
'keeping-time',
|
||||||
|
'hoarding-gold',
|
||||||
|
'decoy-drill',
|
||||||
|
'yakstraction',
|
||||||
|
'sarven-brawl'
|
||||||
|
];
|
||||||
|
|
||||||
|
var campaigns = {
|
||||||
|
'dungeon': dungeonLevels,
|
||||||
|
'forest': forestLevels,
|
||||||
|
'desert': desertLevels
|
||||||
|
};
|
||||||
|
|
||||||
|
// Bucketize events by user
|
||||||
|
print("Getting event data...");
|
||||||
|
var userProgression = {};
|
||||||
|
while (cursor.hasNext()) {
|
||||||
|
var doc = cursor.next();
|
||||||
|
var created = doc.created;
|
||||||
|
var event = doc.event;
|
||||||
|
if (event === 'Saw Victory') var level = doc.properties.level.toLowerCase().replace(/ /g, '-');
|
||||||
|
else var level = doc.properties.levelID
|
||||||
|
if (level) {
|
||||||
|
if (level.length > longestLevelName) longestLevelName = level.length;
|
||||||
|
var user = doc.user.valueOf();
|
||||||
|
if (!userProgression[user]) userProgression[user] = [];
|
||||||
|
userProgression[user].push({
|
||||||
|
created: created,
|
||||||
|
event: event,
|
||||||
|
level: level
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
longestLevelName += 2;
|
||||||
|
|
||||||
|
print("Processing data...");
|
||||||
|
|
||||||
|
// Order user progression by created
|
||||||
|
for (user in userProgression) userProgression[user].sort(function (a,b) {return a.created < b.created ? -1 : 1});
|
||||||
|
|
||||||
|
// Per-level start/drop/finish/drop
|
||||||
|
var levelProgression = {};
|
||||||
|
for (user in userProgression) {
|
||||||
|
for (var i = 0; i < userProgression[user].length; i++) {
|
||||||
|
var event = userProgression[user][i].event;
|
||||||
|
var level = userProgression[user][i].level;
|
||||||
|
if (!levelProgression[level]) {
|
||||||
|
levelProgression[level] = {
|
||||||
|
started: 0,
|
||||||
|
startDropped: 0,
|
||||||
|
finished: 0,
|
||||||
|
finishDropped: 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (event === 'Started Level') {
|
||||||
|
levelProgression[level].started++;
|
||||||
|
if (i === userProgression[user].length - 1) levelProgression[level].startDropped++;
|
||||||
|
}
|
||||||
|
else if (event === 'Saw Victory') {
|
||||||
|
levelProgression[level].finished++;
|
||||||
|
if (i === userProgression[user].length - 1) levelProgression[level].finishDropped++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Put in campaign order
|
||||||
|
// Calculate overall campaign stats
|
||||||
|
var campaignRates = {};
|
||||||
|
for (level in levelProgression) {
|
||||||
|
for (campaign in campaigns) {
|
||||||
|
if (campaigns[campaign].indexOf(level) >= 0) {
|
||||||
|
var started = levelProgression[level].started;
|
||||||
|
var startDropped = levelProgression[level].startDropped;
|
||||||
|
var finished = levelProgression[level].finished;
|
||||||
|
var finishDropped = levelProgression[level].finishDropped;
|
||||||
|
if (!campaignRates[campaign]) {
|
||||||
|
campaignRates[campaign] = { levels: [], overall: {
|
||||||
|
started: 0,
|
||||||
|
startDropped: 0,
|
||||||
|
finished: 0,
|
||||||
|
finishDropped: 0
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
campaignRates[campaign].levels.push({
|
||||||
|
level: level,
|
||||||
|
started: started,
|
||||||
|
startDropped: startDropped,
|
||||||
|
finished: finished,
|
||||||
|
finishDropped: finishDropped
|
||||||
|
});
|
||||||
|
campaignRates[campaign].overall.started += started;
|
||||||
|
campaignRates[campaign].overall.finished += finished;
|
||||||
|
campaignRates[campaign].overall.startDropped += startDropped;
|
||||||
|
|
||||||
|
// Only finishDropped if on last level in campaign
|
||||||
|
if (campaigns[campaign].indexOf(level) === campaigns[campaign].length - 1) {
|
||||||
|
campaignRates[campaign].overall.finishDropped += finishDropped;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
campaignRates[campaign].overall.startDropped += finishDropped;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort level data by campaign order
|
||||||
|
for (campaign in campaignRates) {
|
||||||
|
campaignRates[campaign].levels.sort(function(a, b) {
|
||||||
|
if (campaigns[campaign].indexOf(a.level) < campaigns[campaign].indexOf(b.level)) return -1;
|
||||||
|
return 1;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
print("\nCampaign drop off rates");
|
||||||
|
print("Where do players stop playing?");
|
||||||
|
print("Drop-off point: last start or finish level event.");
|
||||||
|
print("Columns: level, started the level, left after starting, finished level, left after finishing level");
|
||||||
|
|
||||||
|
for (campaign in campaigns) {
|
||||||
|
print("\n" + campaign);
|
||||||
|
var level = "level";
|
||||||
|
var levelSpacer = new Array(longestLevelName - level.length).join(' ');
|
||||||
|
print(level + levelSpacer + "started\tdropped\t\tfinished dropped");
|
||||||
|
for (var i = 0; i < campaignRates[campaign].levels.length; i++) {
|
||||||
|
var level = campaignRates[campaign].levels[i].level;
|
||||||
|
var started = campaignRates[campaign].levels[i].started;
|
||||||
|
var startDropped = campaignRates[campaign].levels[i].startDropped;
|
||||||
|
var finished = campaignRates[campaign].levels[i].finished;
|
||||||
|
var finishDropped = campaignRates[campaign].levels[i].finishDropped;
|
||||||
|
var levelSpacer = new Array(longestLevelName - level.length).join(' ');
|
||||||
|
print(level + levelSpacer + started + "\t" + (started < 100 ? "\t" : "") + startDropped + "\t" + (startDropped / started * 100).toFixed(2) + "%\t" + finished + "\t" + finishDropped + "\t" + (finishDropped / finished * 100).toFixed(2) + "%");
|
||||||
|
}
|
||||||
|
var level = 'Overall';
|
||||||
|
var started = campaignRates[campaign].overall.started;
|
||||||
|
var startDropped = campaignRates[campaign].overall.startDropped;
|
||||||
|
var finished = campaignRates[campaign].overall.finished;
|
||||||
|
var finishDropped = campaignRates[campaign].overall.finishDropped;
|
||||||
|
var levelSpacer = new Array(longestLevelName - level.length).join(' ');
|
||||||
|
print(level + levelSpacer + started + "\t" + (started < 100 ? "\t" : "") + startDropped + "\t" + (startDropped / started * 100).toFixed(2) + "%\t" + finished + "\t" + finishDropped + "\t" + (finishDropped / finished * 100).toFixed(2) + "%");
|
||||||
|
}
|
|
@ -53,10 +53,6 @@ function getCompletionRates() {
|
||||||
}
|
}
|
||||||
}};
|
}};
|
||||||
|
|
||||||
// TODO: sort by level, date,
|
|
||||||
// var sort = {$sort: { "_id.level" : 1, "_id.created" : -1}};
|
|
||||||
//var cursor = db['analytics.log.events'].aggregate(match, proj0, proj1, proj2, group, sort);
|
|
||||||
// var cursor = db['analytics.log.events'].aggregate(match, proj0, group, sort);
|
|
||||||
var cursor = db['analytics.log.events'].aggregate(match, proj0, group);
|
var cursor = db['analytics.log.events'].aggregate(match, proj0, group);
|
||||||
|
|
||||||
// <level><date><data>
|
// <level><date><data>
|
||||||
|
|
|
@ -86,10 +86,10 @@ class LinuxMongoDBDownloader(MongoDBDownloader):
|
||||||
@property
|
@property
|
||||||
def download_url(self):
|
def download_url(self):
|
||||||
if self.dependency.config.mem_width == 64:
|
if self.dependency.config.mem_width == 64:
|
||||||
return u"http://fastdl.mongodb.org/linux/mongodb-linux-x86_64-latest.tgz"
|
return u"http://fastdl.mongodb.org/linux/mongodb-linux-x86_64-2.6.6.tgz"
|
||||||
else:
|
else:
|
||||||
warnings.warn(u"MongoDB *really* doesn't run well on 32 bit systems. You have been warned.")
|
warnings.warn(u"MongoDB *really* doesn't run well on 32 bit systems. You have been warned.")
|
||||||
return u"http://fastdl.mongodb.org/linux/mongodb-linux-i686-latest.tgz"
|
return u"http://fastdl.mongodb.org/linux/mongodb-linux-i686-2.6.6.tgz"
|
||||||
|
|
||||||
class WindowsMongoDBDownloader(MongoDBDownloader):
|
class WindowsMongoDBDownloader(MongoDBDownloader):
|
||||||
@property
|
@property
|
||||||
|
@ -97,13 +97,13 @@ class WindowsMongoDBDownloader(MongoDBDownloader):
|
||||||
#TODO: Implement Windows Vista detection
|
#TODO: Implement Windows Vista detection
|
||||||
warnings.warn(u"If you have a version of Windows older than 7, MongoDB may not function properly!")
|
warnings.warn(u"If you have a version of Windows older than 7, MongoDB may not function properly!")
|
||||||
if self.dependency.config.mem_width == 64:
|
if self.dependency.config.mem_width == 64:
|
||||||
return u"http://fastdl.mongodb.org/win32/mongodb-win32-x86_64-2008plus-latest.zip"
|
return u"http://fastdl.mongodb.org/win32/mongodb-win32-x86_64-2008plus-2.6.6.zip"
|
||||||
else:
|
else:
|
||||||
return u"http://fastdl.mongodb.org/win32/mongodb-win32-i386-latest.zip"
|
return u"http://fastdl.mongodb.org/win32/mongodb-win32-i386-2.6.6.zip"
|
||||||
|
|
||||||
class MacMongoDBDownloader(MongoDBDownloader):
|
class MacMongoDBDownloader(MongoDBDownloader):
|
||||||
@property
|
@property
|
||||||
def download_url(self):
|
def download_url(self):
|
||||||
return u"http://fastdl.mongodb.org/osx/mongodb-osx-x86_64-latest.tgz"
|
return u"http://fastdl.mongodb.org/osx/mongodb-osx-x86_64-2.6.6.tgz"
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -3,8 +3,8 @@ The MIT License (MIT)
|
||||||
|
|
||||||
Copyright (c) 2014 CodeCombat Inc. and other contributors
|
Copyright (c) 2014 CodeCombat Inc. and other contributors
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in allcopies or substantial portions of the Software.
|
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN sCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THESOFTWARE.
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
_____ _ _____ _ _
|
||||||
|
/ __ \ | | / __ \ | | | |
|
||||||
|
| / \/ ___ __| | ___ | / \/ ___ _ __ ___ | |__ __ _| |_
|
||||||
|
| | / _ \ / _` |/ _ \ | | / _ \| '_ ` _ \| '_ \ / _` | __|
|
||||||
|
| \__/\ (_) | (_| | __/ | \__/\ (_) | | | | | | |_) | (_| | |_
|
||||||
|
\____/\___/ \__,_|\___| \____/\___/|_| |_| |_|_.__/ \__,_|\__|
|
||||||
|
|
||||||
|
=============================================================================
|
||||||
|
|
||||||
|
Felicidades, ahora eres parte dela comunidad de CodeCombat.
|
||||||
|
Ahora que el ambiente de desarrollo ha sido instalado, estas listo para comenzar
|
||||||
|
a contribuir y hacer este mundo un mejor lugar.
|
||||||
|
|
||||||
|
Tienes preguntas o te gustaria conocernos?
|
||||||
|
Habla con nosotros en hipchat @ https://www.hipchat.com/g3plnOKqa
|
||||||
|
|
||||||
|
Tambien puedes hablar con nosotros en nuestro foro.
|
||||||
|
El foro esta en @ http://discourse.codecombat.com/
|
||||||
|
|
||||||
|
Puedes leer sobre los ultimos cambios en nuestro blog.
|
||||||
|
El blog esta en @ http://blog.codecombat.com/
|
||||||
|
|
||||||
|
Por ultimo, puedes encontrar casi toda nuestra documentacion
|
||||||
|
e informacion en nuestra wiki @ https://github.com/codecombat/codecombat/wiki
|
||||||
|
|
||||||
|
Esperemos que disfrutes tanto la comunidad como nosotros
|
||||||
|
|
||||||
|
|
||||||
|
- Nick, George, Scott, Michael, Jeremy and Glen
|
|
@ -14,7 +14,7 @@ contributing and help us make this world a better place.
|
||||||
Do you have questions or would you like to meet us?
|
Do you have questions or would you like to meet us?
|
||||||
Talk with us on hipchat @ https://www.hipchat.com/g3plnOKqa
|
Talk with us on hipchat @ https://www.hipchat.com/g3plnOKqa
|
||||||
|
|
||||||
Another way to reach is, is by visiting our forum.
|
Another way to reach us, is by visiting our forum.
|
||||||
You can find it @ http://discourse.codecombat.com/
|
You can find it @ http://discourse.codecombat.com/
|
||||||
|
|
||||||
You can read about the latest developments on our blog site.
|
You can read about the latest developments on our blog site.
|
||||||
|
@ -23,7 +23,7 @@ This one can be found @ http://blog.codecombat.com/
|
||||||
Last but not least, you can find most of our documentation
|
Last but not least, you can find most of our documentation
|
||||||
and information on our wiki @ https://github.com/codecombat/codecombat/wiki
|
and information on our wiki @ https://github.com/codecombat/codecombat/wiki
|
||||||
|
|
||||||
We hope you'll enjoy yourself within our community, just as much as us.
|
We hope you enjoy yourself within our community, just as much as we do.
|
||||||
|
|
||||||
|
|
||||||
- Nick, George, Scott, Michael, Jeremy and Glen
|
- Nick, George, Scott, Michael, Jeremy and Glen
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
1) Si tienes una pregunta, hazla con cuidado y detalles
|
||||||
|
2) Esta instalacion esta en beta y puede tener errores
|
||||||
|
3) Puedes reportar bugs en @ 'https://github.com/codecombat/codecombat/issues'
|
||||||
|
4) Tienes preguntas o sugerencias? Habla con nosotros en HipChat @ https://www.hipchat.com/g3plnOKqa
|
||||||
|
|
||||||
|
Puedes encontrar una guia paso a paso para esta instalacion en @ https://github.com/codecombat/codecombat/wiki/Setup-on-Windows:-a-step-by-step-guide
|
|
@ -1,7 +1,6 @@
|
||||||
1) When there is a question, please answer carefull and correct
|
1) When there is a question, please answer carefully and correctly
|
||||||
2) This setup is still in beta and may contain bugs
|
2) This setup is still in beta and may contain bugs
|
||||||
3) You can report bugs @ 'https://github.com/codecombat/codecombat/issues'
|
3) You can report bugs @ 'https://github.com/codecombat/codecombat/issues'
|
||||||
4) Having questions/suggestions? Talk with us on HipChat via CodeCombat.com
|
4) Having questions/suggestions? Talk with us on HipChat @ https://www.hipchat.com/g3plnOKqa
|
||||||
|
|
||||||
You can find a step-by-step guide for this installation on our wiki.
|
You can find a step-by-step guide for this installation at @ https://github.com/codecombat/codecombat/wiki/Setup-on-Windows:-a-step-by-step-guide
|
||||||
github.com/codecombat/codecombat/wiki/Setup-on-Windows:-a-step-by-step-guide
|
|
||||||
|
|
|
@ -47,7 +47,7 @@
|
||||||
<opensource>CodeCombat is opensource, like you already know.</opensource>
|
<opensource>CodeCombat is opensource, like you already know.</opensource>
|
||||||
<online>All our sourcecode can be found online at Github.</online>
|
<online>All our sourcecode can be found online at Github.</online>
|
||||||
<manual>You can choose to do the entire Git setup yourself.</manual>
|
<manual>You can choose to do the entire Git setup yourself.</manual>
|
||||||
<norec>However we recommend that you instead let us handle it instead.</norec>
|
<norec>However we recommend that you let us handle it instead.</norec>
|
||||||
</intro>
|
</intro>
|
||||||
<skip>
|
<skip>
|
||||||
<question>Do you want to do the Local Git setup manually yourself?</question>
|
<question>Do you want to do the Local Git setup manually yourself?</question>
|
||||||
|
@ -69,7 +69,7 @@
|
||||||
<info>Please enter your github information, to configure your local repository.</info>
|
<info>Please enter your github information, to configure your local repository.</info>
|
||||||
<username>Username: </username>
|
<username>Username: </username>
|
||||||
<password>Password: </password>
|
<password>Password: </password>
|
||||||
<process>Thank you... Configuring your local repistory right now...</process>
|
<process>Thank you... Configuring your local repository right now...</process>
|
||||||
</config>
|
</config>
|
||||||
</github>
|
</github>
|
||||||
<switch>
|
<switch>
|
||||||
|
@ -82,7 +82,7 @@
|
||||||
<binstall>Installing bower packages...</binstall>
|
<binstall>Installing bower packages...</binstall>
|
||||||
<sass>Installing sass...</sass>
|
<sass>Installing sass...</sass>
|
||||||
<npm>Installing npm...</npm>
|
<npm>Installing npm...</npm>
|
||||||
<brnch>Starting brunch....</brnch>
|
<brnch>Starting brunch...</brnch>
|
||||||
<mongodb>Setting up a MongoDB database for you...</mongodb>
|
<mongodb>Setting up a MongoDB database for you...</mongodb>
|
||||||
<db>Downloading the last version of the CodeCombat database...</db>
|
<db>Downloading the last version of the CodeCombat database...</db>
|
||||||
<script>Preparing the automatic startup script for you...</script>
|
<script>Preparing the automatic startup script for you...</script>
|
||||||
|
@ -105,4 +105,4 @@
|
||||||
<s5> 2) Now just open 'localhost:3000' in your prefered browser.</s5>
|
<s5> 2) Now just open 'localhost:3000' in your prefered browser.</s5>
|
||||||
<s6>That's it, you're now ready to start working on CodeCombat!</s6>
|
<s6>That's it, you're now ready to start working on CodeCombat!</s6>
|
||||||
</start>
|
</start>
|
||||||
</variables>
|
</variables>
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
AnalyticsLogEvent = require './AnalyticsLogEvent'
|
AnalyticsLogEvent = require './AnalyticsLogEvent'
|
||||||
Handler = require '../commons/Handler'
|
Handler = require '../commons/Handler'
|
||||||
|
log = require 'winston'
|
||||||
|
|
||||||
class AnalyticsLogEventHandler extends Handler
|
class AnalyticsLogEventHandler extends Handler
|
||||||
modelClass: AnalyticsLogEvent
|
modelClass: AnalyticsLogEvent
|
||||||
|
@ -17,4 +18,280 @@ class AnalyticsLogEventHandler extends Handler
|
||||||
instance.set('user', req.user._id)
|
instance.set('user', req.user._id)
|
||||||
instance
|
instance
|
||||||
|
|
||||||
|
getByRelationship: (req, res, args...) ->
|
||||||
|
return @getLevelCompletionsBySlugs(req, res) if args[1] is 'level_completions'
|
||||||
|
return @getCampaignDropOffs(req, res) if args[1] is 'campaign_drop_offs'
|
||||||
|
super(arguments...)
|
||||||
|
|
||||||
|
getLevelCompletionsBySlugs: (req, res) ->
|
||||||
|
# Returns an array of per-day level starts and finishes
|
||||||
|
# Parameters:
|
||||||
|
# slug - level slug
|
||||||
|
# startDay - Inclusive, optional, e.g. '2014-12-14'
|
||||||
|
# endDay - Exclusive, optional, e.g. '2014-12-16'
|
||||||
|
|
||||||
|
# TODO: An uncached call takes about 15s locally
|
||||||
|
|
||||||
|
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
|
||||||
|
@levelCompletionsCache ?= {}
|
||||||
|
@levelCompletionsCachedSince ?= new Date()
|
||||||
|
if (new Date()) - @levelCompletionsCachedSince > 86400 * 1000 # Dumb cache expiration
|
||||||
|
@levelCompletionsCache = {}
|
||||||
|
@levelCompletionsCachedSince = new Date()
|
||||||
|
cacheKey = levelSlug
|
||||||
|
cacheKey += 's' + startDay if startDay?
|
||||||
|
cacheKey += 'e' + endDay if endDay?
|
||||||
|
return @sendSuccess res, levelCompletions if levelCompletions = @levelCompletionsCache[cacheKey]
|
||||||
|
|
||||||
|
# Build query
|
||||||
|
match = {$match: {$and: [{$or: [{"event" : 'Started Level'}, {"event" : 'Saw Victory'}]}]}}
|
||||||
|
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?
|
||||||
|
project = {"$project": {"_id": 0, "event": 1, "level": {$ifNull: ["$properties.level", "$properties.levelID"]}, "created": {"$concat": [{"$substr": ["$created", 0, 4]}, "-", {"$substr": ["$created", 5, 2]}, "-", {"$substr" : ["$created", 8, 2]}]}}}
|
||||||
|
group = {"$group": {"_id": {"event": "$event", "created": "$created", "level": "$level"}, "count": {"$sum": 1}}}
|
||||||
|
query = AnalyticsLogEvent.aggregate match, project, group
|
||||||
|
|
||||||
|
query.exec (err, data) =>
|
||||||
|
if err? then return @sendDatabaseError res, err
|
||||||
|
|
||||||
|
# Build per-level-day started and finished counts
|
||||||
|
levelDateMap = {}
|
||||||
|
for item in data
|
||||||
|
created = item._id.created
|
||||||
|
event = item._id.event
|
||||||
|
level = item._id.level
|
||||||
|
continue unless level?
|
||||||
|
# 'Started Level' event uses level slug, 'Saw Victory' event uses level name with caps and spaces.
|
||||||
|
level = level.toLowerCase().replace new RegExp(' ', 'g'), '-' if event is 'Saw Victory'
|
||||||
|
|
||||||
|
levelDateMap[level] ?= {}
|
||||||
|
levelDateMap[level][created] ?= {}
|
||||||
|
levelDateMap[level][created] ?= {}
|
||||||
|
if event is 'Saw Victory'
|
||||||
|
levelDateMap[level][created]['finished'] = item.count
|
||||||
|
else
|
||||||
|
levelDateMap[level][created]['started'] = item.count
|
||||||
|
|
||||||
|
# Build list of level completions
|
||||||
|
# Cache every level, since we had to grab all this data anyway
|
||||||
|
completions = {}
|
||||||
|
for level of levelDateMap
|
||||||
|
completions[level] = []
|
||||||
|
for created, item of levelDateMap[level]
|
||||||
|
completions[level].push
|
||||||
|
level: level
|
||||||
|
created: created
|
||||||
|
started: item.started
|
||||||
|
finished: item.finished
|
||||||
|
cacheKey = level
|
||||||
|
cacheKey += 's' + startDay if startDay?
|
||||||
|
cacheKey += 'e' + endDay if endDay?
|
||||||
|
@levelCompletionsCache[cacheKey] = completions[level]
|
||||||
|
@sendSuccess res, completions[levelSlug]
|
||||||
|
|
||||||
|
getCampaignDropOffs: (req, res) ->
|
||||||
|
# Returns a dictionary of per-campaign level start and finish drop-offs
|
||||||
|
# Drop-off: last started or finished level event
|
||||||
|
# Parameters:
|
||||||
|
# slugs - array of campaign slugs
|
||||||
|
# startDay - Inclusive, optional, e.g. '2014-12-14'
|
||||||
|
# endDay - Exclusive, optional, e.g. '2014-12-16'
|
||||||
|
|
||||||
|
# TODO: Read per-campaign level progression data from a legit source
|
||||||
|
# TODO: An uncached call can take over 30s locally
|
||||||
|
# TODO: Returns all the campaigns
|
||||||
|
# TODO: Calculate overall campaign stats
|
||||||
|
|
||||||
|
campaignSlugs = req.query.slugs or req.body.slugs
|
||||||
|
startDay = req.query.startDay or req.body.startDay
|
||||||
|
endDay = req.query.endDay or req.body.endDay
|
||||||
|
|
||||||
|
return @sendSuccess res, [] unless campaignSlugs?
|
||||||
|
|
||||||
|
# Cache results for 1 day
|
||||||
|
@campaignDropOffsCache ?= {}
|
||||||
|
@campaignDropOffsCachedSince ?= new Date()
|
||||||
|
if (new Date()) - @campaignDropOffsCachedSince > 86400 * 1000 # Dumb cache expiration
|
||||||
|
@campaignDropOffsCache = {}
|
||||||
|
@campaignDropOffsCachedSince = new Date()
|
||||||
|
cacheKey = campaignSlugs.join(',')
|
||||||
|
cacheKey += 's' + startDay if startDay?
|
||||||
|
cacheKey += 'e' + endDay if endDay?
|
||||||
|
return @sendSuccess res, campaignDropOffs if campaignDropOffs = @campaignDropOffsCache[cacheKey]
|
||||||
|
|
||||||
|
queryParams = {$and: [{$or: [ {"event" : 'Started Level'}, {"event" : 'Saw Victory'}]}]}
|
||||||
|
queryParams["$and"].push created: {$gte: new Date(startDay + "T00:00:00.000Z")} if startDay?
|
||||||
|
queryParams["$and"].push created: {$lt: new Date(endDay + "T00:00:00.000Z")} if endDay?
|
||||||
|
|
||||||
|
AnalyticsLogEvent.find(queryParams).select('created event properties user').exec (err, data) =>
|
||||||
|
if err? then return @sendDatabaseError res, err
|
||||||
|
|
||||||
|
# Bucketize events by user
|
||||||
|
userProgression = {}
|
||||||
|
for item in data
|
||||||
|
created = item.get('created')
|
||||||
|
event = item.get('event')
|
||||||
|
if event is 'Saw Victory'
|
||||||
|
level = item.get('properties.level').toLowerCase().replace new RegExp(' ', 'g'), '-'
|
||||||
|
else
|
||||||
|
level = item.get('properties.levelID')
|
||||||
|
continue unless level?
|
||||||
|
user = item.get('user')
|
||||||
|
userProgression[user] ?= []
|
||||||
|
userProgression[user].push
|
||||||
|
created: created
|
||||||
|
event: event
|
||||||
|
level: level
|
||||||
|
|
||||||
|
# Order user progression by created
|
||||||
|
for user in userProgression
|
||||||
|
userProgression[user].sort (a,b) -> if a.created < b.created then return -1 else 1
|
||||||
|
|
||||||
|
# Per-level start/drop/finish/drop
|
||||||
|
levelProgression = {}
|
||||||
|
for user of userProgression
|
||||||
|
for i in [0...userProgression[user].length]
|
||||||
|
event = userProgression[user][i].event
|
||||||
|
level = userProgression[user][i].level
|
||||||
|
levelProgression[level] ?=
|
||||||
|
started: 0
|
||||||
|
startDropped: 0
|
||||||
|
finished: 0
|
||||||
|
finishDropped: 0
|
||||||
|
if event is 'Started Level'
|
||||||
|
levelProgression[level].started++
|
||||||
|
levelProgression[level].startDropped++ if i is userProgression[user].length - 1
|
||||||
|
else if event is 'Saw Victory'
|
||||||
|
levelProgression[level].finished++
|
||||||
|
levelProgression[level].finishDropped++ if i is userProgression[user].length - 1
|
||||||
|
|
||||||
|
# Put in campaign order
|
||||||
|
campaignRates = {}
|
||||||
|
for level of levelProgression
|
||||||
|
for campaign of campaigns
|
||||||
|
if level in campaigns[campaign]
|
||||||
|
started = levelProgression[level].started
|
||||||
|
startDropped = levelProgression[level].startDropped
|
||||||
|
finished = levelProgression[level].finished
|
||||||
|
finishDropped = levelProgression[level].finishDropped
|
||||||
|
campaignRates[campaign] ?=
|
||||||
|
levels: []
|
||||||
|
# overall:
|
||||||
|
# started: 0,
|
||||||
|
# startDropped: 0,
|
||||||
|
# finished: 0,
|
||||||
|
# finishDropped: 0
|
||||||
|
campaignRates[campaign].levels.push
|
||||||
|
level: level
|
||||||
|
started: started
|
||||||
|
startDropped: startDropped
|
||||||
|
finished: finished
|
||||||
|
finishDropped: finishDropped
|
||||||
|
break
|
||||||
|
|
||||||
|
# Sort level data by campaign order
|
||||||
|
for campaign of campaignRates
|
||||||
|
campaignRates[campaign].levels.sort (a, b) ->
|
||||||
|
if campaigns[campaign].indexOf(a.level) < campaigns[campaign].indexOf(b.level) then return -1 else 1
|
||||||
|
|
||||||
|
# Return all campaign data for simplicity
|
||||||
|
# Cache other individual campaigns too, since we have them
|
||||||
|
@campaignDropOffsCache[cacheKey] = campaignRates
|
||||||
|
for campaign of campaignRates
|
||||||
|
cacheKey = campaign
|
||||||
|
cacheKey += 's' + startDay if startDay?
|
||||||
|
cacheKey += 'e' + endDay if endDay?
|
||||||
|
@campaignDropOffsCache[cacheKey] = campaignRates
|
||||||
|
@sendSuccess res, campaignRates
|
||||||
|
|
||||||
|
# Copied from WorldMapView
|
||||||
|
dungeonLevels = [
|
||||||
|
'dungeons-of-kithgard',
|
||||||
|
'gems-in-the-deep',
|
||||||
|
'shadow-guard',
|
||||||
|
'kounter-kithwise',
|
||||||
|
'crawlways-of-kithgard',
|
||||||
|
'forgetful-gemsmith',
|
||||||
|
'true-names',
|
||||||
|
'favorable-odds',
|
||||||
|
'the-raised-sword',
|
||||||
|
'haunted-kithmaze',
|
||||||
|
'riddling-kithmaze',
|
||||||
|
'descending-further',
|
||||||
|
'the-second-kithmaze',
|
||||||
|
'dread-door',
|
||||||
|
'known-enemy',
|
||||||
|
'master-of-names',
|
||||||
|
'lowly-kithmen',
|
||||||
|
'closing-the-distance',
|
||||||
|
'tactical-strike',
|
||||||
|
'the-final-kithmaze',
|
||||||
|
'the-gauntlet',
|
||||||
|
'kithgard-gates',
|
||||||
|
'cavern-survival'
|
||||||
|
];
|
||||||
|
|
||||||
|
forestLevels = [
|
||||||
|
'defense-of-plainswood',
|
||||||
|
'winding-trail',
|
||||||
|
'patrol-buster',
|
||||||
|
'endangered-burl',
|
||||||
|
'village-guard',
|
||||||
|
'thornbush-farm',
|
||||||
|
'back-to-back',
|
||||||
|
'ogre-encampment',
|
||||||
|
'woodland-cleaver',
|
||||||
|
'shield-rush',
|
||||||
|
'peasant-protection',
|
||||||
|
'munchkin-swarm',
|
||||||
|
'munchkin-harvest',
|
||||||
|
'swift-dagger',
|
||||||
|
'shrapnel',
|
||||||
|
'arcane-ally',
|
||||||
|
'touch-of-death',
|
||||||
|
'bonemender',
|
||||||
|
'coinucopia',
|
||||||
|
'copper-meadows',
|
||||||
|
'drop-the-flag',
|
||||||
|
'deadly-pursuit',
|
||||||
|
'rich-forager',
|
||||||
|
'siege-of-stonehold',
|
||||||
|
'multiplayer-treasure-grove',
|
||||||
|
'dueling-grounds'
|
||||||
|
];
|
||||||
|
|
||||||
|
desertLevels = [
|
||||||
|
'the-dunes',
|
||||||
|
'the-mighty-sand-yak',
|
||||||
|
'oasis',
|
||||||
|
'sarven-road',
|
||||||
|
'sarven-gaps',
|
||||||
|
'thunderhooves',
|
||||||
|
'medical-attention',
|
||||||
|
'minesweeper',
|
||||||
|
'sarven-sentry',
|
||||||
|
'keeping-time',
|
||||||
|
'hoarding-gold',
|
||||||
|
'decoy-drill',
|
||||||
|
'yakstraction',
|
||||||
|
'sarven-brawl',
|
||||||
|
'desert-combat',
|
||||||
|
'dust',
|
||||||
|
'mirage-maker',
|
||||||
|
'sarven-savior',
|
||||||
|
'odd-sandstorm'
|
||||||
|
];
|
||||||
|
|
||||||
|
campaigns = {
|
||||||
|
'dungeon': dungeonLevels,
|
||||||
|
'forest': forestLevels,
|
||||||
|
'desert': desertLevels
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = new AnalyticsLogEventHandler()
|
module.exports = new AnalyticsLogEventHandler()
|
||||||
|
|
|
@ -72,6 +72,7 @@ LevelHandler = class LevelHandler extends Handler
|
||||||
return @getHistogramData(req, res, args[0]) if args[1] is 'histogram_data'
|
return @getHistogramData(req, res, args[0]) if args[1] is 'histogram_data'
|
||||||
return @checkExistence(req, res, args[0]) if args[1] is 'exists'
|
return @checkExistence(req, res, args[0]) if args[1] is 'exists'
|
||||||
return @getPlayCountsBySlugs(req, res) if args[1] is 'play_counts'
|
return @getPlayCountsBySlugs(req, res) if args[1] is 'play_counts'
|
||||||
|
return @getLevelPlaytimesBySlugs(req, res) if args[1] is 'playtime_averages'
|
||||||
super(arguments...)
|
super(arguments...)
|
||||||
|
|
||||||
fetchLevelByIDAndHandleErrors: (id, req, res, callback) ->
|
fetchLevelByIDAndHandleErrors: (id, req, res, callback) ->
|
||||||
|
@ -321,7 +322,7 @@ LevelHandler = class LevelHandler extends Handler
|
||||||
@playCountCachedSince ?= new Date()
|
@playCountCachedSince ?= new Date()
|
||||||
if (new Date()) - @playCountCachedSince > 86400 * 1000 # Dumb cache expiration
|
if (new Date()) - @playCountCachedSince > 86400 * 1000 # Dumb cache expiration
|
||||||
@playCountCache = {}
|
@playCountCache = {}
|
||||||
@playCountCacheSince = new Date()
|
@playCountCachedSince = new Date()
|
||||||
cacheKey = levelIDs.join ','
|
cacheKey = levelIDs.join ','
|
||||||
if playCounts = @playCountCache[cacheKey]
|
if playCounts = @playCountCache[cacheKey]
|
||||||
return @sendSuccess res, playCounts
|
return @sendSuccess res, playCounts
|
||||||
|
@ -340,4 +341,53 @@ LevelHandler = class LevelHandler extends Handler
|
||||||
return true if method is null or method is 'get'
|
return true if method is null or method is 'get'
|
||||||
super(req, document, method)
|
super(req, document, method)
|
||||||
|
|
||||||
|
|
||||||
|
getLevelPlaytimesBySlugs: (req, res) ->
|
||||||
|
# Returns an array of per-day level average playtimes
|
||||||
|
# Parameters:
|
||||||
|
# slugs - array of level slugs
|
||||||
|
# startDay - Inclusive, optional, e.g. '2014-12-14'
|
||||||
|
# endDay - Exclusive, optional, e.g. '2014-12-16'
|
||||||
|
|
||||||
|
# TODO: An uncached call takes about 5s for dungeons-of-kithgard locally
|
||||||
|
# TODO: This is very similar to getLevelCompletionsBySlugs(), time to generalize analytics APIs?
|
||||||
|
|
||||||
|
levelSlugs = req.query.slugs or req.body.slugs
|
||||||
|
startDay = req.query.startDay or req.body.startDay
|
||||||
|
endDay = req.query.endDay or req.body.endDay
|
||||||
|
|
||||||
|
return @sendSuccess res, [] unless levelSlugs?
|
||||||
|
|
||||||
|
# Cache results for 1 day
|
||||||
|
@levelPlaytimesCache ?= {}
|
||||||
|
@levelPlaytimesCachedSince ?= new Date()
|
||||||
|
if (new Date()) - @levelPlaytimesCachedSince > 86400 * 1000 # Dumb cache expiration
|
||||||
|
@levelPlaytimesCache = {}
|
||||||
|
@levelPlaytimesCachedSince = new Date()
|
||||||
|
cacheKey = levelSlugs.join(',')
|
||||||
|
cacheKey += 's' + startDay if startDay?
|
||||||
|
cacheKey += 'e' + endDay if endDay?
|
||||||
|
return @sendSuccess res, levelPlaytimes if levelPlaytimes = @levelPlaytimesCache[cacheKey]
|
||||||
|
|
||||||
|
# Build query
|
||||||
|
match = {$match: {$and: [{"state.complete": true}, {"playtime": {$gt: 0}}, {levelID: {$in: levelSlugs}}]}}
|
||||||
|
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?
|
||||||
|
project = {"$project": {"_id": 0, "levelID": 1, "playtime": 1, "created": {"$concat": [{"$substr": ["$created", 0, 4]}, "-", {"$substr": ["$created", 5, 2]}, "-", {"$substr" : ["$created", 8, 2]}]}}}
|
||||||
|
group = {"$group": {"_id": {"created": "$created", "level": "$levelID"}, "average": {"$avg": "$playtime"}}}
|
||||||
|
query = Session.aggregate match, project, group
|
||||||
|
|
||||||
|
query.exec (err, data) =>
|
||||||
|
if err? then return @sendDatabaseError res, err
|
||||||
|
|
||||||
|
# Build list of level average playtimes
|
||||||
|
playtimes = []
|
||||||
|
for item in data
|
||||||
|
playtimes.push
|
||||||
|
level: item._id.level
|
||||||
|
created: item._id.created
|
||||||
|
average: item.average
|
||||||
|
@levelPlaytimesCache[cacheKey] = playtimes
|
||||||
|
@sendSuccess res, playtimes
|
||||||
|
|
||||||
module.exports = new LevelHandler()
|
module.exports = new LevelHandler()
|
||||||
|
|
|
@ -33,6 +33,7 @@ ThangTypeHandler = class ThangTypeHandler extends Handler
|
||||||
'tier'
|
'tier'
|
||||||
'extendedName'
|
'extendedName'
|
||||||
'unlockLevelName'
|
'unlockLevelName'
|
||||||
|
'tasks'
|
||||||
]
|
]
|
||||||
|
|
||||||
hasAccess: (req) ->
|
hasAccess: (req) ->
|
||||||
|
|
|
@ -9,9 +9,12 @@ User = require '../../../server/users/User'
|
||||||
# TODO: these tests have some rerun/cleanup issues
|
# TODO: these tests have some rerun/cleanup issues
|
||||||
# TODO: add tests for purchase, payment, subscribe, unsubscribe, and earned achievements
|
# TODO: add tests for purchase, payment, subscribe, unsubscribe, and earned achievements
|
||||||
|
|
||||||
|
# TODO: AnalyticsUsersActive collection isn't currently used.
|
||||||
|
# TODO: Will remove these tests if we end up ripping out the disabled saveActiveUser calls.
|
||||||
|
|
||||||
describe 'Analytics', ->
|
describe 'Analytics', ->
|
||||||
|
|
||||||
it 'registered user', (done) ->
|
xit 'registered user', (done) ->
|
||||||
clearModels [AnalyticsUsersActive], (err) ->
|
clearModels [AnalyticsUsersActive], (err) ->
|
||||||
expect(err).toBeNull()
|
expect(err).toBeNull()
|
||||||
user = new User
|
user = new User
|
||||||
|
@ -29,7 +32,7 @@ describe 'Analytics', ->
|
||||||
expect(activeUsers[0]?.get('event')).toEqual('register')
|
expect(activeUsers[0]?.get('event')).toEqual('register')
|
||||||
done()
|
done()
|
||||||
|
|
||||||
it 'level completed', (done) ->
|
xit 'level completed', (done) ->
|
||||||
clearModels [AnalyticsUsersActive], (err) ->
|
clearModels [AnalyticsUsersActive], (err) ->
|
||||||
expect(err).toBeNull()
|
expect(err).toBeNull()
|
||||||
unittest.getNormalJoe (joe) ->
|
unittest.getNormalJoe (joe) ->
|
||||||
|
@ -53,7 +56,7 @@ describe 'Analytics', ->
|
||||||
expect(activeUsers[0]?.get('event')).toEqual('level-completed/lotr')
|
expect(activeUsers[0]?.get('event')).toEqual('level-completed/lotr')
|
||||||
done()
|
done()
|
||||||
|
|
||||||
it 'level playtime', (done) ->
|
xit 'level playtime', (done) ->
|
||||||
clearModels [AnalyticsUsersActive], (err) ->
|
clearModels [AnalyticsUsersActive], (err) ->
|
||||||
expect(err).toBeNull()
|
expect(err).toBeNull()
|
||||||
unittest.getNormalJoe (joe) ->
|
unittest.getNormalJoe (joe) ->
|
||||||
|
|
Reference in a new issue