diff --git a/app/assets/javascripts/run-tests.js b/app/assets/javascripts/run-tests.js index ad1de1989..5165dd49d 100644 --- a/app/assets/javascripts/run-tests.js +++ b/app/assets/javascripts/run-tests.js @@ -2,5 +2,6 @@ // Hooks into the test view logic for running tests. require('initialize'); +console.debug = function() {}; // Karma conf doesn't seem to work? Debug messages are still emitted when they shouldn't be. TestView = require('views/TestView'); TestView.runTests(); \ No newline at end of file diff --git a/app/lib/LevelLoader.coffee b/app/lib/LevelLoader.coffee index 7c7ead9ea..48a980831 100644 --- a/app/lib/LevelLoader.coffee +++ b/app/lib/LevelLoader.coffee @@ -67,16 +67,17 @@ module.exports = class LevelLoader extends CocoClass session = new LevelSession().setURL url @sessionResource = @supermodel.loadModel(session, 'level_session', {cache: false}) @session = @sessionResource.model - @listenToOnce @session, 'sync', @onSessionLoaded - + @listenToOnce @session, 'sync', -> + @session.url = -> '/db/level.session/' + @id + @loadDependenciesForSession(@session) + if @opponentSessionID opponentSession = new LevelSession().setURL "/db/level_session/#{@opponentSessionID}" @opponentSessionResource = @supermodel.loadModel(opponentSession, 'opponent_session') @opponentSession = @opponentSessionResource.model - @listenToOnce @opponentSession, 'sync', @onSessionLoaded - - onSessionLoaded: (session) -> - session.url = -> '/db/level.session/' + @id + @listenToOnce @opponentSession, 'sync', @loadDependenciesForSession + + loadDependenciesForSession: (session) -> if heroConfig = session.get('heroConfig') url = "/db/thang.type/#{heroConfig.thangType}/version?project=name,components,original" @worldNecessities.push @maybeLoadURL(url, ThangType, 'thang') @@ -153,6 +154,7 @@ module.exports = class LevelLoader extends CocoClass @worldNecessities = @worldNecessities.concat worldNecessities populateHero: -> + return return if @inLevelEditor return unless @level.get('type') is 'hero' and hero = _.find @level.get('thangs'), id: 'Hero Placeholder' heroConfig = @session.get('heroConfig') @@ -218,6 +220,7 @@ module.exports = class LevelLoader extends CocoClass for thangTypeName in thangsToLoad thangType = nameModelMap[thangTypeName] + console.log 'found ThangType', thangType, 'for', thangTypeName, 'of nameModelMap', nameModelMap unless thangType continue if thangType.isFullyLoaded() thangType.fetch() thangType = @supermodel.loadModel(thangType, 'thang').model @@ -226,7 +229,7 @@ module.exports = class LevelLoader extends CocoClass res.markLoading() @spriteSheetsToBuild.push res - @buildLoopInterval = setInterval @buildLoop, 5 + @buildLoopInterval = setInterval @buildLoop, 5 if @spriteSheetsToBuild.length maybeLoadURL: (url, Model, resourceName) -> return if @supermodel.getModel(url) diff --git a/app/lib/surface/CoordinateDisplay.coffee b/app/lib/surface/CoordinateDisplay.coffee index bb3e233f8..f650e09b1 100644 --- a/app/lib/surface/CoordinateDisplay.coffee +++ b/app/lib/surface/CoordinateDisplay.coffee @@ -70,36 +70,66 @@ module.exports = class CoordinateDisplay extends createjs.Container contentWidth = @label.getMeasuredWidth() + (2 * margin) contentHeight = @label.getMeasuredHeight() + (2 * margin) - # Shift all contents up so marker is at pointer (affects container cache position) - @label.regY = @background.regY = @pointMarker.regY = contentHeight + # Shift pointmarker up so it centers at pointer (affects container cache position) + @pointMarker.regY = contentHeight pointMarkerStroke = 2 pointMarkerLength = 8 + fullPointMarkerLength = pointMarkerLength + (pointMarkerStroke / 2) contributionsToTotalSize = [] - contributionsToTotalSize = contributionsToTotalSize.concat @updateCoordinates contentWidth, contentHeight, pointMarkerLength + contributionsToTotalSize = contributionsToTotalSize.concat @updateCoordinates contentWidth, contentHeight, fullPointMarkerLength contributionsToTotalSize = contributionsToTotalSize.concat @updatePointMarker 0, contentHeight, pointMarkerLength, pointMarkerStroke totalWidth = contentWidth + contributionsToTotalSize.reduce (a, b) -> a + b totalHeight = contentHeight + contributionsToTotalSize.reduce (a, b) -> a + b - @cache -pointMarkerLength, -totalHeight + pointMarkerLength, totalWidth, totalHeight + if @isNearTopEdge() + verticalEdge = + startPos: -fullPointMarkerLength + posShift: -contentHeight + 4 + else + verticalEdge = + startPos: -totalHeight + fullPointMarkerLength + posShift: contentHeight - updateCoordinates: (contentWidth, contentHeight, initialXYOffset) -> - offsetForPointMarker = initialXYOffset + if @isNearRightEdge() + horizontalEdge = + startPos: -totalWidth + fullPointMarkerLength + posShift: totalWidth + else + horizontalEdge = + startPos: -fullPointMarkerLength + posShift: 0 + @orient verticalEdge, horizontalEdge, totalHeight, totalWidth + + isNearTopEdge: -> + yRatio = 1 - (@camera.worldViewport.y - @lastPos.y) / @camera.worldViewport.height + yRatio > 0.9 + + isNearRightEdge: -> + xRatio = (@lastPos.x - @camera.worldViewport.x) / @camera.worldViewport.width + xRatio > 0.85 + + orient: (verticalEdge, horizontalEdge, totalHeight, totalWidth) -> + @label.regY = @background.regY = verticalEdge.posShift + @label.regX = @background.regX = horizontalEdge.posShift + @cache horizontalEdge.startPos, verticalEdge.startPos, totalWidth, totalHeight + + updateCoordinates: (contentWidth, contentHeight, offset) -> # Center label horizontally and vertically - @label.x = contentWidth / 2 - (@label.getMeasuredWidth() / 2) + offsetForPointMarker - @label.y = contentHeight / 2 - (@label.getMeasuredHeight() / 2) - offsetForPointMarker + @label.x = contentWidth / 2 - (@label.getMeasuredWidth() / 2) + offset + @label.y = contentHeight / 2 - (@label.getMeasuredHeight() / 2) - offset @background.graphics .clear() .beginFill('rgba(0,0,0,0.4)') .beginStroke('rgba(0,0,0,0.6)') .setStrokeStyle(backgroundStroke = 1) - .drawRoundRect(offsetForPointMarker, -offsetForPointMarker, contentWidth, contentHeight, radius = 2.5) + .drawRoundRect(offset, -offset, contentWidth, contentHeight, radius = 2.5) .endFill() .endStroke() - contributionsToTotalSize = [offsetForPointMarker, backgroundStroke] + contributionsToTotalSize = [offset, backgroundStroke] updatePointMarker: (centerX, centerY, length, strokeSize) -> strokeStyle = 'square' diff --git a/app/locale/pt-PT.coffee b/app/locale/pt-PT.coffee index 61bf857aa..70496e7f8 100644 --- a/app/locale/pt-PT.coffee +++ b/app/locale/pt-PT.coffee @@ -49,9 +49,9 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription: blog: "Blog" forum: "Fórum" account: "Conta" -# profile: "Profile" -# stats: "Stats" -# code: "Code" + profile: "Perfil" + stats: "Estatísticas" + code: "Código" admin: "Administrador" home: "Início" contribute: "Contribuir" @@ -212,15 +212,15 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription: profile_for_suffix: "" # featured: "Featured" # not_featured: "Not Featured" -# looking_for: "Looking for:" + looking_for: "À procura de:" # last_updated: "Last updated:" # contact: "Contact" -# active: "Looking for interview offers now" -# inactive: "Not looking for offers right now" + active: "Estou à procura de ofertas de intrevistas agora" + inactive: "Não estou à procura de ofertas neste momento" # complete: "complete" next: "Seguinte" next_city: "cidade?" -# next_country: "pick your country." + next_country: "escolha o seu país." next_name: "nome?" # next_short_description: "write a short description." # next_long_description: "describe your desired position." @@ -244,9 +244,9 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription: # basics_job_title: "Desired Job Title" # basics_job_title_help: "What role are you looking for?" basics_city: "Cidade" -# basics_city_help: "City you want to work in (or live in now)." + basics_city_help: "Cidade na qual quer trabalhar (ou onde vive agora)." basics_country: "País" -# basics_country_help: "Country you want to work in (or live in now)." + basics_country_help: "País no qual quer trabalhar (ou onde vive agora)." # basics_visa: "US Work Status" # basics_visa_help: "Are you authorized to work in the US, or do you need visa sponsorship? (If you live in Canada or Australia, mark authorized.)" basics_looking_for: "À Procura De" @@ -295,17 +295,17 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription: # education_description_help: "Highlight anything about this educational experience. (140 chars; optional)" # our_notes: "CodeCombat's Notes" # remarks: "Remarks" -# projects: "Projects" -# projects_header: "Add 3 projects" + projects: "Projetos" + projects_header: "Adicione 3 projetos" # projects_header_2: "Projects (Top 3)" # projects_blurb: "Highlight your projects to amaze employers." -# project_name: "Project Name" + project_name: "Nome do Projeto" # project_name_help: "What was the project called?" -# project_description: "Description" + project_description: "Descrição" # project_description_help: "Briefly describe the project." -# project_picture: "Picture" + project_picture: "Imagem" # project_picture_help: "Upload a 230x115px or larger image showing off the project." -# project_link: "Link" + project_link: "Ligação" # project_link_help: "Link to the project." # player_code: "Player Code" @@ -325,18 +325,18 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription: filter_experience: "Experiência" filter_experience_senior: "Sénior" filter_experience_junior: "Júnior" -# filter_experience_recent_grad: "Recent Grad" + filter_experience_recent_grad: "Graduado Recentemente" filter_experience_student: "Estudante Universitário" filter_results: "resultados" start_hiring: "Começar a contratar." -# reasons: "Three reasons you should hire through us:" -# everyone_looking: "Everyone here is looking for their next opportunity." -# everyone_looking_blurb: "Forget about 20% LinkedIn InMail response rates. Everyone that we list on this site wants to find their next position and will respond to your request for an introduction." -# weeding: "Sit back; we've done the weeding for you." -# weeding_blurb: "Every player that we list has been screened for technical ability. We also perform phone screens for select candidates and make notes on their profiles to save you time." -# pass_screen: "They will pass your technical screen." -# pass_screen_blurb: "Review each candidate's code before reaching out. One employer found that 5x as many of our devs passed their technical screen than hiring from Hacker News." -# make_hiring_easier: "Make my hiring easier, please." + reasons: "Três razões pelas quais deve contratar através de nós:" + everyone_looking: "Aqui todos estão à procura da próxima oportunidade deles." + everyone_looking_blurb: "Esqueça os cerca de 20% de taxas de respostta do LinkedIn InMail. Todos os que nós listamos neste sítio quer encontrar a nova posição deles e responderá ao seu pedido para uma introdução." + weeding: "Relaxe; fizemos a parte mais difícil por si." + weeding_blurb: "Cada jogador que listamos foi sujeito a um teste das habilidades técnicas. Também fazemos testes por telefone para selecionar candidatos e fazer anotações nos perfis deles para lhe poupar tempo." + pass_screen: "Eles passarão o seu teste técnico." + pass_screen_blurb: "Reveja o código de cada candidato antes de chegar a ele. Um funcionário descobriu que 5x mais programadores nossos passaram o teste técnico deles do que os contratados através do Hacker News." + make_hiring_easier: "Torne a minha contratação mais fácil, por favor." what: "O que é o CodeCombat?" what_blurb: "O CodeCombat é um jogo de programação, no navegador e multijogador. Os jogadores escrevem código para controlar as forças deles em batalha contra outros programadores. Os nossos jogadores têm experiência com todos os conceitos tecnológicos principais." cost: "Quanto é que cobramos?" @@ -430,7 +430,7 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription: time_goto: "Ir para:" infinite_loop_try_again: "Tentar Novamente" infinite_loop_reset_level: "Reiniciar Nível" -# infinite_loop_comment_out: "Comment Out My Code" + infinite_loop_comment_out: "Comentar o Meu Código" game_menu: inventory_tab: "Inventário" @@ -499,16 +499,16 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription: enter: "Enter" escape: "Esc" cast_spell: "Lançar feitiço atual." -# continue_script: "Continue past current script." -# skip_scripts: "Skip past all skippable scripts." + continue_script: "Saltar o script atual." + skip_scripts: "Saltar todos os scripts saltáveis." toggle_playback: "Alternar entre Jogar e Pausar." scrub_playback: "Andar para a frente e para trás no tempo." single_scrub_playback: "Andar para a frente e para trás no tempo um único frame." -# scrub_execution: "Scrub through current spell execution." -# toggle_debug: "Toggle debug display." -# toggle_grid: "Toggle grid overlay." -# toggle_pathfinding: "Toggle pathfinding overlay." -# beautify: "Beautify your code by standardizing its formatting." + scrub_execution: "Analisar a execução do feitiço atual." + toggle_debug: "Ativar/desativar a janela de depuração." + toggle_grid: "Ativar/desativar a sobreposição da grelha." + toggle_pathfinding: "Ativar/desativar a sobreposição do encontrador de caminho." + beautify: "Embelezar o código ao estandardizar a formatação." move_wizard: "Mover o seu Feiticeiro pelo nível." admin: @@ -525,13 +525,13 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription: community: main_title: "Comunidade do CodeCombat" -# introduction: "Check out the ways you can get involved below and decide what sounds the most fun. We look forward to working with you!" -# level_editor_prefix: "Use the CodeCombat" -# level_editor_suffix: "to create and edit levels. Users have created levels for their classes, friends, hackathons, students, and siblings. If create a new level sounds intimidating you can start by forking one of ours!" -# thang_editor_prefix: "We call units within the game 'thangs'. Use the" -# thang_editor_suffix: "to modify the CodeCombat source artwork. Allow units to throw projectiles, alter the direction of an animation, change a unit's hit points, or upload your own vector sprites." -# article_editor_prefix: "See a mistake in some of our docs? Want to make some instructions for your own creations? Check out the" -# article_editor_suffix: "and help CodeCombat players get the most out of their playtime." + introduction: "Confira abaixo as formas de se envolver e decida o que lhe parece melhor. Estamos ansiosos por trabalhar consigo!" + level_editor_prefix: "Use o" + level_editor_suffix: "do CodeCombat para criar e editar níveis. Os utilizadores já criaram níveis para aulas, amigos, maratonas hacker, estudantes e familiares. Se criar um nível parece intimidante, pode começar por bifurcar um dos nossos!" + thang_editor_prefix: "Chamamos 'thangs' às unidades do jogo. Use o" + thang_editor_suffix: "para modificar a arte do CodeCombat. Permita às unidades lançar projéteis, altere a direção de uma animação, altere os pontos de vida de uma unidade ou anexe as suas próprias unidades." + article_editor_prefix: "Vê um erro em alguns dos nossos documentos? Quer escrever algumas instruções para as suas criações? Confira o" + article_editor_suffix: "e ajude os jogadores do CodeCombat a obter o máximo do tempo de jogo deles." find_us: "Encontre-nos nestes sítios" contribute_to_the_project: "Contribua para o projeto" @@ -703,9 +703,9 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription: # art_description_prefix: "All common content is available under the" # cc_license_url: "Creative Commons Attribution 4.0 International License" # art_description_suffix: "Common content is anything made generally available by CodeCombat for the purpose of creating Levels. This includes:" -# art_music: "Music" -# art_sound: "Sound" -# art_artwork: "Artwork" + art_music: "Música" + art_sound: "Som" + art_artwork: "Arte" # art_sprites: "Sprites" # art_other: "Any and all other non-code creative works that are made available when creating Levels." # art_access: "Currently there is no universal, easy system for fetching these assets. In general, fetch them from the URLs as used by the site, contact us for assistance, or help us in extending the site to make these assets more easily accessible." @@ -713,11 +713,11 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription: # use_list_1: "If used in a movie or another game, include codecombat.com in the credits." # use_list_2: "If used on a website, include a link near the usage, for example underneath an image, or in a general attributions page where you might also mention other Creative Commons works and open source software being used on the site. Something that's already clearly referencing CodeCombat, such as a blog post mentioning CodeCombat, does not need some separate attribution." # art_paragraph_2: "If the content being used is created not by CodeCombat but instead by a user of codecombat.com, attribute them instead, and follow attribution directions provided in that resource's description if there are any." -# rights_title: "Rights Reserved" + rights_title: "Direitos Reservados" # rights_desc: "All rights are reserved for Levels themselves. This includes" -# rights_scripts: "Scripts" -# rights_unit: "Unit configuration" -# rights_description: "Description" + rights_scripts: "Scripts" + rights_unit: "Configuração da unidade" + rights_description: "Descrição" # rights_writings: "Writings" # rights_media: "Media (sounds, music) and any other creative content made specifically for that Level and not made generally available when creating Levels." # rights_clarification: "To clarify, anything that is made available in the Level Editor for the purpose of making levels is under CC, whereas the content created with the Level Editor or uploaded in the course of creation of Levels is not." @@ -727,22 +727,22 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription: contribute: page_title: "Contribuir" - character_classes_title: "Classes de Personagens" + character_classes_title: "Classes das Personagens" introduction_desc_intro: "Temos esperanças elevadas para o CodeCombat." # introduction_desc_pref: "We want to be where programmers of all stripes come to learn and play together, introduce others to the wonderful world of coding, and reflect the best parts of the community. We can't and don't want to do that alone; what makes projects like GitHub, Stack Overflow and Linux great are the people who use them and build on them. To that end, " introduction_desc_github_url: "o CodeCombat é totalmente open source" # introduction_desc_suf: ", and we aim to provide as many ways as possible for you to take part and make this project as much yours as ours." - introduction_desc_ending: "Esperemos que se junte a nós!" + introduction_desc_ending: "Esperamos que se junte a nós!" introduction_desc_signature: "- Nick, George, Scott, Michael, Jeremy e Matt" -# alert_account_message_intro: "Hey there!" -# alert_account_message: "To subscribe for class emails, you'll need to be logged in first." + alert_account_message_intro: "Hey, você!" + alert_account_message: "Para se subscrever para receber e-mails de classes, antes precisará de iniciar sessão." # archmage_summary: "Interested in working on game graphics, user interface design, database and server organization, multiplayer networking, physics, sound, or game engine performance? Want to help build a game to help other people learn what you are good at? We have a lot to do and if you are an experienced programmer and want to develop for CodeCombat, this class is for you. We would love your help building the best programming game ever." # 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" + class_attributes: "Atributos da Classe" # 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" + how_to_join: "Como Me Junto" # 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 " @@ -750,7 +750,7 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription: # join_url_email: "Email us" # join_url_hipchat: "public HipChat room" more_about_archmage: "Aprenda Mais Sobre Tornar-se um Arcomago" -# archmage_subscribe_desc: "Get emails on new coding opportunities and announcements." + archmage_subscribe_desc: "Receber e-mails relativos a novas oportunidades de programação e anúncios." # artisan_summary_pref: "Want to design levels and expand CodeCombat's arsenal? People are playing through our content at a pace faster than we can build! Right now, our level editor is barebone, so be wary. Making levels will be a little challenging and buggy. If you have visions of campaigns spanning for-loops to" # artisan_summary_suf: ", then this class is for you." # artisan_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" @@ -764,7 +764,7 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription: # artisan_join_step3: "Find us in our public HipChat room for help." # artisan_join_step4: "Post your levels on the forum for feedback." more_about_artisan: "Aprenda Mais Sobre Tornar-se um Artesão" -# artisan_subscribe_desc: "Get emails on level editor updates and announcements." + artisan_subscribe_desc: "Receber e-mails relativos a novidades do editor de níveis e anúncios." # adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." # adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." # adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." @@ -773,7 +773,7 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription: # adventurer_forum_url: "our forum" # adventurer_join_suf: "so if you prefer to be notified those ways, sign up there!" more_about_adventurer: "Aprenda Mais Sobre Tornar-se um Aventureiro" -# adventurer_subscribe_desc: "Get emails when there are new levels to test." + adventurer_subscribe_desc: "Receber e-mails quando houver novos níveis para testar." # scribe_summary_pref: "CodeCombat is not just going to be a bunch of levels. It will also be a resource of programming knowledge that players can hook into. That way, each Artisan can link to a detailed article that for the player's edification: documentation akin to what the " # scribe_summary_suf: " has built. If you enjoy explaining programming concepts, then this class is for you." # scribe_introduction_pref: "CodeCombat isn't just going to be a bunch of levels. It will also include a resource for knowledge, a wiki of programming concepts that levels can hook into. That way rather than each Artisan having to describe in detail what a comparison operator is, they can simply link their level to the Article describing them that is already written for the player's edification. Something along the lines of what the " @@ -783,7 +783,7 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription: # contact_us_url: "Contact us" # scribe_join_description: "tell us a little about yourself, your experience with programming and what sort of things you'd like to write about. We'll go from there!" more_about_scribe: "Aprenda Mais Sobre Tornar-se um Escrivão" -# scribe_subscribe_desc: "Get emails about article writing announcements." + scribe_subscribe_desc: "Receber e-mails sobre anúncios relativos à escrita de artigos." # diplomat_summary: "There is a large interest in CodeCombat in other countries that do not speak English! We are looking for translators who are willing to spend their time translating the site's corpus of words so that CodeCombat is accessible across the world as soon as possible. If you'd like to help getting CodeCombat international, then this class is for you." # diplomat_introduction_pref: "So, if there's one thing we learned from the " # diplomat_launch_url: "launch in October" @@ -793,7 +793,7 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription: # diplomat_github_url: "on GitHub" # diplomat_join_suf_github: ", edit it online, and submit a pull request. Also, check this box below to keep up-to-date on new internationalization developments!" more_about_diplomat: "Aprenda Mais Sobre Tornar-se um Diplomata" -# diplomat_subscribe_desc: "Get emails about i18n developments and levels to translate." + diplomat_subscribe_desc: "Receber e-mails sobre desenvolvimentos da i18n e níveis para traduzir." # ambassador_summary: "We are trying to build a community, and every community needs a support team when there are troubles. We have got chats, emails, and social networks so that our users can get acquainted with the game. If you want to help people get involved, have fun, and learn some programming, then this class is for you." # ambassador_introduction: "This is a community we're building, and you are the connections. We've got Olark chats, emails, and social networks with lots of people to talk with and help get acquainted with the game and learn from. If you want to help people get involved and have fun, and get a good feel of the pulse of CodeCombat and where we're going, then this class might be for you." # ambassador_attribute_1: "Communication skills. Be able to identify the problems players are having and help them solve them. Also, keep the rest of us informed about what players are saying, what they like and don't like and want more of!" @@ -801,7 +801,7 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription: # ambassador_join_note_strong: "Note" # ambassador_join_note_desc: "One of our top priorities is to build multiplayer where players having difficulty solving levels can summon higher level wizards to help them. This will be a great way for ambassadors to do their thing. We'll keep you posted!" more_about_ambassador: "Aprenda Mais Sobre Tornar-se um Embaixador" -# ambassador_subscribe_desc: "Get emails on support updates and multiplayer developments." + ambassador_subscribe_desc: "Receber e-mails relativos a novidades do suporte e desenvolvimentos do modo multijogador." changes_auto_save: "As alterações são guardadas automaticamente quando clica nas caixas." diligent_scribes: "Os Nossos Dedicados Escrivões:" powerful_archmages: "Os Nossos Poderosos Arcomagos:" @@ -830,7 +830,7 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription: simulate: "Simular" simulation_explanation: "Ao simular jogos pode ter o seu jogo classificado mais rapidamente!" simulate_games: "Simular Jogos!" -# simulate_all: "RESET AND SIMULATE GAMES" + simulate_all: "REINICIAR E SIMULAR JOGOS" games_simulated_by: "Jogos simulados por si:" games_simulated_for: "Jogos simulados para si:" games_simulated: "Jogos simulados" @@ -934,8 +934,8 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription: level_session: "A Sua Sessão" opponent_session: "Sessão do Adversário" article: "Artigo" -# user_names: "User Names" -# thang_names: "Thang Names" + user_names: "Nomes de Utilizador" + thang_names: "Nomes de Thangs" files: "Ficheiros" # top_simulators: "Top Simulators" source_document: "Documento Fonte" diff --git a/app/models/Level.coffee b/app/models/Level.coffee index 2d4ff0ff3..ad3458692 100644 --- a/app/models/Level.coffee +++ b/app/models/Level.coffee @@ -46,6 +46,9 @@ module.exports = class Level extends CocoModel for thangComponent in levelThang.components ? [] placeholders[thangComponent.original] = thangComponent levelThang.components = [] + heroThangType = session?.get('heroConfig')?.thangType + console.log "got thang type", heroThangType + levelThang.thangType = heroThangType if heroThangType configs = {} for thangComponent in levelThang.components diff --git a/app/views/editor/component/ThangComponentConfigView.coffee b/app/views/editor/component/ThangComponentConfigView.coffee index 39717ef6b..b27ddb177 100644 --- a/app/views/editor/component/ThangComponentConfigView.coffee +++ b/app/views/editor/component/ThangComponentConfigView.coffee @@ -37,10 +37,14 @@ module.exports = class ThangComponentConfigView extends CocoView teams = _.filter(_.pluck(thangs, 'team')) superteams = _.filter(_.pluck(thangs, 'superteam')) superteams = _.union(teams, superteams) + config = $.extend true, {}, @config + schema = $.extend true, {}, @component.get('configSchema') + if @level?.get('type') is 'hero' + schema.required = [] treemaOptions = supermodel: @supermodel - schema: @component.attributes.configSchema - data: _.cloneDeep @config + schema: schema + data: config callbacks: {change: @onConfigEdited} world: @world view: @ @@ -75,4 +79,4 @@ module.exports = class ThangComponentConfigView extends CocoView data: -> @editThangTreema.data class ComponentConfigNode extends TreemaObjectNode - nodeDescription: 'Component Property' \ No newline at end of file + nodeDescription: 'Component Property' diff --git a/app/views/editor/level/RelatedAchievementsView.coffee b/app/views/editor/level/RelatedAchievementsView.coffee index e0ff97d54..ccb9969eb 100644 --- a/app/views/editor/level/RelatedAchievementsView.coffee +++ b/app/views/editor/level/RelatedAchievementsView.coffee @@ -16,7 +16,7 @@ module.exports = class RelatedAchievementsView extends CocoView constructor: (options) -> super options @level = options.level - @relatedID = @level.id + @relatedID = @level.get('original') @achievements = new RelatedAchievementsCollection @relatedID @supermodel.loadCollection @achievements, 'achievements' diff --git a/app/views/editor/level/modals/NewAchievementModal.coffee b/app/views/editor/level/modals/NewAchievementModal.coffee index a666bd239..dba9a0e93 100644 --- a/app/views/editor/level/modals/NewAchievementModal.coffee +++ b/app/views/editor/level/modals/NewAchievementModal.coffee @@ -37,6 +37,7 @@ module.exports = class NewAchievementModal extends NewModelModal query = subQueries[0] else query = $or: subQueries + query['level.original'] = @level.get 'original' query makeNewModel: -> @@ -50,5 +51,6 @@ module.exports = class NewAchievementModal extends NewModelModal achievement.set 'query', query achievement.set 'collection', 'level.sessions' achievement.set 'userField', 'creator' + achievement.set 'related', @level.get('original') achievement diff --git a/app/views/editor/level/thangs/ThangsTabView.coffee b/app/views/editor/level/thangs/ThangsTabView.coffee index 2d1ddf0de..0c2b030d8 100644 --- a/app/views/editor/level/thangs/ThangsTabView.coffee +++ b/app/views/editor/level/thangs/ThangsTabView.coffee @@ -311,10 +311,14 @@ module.exports = class ThangsTabView extends CocoView else @addThangSprite = null - createEssentialComponents: -> + createEssentialComponents: (defaultComponents) -> + physicalConfig = {pos: {x: 10, y: 10, z: 1}} + if physicalOriginal = _.find(defaultComponents ? [], original: componentOriginals['physics.Physical']) + physicalConfig.pos.z = physicalOriginal.config.pos.z # Get the z right + console.log physicalOriginal, defaultComponents, componentOriginals['physics.Physical'], physicalConfig [ {original: componentOriginals['existence.Exists'], majorVersion: 0, config: {}} - {original: componentOriginals['physics.Physical'], majorVersion: 0, config: {pos: {x: 10, y: 10, z: 1}, width: 2, height: 2, depth: 2, shape: 'box'}} + {original: componentOriginals['physics.Physical'], majorVersion: 0, config: physicalConfig} ] createAddThang: -> @@ -424,9 +428,11 @@ module.exports = class ThangsTabView extends CocoView if @cloneSourceThang components = _.cloneDeep @thangsTreema.get "id=#{@cloneSourceThang.id}/components" @selectAddThang null + else if @level.get('type') is 'hero' + components = [] # Load them all from default ThangType Components else components = _.cloneDeep thangType.get('components') ? [] - components = @createEssentialComponents() unless components.length + components = @createEssentialComponents(thangType.get('components')) unless components.length physical = _.find components, (c) -> c.config?.pos? physical.config.pos = x: pos.x, y: pos.y, z: physical.config.pos.z if physical thang = thangType: thangType.get('original'), id: thangID, components: components diff --git a/app/views/play/MainPlayView.coffee b/app/views/play/MainPlayView.coffee index c46f906c4..c2e557d1a 100644 --- a/app/views/play/MainPlayView.coffee +++ b/app/views/play/MainPlayView.coffee @@ -229,6 +229,13 @@ module.exports = class MainPlayView extends RootView image: '/file/db/level/525ef8ef06e1ab0962000003/commanding_followers_icon.png' description: 'Learn Quicksort while sorting a spiral of ogres! - by Alexandru Caciulescu' } + { + name: 'Minimax Tic-Tac-Toe' + difficulty: 4 + id: 'minimax-tic-tac-toe' + image: '/file/db/level/525ef8ef06e1ab0962000003/commanding_followers_icon.png' + description: 'Learn how to make a game AI with the Minimax algorithm. - by Alexandru Caciulescu' + } ] playerCreated = [ diff --git a/bower.json b/bower.json index 06a1d55b4..8ac33ff3c 100644 --- a/bower.json +++ b/bower.json @@ -44,7 +44,7 @@ "bootstrap": "~3.1.1", "validated-backbone-mediator": "~0.1.3", "jquery.browser": "~0.0.6", - "zatanna": "~0.0.5", + "zatanna": "~0.0.6", "modernizr": "~2.8.3" }, "overrides": { diff --git a/karma.conf.js b/karma.conf.js index bf0924f77..5a4224edf 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -43,7 +43,7 @@ module.exports = function(config) { // level of logging // possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG - logLevel : config.LOG_INFO, + logLevel : config.LOG_ERROR, // doesn't seem to work? // enable / disable watching file and executing tests whenever any file changes autoWatch : true, diff --git a/test/app/lib/LevelLoader.spec.coffee b/test/app/lib/LevelLoader.spec.coffee index d9102a299..e3cb98dd1 100644 --- a/test/app/lib/LevelLoader.spec.coffee +++ b/test/app/lib/LevelLoader.spec.coffee @@ -41,6 +41,8 @@ levelWithShamanWithSuperWand = { sessionWithTharinWithHelmet = { heroConfig: { thangType: 'tharin', inventory: { 'head': 'helmet' }}} +sessionWithAnyaWithGloves = { heroConfig: { thangType: 'anya', inventory: { 'head': 'gloves' }}} + # THANG TYPES thangTypeOgreWithPhysicalComponent = { @@ -80,35 +82,56 @@ thangTypeWand = { }] } +thangTypeAnyaWithJumpsComponent = { + name: 'Anya' + original: 'anya' + components: [{ + original: 'jumps' + majorVersion: 0 + }] +} + describe 'LevelLoader', -> - it 'loads hero and item thang types from heroConfig in the LevelSession', -> - new LevelLoader({supermodel:new SuperModel(), sessionID: 'id', levelID: 'id'}) - - responses = { - '/db/level_session/id': sessionWithTharinWithHelmet - } - - jasmine.Ajax.requests.sendResponses(responses) - requests = jasmine.Ajax.requests.all() - urls = (r.url for r in requests) - expect('/db/thang.type/helmet/version?project=name,components,original' in urls).toBeTruthy() - expect('/db/thang.type/tharin/version?project=name,components,original' in urls).toBeTruthy() - - it 'loads components for the hero in the heroConfig in the LevelSession', -> - new LevelLoader({supermodel:new SuperModel(), sessionID: 'id', levelID: 'id'}) + describe 'loadDependenciesForSession', -> + it 'loads hero and item thang types from heroConfig in the given session', -> + levelLoader = new LevelLoader({supermodel:new SuperModel(), sessionID: 'id', levelID: 'id'}) + session = new LevelSession(sessionWithAnyaWithGloves) + levelLoader.loadDependenciesForSession(session) + requests = jasmine.Ajax.requests.all() + urls = (r.url for r in requests) + expect('/db/thang.type/gloves/version?project=name,components,original' in urls).toBeTruthy() + expect('/db/thang.type/anya/version?project=name,components,original' in urls).toBeTruthy() - responses = { - '/db/level_session/id': sessionWithTharinWithHelmet - '/db/thang.type/tharin/version?project=name,components,original': thangTypeTharinWithHealsComponent - } + it 'loads components for the hero in the heroConfig in the given session', -> + levelLoader = new LevelLoader({supermodel:new SuperModel(), sessionID: 'id', levelID: 'id'}) + session = new LevelSession(sessionWithAnyaWithGloves) + levelLoader.loadDependenciesForSession(session) + responses = { + '/db/thang.type/anya/version?project=name,components,original': thangTypeAnyaWithJumpsComponent + } + jasmine.Ajax.requests.sendResponses(responses) + requests = jasmine.Ajax.requests.all() + urls = (r.url for r in requests) + expect('/db/level.component/jumps/version/0' in urls).toBeTruthy() + + it 'is idempotent', -> + levelLoader = new LevelLoader({supermodel:new SuperModel(), sessionID: 'id', levelID: 'id'}) - jasmine.Ajax.requests.sendResponses(responses) - requests = jasmine.Ajax.requests.all() - urls = (r.url for r in requests) - expect('/db/level.component/heals/version/0' in urls).toBeTruthy() - + # first load Tharin by the 'normal' session load + responses = { '/db/level_session/id': sessionWithTharinWithHelmet } + jasmine.Ajax.requests.sendResponses(responses) + numRequestsBefore = jasmine.Ajax.requests.count() + + # then try to load Tharin some more + session = new LevelSession(sessionWithTharinWithHelmet) + levelLoader.loadDependenciesForSession(session) + levelLoader.loadDependenciesForSession(session) + levelLoader.loadDependenciesForSession(session) + numRequestsAfter = jasmine.Ajax.requests.count() + expect(numRequestsBefore).toBe(numRequestsAfter) + it 'loads thangs for items that the level thangs have in their Equips component configs', -> new LevelLoader({supermodel:supermodel = new SuperModel(), sessionID: 'id', levelID: 'id'}) diff --git a/test/app/models/Level.spec.coffee b/test/app/models/Level.spec.coffee index db90afd8d..3db7cabe1 100644 --- a/test/app/models/Level.spec.coffee +++ b/test/app/models/Level.spec.coffee @@ -17,6 +17,7 @@ describe 'Level', -> ] } ] + type: 'hero' }) thangType = new ThangType({ @@ -28,17 +29,17 @@ describe 'Level', -> {"original": "d", "majorVersion": 0, "config": {i: 1}} ] }) - + supermodel = new SuperModel() supermodel.registerModel(thangType) - + result = level.denormalize(supermodel) tharinThangComponents = result.thangs[0].components - + it 'adds default configs to thangs without any config', -> aComp = _.find tharinThangComponents, {original:'a'} expect(_.isEqual(aComp.config, {i:1})).toBeTruthy() - + it 'leaves alone configs for components the level thang has but the thang type does not', -> bComp = _.find tharinThangComponents, {original:'b'} expect(_.isEqual(bComp.config, {i:2})).toBeTruthy() @@ -46,7 +47,8 @@ describe 'Level', -> it 'merges configs where both the level thang and thang type have one, giving priority to the level thang', -> cComp = _.find tharinThangComponents, {original:'c'} expect(_.isEqual(cComp.config, {i: 1, ii: 2, nest: {iii: 3, iv: 4}})).toBeTruthy() - + it 'adds components from the thang type that do not exist in the level thang', -> dComp = _.find tharinThangComponents, {original:'d'} - expect(_.isEqual(dComp.config, {i: 1})).toBeTruthy() \ No newline at end of file + expect(dComp).toBeTruthy() + expect(_.isEqual(dComp?.config, {i: 1})).toBeTruthy()