mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2024-11-24 08:08:15 -05:00
Merge branch 'master' into production
This commit is contained in:
commit
5da7244f57
36 changed files with 1080 additions and 205 deletions
Binary file not shown.
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 18 KiB |
|
@ -8,7 +8,7 @@ module.exports = [
|
|||
focus:
|
||||
bounds: [{x: 0, y: 0}, {x: 80, y: 68}]
|
||||
target: "Hero Placeholder"
|
||||
zoom: 2
|
||||
zoom: 0.5
|
||||
sound:
|
||||
music:
|
||||
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_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_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:
|
||||
inventory_tab: "Inventář"
|
||||
|
@ -329,7 +329,7 @@ module.exports = nativeDescription: "čeština", englishDescription: "Czech", tr
|
|||
inventory:
|
||||
choose_inventory: "Nasadit předměty"
|
||||
equipped_item: "Nasazeno"
|
||||
# required_purchase_title: "Required"
|
||||
required_purchase_title: "Vyžadováno"
|
||||
available_item: "Dostupné"
|
||||
restricted_title: "Omezeno"
|
||||
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_page: "naše fórum"
|
||||
forum_suffix: "."
|
||||
# faq_prefix: "There's also a"
|
||||
# faq: "FAQ"
|
||||
faq_prefix: "Také máme "
|
||||
faq: "FAQ"
|
||||
subscribe_prefix: "Pokud potřebujete pomoc s nějakou úrovní, prosím"
|
||||
subscribe: "zakupte si CodeCombat předplatné"
|
||||
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."
|
||||
# screenshot_included: "Screenshot included."
|
||||
screenshot_included: "Snímek obrazovky zahrnut."
|
||||
where_reply: "Kam máme odpovědět?"
|
||||
send: "Odeslat připomínku"
|
||||
contact_candidate: "Kontaktovat kandidáta" # Deprecated
|
||||
|
|
|
@ -3,8 +3,7 @@
|
|||
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_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
|
||||
try_it: "Try It" # Alternate wording for Play button
|
||||
play: "Play" # The big play button that opens up the campaign view.
|
||||
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."
|
||||
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"
|
||||
level_completion: "Level Completion"
|
||||
pop_i18n: "Populate I18N"
|
||||
tasks: "Tasks"
|
||||
|
||||
article:
|
||||
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_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
|
||||
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_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."
|
||||
|
@ -593,63 +592,63 @@ module.exports = nativeDescription: "Español (América Latina)", englishDescrip
|
|||
editor:
|
||||
main_title: "Editor de CodeCombat"
|
||||
article_title: "Editor de Artículo"
|
||||
# thang_title: "Thang Editor"
|
||||
thang_title: "Editor de Thangs"
|
||||
level_title: "Editor de Nivel"
|
||||
# achievement_title: "Achievement Editor"
|
||||
achievement_title: "Editor de logros"
|
||||
back: "Atrás"
|
||||
revert: "Revertir"
|
||||
revert_models: "Revertir Modelos"
|
||||
pick_a_terrain: "Elije un Terreno"
|
||||
small: "Pequeño"
|
||||
grassy: "Herboso"
|
||||
# fork_title: "Fork New Version"
|
||||
# fork_creating: "Creating Fork..."
|
||||
fork_title: "Fork de Nueva Versión"
|
||||
fork_creating: "Creando Fork..."
|
||||
generate_terrain: "Generar terreno"
|
||||
more: "Más"
|
||||
wiki: "Wiki"
|
||||
live_chat: "Chat en vivo"
|
||||
# thang_main: "Main"
|
||||
# thang_spritesheets: "Spritesheets"
|
||||
# thang_colors: "Colors"
|
||||
# level_some_options: "Some Options?"
|
||||
# level_tab_thangs: "Thangs"
|
||||
# level_tab_scripts: "Scripts"
|
||||
thang_main: "Principal"
|
||||
thang_spritesheets: "Spritesheets"
|
||||
thang_colors: "Colores"
|
||||
level_some_options: "¿Algunas opciones?"
|
||||
level_tab_thangs: "Thangs"
|
||||
level_tab_scripts: "Scripts"
|
||||
level_tab_settings: "Opciones"
|
||||
level_tab_components: "Componentes"
|
||||
level_tab_systems: "Sistemas"
|
||||
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_conditions: "Starting Conditions"
|
||||
# level_tab_thangs_add: "Add Thangs"
|
||||
level_tab_thangs_conditions: "Condiciones Iniciales"
|
||||
level_tab_thangs_add: "Agregar Thangs"
|
||||
delete: "Borrar"
|
||||
duplicate: "Duplicar"
|
||||
rotate: "Rotar"
|
||||
level_settings_title: "Opciones"
|
||||
level_component_tab_title: "Componentes Actuales"
|
||||
level_component_btn_new: "Crear Nuevo Componente"
|
||||
level_systems_tab_title: "Sistemas Actuales Systems"
|
||||
level_systems_btn_new: "Crear Nuevo Sistema New System"
|
||||
level_systems_tab_title: "Sistemas Actuales"
|
||||
level_systems_btn_new: "Crear Nuevo 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_component_edit_title: "Editar Componente"
|
||||
# level_component_config_schema: "Config Schema"
|
||||
level_component_config_schema: "Config Schema"
|
||||
level_component_settings: "Opciones"
|
||||
level_system_edit_title: "Editar Sistema"
|
||||
create_system_title: "Crear Nuevo Sistema"
|
||||
new_component_title: "Crear Nuevo Componente"
|
||||
new_component_field_system: "Sistema"
|
||||
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_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_achievement_title: "Crear un Nuevo Logro"
|
||||
new_achievement_title_login: "Ingresa para Crear un Nuevo Logro"
|
||||
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í"
|
||||
achievement_search_title: "Buscar logros"
|
||||
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_article_title: "Editar Artículo"
|
||||
|
||||
# contribute:
|
||||
# page_title: "Contributing"
|
||||
# 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!"
|
||||
# alert_account_message_intro: "Hey there!"
|
||||
# alert_account_message: "To subscribe for class emails, you'll need to be logged in first."
|
||||
# archmage_introduction: "One of the best parts about building games is they synthesize so many different things. Graphics, sound, real-time networking, social networking, and of course many of the more common aspects of programming, from low-level database management, and server administration to user facing design and interface building. There's a lot to do, and if you're an experienced programmer with a hankering to really dive into the nitty-gritty of CodeCombat, this class might be for you. We would love to have your help building the best programming game ever."
|
||||
# class_attributes: "Class Attributes"
|
||||
# archmage_attribute_1_pref: "Knowledge in "
|
||||
# archmage_attribute_1_suf: ", or a desire to learn. Most of our code is in this language. If you're a fan of Ruby or Python, you'll feel right at home. It's JavaScript, but with a nicer syntax."
|
||||
# archmage_attribute_2: "Some experience in programming and personal initiative. We'll help you get oriented, but we can't spend much time training you."
|
||||
# how_to_join: "How To Join"
|
||||
# join_desc_1: "Anyone can help out! Just check out our "
|
||||
# join_desc_2: "to get started, and check the box below to mark yourself as a brave Archmage and get the latest news by email. Want to chat about what to do or how to get more deeply involved? "
|
||||
# join_desc_3: ", or find us in our "
|
||||
# join_desc_4: "and we'll go from there!"
|
||||
# join_url_email: "Email us"
|
||||
# join_url_hipchat: "public HipChat room"
|
||||
# archmage_subscribe_desc: "Get emails on new coding opportunities and announcements."
|
||||
contribute:
|
||||
page_title: "Contribuyendo"
|
||||
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: "¡Hola!"
|
||||
alert_account_message: "Para suscribirte para los correos, necesitas ingresar primero."
|
||||
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: "Atributos de Clase"
|
||||
archmage_attribute_1_pref: "Conocimiento en "
|
||||
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: "Alguna experiencia programando e iniciativa personal. Te ayudaremos a orientarte, pero no podemos perder mucho tiempo entrenando."
|
||||
how_to_join: "Unirse:"
|
||||
join_desc_1: "¡Cualquiera puede unirse! Sólo checa nuestro "
|
||||
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: ", o encuéntranos en "
|
||||
join_desc_4: "y ahí empezaremos!"
|
||||
join_url_email: "Escríbenos"
|
||||
join_url_hipchat: "chat público HipChat"
|
||||
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_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!"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
module.exports = nativeDescription: "Македонски", englishDescription: "Macedonian", translation:
|
||||
home:
|
||||
slogan: "Научи да Програмираш Преку Игра"
|
||||
slogan: "Научи да програмираш преку игра"
|
||||
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
|
||||
play: "Играј" # The big play button that just starts playing a level
|
||||
|
@ -9,8 +9,8 @@ module.exports = nativeDescription: "Македонски", englishDescription:
|
|||
old_browser_suffix: "Можеш да пробаш и покрај тоа, но најверојатно нема да работи."
|
||||
ipad_browser: "Лоши вести: CodeCombat не работи во прелистувачот на iPad. Добри вести: Нашата апликација за iPad е готова и чека одобрение од Apple."
|
||||
campaign: "Кампања"
|
||||
for_beginners: "За Почетници"
|
||||
multiplayer: "Повеќе Играчи" # Not currently shown on home page
|
||||
for_beginners: "За почетници"
|
||||
multiplayer: "Повеќе играчи" # Not currently shown on home page
|
||||
for_developers: "За Developer-и" # Not currently shown on home page.
|
||||
or_ipad: "Или симни за iPad"
|
||||
|
||||
|
@ -24,7 +24,7 @@ module.exports = nativeDescription: "Македонски", englishDescription:
|
|||
profile: "Профил"
|
||||
stats: "Статистики"
|
||||
# code: "Code"
|
||||
# admin: "Admin" # Only shows up when you are an admin
|
||||
admin: "Админ" # Only shows up when you are an admin
|
||||
home: "Дома"
|
||||
contribute: "Допринеси"
|
||||
legal: "Законски"
|
||||
|
@ -43,87 +43,87 @@ module.exports = nativeDescription: "Македонски", englishDescription:
|
|||
diplomat_suggestion:
|
||||
title: "Помогни да се преведе CodeCombat!" # This shows up when a player switches to a non-English language using the language selector.
|
||||
sub_heading: "Ни требаат твоите јазични вештини."
|
||||
pitch_body: "Ние ја развиваме CodeCombat на Англиски, но веќе имаме играчи низ целиот свет. Многу од нив сакаат да играат на Македонски, а не разбираат Англиски, па ако ги зборуваш и двата јазика, размисли дали би сакал/а да се зачлениш како Дипломат и да помогнеш да се преведат на Македонски CodeCombat веб сајтот и сите нивоа од играта."
|
||||
missing_translations: "Додека не преведеме сè на Македонски, содржината ќе биде на Англиски каде што Македонскиот не е достапен."
|
||||
pitch_body: "Ние ја развиваме CodeCombat на англиски, но веќе имаме играчи низ целиот свет. Многу од нив сакаат да играат на македонски, а не разбираат англиски, па ако ги зборуваш и двата јазика, размисли дали би сакал/а да се зачлениш како Дипломат и да помогнеш да се преведат на македонски CodeCombat веб сајтот и сите нивоа од играта."
|
||||
missing_translations: "Додека не преведеме сè на македонски, содржината ќе биде на англиски каде што македонскиот не е достапен."
|
||||
learn_more: "Научи повеќе за тоа како е да се биде Дипломат"
|
||||
subscribe_as_diplomat: "Зачлени се како Дипломат"
|
||||
|
||||
# play:
|
||||
# play_as: "Play As" # Ladder page
|
||||
# spectate: "Spectate" # Ladder page
|
||||
# players: "players" # Hover over a level on /play
|
||||
# hours_played: "hours played" # Hover over a level on /play
|
||||
# items: "Items" # Tooltip on item shop button from /play
|
||||
# unlock: "Unlock" # For purchasing items and heroes
|
||||
# confirm: "Confirm"
|
||||
# owned: "Owned" # For items you own
|
||||
# locked: "Locked"
|
||||
# purchasable: "Purchasable" # For a hero you unlocked but haven't purchased
|
||||
# available: "Available"
|
||||
# skills_granted: "Skills Granted" # Property documentation details
|
||||
# heroes: "Heroes" # Tooltip on hero shop button from /play
|
||||
# achievements: "Achievements" # Tooltip on achievement list button from /play
|
||||
# account: "Account" # Tooltip on account button from /play
|
||||
# settings: "Settings" # Tooltip on settings button from /play
|
||||
# next: "Next" # Go from choose hero to choose inventory before playing a level
|
||||
# change_hero: "Change Hero" # Go back from choose inventory to choose hero
|
||||
# choose_inventory: "Equip Items"
|
||||
# buy_gems: "Buy Gems"
|
||||
# campaign_desert: "Desert Campaign"
|
||||
# campaign_forest: "Forest Campaign"
|
||||
# campaign_dungeon: "Dungeon Campaign"
|
||||
# subscription_required: "Subscription Required"
|
||||
# free: "Free"
|
||||
# subscribed: "Subscribed"
|
||||
# older_campaigns: "Older Campaigns"
|
||||
# anonymous: "Anonymous Player"
|
||||
# level_difficulty: "Difficulty: "
|
||||
# campaign_beginner: "Beginner Campaign"
|
||||
# awaiting_levels_adventurer_prefix: "We release five levels per week."
|
||||
# awaiting_levels_adventurer: "Sign up as an Adventurer"
|
||||
# awaiting_levels_adventurer_suffix: "to be the first to play new levels."
|
||||
# choose_your_level: "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_forum: "the Adventurer forum"
|
||||
# adventurer_suffix: "."
|
||||
# campaign_old_beginner: "Old Beginner Campaign"
|
||||
# campaign_old_beginner_description: "... in which you learn the wizardry of programming."
|
||||
# campaign_dev: "Random Harder Levels"
|
||||
# campaign_dev_description: "... in which you learn the interface while doing something a little harder."
|
||||
# campaign_multiplayer: "Multiplayer Arenas"
|
||||
# campaign_multiplayer_description: "... in which you code head-to-head against other players."
|
||||
# campaign_player_created: "Player-Created"
|
||||
# campaign_player_created_description: "... in which you battle against the creativity of your fellow <a href=\"/contribute/artisan\">Artisan Wizards</a>."
|
||||
# campaign_classic_algorithms: "Classic Algorithms"
|
||||
# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science."
|
||||
play:
|
||||
play_as: "Играј како" # Ladder page
|
||||
spectate: "Набљудувај" # Ladder page
|
||||
players: "играчи" # Hover over a level on /play
|
||||
hours_played: "часови изиграни" # Hover over a level on /play
|
||||
items: "Предмети" # Tooltip on item shop button from /play
|
||||
unlock: "Отклучи" # For purchasing items and heroes
|
||||
confirm: "Потврди"
|
||||
owned: "Во сопственост" # For items you own
|
||||
locked: "Заклучено"
|
||||
purchasable: "Може да се купи" # For a hero you unlocked but haven't purchased
|
||||
available: "Достапно"
|
||||
skills_granted: "Доделени вештини" # Property documentation details
|
||||
heroes: "Херои" # Tooltip on hero shop button from /play
|
||||
achievements: "Постигнувања" # Tooltip on achievement list button from /play
|
||||
account: "Сметка" # Tooltip on account button from /play
|
||||
settings: "Поставки" # Tooltip on settings button from /play
|
||||
next: "Следно" # Go from choose hero to choose inventory before playing a level
|
||||
change_hero: "Смени херој" # Go back from choose inventory to choose hero
|
||||
choose_inventory: "Опреми се"
|
||||
buy_gems: "Купи скапоцени камења"
|
||||
campaign_desert: "Пустинска кампања"
|
||||
campaign_forest: "Шумска кампања"
|
||||
campaign_dungeon: "Занданска кампања"
|
||||
subscription_required: "Потребно е зачленување"
|
||||
free: "Бесплатно"
|
||||
subscribed: "Зачленет"
|
||||
older_campaigns: "Постари кампањи"
|
||||
anonymous: "Анонимен играч"
|
||||
level_difficulty: "Тешкотија: "
|
||||
campaign_beginner: "Почетничка кампања"
|
||||
awaiting_levels_adventurer_prefix: "Пуштаме пет нивоа неделно."
|
||||
awaiting_levels_adventurer: "Зачлени се како Авантурист"
|
||||
awaiting_levels_adventurer_suffix: "за да бидеш првиот кој ќе ги игра новите нивоа."
|
||||
choose_your_level: "Избери го твоето ниво" # The rest of this section is the old play view at /play-old and isn't very important.
|
||||
adventurer_prefix: "Можеш да отидеш на било кое од подолните нивоа, или да дискутираш за нивоата на "
|
||||
adventurer_forum: "форумот на Авантуристите"
|
||||
adventurer_suffix: "."
|
||||
campaign_old_beginner: "Стара почетничка кампања"
|
||||
campaign_old_beginner_description: "... во која учиш за волшепството на програмирањето."
|
||||
campaign_dev: "Призволни потешки нивоа"
|
||||
campaign_dev_description: "... во кои го учиш интерфејсот додека правиш нешто малку потешко."
|
||||
campaign_multiplayer: "Арени за повеќе играчи"
|
||||
campaign_multiplayer_description: "... во кои кодираш лице-во-лице против други играчи."
|
||||
campaign_player_created: "Направено од играчи"
|
||||
campaign_player_created_description: "... се бориш наспроти креативноста на останатите играчи од <a href=\"/contribute/artisan\">Волшебничкиот занает</a>."
|
||||
campaign_classic_algorithms: "Класични алгоритми"
|
||||
campaign_classic_algorithms_description: "... во кои ги учиш најпопуларните алгоритми во компјутерската наука."
|
||||
|
||||
login:
|
||||
sign_up: "Направи Сметка"
|
||||
sign_up: "Направи сметка"
|
||||
log_in: "Најави се"
|
||||
logging_in: "Најавувањето е во тек"
|
||||
log_out: "Одјави се"
|
||||
forgot_password: "Ја заборави својата лозинка?"
|
||||
authenticate_gplus: "Провери G+ најава"
|
||||
load_profile: "Вчитај G+ Профил"
|
||||
load_email: "Вчитај G+ Email"
|
||||
load_profile: "Вчитај G+ профил"
|
||||
load_email: "Вчитај G+ e-mail"
|
||||
finishing: "Завршување"
|
||||
sign_in_with_facebook: "Најави се со Facebook"
|
||||
sign_in_with_gplus: "Најави се со G+"
|
||||
signup_switch: "Сакаш да направиш сметка?"
|
||||
|
||||
signup:
|
||||
email_announcements: "Примај соопштенија преку email"
|
||||
email_announcements: "Примај соопштенија преку e-mail"
|
||||
creating: "Сметката се прави..."
|
||||
sign_up: "Направи Сметка"
|
||||
sign_up: "Направи сметка"
|
||||
log_in: "најави се со лозинка"
|
||||
social_signup: "Или, можеш да се пријавиш преку Facebook или G+:"
|
||||
required: "Мораш да се најавиш за да имаш пристап таму."
|
||||
login_switch: "Веќе имаш сметка?"
|
||||
|
||||
recover:
|
||||
recover_account_title: "Врати Сметка"
|
||||
recover_account_title: "Врати сметка"
|
||||
send_password: "Испрати лозинка за враќање"
|
||||
recovery_sent: "Email-от за враќање на лозинката е испратен."
|
||||
recovery_sent: "E-mail-от за враќање на лозинката е испратен."
|
||||
|
||||
items:
|
||||
primary: "Главно"
|
||||
|
@ -151,37 +151,37 @@ module.exports = nativeDescription: "Македонски", englishDescription:
|
|||
help: "Помош"
|
||||
# watch: "Watch"
|
||||
# unwatch: "Unwatch"
|
||||
submit_patch: "Поднеси Закрпа"
|
||||
submit_changes: "Поднеси Промени"
|
||||
submit_patch: "Поднеси закрпа"
|
||||
submit_changes: "Поднеси промени"
|
||||
|
||||
# general:
|
||||
# and: "and"
|
||||
# name: "Name"
|
||||
# date: "Date"
|
||||
# body: "Body"
|
||||
# version: "Version"
|
||||
# submitter: "Submitter"
|
||||
# submitted: "Submitted"
|
||||
general:
|
||||
and: "и"
|
||||
name: "Име"
|
||||
date: "Датум"
|
||||
body: "Тело" # Original was 'Body'. Not sure if this is the best translation.
|
||||
version: "Верзија"
|
||||
submitter: "Подносител"
|
||||
submitted: "Поднесено"
|
||||
# commit_msg: "Commit Message"
|
||||
# review: "Review"
|
||||
# version_history: "Version History"
|
||||
# version_history_for: "Version History for: "
|
||||
version_history: "Историја на верзии"
|
||||
version_history_for: "Историја на верзии за: "
|
||||
# select_changes: "Select two changes below to see the difference."
|
||||
# undo: "Undo (Ctrl+Z)"
|
||||
# redo: "Redo (Ctrl+Shift+Z)"
|
||||
# play_preview: "Play preview of current level"
|
||||
# result: "Result"
|
||||
# results: "Results"
|
||||
# description: "Description"
|
||||
# or: "or"
|
||||
# subject: "Subject"
|
||||
# email: "Email"
|
||||
# password: "Password"
|
||||
# message: "Message"
|
||||
result: "Резултат"
|
||||
results: "Резултати"
|
||||
description: "Опис"
|
||||
or: "или"
|
||||
subject: "Предмет на пораката" # Original: 'Subject'. Translated as 'Message subject' because in macedonian the word 'Предмет' can have different meaning.
|
||||
email: "E-mail"
|
||||
password: "Лозинка"
|
||||
message: "Порака"
|
||||
# code: "Code"
|
||||
# ladder: "Ladder"
|
||||
# when: "When"
|
||||
# opponent: "Opponent"
|
||||
when: "Кога"
|
||||
opponent: "Противник"
|
||||
# rank: "Rank"
|
||||
# score: "Score"
|
||||
# win: "Win"
|
||||
|
@ -190,7 +190,7 @@ module.exports = nativeDescription: "Македонски", englishDescription:
|
|||
# easy: "Easy"
|
||||
# medium: "Medium"
|
||||
# hard: "Hard"
|
||||
# player: "Player"
|
||||
player: "Играч"
|
||||
# player_level: "Level" # Like player level 5, not like level: Dungeons of Kithgard
|
||||
|
||||
units:
|
||||
|
@ -209,45 +209,45 @@ module.exports = nativeDescription: "Македонски", englishDescription:
|
|||
year: "година"
|
||||
years: "години"
|
||||
|
||||
# play_level:
|
||||
# done: "Done"
|
||||
# home: "Home" # Not used any more, will be removed soon.
|
||||
# level: "Level" # Like "Level: Dungeons of Kithgard"
|
||||
# skip: "Skip"
|
||||
# game_menu: "Game Menu"
|
||||
# guide: "Guide"
|
||||
play_level:
|
||||
done: "Готово"
|
||||
home: "Дома" # Not used any more, will be removed soon.
|
||||
level: "Ниво" # Like "Level: Dungeons of Kithgard"
|
||||
skip: "Прескокни"
|
||||
game_menu: "Мени"
|
||||
guide: "Водич"
|
||||
# restart: "Restart"
|
||||
# goals: "Goals"
|
||||
# goal: "Goal"
|
||||
goals: "Цели"
|
||||
goal: "Цел"
|
||||
# running: "Running..."
|
||||
# success: "Success!"
|
||||
# incomplete: "Incomplete"
|
||||
# timed_out: "Ran out of time"
|
||||
success: "Успешно!"
|
||||
incomplete: "Некомплетирано"
|
||||
timed_out: "Истече времето"
|
||||
# failing: "Failing"
|
||||
# action_timeline: "Action Timeline"
|
||||
# click_to_select: "Click on a unit to select it."
|
||||
# control_bar_multiplayer: "Multiplayer"
|
||||
# control_bar_join_game: "Join Game"
|
||||
control_bar_multiplayer: "Повеќе играчи"
|
||||
control_bar_join_game: "Приклучи се во игра"
|
||||
# reload: "Reload"
|
||||
# reload_title: "Reload All Code?"
|
||||
# reload_really: "Are you sure you want to reload this level back to the beginning?"
|
||||
# reload_confirm: "Reload All"
|
||||
# victory: "Victory"
|
||||
victory: "Победа"
|
||||
# victory_title_prefix: ""
|
||||
# victory_title_suffix: " Complete"
|
||||
# victory_sign_up: "Sign Up to Save Progress"
|
||||
# victory_sign_up_poke: "Want to save your code? Create a free account!"
|
||||
# victory_rate_the_level: "Rate the level: " # Only in old-style levels.
|
||||
victory_sign_up: "Направи сметка за да го зачуваш напредокот"
|
||||
victory_sign_up_poke: "Сакаш да го зачуваш твојот код? Направи бесплатна сметка!"
|
||||
victory_rate_the_level: "Оцени го нивото: " # Only in old-style levels.
|
||||
# victory_return_to_ladder: "Return to Ladder"
|
||||
# victory_play_continue: "Continue"
|
||||
# victory_saving_progress: "Saving Progress"
|
||||
# victory_go_home: "Go Home" # Only in old-style levels.
|
||||
# victory_review: "Tell us more!" # Only in old-style levels.
|
||||
# victory_hour_of_code_done: "Are You Done?"
|
||||
victory_play_continue: "Продолжи"
|
||||
victory_saving_progress: "Напредокот се зачувува"
|
||||
victory_go_home: "Оди дома" # Only in old-style levels.
|
||||
victory_review: "Кажи ни повеќе!" # Only in old-style levels.
|
||||
victory_hour_of_code_done: "Дали си готов?"
|
||||
# victory_hour_of_code_done_yes: "Yes, I'm finished with my Hour of Code™!"
|
||||
# victory_experience_gained: "XP Gained"
|
||||
# victory_gems_gained: "Gems Gained"
|
||||
# guide_title: "Guide"
|
||||
victory_experience_gained: "Добиено искуство"
|
||||
victory_gems_gained: "Добиени скапоцени камења"
|
||||
guide_title: "Водич"
|
||||
# tome_minion_spells: "Your Minions' 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.
|
||||
|
@ -326,16 +326,16 @@ module.exports = nativeDescription: "Македонски", englishDescription:
|
|||
# multiplayer_caption: "Play with friends!"
|
||||
# auth_caption: "Save your progress."
|
||||
|
||||
# inventory:
|
||||
inventory:
|
||||
# choose_inventory: "Equip Items"
|
||||
# equipped_item: "Equipped"
|
||||
# required_purchase_title: "Required"
|
||||
# available_item: "Available"
|
||||
# restricted_title: "Restricted"
|
||||
required_purchase_title: "Задолжително"
|
||||
available_item: "Достапно"
|
||||
restricted_title: "Забрането"
|
||||
# should_equip: "(double-click to equip)"
|
||||
# equipped: "(equipped)"
|
||||
# locked: "(locked)"
|
||||
# restricted: "(restricted in this level)"
|
||||
locked: "(заклучено)"
|
||||
restricted: "(забрането во ова ниво)"
|
||||
# equip: "Equip"
|
||||
# unequip: "Unequip"
|
||||
|
||||
|
|
|
@ -14,6 +14,8 @@ module.exports = class LevelComponent extends CocoModel
|
|||
@PlansID: '524b7b517fc0f6d51900000d'
|
||||
@ProgrammableID: '524b7b5a7fc0f6d51900000e'
|
||||
@MovesID: '524b7b8c7fc0f6d519000013'
|
||||
@MissileID: '524cc2593ea855e0ab000142'
|
||||
@FindsPaths: '52872b0ead92b98561000002'
|
||||
urlRoot: '/db/level.component'
|
||||
|
||||
set: (key, val, options) ->
|
||||
|
|
|
@ -16,7 +16,7 @@ defaultTasks = [
|
|||
'Choose music file in Introduction script.'
|
||||
|
||||
'Add to a campaign.'
|
||||
'Publish for playtesting.'
|
||||
'Publish.'
|
||||
'Choose level options like required/restricted gear.'
|
||||
'Create achievements, including unlocking next level.'
|
||||
|
||||
|
@ -25,17 +25,17 @@ defaultTasks = [
|
|||
'Playtest with a couple random seeds.'
|
||||
'Make sure the level ends promptly on success and failure.'
|
||||
'Remove/simplify unnecessary doodad collision.'
|
||||
'Release to adventurers.'
|
||||
'Release to adventurers via MailChimp.'
|
||||
|
||||
'Write the description.'
|
||||
'Translate the sample code comments.'
|
||||
'Add Io/Clojure/Lua/CoffeeScript.'
|
||||
'Write the guide.'
|
||||
'Write a loading tip, if needed.'
|
||||
'Populate i18n.'
|
||||
'Click the Populate i18n button.'
|
||||
|
||||
'Mark whether it requires a subscription (after adventurer week).'
|
||||
'Release to everyone.'
|
||||
'Release to everyone via MailChimp.'
|
||||
|
||||
'Check completion/engagement/problem analytics.'
|
||||
'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.'}
|
||||
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.'}
|
||||
tasks: c.array {title: 'Tasks', description: 'Tasks to be completed for this ThangType.'}, c.task
|
||||
|
||||
|
||||
ThangTypeSchema.required = []
|
||||
|
||||
|
|
|
@ -21,3 +21,12 @@
|
|||
bottom: 0
|
||||
right: 0
|
||||
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
|
||||
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
|
||||
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
|
||||
#campaign-view
|
||||
#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
|
||||
|
|
|
@ -5,8 +5,42 @@
|
|||
a(href="/editor/level/#{level.get('slug')}", target="_blank") (edit)
|
||||
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')
|
||||
.tasks
|
||||
h3 Tasks (read only)
|
||||
|
|
|
@ -14,6 +14,7 @@ block tableHeader
|
|||
th(data-i18n="general.name") Name
|
||||
th(data-i18n="general.description") Description
|
||||
th(data-i18n="general.version") Version
|
||||
th(data-i18n="editor.tasks") Tasks
|
||||
|
||||
block tableBody
|
||||
for thang in documents
|
||||
|
@ -25,4 +26,10 @@ block tableBody
|
|||
| #{thang.get('name')}
|
||||
td.body-row #{thang.get('description')}
|
||||
- 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(data-i18n="play.players") players
|
||||
.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.isMobile = @isMobile()
|
||||
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
|
||||
|
||||
onClickBeginnerCampaign: (e) ->
|
||||
|
|
|
@ -6,7 +6,7 @@ app = require 'core/application'
|
|||
class SearchCollection extends Backbone.Collection
|
||||
initialize: (modelURL, @model, @term, @projection) ->
|
||||
@url = "#{modelURL}?project="
|
||||
if @projection? and not (@projection == [])
|
||||
if @projection?.length
|
||||
@url += 'created,permissions'
|
||||
@url += ',' + projected for projected in projection
|
||||
else @url += 'true'
|
||||
|
|
|
@ -47,6 +47,8 @@ module.exports = class CampaignEditorView extends RootView
|
|||
@listenToOnce @levels, 'sync', @onFundamentalLoaded
|
||||
@listenToOnce @achievements, 'sync', @onFundamentalLoaded
|
||||
|
||||
_.delay @getCampaignDropOffs, 1000
|
||||
|
||||
loadThangTypeNames: ->
|
||||
# Load the names of the ThangTypes that this level's Treema nodes might want to display.
|
||||
originals = []
|
||||
|
@ -130,6 +132,7 @@ module.exports = class CampaignEditorView extends RootView
|
|||
getRenderData: ->
|
||||
c = super()
|
||||
c.campaign = @campaign
|
||||
c.campaignDropOffs = @campaignDropOffs
|
||||
c
|
||||
|
||||
onClickSaveButton: ->
|
||||
|
@ -236,6 +239,35 @@ module.exports = class CampaignEditorView extends RootView
|
|||
achievement.set 'rewards', newRewards
|
||||
if achievement.hasLocalChanges()
|
||||
@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
|
||||
valueClass: 'treema-levels'
|
||||
|
|
|
@ -14,11 +14,61 @@ module.exports = class CampaignLevelView extends CocoView
|
|||
@fullLevel.fetch()
|
||||
@listenToOnce @fullLevel, 'sync', => @render?()
|
||||
|
||||
@levelSlug = @level.get('slug')
|
||||
@getLevelCompletions()
|
||||
@getLevelPlaytimes()
|
||||
|
||||
getRenderData: ->
|
||||
c = super()
|
||||
c.level = if @fullLevel.loaded then @fullLevel else @level
|
||||
c.levelCompletions = @levelCompletions
|
||||
c.levelPlaytimes = @levelPlaytimes
|
||||
c
|
||||
|
||||
onClickClose: ->
|
||||
@$el.addClass('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
|
||||
DEFAULT_COMPONENTS =
|
||||
Unit: [LC('Equips')]
|
||||
Hero: [LC('Equips')]
|
||||
Unit: [LC('Equips'), LC('FindsPaths')]
|
||||
Hero: [LC('Equips'), LC('FindsPaths')]
|
||||
Floor: [
|
||||
LC('Exists', stateless: true)
|
||||
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')]
|
||||
Mark: []
|
||||
Item: [LC('Item')]
|
||||
Missile: [LC('Missile')]
|
||||
|
||||
module.exports = class ThangComponentsEditView extends CocoView
|
||||
id: 'thang-components-edit-view'
|
||||
|
|
|
@ -23,6 +23,106 @@ storage = require 'core/storage'
|
|||
|
||||
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
|
||||
id: 'thang-type-edit-view'
|
||||
className: 'editor'
|
||||
|
@ -435,6 +535,8 @@ module.exports = class ThangTypeEditView extends RootView
|
|||
Backbone.Mediator.publish 'editor:thang-type-kind-changed', kind: kind
|
||||
if kind in ['Doodad', 'Floor', 'Wall'] and not @treema.data.terrains
|
||||
@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) =>
|
||||
selected = selected[0]
|
||||
|
|
|
@ -6,7 +6,7 @@ module.exports = class ThangTypeSearchView extends SearchView
|
|||
model: require 'models/ThangType'
|
||||
modelURL: '/db/thang.type'
|
||||
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'
|
||||
|
||||
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]))
|
||||
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):
|
||||
mongo_executable = "mongod"
|
||||
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);
|
||||
|
||||
// <level><date><data>
|
||||
|
|
|
@ -86,10 +86,10 @@ class LinuxMongoDBDownloader(MongoDBDownloader):
|
|||
@property
|
||||
def download_url(self):
|
||||
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:
|
||||
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):
|
||||
@property
|
||||
|
@ -97,13 +97,13 @@ class WindowsMongoDBDownloader(MongoDBDownloader):
|
|||
#TODO: Implement Windows Vista detection
|
||||
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:
|
||||
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:
|
||||
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):
|
||||
@property
|
||||
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
|
||||
|
||||
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?
|
||||
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 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
|
||||
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
|
||||
|
|
|
@ -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
|
||||
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.
|
||||
github.com/codecombat/codecombat/wiki/Setup-on-Windows:-a-step-by-step-guide
|
||||
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
|
||||
|
|
|
@ -47,7 +47,7 @@
|
|||
<opensource>CodeCombat is opensource, like you already know.</opensource>
|
||||
<online>All our sourcecode can be found online at Github.</online>
|
||||
<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>
|
||||
<skip>
|
||||
<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>
|
||||
<username>Username: </username>
|
||||
<password>Password: </password>
|
||||
<process>Thank you... Configuring your local repistory right now...</process>
|
||||
<process>Thank you... Configuring your local repository right now...</process>
|
||||
</config>
|
||||
</github>
|
||||
<switch>
|
||||
|
@ -82,7 +82,7 @@
|
|||
<binstall>Installing bower packages...</binstall>
|
||||
<sass>Installing sass...</sass>
|
||||
<npm>Installing npm...</npm>
|
||||
<brnch>Starting brunch....</brnch>
|
||||
<brnch>Starting brunch...</brnch>
|
||||
<mongodb>Setting up a MongoDB database for you...</mongodb>
|
||||
<db>Downloading the last version of the CodeCombat database...</db>
|
||||
<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>
|
||||
<s6>That's it, you're now ready to start working on CodeCombat!</s6>
|
||||
</start>
|
||||
</variables>
|
||||
</variables>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
AnalyticsLogEvent = require './AnalyticsLogEvent'
|
||||
Handler = require '../commons/Handler'
|
||||
log = require 'winston'
|
||||
|
||||
class AnalyticsLogEventHandler extends Handler
|
||||
modelClass: AnalyticsLogEvent
|
||||
|
@ -17,4 +18,280 @@ class AnalyticsLogEventHandler extends Handler
|
|||
instance.set('user', req.user._id)
|
||||
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()
|
||||
|
|
|
@ -72,6 +72,7 @@ LevelHandler = class LevelHandler extends Handler
|
|||
return @getHistogramData(req, res, args[0]) if args[1] is 'histogram_data'
|
||||
return @checkExistence(req, res, args[0]) if args[1] is 'exists'
|
||||
return @getPlayCountsBySlugs(req, res) if args[1] is 'play_counts'
|
||||
return @getLevelPlaytimesBySlugs(req, res) if args[1] is 'playtime_averages'
|
||||
super(arguments...)
|
||||
|
||||
fetchLevelByIDAndHandleErrors: (id, req, res, callback) ->
|
||||
|
@ -321,7 +322,7 @@ LevelHandler = class LevelHandler extends Handler
|
|||
@playCountCachedSince ?= new Date()
|
||||
if (new Date()) - @playCountCachedSince > 86400 * 1000 # Dumb cache expiration
|
||||
@playCountCache = {}
|
||||
@playCountCacheSince = new Date()
|
||||
@playCountCachedSince = new Date()
|
||||
cacheKey = levelIDs.join ','
|
||||
if playCounts = @playCountCache[cacheKey]
|
||||
return @sendSuccess res, playCounts
|
||||
|
@ -340,4 +341,53 @@ LevelHandler = class LevelHandler extends Handler
|
|||
return true if method is null or method is 'get'
|
||||
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()
|
||||
|
|
|
@ -33,6 +33,7 @@ ThangTypeHandler = class ThangTypeHandler extends Handler
|
|||
'tier'
|
||||
'extendedName'
|
||||
'unlockLevelName'
|
||||
'tasks'
|
||||
]
|
||||
|
||||
hasAccess: (req) ->
|
||||
|
|
|
@ -9,9 +9,12 @@ User = require '../../../server/users/User'
|
|||
# TODO: these tests have some rerun/cleanup issues
|
||||
# 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', ->
|
||||
|
||||
it 'registered user', (done) ->
|
||||
xit 'registered user', (done) ->
|
||||
clearModels [AnalyticsUsersActive], (err) ->
|
||||
expect(err).toBeNull()
|
||||
user = new User
|
||||
|
@ -29,7 +32,7 @@ describe 'Analytics', ->
|
|||
expect(activeUsers[0]?.get('event')).toEqual('register')
|
||||
done()
|
||||
|
||||
it 'level completed', (done) ->
|
||||
xit 'level completed', (done) ->
|
||||
clearModels [AnalyticsUsersActive], (err) ->
|
||||
expect(err).toBeNull()
|
||||
unittest.getNormalJoe (joe) ->
|
||||
|
@ -53,7 +56,7 @@ describe 'Analytics', ->
|
|||
expect(activeUsers[0]?.get('event')).toEqual('level-completed/lotr')
|
||||
done()
|
||||
|
||||
it 'level playtime', (done) ->
|
||||
xit 'level playtime', (done) ->
|
||||
clearModels [AnalyticsUsersActive], (err) ->
|
||||
expect(err).toBeNull()
|
||||
unittest.getNormalJoe (joe) ->
|
||||
|
|
Loading…
Reference in a new issue