diff --git a/app/application.coffee b/app/application.coffee index 11dc4ba15..b3f1d4bc3 100644 --- a/app/application.coffee +++ b/app/application.coffee @@ -8,6 +8,8 @@ CocoView = require 'views/kinds/CocoView' preventBackspace = (event) -> if event.keyCode is 8 and not elementAcceptsKeystrokes(event.srcElement or event.target) event.preventDefault() + else if (key.ctrl or key.command) and not key.alt and event.keyCode in [219, 221] # prevent Ctrl/Cmd + [ / ] + event.preventDefault() elementAcceptsKeystrokes = (el) -> # http://stackoverflow.com/questions/1495219/how-can-i-prevent-the-backspace-key-from-navigating-back diff --git a/app/lib/God.coffee b/app/lib/God.coffee index 71d91d82d..816bf3f92 100644 --- a/app/lib/God.coffee +++ b/app/lib/God.coffee @@ -152,7 +152,7 @@ class Angel @ids[@lastID] # https://github.com/codecombat/codecombat/issues/81 -- TODO: we need to wait for worker initialization first - infiniteLoopIntervalDuration: 15000 # check this often (must be more than the others added) + infiniteLoopIntervalDuration: 1500000 # check this often (must be more than the others added) infiniteLoopTimeoutDuration: 1500 # wait this long when we check abortTimeoutDuration: 500 # give in-process or dying workers this long to give up constructor: (@god) -> diff --git a/app/lib/surface/Mark.coffee b/app/lib/surface/Mark.coffee index ab9fef4cd..54cead432 100644 --- a/app/lib/surface/Mark.coffee +++ b/app/lib/surface/Mark.coffee @@ -140,7 +140,7 @@ module.exports = class Mark extends CocoClass worldZ = @sprite.thang.pos.z - @sprite.thang.depth / 2 @mark.alpha = 0.451 / Math.sqrt(worldZ / 2 + 1) else - pos ?= @sprite.displayObject + pos ?= @sprite?.displayObject @mark.x = pos.x @mark.y = pos.y if @name is 'highlight' diff --git a/app/lib/surface/SpriteBoss.coffee b/app/lib/surface/SpriteBoss.coffee index d9ecf8ed0..e5da46c50 100644 --- a/app/lib/surface/SpriteBoss.coffee +++ b/app/lib/surface/SpriteBoss.coffee @@ -259,6 +259,6 @@ module.exports = class SpriteBoss extends CocoClass target = thang?.target targetPos = thang?.targetPos targetPos = null if targetPos?.isZero?() # Null targetPos get serialized as (0, 0, 0) - @targetMark.toggle target or targetPos @targetMark.setSprite if target then @sprites[target.id] else null + @targetMark.toggle @targetMark.sprite or targetPos @targetMark.update if targetPos then @camera.worldToSurface targetPos else null diff --git a/app/locale/pt-PT.coffee b/app/locale/pt-PT.coffee index 40641becf..836669a2f 100644 --- a/app/locale/pt-PT.coffee +++ b/app/locale/pt-PT.coffee @@ -4,12 +4,12 @@ module.exports = nativeDescription: "Português europeu", englishDescription: "P saving: "A guardar..." sending: "A enviar..." cancel: "Cancelar" -# save: "Save" -# delay_1_sec: "1 second" -# delay_3_sec: "3 seconds" -# delay_5_sec: "5 seconds" -# manual: "Manual" -# fork: "Fork" + save: "Save" + delay_1_sec: "1 segundo" + delay_3_sec: "3 segundos" + delay_5_sec: "5 segundos" + manual: "Manual" + fork: "Fork" play: "Jogar" modal: @@ -31,28 +31,28 @@ module.exports = nativeDescription: "Português europeu", englishDescription: "P about: "Sobre" contact: "Contacto" twitter_follow: "Seguir" -# employers: "Employers" + employers: "Patrões" -# versions: -# save_version_title: "Save New Version" -# new_major_version: "New Major Version" -# cla_prefix: "To save changes, first you must agree to our" -# cla_url: "CLA" -# cla_suffix: "." -# cla_agree: "I AGREE" + versions: + save_version_title: "Guardar Nova Versão" + new_major_version: "Nova Versão Principal" + cla_prefix: "Para guardar as alterações, precisas concordar com o nosso" + cla_url: "CLA" + cla_suffix: "." + cla_agree: "EU CONCORDO" login: sign_up: "Criar conta" log_in: "Iniciar sessão" - log_out: "Terminar sessão" + log_out: "Sair" recover: "recuperar conta" recover: - recover_account_title: "recuperar conta" -# send_password: "Send Recovery Password" + recover_account_title: "Recuperar conta" + send_password: "Recuperar password" signup: -# create_account_title: "Create Account to Save Progress" + create_account_title: "Cria uma conta para guardar o teu progresso." description: "É grátis. Só precisamos de umas coisas e fica tudo pronto:" email_announcements: "Receber anúncios por e-mail" coppa: "13+ ou não-EUA " @@ -101,9 +101,9 @@ module.exports = nativeDescription: "Português europeu", englishDescription: "P learn_more: "Sabe mais sobre ser um Diplomata" subscribe_as_diplomat: "Subscrever como Diplomata" -# wizard_settings: -# title: "Wizard Settings" -# customize_avatar: "Customize Your Avatar" + wizard_settings: + title: "Definições do Wizard" + customize_avatar: "Altera o teu Avatar" account_settings: title: "Definições da Conta" @@ -122,7 +122,7 @@ module.exports = nativeDescription: "Português europeu", englishDescription: "P new_password_verify: "Verificar" email_subscriptions: "Subscrições de E-mail" email_announcements: "Anúncios" -# email_notifications_description: "Get periodic notifications for your account." + email_notifications_description: "Recebe notificações periódicas sobre a tua conta." email_announcements_description: "Recebe e-mails sobre as últimas novidades e desenvolvimentos no CodeCombat." contributor_emails: "E-mails para Contribuintes" contribute_prefix: "Estamos à procura de pessoas para se juntarem a nós! Visita a " @@ -136,11 +136,11 @@ module.exports = nativeDescription: "Português europeu", englishDescription: "P account_profile: edit_settings: "Editar Definições" profile_for_prefix: "Perfil de " -# profile_for_suffix: "" + profile_for_suffix: "" profile: "Perfil" user_not_found: "Nenhum utilizador encontrado. Verifica o URL?" gravatar_not_found_mine: "Não conseguimos encontrar o teu perfil associado com:" -# gravatar_not_found_email_suffix: "." + gravatar_not_found_email_suffix: "." gravatar_signup_prefix: "Regista-te no " gravatar_signup_suffix: " para começares!" gravatar_not_found_other: "Infelizmente, não existe nenhum perfil associado ao endereço de e-mail desta pessoa." @@ -164,95 +164,95 @@ module.exports = nativeDescription: "Português europeu", englishDescription: "P reload_title: "Recarregar todo o código?" reload_really: "Tens a certeza que queres recarregar este nível de volta ao início?" reload_confirm: "Recarregar tudo" -# victory_title_prefix: "" + victory_title_prefix: "" victory_title_suffix: " Concluído" -# victory_sign_up: "Sign Up to Save Progress" -# victory_sign_up_poke: "Want to save your code? Create a free account!" -# victory_rate_the_level: "Rate the level: " -# victory_play_next_level: "Play Next Level" -# victory_go_home: "Go Home" -# victory_review: "Tell us more!" -# victory_hour_of_code_done: "Are You Done?" -# victory_hour_of_code_done_yes: "Yes, I'm finished with my Hour of Code!" -# multiplayer_title: "Multiplayer Settings" -# multiplayer_link_description: "Give this link to anyone to have them join you." -# multiplayer_hint_label: "Hint:" -# multiplayer_hint: " Click the link to select all, then press ⌘-C or Ctrl-C to copy the link." -# multiplayer_coming_soon: "More multiplayer features to come!" -# guide_title: "Guide" -# tome_minion_spells: "Your Minions' Spells" -# tome_read_only_spells: "Read-Only Spells" -# tome_other_units: "Other Units" + victory_sign_up: "Cria uma conta para guardar o teu progresso" + victory_sign_up_poke: "Queres guardar o teu código? Cria uma conta grátis!" + victory_rate_the_level: "Classifica este nível: " + victory_play_next_level: "Jogar próximo nível" + victory_go_home: "Ir para a Home" + victory_review: "Conta-nos mais!" + victory_hour_of_code_done: "É tudo?" + victory_hour_of_code_done_yes: "Sim, a minha Hora de Código chegou ao fim!" + multiplayer_title: "Definições de Multiplayer" + multiplayer_link_description: "Dá este link a alguém para se juntar a ti." + multiplayer_hint_label: "Dica:" + multiplayer_hint: " Carrega no link para seleccionar tudp, depois pressiona ⌘-C ou Ctrl-C para copiar o link." + multiplayer_coming_soon: "Mais funcionalidades de multiplayer hão de vir!" + guide_title: "Guia" + tome_minion_spells: "Feitiços dos teus Minions" + tome_read_only_spells: "Feitiços Read-Only" + tome_other_units: "Outras Unidades" # tome_cast_button_castable: "Cast" # tome_cast_button_casting: "Casting" -# tome_cast_button_cast: "Spell Cast" + tome_cast_button_cast: "Lançar Feitiço" # tome_autocast_delay: "Autocast Delay" -# tome_select_spell: "Select a Spell" -# tome_select_a_thang: "Select Someone for " -# tome_available_spells: "Available Spells" -# hud_continue: "Continue (press shift-space)" -# spell_saved: "Spell Saved" + tome_select_spell: "Escolhe um Feitiço" + tome_select_a_thang: "Escolhe Alguém para " + tome_available_spells: "Feitiços disponíveis" + hud_continue: "Continuar (pressiona shift-space)" + spell_saved: "Feitiço Guardado" -# admin: -# av_title: "Admin Views" -# av_entities_sub_title: "Entities" -# av_entities_users_url: "Users" -# av_entities_active_instances_url: "Active Instances" -# av_other_sub_title: "Other" -# av_other_debug_base_url: "Base (for debugging base.jade)" -# u_title: "User List" -# lg_title: "Latest Games" + admin: + av_title: "Visualizações de Admin" + av_entities_sub_title: "Entidades" + av_entities_users_url: "utilizadores" + av_entities_active_instances_url: "Activar Instancias" + av_other_sub_title: "Outro" + av_other_debug_base_url: "Base (para fazer debug base.jade)" + u_title: "Lista de Utilizadores" + lg_title: "Últimos Jogos" -# editor: -# main_title: "CodeCombat Editors" -# main_description: "Build your own levels, campaigns, units and educational content. We provide all the tools you need!" -# article_title: "Article Editor" -# article_description: "Write articles that give players overviews of programming concepts which can be used across a variety of levels and campaigns." -# thang_title: "Thang Editor" -# thang_description: "Build units, defining their default logic, graphics and audio. Currently only supports importing Flash exported vector graphics." -# level_title: "Level Editor" -# level_description: "Includes the tools for scripting, uploading audio, and constructing custom logic to create all sorts of levels. Everything we use ourselves!" -# security_notice: "Many major features in these editors are not currently enabled by default. As we improve the security of these systems, they will be made generally available. If you'd like to use these features sooner, " -# contact_us: "contact us!" -# hipchat_prefix: "You can also find us in our" -# hipchat_url: "HipChat room." -# level_some_options: "Some Options?" -# level_tab_thangs: "Thangs" -# level_tab_scripts: "Scripts" -# level_tab_settings: "Settings" -# level_tab_components: "Components" -# level_tab_systems: "Systems" -# level_tab_thangs_title: "Current Thangs" -# level_tab_thangs_conditions: "Starting Conditions" -# level_tab_thangs_add: "Add Thangs" -# level_settings_title: "Settings" -# level_component_tab_title: "Current Components" -# level_component_btn_new: "Create New Component" -# level_systems_tab_title: "Current Systems" -# level_systems_btn_new: "Create New System" -# level_systems_btn_add: "Add System" -# level_components_title: "Back to All Thangs" -# level_components_type: "Type" -# level_component_edit_title: "Edit Component" -# level_system_edit_title: "Edit System" -# create_system_title: "Create New System" -# new_component_title: "Create New Component" -# new_component_field_system: "System" + editor: + main_title: "Editores para CodeCombat" + main_description: "Constrói os teus níveis, campanhas, unidades e conteúdo educacional. Nós fornecemos todas as ferramentas que precisas!" + article_title: "Editor de Artigos" + article_description: "Escreve artigos que dêem aos jogadores uma visão geral dos conceitos de programação que podem ser usados nos mais diversos níveis e campanhas." + thang_title: "Editor de Thang" + thang_description: "Constrói unidades, definindo a sua logica, visual e audio por defeito. De momento só é suportado 'importing Flash exported vector graphics'." + level_title: "Editor de níveis" + level_description: "Inclui ferramentas para a criação de scripts, upload de áudio, e construção de lógica personalizada para criar todos os tipos de níveis. Tudo o que nós usamos!" + security_notice: "Muitas das principais funcionalidades nestes editores não estão activas por defeito. À medida que a segurança destes sistemas é melhorada, eles serão disponibilizados. Se quiseres utilizar estas uncionalidades mais cedo, " + contact_us: "contacta-nos!" + hipchat_prefix: "Podes encontrar-nos no nosso" + hipchat_url: "canal HipChat." + level_some_options: "Algumas opções?" + level_tab_thangs: "Thangs" + level_tab_scripts: "Scripts" + level_tab_settings: "Configurações" + level_tab_components: "Componentes" + level_tab_systems: "Sistemas" + level_tab_thangs_title: "Thangs atuais" + level_tab_thangs_conditions: "Condições iniciais" + level_tab_thangs_add: "Adiciona Thangs" + level_settings_title: "Configurações" + level_component_tab_title: "Componentes atuais" + level_component_btn_new: "Cria um novo Componente" + level_systems_tab_title: "Sistemas atuais" + level_systems_btn_new: "Cria um novo Sistema" + level_systems_btn_add: "Adiciona um Sistema" + level_components_title: "Voltar para Todos os Thangs" + level_components_type: "Tipo" + level_component_edit_title: "Editar Componente" + level_system_edit_title: "Editar Sistema" + create_system_title: "Criar novo Sistema" + new_component_title: "Criar novo Componente" + new_component_field_system: "Sistema" -# article: -# edit_btn_preview: "Preview" -# edit_article_title: "Edit Article" + article: + edit_btn_preview: "Visualizar" + edit_article_title: "Editar Artigo" general: -# and: "and" + and: "e" or: "ou" name: "Nome" -# body: "Body" -# version: "Version" -# commit_msg: "Commit Message" -# version_history_for: "Version History for: " -# results: "Results" -# description: "Description" + body: "Corpo" + version: "Versão" + commit_msg: "Mensagem de Commit" + version_history_for: "Histórico de versões por: " + results: "Resultados" + description: "Descrição" email: "E-mail" message: "Mensagem" @@ -424,18 +424,18 @@ module.exports = nativeDescription: "Português europeu", englishDescription: "P # translating_diplomats: "Our Translating Diplomats:" # helpful_ambassadors: "Our Helpful Ambassadors:" -# classes: -# archmage_title: "Archmage" -# archmage_title_description: "(Coder)" -# artisan_title: "Artisan" -# artisan_title_description: "(Level Builder)" -# adventurer_title: "Adventurer" -# adventurer_title_description: "(Level Playtester)" -# scribe_title: "Scribe" -# scribe_title_description: "(Article Editor)" -# diplomat_title: "Diplomat" -# diplomat_title_description: "(Translator)" -# ambassador_title: "Ambassador" -# ambassador_title_description: "(Support)" -# counselor_title: "Counselor" -# counselor_title_description: "(Expert/Teacher)" + classes: + archmage_title: "Archmage" + archmage_title_description: "(Coder)" + artisan_title: "Artisan" + artisan_title_description: "(Construtor de Níveis)" + adventurer_title: "Adventurer" + adventurer_title_description: "(Play-tester de Níveis)" + scribe_title: "Scribe" + scribe_title_description: "(Editor de Artigos)" + diplomat_title: "Diplomat" + diplomat_title_description: "(Tradutor)" + ambassador_title: "Ambassador" + ambassador_title_description: "(Suporte)" + counselor_title: "Counselor" + counselor_title_description: "(Expert/ Professor)" diff --git a/app/styles/editor/level/thangs_tab.sass b/app/styles/editor/level/thangs_tab.sass index 2caa3552e..bffe51f57 100644 --- a/app/styles/editor/level/thangs_tab.sass +++ b/app/styles/editor/level/thangs_tab.sass @@ -22,11 +22,41 @@ #thangs-treema position: absolute - top: 40px + top: 80px left: 0 right: 0 bottom: 0 overflow: scroll + + &.hide-except-Unit + .treema-node + display: none + .treema-node.treema-Unit + display: block + + &.hide-except-Doodad + .treema-node + display: none + .treema-node.treema-Doodad + display: block + + &.hide-except-Floor + .treema-node + display: none + .treema-node.treema-Floor + display: block + + &.hide-except-Wall + .treema-node + display: none + .treema-node.treema-Wall + display: block + + &.hide-except-Misc + .treema-node + display: none + .treema-node.treema-Misc + display: block .treema-children .treema-row * cursor: pointer !important diff --git a/app/styles/play/level/tome/cast_button.sass b/app/styles/play/level/tome/cast_button.sass index a77eeb3c7..6543804f9 100644 --- a/app/styles/play/level/tome/cast_button.sass +++ b/app/styles/play/level/tome/cast_button.sass @@ -30,11 +30,13 @@ color: white #cast-button-view + display: none + .cast-button-group position: absolute - // Bottom/right margins must appear to scroll to size of any paper gashes - top: 2% - right: 4% + top: 55px + left: 20px + z-index: 2 @include opacity(77) .button-progress-overlay diff --git a/app/styles/play/level/tome/spell.sass b/app/styles/play/level/tome/spell.sass index 0ff18cf23..684181e16 100644 --- a/app/styles/play/level/tome/spell.sass +++ b/app/styles/play/level/tome/spell.sass @@ -32,10 +32,11 @@ .ace_editor @include box-sizing(border-box) + margin-top: 40px width: 100% - height: 90% - height: -webkit-calc(100% - 60px) - height: calc(100% - 60px) + height: 83% + height: -webkit-calc(100% - 60px - 40px) + height: calc(100% - 60px - 40px) position: relative background-color: transparent line-height: 20px @@ -75,11 +76,10 @@ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA2hpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYxIDY0LjE0MDk0OSwgMjAxMC8xMi8wNy0xMDo1NzowMSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDowMjgwMTE3NDA3MjA2ODExOEE2REU4Q0M1MTM1MkIxRiIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDpBQjVEQUNDMzQ4RUIxMUUxOEVGRUUyNzFENDM3RDVFMCIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpBQjVEQUNDMjQ4RUIxMUUxOEVGRUUyNzFENDM3RDVFMCIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M1LjEgTWFjaW50b3NoIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6QTU1MjE3RDIzMTIwNjgxMThEQkI4NTlBMjQ1QTEwOTUiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MDI4MDExNzQwNzIwNjgxMThBNkRFOENDNTEzNTJCMUYiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz7SazaGAAAAiElEQVR42mL8//8/AzUBEwOVweA3kAWboI2jCyhgDwBx4ZH9ey5Qy4UOQHweaHg/EAtQ08sFUIMDqBmGCkC8HmgoCCtQM1ICoK5toGYsg8KzHmjo+UGbDj8AcSMwORkSnQ7xgA3QtPmApISNBTyAGrSBGl6eAMSGxBhGyIVkZT3G0fKQYgAQYACL+C2ZM6PC7AAAAABJRU5ErkJggg==) background-position: 0px center - //.user-code-problem - //.ace_scroller - // background-color: #470000 - .ace_marker-layer .ace_bracket // Override faint gray - border-color: #0FF + border-color: #8FF + + .ace_identifier + background-color: rgba(255, 128, 128, 0.15) diff --git a/app/styles/play/level/tome/spell_debug.sass b/app/styles/play/level/tome/spell_debug.sass new file mode 100644 index 000000000..d0f97a28f --- /dev/null +++ b/app/styles/play/level/tome/spell_debug.sass @@ -0,0 +1,10 @@ +@import "../../../bootstrap/mixins" + +.spell-debug-view + position: absolute + z-index: 9001 + max-width: 400px + padding: 10px + background: transparent url(/images/level/popover_background.png) + background-size: 100% 100% + diff --git a/app/styles/play/level/tome/spell_toolbar.sass b/app/styles/play/level/tome/spell_toolbar.sass new file mode 100644 index 000000000..7db7a8e40 --- /dev/null +++ b/app/styles/play/level/tome/spell_toolbar.sass @@ -0,0 +1,54 @@ +@import "../../../bootstrap/mixins" + +.spell-toolbar-view + position: absolute + z-index: 2 + top: 2px + left: 5px + box-sizing: border-box + padding-left: 150px + height: 36px + width: 95% + width: -webkit-calc(95% - 5px) + width: calc(95% - 5px) + background-color: rgba(100, 45, 210, 0.05) + + .spell-progress + position: relative + height: 100% + width: 50% + display: inline-block + + .progress + position: absolute + left: 0px + top: 8px + bottom: 0px + width: 100% + cursor: pointer + overflow: visible + + .bar + @include transition(width .0s linear) + position: relative + pointer-events: none + background-color: #67A4C8 + width: 50% + + .scrubber-handle + position: absolute + pointer-events: none + right: -16px + top: -7px + background: transparent url(/images/level/playback_thumb.png) + width: 32px + height: 32px + + .btn-group + // I don't know, I can figure this out for real later + margin: -26px 0 0 18px + + .metrics + display: inline-block + margin: -30px 0 0 10px + vertical-align: middle diff --git a/app/templates/editor/level/thangs_tab.jade b/app/templates/editor/level/thangs_tab.jade index e3d74429e..431c1e27e 100644 --- a/app/templates/editor/level/thangs_tab.jade +++ b/app/templates/editor/level/thangs_tab.jade @@ -1,5 +1,17 @@ .thangs-container.thangs-column h3(data-i18n="editor.level_tab_thangs_title") Current Thangs + .btn-group(data-toggle="buttons-radio")#extant-thangs-filter + button.btn.btn-primary All + button.btn.btn-primary(value="Unit") + i.icon-user + button.btn.btn-primary(value="Wall") + i.icon-home + button.btn.btn-primary(value="Floor") + i.icon-globe + button.btn.btn-primary(value="Doodad") + i.icon-leaf + button.btn.btn-primary(value="Misc") + i.icon-question-sign #thangs-treema(title="Double click to configure a thang") .world-container.thangs-column diff --git a/app/templates/play/level/playback.jade b/app/templates/play/level/playback.jade index e8f25e17c..e18b55f6a 100644 --- a/app/templates/play/level/playback.jade +++ b/app/templates/play/level/playback.jade @@ -1,4 +1,4 @@ -button.btn.btn-mini.btn-inverse#play-button.playing(title="Alt-P: Toggle level play/pause") +button.btn.btn-mini.btn-inverse#play-button.playing(title="Ctrl/Cmd + P: Toggle level play/pause") i.icon-play.icon-white.big i.icon-pause.icon-white.big i.icon-repeat.icon-white.big @@ -16,19 +16,19 @@ button.btn.btn-mini.btn-inverse#music-button(title="Toggle Music") .scrubber-handle .btn-group.dropup#playback-settings - button.btn.btn-mini.btn-inverse#zoom-in-button + button.btn.btn-mini.btn-inverse#zoom-in-button(title="Zoom In (or scroll down)") i.icon-zoom-in.icon-white - button.btn.btn-mini.btn-inverse#zoom-out-button + button.btn.btn-mini.btn-inverse#zoom-out-button(title="Zoom Out (or scroll up)") i.icon-zoom-out.icon-white button.btn.btn-mini.btn-inverse.dropdown-toggle(data-toggle="dropdown")#settings-button i.icon-cog.icon-white.big ul.dropdown-menu if me.get('name') == "Nick" - li(title="\\: Toggle debug display").selectable#debug-toggle + li(title="Ctrl/Cmd + \\: Toggle debug display").selectable#debug-toggle i.icon-globe | Debug Mode i.icon-ok.hide - li(title="G: Toggle grid display").selectable#grid-toggle + li(title="Ctrl/Cmd + G: Toggle grid display").selectable#grid-toggle i.icon-th span(data-i18n="play_level.grid") Grid i.icon-ok.hide diff --git a/app/templates/play/level/tome/spell_debug.jade b/app/templates/play/level/tome/spell_debug.jade new file mode 100644 index 000000000..428446e3a --- /dev/null +++ b/app/templates/play/level/tome/spell_debug.jade @@ -0,0 +1,2 @@ +pre + code \ No newline at end of file diff --git a/app/templates/play/level/tome/spell_toolbar.jade b/app/templates/play/level/tome/spell_toolbar.jade new file mode 100644 index 000000000..3eb45cca2 --- /dev/null +++ b/app/templates/play/level/tome/spell_toolbar.jade @@ -0,0 +1,22 @@ +.spell-progress + .progress + .bar + .scrubber-handle + +.btn-group + button.btn.btn-mini.btn-inverse.banner.step-backward(title="Ctrl/Cmd + Alt + [: Step Backward") + i.icon-arrow-left.icon-white + button.btn.btn-mini.btn-inverse.banner.step-forward(title="Ctrl/Cmd + Alt + ]: Step Forward") + i.icon-arrow-right.icon-white + +.metrics + code.statements-metric + span.metric.statement-index + | / + span.metric.statements-executed + span.metric.statements-executed-total + | + code.calls-metric + span.metric.call-index + | / + span.metric.calls-executed diff --git a/app/views/editor/level/thangs_tab_view.coffee b/app/views/editor/level/thangs_tab_view.coffee index c03b81724..9c7994c83 100644 --- a/app/views/editor/level/thangs_tab_view.coffee +++ b/app/views/editor/level/thangs_tab_view.coffee @@ -43,10 +43,21 @@ module.exports = class ThangsTabView extends View 'sprite:mouse-up': 'onSpriteMouseUp' 'sprite:double-clicked': 'onSpriteDoubleClicked' 'surface:stage-mouse-down': 'onStageMouseDown' - + + events: + 'click #extant-thangs-filter button': 'onFilterExtantThangs' + shortcuts: 'esc': -> @selectAddThang() + onFilterExtantThangs: (e) -> + button = $(e.target).closest('button') + button.button('toggle') + val = button.val() + @thangsTreema.$el.removeClass(@lastHideClass) if @lastHideClass + @thangsTreema.$el.addClass(@lastHideClass = "hide-except-#{val}") if val + + constructor: (options) -> super options @world = options.world @@ -87,12 +98,20 @@ module.exports = class ThangsTabView extends View return if @startsLoading super() $('.tab-content').click @selectAddThang + $('#thangs-list').bind 'mousewheel', @preventBodyScrollingInThangList + @$el.find('#extant-thangs-filter button:first').button('toggle') + + # TODO: move these into the shortcuts list key 'left', _.bind @moveAddThangSelection, @, -1 key 'right', _.bind @moveAddThangSelection, @, 1 key 'delete, del, backspace', @deleteSelectedExtantThang key 'f', => Backbone.Mediator.publish('level-set-debug', debug: not @surface.debug) key 'g', => Backbone.Mediator.publish('level-set-grid', grid: not @surface.gridShowing()) + preventBodyScrollingInThangList: (e) -> + @scrollTop += (if e.deltaY < 0 then 1 else -1) * 30 + e.preventDefault() + onLevelLoaded: (e) -> @level = e.level return if @startsLoading @@ -188,9 +207,12 @@ module.exports = class ThangsTabView extends View else if @addThangSprite # We clicked on the background when we had an add Thang selected, so add it @addThang @addThangType, @addThangSprite.thang.pos - else - # We clicked on the background, so deselect anything selected - @thangsTreema.deselectAll() + + # Commented out this bit so the extant thangs treema editor can select invisible thangs like arrows. + # Couldn't spot any bugs... But if there are any, better come up with a better solution. +# else +# # We clicked on the background, so deselect anything selected +# @thangsTreema.deselectAll() selectAddThang: (e) => if e then target = $(e.target) else target = @$el.find('.add-thangs-palette') # pretend to click on background if no event @@ -370,6 +392,7 @@ class ThangNode extends TreemaObjectNode valueClass: 'treema-thang' collection: false @thangNameMap: {} + @thangKindMap: {} buildValueForDisplay: (valEl) -> pos = _.find(@data.components, (c) -> c.config?.pos?)?.config.pos # TODO: hack s = "#{@data.thangType}" @@ -377,6 +400,9 @@ class ThangNode extends TreemaObjectNode unless name = ThangNode.thangNameMap[s] thangType = _.find @settings.supermodel.getModels(ThangType), (m) -> m.get('original') is s name = ThangNode.thangNameMap[s] = thangType.get 'name' + ThangNode.thangKindMap[s] = thangType.get 'kind' + kind = ThangNode.thangKindMap[s] + @$el.addClass "treema-#{kind}" s = name s += " - " + @data.id if @data.id isnt s if pos diff --git a/app/views/play/level/tome/cast_button_view.coffee b/app/views/play/level/tome/cast_button_view.coffee index 4b17f4288..f8526ba1c 100644 --- a/app/views/play/level/tome/cast_button_view.coffee +++ b/app/views/play/level/tome/cast_button_view.coffee @@ -24,7 +24,6 @@ module.exports = class CastButtonView extends View context.castShortcutVerbose = @castShortcutVerbose context - afterRender: -> super() # TODO: use a User setting instead of localStorage diff --git a/app/views/play/level/tome/spell.coffee b/app/views/play/level/tome/spell.coffee index 19f88bd3a..84f6cd906 100644 --- a/app/views/play/level/tome/spell.coffee +++ b/app/views/play/level/tome/spell.coffee @@ -64,7 +64,11 @@ module.exports = class Spell functionParameters: @parameters yieldConditionally: thang.plan? requiresThis: thang.requiresThis - if @name is 'chooseAction' or not (me.team in @permissions.readwrite) or thang.id is 'Thoktar' # Gridmancer can't handle it + includeFlow: true + #callIndex: 0 + #timelessVariables: ['i'] + #statementIndex: 9001 + if not (me.team in @permissions.readwrite)# or @name is 'chooseAction' or thang.id is 'Thoktar' # Gridmancer can't handle it #console.log "Turning off includeFlow for", @spellKey aetherOptions.includeFlow = false aether = new Aether aetherOptions diff --git a/app/views/play/level/tome/spell_debug_view.coffee b/app/views/play/level/tome/spell_debug_view.coffee new file mode 100644 index 000000000..bc613c2bf --- /dev/null +++ b/app/views/play/level/tome/spell_debug_view.coffee @@ -0,0 +1,63 @@ +View = require 'views/kinds/CocoView' +template = require 'templates/play/level/tome/spell_debug' +Range = ace.require("ace/range").Range + +module.exports = class DebugView extends View + className: 'spell-debug-view' + template: template + subscriptions: {} + events: {} + + constructor: (options) -> + super options + @ace = options.ace + @variableStates = {} + + afterRender: -> + super() + @ace.on "mousemove", @onMouseMove + #@ace.on "click", onClick # same ACE API as mousemove + + setVariableStates: (@variableStates) -> + @update() + + onMouseMove: (e) => + pos = e.getDocumentPosition() + column = pos.column + until column < 0 + if token = e.editor.session.getTokenAt pos.row, column + break if token.type is 'identifier' + column = token.start - 1 + else + --column + if token?.type is 'identifier' and token.value of @variableStates + @variable = token.value + @pos = {left: e.domEvent.offsetX + 50, top: e.domEvent.offsetY + 50} + @markerRange = new Range pos.row, token.start, pos.row, token.start + token.value.length + else + @variable = @markerRange = null + @update() + + onMouseOut: (e) => + @variable = @markerRange = null + @update() + + update: -> + if @variable + value = @variableStates[@variable] + @$el.find("code").text "#{@variable}: #{value}" + @$el.show().css(@pos) + else + @$el.hide() + @updateMarker() + + updateMarker: -> + if @marker + @ace.getSession().removeMarker @marker + @marker = null + if @markerRange + @marker = @ace.getSession().addMarker @markerRange, "ace_bracket", "text" + + destroy: -> + super() + @ace?.removeEventListener "mousemove", @onMouseMove diff --git a/app/views/play/level/tome/spell_toolbar_view.coffee b/app/views/play/level/tome/spell_toolbar_view.coffee new file mode 100644 index 000000000..573689742 --- /dev/null +++ b/app/views/play/level/tome/spell_toolbar_view.coffee @@ -0,0 +1,94 @@ +View = require 'views/kinds/CocoView' +template = require 'templates/play/level/tome/spell_toolbar' + +module.exports = class SpellToolbarView extends View + className: 'spell-toolbar-view' + template: template + + subscriptions: + 'spell-step-backward': 'onStepBackward' + 'spell-step-forward': 'onStepForward' + + events: + 'mousemove .progress': 'onProgressHover' + 'mouseout .progress': 'onProgressMouseOut' + 'click .step-backward': 'onStepBackward' + 'click .step-forward': 'onStepForward' + + constructor: (options) -> + super options + @ace = options.ace + + afterRender: -> + super() + + setStatementIndex: (statementIndex) -> + return unless total = @callState?.statementsExecuted + @statementIndex = Math.min(total - 1, Math.max(0, statementIndex)) + @statementRatio = @statementIndex / (total - 1) + @statementTime = @callState.statements[@statementIndex].userInfo.time + @$el.find('.bar').css('width', 100 * @statementRatio + '%') + Backbone.Mediator.publish 'tome:spell-statement-index-updated', statementIndex: @statementIndex, ace: @ace + @$el.find('.step-backward').prop('disabled', @statementIndex is 0) + @$el.find('.step-forward').prop('disabled', @statementIndex is total - 1) + @updateMetrics() + + updateMetrics: -> + statementsExecuted = @callState.statementsExecuted + $metrics = @$el.find('.metrics') + if @metrics.callsExecuted > 1 + $metrics.find('.call-index').text @callIndex + 1 + $metrics.find('.calls-executed').text @metrics.callsExecuted + $metrics.find('.calls-metric').show().attr('title', "Method call #{@callIndex + 1} of #{@metrics.callsExecuted} calls") + else + $metrics.find('.calls-metric').hide() + if @metrics.statementsExecuted + $metrics.find('.statement-index').text @statementIndex + 1 + $metrics.find('.statements-executed').text statementsExecuted + if @metrics.statementsExecuted > statementsExecuted + $metrics.find('.statements-executed-total').text " (#{@metrics.statementsExecuted})" + titleSuffix = " (#{@metrics.statementsExecuted} statements total)" + else + $metrics.find('.statements-executed-total').text "" + titleSuffix = "" + $metrics.find('.statements-metric').show().attr('title', "Statement #{@statementIndex + 1} of #{statementsExecuted} this call#{titleSuffix}") + else + $metrics.find('.statements-metric').hide() + + setStatementRatio: (ratio) -> + return unless total = @callState?.statementsExecuted + @setStatementIndex Math.floor ratio * total + + onProgressHover: (e) -> + @setStatementRatio e.offsetX / @$el.find('.progress').width() + @updateTime() + @maintainIndexHover = true + + onProgressMouseOut: (e) -> + @maintainIndexHover = false + + onStepBackward: (e) -> @step -1 + onStepForward: (e) -> @step 1 + step: (delta) -> + lastTime = @statementTime + @setStatementIndex @statementIndex + delta + @updateTime() if @statementIndex isnt lastTime + + updateTime: -> + @maintainIndexScrub = true + clearTimeout @maintainIndexScrubTimeout if @maintainIndexScrubTimeout + @maintainIndexScrubTimeout = _.delay (=> @maintainIndexScrub = false), 500 + Backbone.Mediator.publish 'level-set-time', time: @statementTime, scrubDuration: 500 + + setCallState: (callState, statementIndex, @callIndex, @metrics) -> + return if callState is @callState and statementIndex is @statementIndex + return unless @callState = callState + if not @maintainIndexHover and not @maintainIndexScrub and statementIndex? and callState.statements[statementIndex].userInfo.time isnt @statementTime + @setStatementIndex statementIndex + else + @setStatementRatio @statementRatio + # Not sure yet whether it's better to maintain @statementIndex or @statementRatio + #else if @statementRatio is 1 or not @statementIndex? + # @setStatementRatio 1 + #else + # @setStatementIndex @statementIndex diff --git a/app/views/play/level/tome/spell_view.coffee b/app/views/play/level/tome/spell_view.coffee index 33314e2e9..7d319cef4 100644 --- a/app/views/play/level/tome/spell_view.coffee +++ b/app/views/play/level/tome/spell_view.coffee @@ -4,6 +4,8 @@ template = require 'templates/play/level/tome/spell' filters = require 'lib/image_filter' Range = ace.require("ace/range").Range Problem = require './problem' +SpellDebugView = require './spell_debug_view' +SpellToolbarView = require './spell_toolbar_view' module.exports = class SpellView extends View id: 'spell-view' @@ -25,9 +27,10 @@ module.exports = class SpellView extends View 'level:session-will-save': 'onSessionWillSave' 'modal-closed': 'focus' 'focus-editor': 'focus' + 'tome:spell-statement-index-updated': 'onStatementIndexUpdated' events: - 'click .ace': -> console.log 'clicked ace', @ + 'mouseout': 'onMouseOut' constructor: (options) -> super options @@ -48,6 +51,7 @@ module.exports = class SpellView extends View else # needs to happen after the code generating this view is complete setTimeout @onLoaded, 1 + @createDebugView() createACE: -> # Test themes and settings here: http://ace.ajax.org/build/kitchen-sink.html @@ -86,24 +90,30 @@ module.exports = class SpellView extends View name: 'end-all-scripts' bindKey: {win: 'Escape', mac: 'Escape'} exec: -> Backbone.Mediator.publish 'level:escape-pressed' - - # TODO: These don't work on, for example, Danish keyboards. Figure out a more universal solution. -# @ace.commands.addCommand -# name: 'toggle-grid' -# bindKey: {win: 'Alt-G', mac: 'Alt-G'} -# exec: -> Backbone.Mediator.publish 'level-toggle-grid' -# @ace.commands.addCommand -# name: 'toggle-debug' -# bindKey: {win: 'Alt-\\', mac: 'Alt-\\'} -# exec: -> Backbone.Mediator.publish 'level-toggle-debug' -# @ace.commands.addCommand -# name: 'level-scrub-forward' -# bindKey: {win: 'Alt-]', mac: 'Alt-]'} -# exec: -> Backbone.Mediator.publish 'level-scrub-forward' -# @ace.commands.addCommand -# name: 'level-scrub-back' -# bindKey: {win: 'Alt-[', mac: 'Alt-['} -# exec: -> Backbone.Mediator.publish 'level-scrub-back' + @ace.commands.addCommand + name: 'toggle-grid' + bindKey: {win: 'Ctrl-G', mac: 'Command-G|Ctrl-G'} + exec: -> Backbone.Mediator.publish 'level-toggle-grid' + @ace.commands.addCommand + name: 'toggle-debug' + bindKey: {win: 'Ctrl-\\', mac: 'Command-\\|Ctrl-\\'} + exec: -> Backbone.Mediator.publish 'level-toggle-debug' + @ace.commands.addCommand + name: 'level-scrub-forward' + bindKey: {win: 'Ctrl-]', mac: 'Command-]|Ctrl-]'} + exec: -> Backbone.Mediator.publish 'level-scrub-forward' + @ace.commands.addCommand + name: 'level-scrub-back' + bindKey: {win: 'Ctrl-[', mac: 'Command-[|Ctrl-]'} + exec: -> Backbone.Mediator.publish 'level-scrub-back' + @ace.commands.addCommand + name: 'spell-step-forward' + bindKey: {win: 'Ctrl-Alt-]', mac: 'Command-Alt-]|Ctrl-Alt-]'} + exec: -> Backbone.Mediator.publish 'spell-step-forward' + @ace.commands.addCommand + name: 'spell-step-backward' + bindKey: {win: 'Ctrl-Alt-[', mac: 'Command-Alt-[|Ctrl-Alt-]'} + exec: -> Backbone.Mediator.publish 'spell-step-backward' fillACE: -> @ace.setValue @spell.source @@ -145,6 +155,17 @@ module.exports = class SpellView extends View Backbone.Mediator.publish 'tome:spell-loaded', spell: @spell @eventsSuppressed = false # Now that the initial change is in, we can start running any changed code + createDebugView: -> + @debugView = new SpellDebugView ace: @ace + @$el.append @debugView.render().$el.hide() + + createToolbarView: -> + @toolbarView = new SpellToolbarView ace: @ace + @$el.prepend @toolbarView.render().$el + + onMouseOut: (e) -> + @debugView.onMouseOut e + getSource: -> @ace.getValue() # could also do @firepad.getText() @@ -368,22 +389,33 @@ module.exports = class SpellView extends View @thang = e.selectedThang # update our thang to the current version @highlightCurrentLine() + onStatementIndexUpdated: (e) -> + return unless e.ace is @ace + @highlightCurrentLine() + highlightCurrentLine: (flow) => + # TODO: move this whole thing into SpellDebugView or somewhere? flow ?= @spellThang?.castAether?.flow return unless flow executed = [] matched = false - for callState, callNumber in flow.states or [] + states = flow.states ? [] + currentCallIndex = null + for callState, callNumber in states + if not currentCallIndex? and callState.userInfo?.time > @thang.world.age + currentCallIndex = callNumber - 1 if matched executed.pop() break executed.push [] - for state, statementNumber in callState + for state, statementNumber in callState.statements if state.userInfo?.time > @thang.world.age matched = true break _.last(executed).push state #state.executing = true if state.userInfo?.time is @thang.world.age # no work + currentCallIndex ?= callNumber - 1 + #console.log "got call index", currentCallIndex, "for time", @thang.world.age, "out of", states.length # TODO: don't redo the markers if they haven't actually changed text = @aceDoc.getValue() @@ -397,11 +429,20 @@ module.exports = class SpellView extends View markerRange.end.detach() @aceSession.removeMarker markerRange.id @markerRanges = [] + @debugView.setVariableStates {} @aceSession.removeGutterDecoration row, 'executing' for row in [0 ... @aceSession.getLength()] $(@ace.container).find('.ace_gutter-cell.executing').removeClass('executing') - return unless executed.length + unless executed.length + @toolbarView?.$el.hide() + return + unless @toolbarView or (@spell.name is "plan" and @spellThang.castAether.metrics.statementsExecuted < 20) + @createToolbarView() lastExecuted = _.last executed + @toolbarView?.$el.show() + statementIndex = Math.max 0, lastExecuted.length - 1 + @toolbarView?.setCallState states[currentCallIndex], statementIndex, currentCallIndex, @spellThang.castAether.metrics marked = {} + lastExecuted = lastExecuted[0 .. @toolbarView.statementIndex] if @toolbarView?.statementIndex? for state, i in lastExecuted #clazz = if state.executing then 'executing' else 'executed' # doesn't work clazz = if i is lastExecuted.length - 1 then 'executing' else 'executed' @@ -410,6 +451,9 @@ module.exports = class SpellView extends View continue if marked[key] > 2 # don't allow more than three of the same marker marked[key] ?= 0 ++marked[key] + else + @debugView.setVariableStates state.variables + #console.log "at", state.userInfo.time, "vars are now:", state.variables [start, end] = [offsetToPos(state.range[0]), offsetToPos(state.range[1])] markerRange = new Range(start.row, start.column, end.row, end.column) markerRange.start = @aceDoc.createAnchor markerRange.start @@ -454,3 +498,4 @@ module.exports = class SpellView extends View super() @firepad?.dispose() @ace.destroy() + @debugView.destroy() diff --git a/app/views/play/level/tome/tome_view.coffee b/app/views/play/level/tome/tome_view.coffee index 6b43cd3e0..e87fc3949 100644 --- a/app/views/play/level/tome/tome_view.coffee +++ b/app/views/play/level/tome/tome_view.coffee @@ -114,6 +114,7 @@ module.exports = class TomeView extends View @spellTabView?.$el.after('
').detach() @spellTabView = null @removeSubView @spellPaletteView if @spellPaletteView + @castButton?.$el.hide() @thangList?.$el.show() onSpriteSelected: (e) -> @@ -137,6 +138,7 @@ module.exports = class TomeView extends View @$el.find('#' + @spellTabView.id).after(@spellTabView.el).remove() @spellView.setThang thang @spellTabView.setThang thang + @castButton.$el.show() @thangList.$el.hide() @spellPaletteView = @insertSubView new SpellPaletteView thang: thang @spellPaletteView.toggleControls {}, @spellView.controlsEnabled # TODO: know when palette should have been disabled but didn't exist