From f77629594444a04c2dfbabf538b009d18f6ef928 Mon Sep 17 00:00:00 2001 From: gorechi Date: Tue, 11 Nov 2014 17:57:53 +0300 Subject: [PATCH 1/5] Update ru.coffee --- app/locale/ru.coffee | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/locale/ru.coffee b/app/locale/ru.coffee index ee885603b..554306543 100644 --- a/app/locale/ru.coffee +++ b/app/locale/ru.coffee @@ -276,7 +276,7 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi tip_hardware_problem: "В: Сколько программистов нужно, чтобы вкрутить лампочку? О: Нисколько, это проблемы с железом." # tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law." tip_premature_optimization: "Поспешная оптимизация - корень всех зол. - Donald Knuth" -# tip_brute_force: "When in doubt, use brute force. - Ken Thompson" + tip_brute_force: "Когда сомневаешься используй грубую силу. - Кен Томпсон" customize_wizard: "Настройки волшебника" game_menu: @@ -301,7 +301,7 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi should_equip: "(двойной клик чтобы одеть)" equipped: "(выбранный)" locked: "(заблокированный)" -# restricted: "(restricted in this level)" + restricted: "(запрещен в этом уровне)" choose_hero: choose_hero: "Выберите героя" @@ -315,9 +315,9 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi io_blurb: "Простой, но непонятный." status: "Статус" weapons: "Оружие" -# weapons_warrior: "Swords - Short Range, No Magic" -# weapons_ranger: "Crossbows, Guns - Long Range, No Magic" -# weapons_wizard: "Wands, Staffs - Long Range, Magic" + weapons_warrior: "Меч - ближний бой, не магический" + weapons_ranger: "Арбалеты, ружья - дальнобойные, не магические" + weapons_wizard: "Палочки, посохи - дальнобойные, магические" attack: "Урон" # Can also translate as "Attack" health: "Жизнь" speed: "Скорость" From bc4c4353b2f4e71a4eff11cea217cb2793081f23 Mon Sep 17 00:00:00 2001 From: Nick Winter Date: Tue, 11 Nov 2014 08:49:16 -0800 Subject: [PATCH 2/5] Added a signup button to the level when not logged in. --- app/styles/play/level/control_bar.sass | 2 +- app/templates/play/level/control_bar.jade | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/app/styles/play/level/control_bar.sass b/app/styles/play/level/control_bar.sass index b01db87f3..2a3745a37 100644 --- a/app/styles/play/level/control_bar.sass +++ b/app/styles/play/level/control_bar.sass @@ -152,7 +152,7 @@ .hamburger span.icon-bar background-color: $control-yellow-highlight - #level-done-button, #next-game-button + #level-done-button, #next-game-button, #control-bar-sign-up-button top: 7px font-size: 13px height: 24px diff --git a/app/templates/play/level/control_bar.jade b/app/templates/play/level/control_bar.jade index dfac0042d..bb637b16a 100644 --- a/app/templates/play/level/control_bar.jade +++ b/app/templates/play/level/control_bar.jade @@ -27,3 +27,6 @@ button.btn.btn-xs.btn-inverse.banner#next-game-button(title="Next Game", data-i18n="play_level.next-game") Next game! button.btn.btn-xs.btn-primary.banner#level-done-button(data-i18n="play_level.done") Done + + if me.get('anonymous') + button.btn.btn-xs.btn-primary.banner#control-bar-sign-up-button(data-toggle='coco-modal', data-target='modal/AuthModal', data-i18n="signup.sign_up") From ebc35ae0cb398ca8d009eda5f3a30f26d41a5fb5 Mon Sep 17 00:00:00 2001 From: Matteo Canu Date: Tue, 11 Nov 2014 20:23:52 +0100 Subject: [PATCH 3/5] Translated more strings Also modified Wizard translation from "stregone" to "mago" --- app/locale/it.coffee | 80 ++++++++++++++++++++++---------------------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/app/locale/it.coffee b/app/locale/it.coffee index fe207f456..b06ae7188 100644 --- a/app/locale/it.coffee +++ b/app/locale/it.coffee @@ -13,14 +13,14 @@ module.exports = nativeDescription: "Italiano", englishDescription: "Italian", t nav: play: "Livelli" # The top nav bar entry where players choose which levels to play -# community: "Community" + community: "Comunità" editor: "Editor" blog: "Blog" forum: "Forum" account: "Account" profile: "Profilo" stats: "Statistiche" -# code: "Code" +# code: "Codice" admin: "Amministratore" # Only shows up when you are an admin home: "Pagina iniziale" contribute: "Contribuisci" @@ -28,7 +28,7 @@ module.exports = nativeDescription: "Italiano", englishDescription: "Italian", t about: "Informazioni" contact: "Contatti" twitter_follow: "Segui" -# teachers: "Teachers" + teachers: "Docenti" modal: close: "Chiudi" @@ -50,22 +50,22 @@ module.exports = nativeDescription: "Italiano", englishDescription: "Italian", t spectate: "Spettatore" # Ladder page players: "giocatori" # Hover over a level on /play hours_played: "ore di gioco" # 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" -# available: "Available" + items: "Oggetti" # Tooltip on item shop button from /play + unlock: "Sblocca" # For purchasing items and heroes + confirm: "Conferma" + owned: "In tuo possesso" # For items you own + locked: "Bloccato" + available: "Disponibile" # skills_granted: "Skills Granted" # Property documentation details -# heroes: "Heroes" # Tooltip on hero shop button from /play + heroes: "Eroi" # 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 + account: "Account" # Tooltip on account button from /play + settings: "Impostazioni" # Tooltip on settings button from /play + next: "Procedi" # Go from choose hero to choose inventory before playing a level + change_hero: "Cambia Eroe" # Go back from choose inventory to choose hero # choose_inventory: "Equip Items" -# older_campaigns: "Older Campaigns" -# anonymous: "Anonymous Player" + older_campaigns: "Campagne precedenti" + anonymous: "Giocatore Anonimo" level_difficulty: "Difficoltà: " campaign_beginner: "Campagne per principianti" choose_your_level: "Scegli il tuo livello" # The rest of this section is the old play view at /play-old and isn't very important. @@ -82,8 +82,8 @@ module.exports = nativeDescription: "Italiano", englishDescription: "Italian", t campaign_player_created_description: "... nei quali affronterai la creatività dei tuoi compagni Stregoni Artigiani." campaign_classic_algorithms: "Algoritmi classici" # campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." -# campaign_forest: "Forest Campaign" -# campaign_dungeon: "Dungeon Campaign" + campaign_forest: "Campagna nella Foresta" + campaign_dungeon: "Campagna nelle Segrete" login: sign_up: "Crea account" @@ -109,13 +109,13 @@ module.exports = nativeDescription: "Italiano", englishDescription: "Italian", t send_password: "Invia password di recupero" # recovery_sent: "Recovery email sent." -# items: -# primary: "Primary" -# secondary: "Secondary" -# armor: "Armor" -# accessories: "Accessories" -# misc: "Misc" -# books: "Books" + items: + primary: "Primario" + secondary: "Secondario" + armor: "Armatura" + accessories: "Accessori" + misc: "Varie" + books: "Libri" common: loading: "Caricamento in corso..." @@ -137,17 +137,17 @@ module.exports = nativeDescription: "Italiano", englishDescription: "Italian", t general: and: "e" name: "Nome" -# date: "Date" + date: "Data" body: "Testo" version: "Versione" -# commit_msg: "Commit Message" + commit_msg: "Messaggio del Commit" # version_history: "Version History" # version_history_for: "Version History for: " -# result: "Result" + result: "Risultato" results: "Risultati" description: "Descrizione" or: "o" -# subject: "Subject" + subject: "Oggetto" email: "Email" password: "Password" message: "Messaggio" @@ -159,11 +159,11 @@ module.exports = nativeDescription: "Italiano", englishDescription: "Italian", t score: "Punteggio" win: "Vittoria" loss: "Sconfitta" -# tie: "Tie" + tie: "Pareggio" easy: "Facile" medium: "Medio" hard: "Difficile" -# player: "Player" + player: "Giocatore" units: second: "secondo" @@ -184,21 +184,21 @@ module.exports = nativeDescription: "Italiano", englishDescription: "Italian", t play_level: done: "Fatto" home: "Pagina iniziale" # Not used any more, will be removed soon. -# level: "Level" # Like "Level: Dungeons of Kithgard" + level: "Livello" # Like "Level: Dungeons of Kithgard" # skip: "Skip" game_menu: "Menu" guide: "Guida" restart: "Ricomincia" goals: "Obiettivi" -# goal: "Goal" -# running: "Running..." -# success: "Success!" + goal: "Obiettivo" + running: "Eseguo..." + success: "Successo!" incomplete: "Incompleto" timed_out: "Tempo Scaduto" # failing: "Failing" action_timeline: "Barra temporale delle azioni" click_to_select: "Clicca un'unità per selezionarla." -# reload: "Reload" + reload: "Ricarica" reload_title: "Ricarica tutto il codice?" reload_really: "Sei sicuro di voler ricominciare il livello?" reload_confirm: "Ricarica tutto" @@ -208,7 +208,7 @@ module.exports = nativeDescription: "Italiano", englishDescription: "Italian", t victory_sign_up_poke: "Vuoi ricevere le ultime novità per email? Crea un account gratuito e ti terremo aggiornato!" victory_rate_the_level: "Vota il livello: " # Only in old-style levels. # victory_return_to_ladder: "Return to Ladder" -# victory_play_continue: "Continue" + victory_play_continue: "Continua" # victory_play_skip: "Skip Ahead" victory_play_next_level: "Gioca il prossimo livello" # victory_play_more_practice: "More Practice" @@ -235,13 +235,13 @@ module.exports = nativeDescription: "Italiano", englishDescription: "Italian", t tome_available_spells: "Incantesimi disponibili" # tome_your_skills: "Your Skills" # tome_current_method: "Current Method" -# hud_continue_short: "Continue" + hud_continue_short: "Continua" # code_saved: "Code Saved" skip_tutorial: "Salta (esc)" # keyboard_shortcuts: "Key Shortcuts" loading_ready: "Pronto!" # loading_start: "Start Level" -# problem_alert_title: "Fix Your Code" + problem_alert_title: "Sistema il codice" # time_current: "Now:" # time_total: "Max:" # time_goto: "Go to:" @@ -277,7 +277,7 @@ module.exports = nativeDescription: "Italiano", englishDescription: "Italian", t # tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law." # tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth" # tip_brute_force: "When in doubt, use brute force. - Ken Thompson" - customize_wizard: "Personalizza stregone" + customize_wizard: "Personalizza il mago" game_menu: inventory_tab: "Inventario" From 12e860a98549ae7e780964402feab8c0885e6036 Mon Sep 17 00:00:00 2001 From: Scott Erickson Date: Tue, 11 Nov 2014 14:52:43 -0800 Subject: [PATCH 4/5] Fixed #1739. Darnit mongoose. --- server/purchases/purchase_handler.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/purchases/purchase_handler.coffee b/server/purchases/purchase_handler.coffee index 5f1f34560..0b50b8439 100644 --- a/server/purchases/purchase_handler.coffee +++ b/server/purchases/purchase_handler.coffee @@ -61,7 +61,7 @@ PurchaseHandler = class PurchaseHandler extends Handler addPurchaseToUser: (req) -> user = req.user purchased = user.get('purchased') or {} - purchased = _.clone purchased + purchased = _.cloneDeep purchased item = req.purchasedItem group = switch item.get('kind') From 0af7b4d5b7a1796c83cd2a424a6f75e5afc14a6f Mon Sep 17 00:00:00 2001 From: Nick Winter Date: Tue, 11 Nov 2014 16:36:44 -0800 Subject: [PATCH 5/5] Preparing for being able to use multiple hero classes. Deleted old ChooseHeroView. Improvements to missile trajectories. --- app/lib/LevelOptions.coffee | 6 +- app/lib/surface/Lank.coffee | 3 +- app/models/Level.coffee | 4 + app/models/ThangType.coffee | 3 +- app/models/User.coffee | 5 +- app/styles/game-menu/choose-hero-view.sass | 176 ----------- app/templates/game-menu/choose-hero-view.jade | 65 ---- app/views/game-menu/ChooseHeroView.coffee | 293 ------------------ app/views/game-menu/InventoryModal.coffee | 51 +-- app/views/play/WorldMapView.coffee | 26 +- app/views/play/level/ControlBarView.coffee | 1 + app/views/play/level/LevelHUDView.coffee | 4 +- app/views/play/modal/PlayHeroesModal.coffee | 1 + .../levels/thangs/thang_type_handler.coffee | 14 +- 14 files changed, 66 insertions(+), 586 deletions(-) delete mode 100644 app/styles/game-menu/choose-hero-view.sass delete mode 100644 app/templates/game-menu/choose-hero-view.jade delete mode 100644 app/views/game-menu/ChooseHeroView.coffee diff --git a/app/lib/LevelOptions.coffee b/app/lib/LevelOptions.coffee index 8af9f40ce..908fb1239 100644 --- a/app/lib/LevelOptions.coffee +++ b/app/lib/LevelOptions.coffee @@ -210,12 +210,12 @@ module.exports = LevelOptions = 'drop-the-flag': requiredGear: {'programming-book': 'programmaticon-i', feet: 'leather-boots', flag: 'basic-flags', eyes: 'wooden-glasses', 'right-hand': 'builders-hammer'} restrictedGear: {'right-hand': 'longsword'} - 'rich-forager': - requiredGear: {'programming-book': 'programmaticon-i', feet: 'leather-boots', flag: 'basic-flags', eyes: 'wooden-glasses', torso: 'leather-tunic', 'right-hand': 'longsword', 'left-hand': 'bronze-shield'} - restrictedGear: {'right-hand': 'builders-hammer'} 'deadly-pursuit': requiredGear: {'programming-book': 'programmaticon-i', feet: 'leather-boots', flag: 'basic-flags', eyes: 'wooden-glasses', 'right-hand': 'builders-hammer'} restrictedGear: {'right-hand': 'longsword'} + 'rich-forager': + requiredGear: {'programming-book': 'programmaticon-i', feet: 'leather-boots', flag: 'basic-flags', eyes: 'wooden-glasses', torso: 'leather-tunic', 'right-hand': 'longsword', 'left-hand': 'bronze-shield'} + restrictedGear: {'right-hand': 'builders-hammer'} 'multiplayer-treasure-grove': requiredGear: {'programming-book': 'programmaticon-i', feet: 'leather-boots', flag: 'basic-flags', eyes: 'wooden-glasses', torso: 'leather-tunic'} restrictedGear: {} diff --git a/app/lib/surface/Lank.coffee b/app/lib/surface/Lank.coffee index 91816dc6e..731119d1c 100644 --- a/app/lib/surface/Lank.coffee +++ b/app/lib/surface/Lank.coffee @@ -59,7 +59,8 @@ module.exports = Lank = class Lank extends CocoClass constructor: (@thangType, options) -> super() - @isMissile = @thangType.get('name') in ['Arrow', 'Spear'] + spriteName = @thangType.get('name') + @isMissile = /(Missile|Arrow|Spear)/.test(spriteName) and not /Tower/.test(spriteName) @options = _.extend($.extend(true, {}, @options), options) @setThang @options.thang if @thang? diff --git a/app/models/Level.coffee b/app/models/Level.coffee index 8e38a9c70..e9e4676b4 100644 --- a/app/models/Level.coffee +++ b/app/models/Level.coffee @@ -97,19 +97,23 @@ module.exports = class Level extends CocoModel placeholdersUsed[placeholderComponent.original] = true placeholderConfig = placeholderComponent.config ? {} if placeholderConfig.pos # Pull in Physical pos x and y + levelThangComponent.config ?= {} levelThangComponent.config.pos ?= {} levelThangComponent.config.pos.x = placeholderConfig.pos.x levelThangComponent.config.pos.y = placeholderConfig.pos.y levelThangComponent.config.rotation = placeholderConfig.rotation else if placeholderConfig.team # Pull in Allied team + levelThangComponent.config ?= {} levelThangComponent.config.team = placeholderConfig.team else if placeholderConfig.significantProperty # For levels where we cheat on what counts as an enemy + levelThangComponent.config ?= {} levelThangComponent.config.significantProperty = placeholderConfig.significantProperty else if placeholderConfig.programmableMethods # Take the ThangType default Programmable and merge level-specific Component config into it copy = $.extend true, {}, placeholderConfig levelThangComponent.config = _.merge copy, levelThangComponent.config else if placeholderConfig.extraHUDProperties + levelThangComponent.config ?= {} levelThangComponent.config.extraHUDProperties = _.union(levelThangComponent.config.extraHUDProperties ? [], placeholderConfig.extraHUDProperties) if isHero diff --git a/app/models/ThangType.coffee b/app/models/ThangType.coffee index 789abab76..d72da47d8 100644 --- a/app/models/ThangType.coffee +++ b/app/models/ThangType.coffee @@ -19,6 +19,7 @@ module.exports = class ThangType extends CocoModel 'robot-walker': '5301696ad82649ec2c0c9b0d' 'michael-heasell': '53e126a4e06b897606d38bef' 'ian-elliott': '53e12be0d042f23505c3023b' + 'ninja': '52fc0ed77e01835453bd8f6c' @items: 'simple-boots': '53e237bf53457600003e3f05' urlRoot: '/db/thang.type' @@ -305,7 +306,7 @@ module.exports = class ThangType extends CocoModel ['Warrior', 'Ranger', 'Wizard'] getHeroStats: -> - # Translate from raw hero properties into appropriate display values for the ChooseHeroView. + # Translate from raw hero properties into appropriate display values for the PlayHeroesModal. # Adapted from https://docs.google.com/a/codecombat.com/spreadsheets/d/1BGI1bzT4xHvWA81aeyIaCKWWw9zxn7-MwDdydmB5vw4/edit#gid=809922675 return unless heroClass = @get('heroClass') components = @get('components') or [] diff --git a/app/models/User.coffee b/app/models/User.coffee index 7fab3e260..45cb92827 100644 --- a/app/models/User.coffee +++ b/app/models/User.coffee @@ -85,7 +85,10 @@ module.exports = class User extends CocoModel gemsSpent = @get('spent') ? 0 gemsEarned + gemsPurchased - gemsSpent - heroes: -> (me.get('earned')?.heroes ? []).concat(me.get('purchased')?.heroes ? []) + heroes: -> + heroes = (me.get('earned')?.heroes ? []).concat(me.get('purchased')?.heroes ? []).concat([ThangType.heroes.captain, ThangType.heroes.knight]) + heroes = heroes.concat [ThangType.heroes.ninja, ThangType.heroes.librarian] if me.isAdmin() + heroes items: -> (me.get('earned')?.items ? []).concat(me.get('purchased')?.items ? []).concat([ThangType.items['simple-boots']]) levels: -> (me.get('earned')?.levels ? []).concat(me.get('purchased')?.levels ? []) ownsHero: (heroOriginal) -> heroOriginal in @heroes() diff --git a/app/styles/game-menu/choose-hero-view.sass b/app/styles/game-menu/choose-hero-view.sass deleted file mode 100644 index 7f9aa713a..000000000 --- a/app/styles/game-menu/choose-hero-view.sass +++ /dev/null @@ -1,176 +0,0 @@ -@import "app/styles/mixins" -@import "app/styles/bootstrap/variables" - -$maxHeroPortraitSize: 100px -$minHeroPortraitSize: 50px - -$heroCanvasHeight: 265px - -#choose-hero-view - - #hero-carousel - margin: 0 auto 20px auto - - .carousel-indicator-container - height: $maxHeroPortraitSize - text-align: center - width: 100% - margin-bottom: 10px - margin-top: 10px - position: relative - z-index: 1 - - .carousel-indicators - position: static - height: $maxHeroPortraitSize - width: 100% - margin-left: 0 - background-repeat: no-repeat - - .hero-indicator - width: $maxHeroPortraitSize - height: $maxHeroPortraitSize - margin: 0px 3px - border: 2px solid black - border-radius: 2px - position: relative - - .hero-avatar - width: 100% - height: 100% - background-size: contain - - &.initialized - @include transition(0.5s ease) - - &.active - border-color: gold - - &.locked - .hero-avatar - @include filter(contrast(50%) brightness(65%)) - - .lock-indicator - position: absolute - width: 40% - left: 30% - top: 30% - @include filter(invert(90%)) - - .hero-item - - &.locked - @include opacity(0.6) - - canvas, .hero-feature-image, .hero-stats - width: 45% - height: $heroCanvasHeight - float: left - - canvas, .hero-feature-image - margin-left: 5% - - .hero-feature-image - display: none - text-align: center - - img - height: $heroCanvasHeight - - .hero-stats - text-shadow: 0 1px 1px rgba(255, 255, 255, 0.6) - - .hero-class, .hero-status, .hero-skills - font-size: 14px - - .hero-stat - margin: 10px 0 - - .progress - width: 90% - text-align: center - height: 26px - position: relative - margin: 0 - - .hero-stat-label - position: absolute - width: 100% - font-size: 18px - text-transform: uppercase - color: white - text-shadow: 0px 1px 0px black, 0px -1px 0px black, -1px 0px 0px black, 1px 0px 0px black - - .carousel-control.left - border-top-left-radius: 10px - border-bottom-left-radius: 10px - - .carousel-control.right - border-top-right-radius: 10px - border-bottom-right-radius: 10px - - .carousel-control - height: 75% - top: 30% - width: 10% - - .select-group - display: inline-block - min-height: 20px - margin-top: 10px - margin-bottom: 10px - padding-left: 20px - vertical-align: middle - - label - margin-right: 20px - margin-bottom: 0 - text-align: top - - &[for="option-code-language"] - vertical-align: top - margin-top: 10px - - select - font-size: 18px - - .fancy-select - display: inline-block - - .trigger, .options - text-transform: none - background-color: #def - - .options - padding-left: 5px - overflow: visible - - .selected - color: #111 - - .hover - color: #111 - background-color: #abc - - .options - li - padding-left: 40px - background: transparent url(/images/common/code_languages/javascript_small.png) no-repeat left center - background-size: 32px 32px - margin-bottom: -1px - - &[data-value="python"] - background-image: url(/images/common/code_languages/python_small.png) - &[data-value="coffeescript"] - background-image: url(/images/common/code_languages/coffeescript_small.png) - &[data-value="clojure"] - background-image: url(/images/common/code_languages/clojure_small.png) - &[data-value="lua"] - background-image: url(/images/common/code_languages/lua_small.png) - &[data-value="io"] - background-image: url(/images/common/code_languages/io_small.png) - -body.ipad #choose-hero-view - // iPad is Python-only for now. - .form - display: none diff --git a/app/templates/game-menu/choose-hero-view.jade b/app/templates/game-menu/choose-hero-view.jade deleted file mode 100644 index 45a90ee45..000000000 --- a/app/templates/game-menu/choose-hero-view.jade +++ /dev/null @@ -1,65 +0,0 @@ -#hero-carousel.carousel.slide(data-interval=0) - .carousel-indicator-container - ol.carousel-indicators - for hero, index in heroes - li(data-hero-id=hero.get('original'), title=hero.get('name'), data-slide-to=index, data-target="#hero-carousel", class="hero-indicator" + (hero.locked ? " locked" : "")) - .hero-avatar - if hero.locked - img.lock-indicator(src="/images/pages/game-menu/lock.png") - - .carousel-inner - for hero in heroes - - var info = heroInfo[hero.get('slug')] - div(class="item hero-item" + (hero.locked ? " locked" : ""), data-hero-id=hero.get('original')) - canvas.hero-canvas - .hero-feature-image - img - .hero-stats - h2= info.fullName - //.hero-description= info.description // Not until we have written the descriptions. - .hero-status - span(data-i18n="choose_hero.status") Status - span.spr : - if hero.locked - | #{info.status} - else - | Available - .hero-class - span(data-i18n="choose_hero.weapons") Weapons - span.spr : - | #{info.weapons} - for statInfo in [{name: 'attack', color: 'danger'}, {name: 'health', color: 'info'}, {name: 'speed', color: 'success'}] - - var tooltip = ""; - - if (statInfo.name == 'attack') - - tooltip = Math.round(100 * info[statInfo.name + 'Factor']) + '% ' + info.class.toLowerCase() + ' weapon damage'; - - else if (statInfo.name == 'health') - - tooltip = Math.round(100 * info[statInfo.name + 'Factor']) + '% ' + info.class.toLowerCase() + ' armor health'; - - else if (statInfo.name == 'speed') - - tooltip = info.speedAbsolute + ' meters per second'; - .hero-stat(title=tooltip) - .progress - div(class="progress-bar progress-bar-" + statInfo.color, style="width: " + (10 * info[statInfo.name]) + "%") - .hero-stat-label - span(data-i18n="choose_hero." + statInfo.name) - span.spr : - | #{info[statInfo.name]} - - if info.skills - .hero-skills - span(data-i18n="choose_hero.skills") Skills - span.spr : - for skill in info.skills - code.spl.spr= skill - a.carousel-control.left(role="button", data-slide="prev", href="#hero-carousel") - span.glyphicon.glyphicon-chevron-left - a.carousel-control.right(role="button", data-slide="next", href="#hero-carousel") - span.glyphicon.glyphicon-chevron-right - - -.form - .form-group.select-group - //span.help-block(data-i18n="choose_hero.programming_language_description") Which programming language do you want to use? - label.control-label(for="option-code-language", data-i18n="choose_hero.programming_language") Programming Language - select#option-code-language(name="code-language") - for option in codeLanguages - option(value=option.id, selected=codeLanguage === option.id)= option.name diff --git a/app/views/game-menu/ChooseHeroView.coffee b/app/views/game-menu/ChooseHeroView.coffee deleted file mode 100644 index cad4e72dd..000000000 --- a/app/views/game-menu/ChooseHeroView.coffee +++ /dev/null @@ -1,293 +0,0 @@ -CocoView = require 'views/kinds/CocoView' -template = require 'templates/game-menu/choose-hero-view' -{me} = require 'lib/auth' -ThangType = require 'models/ThangType' -CocoCollection = require 'collections/CocoCollection' -SpriteBuilder = require 'lib/sprites/SpriteBuilder' -AudioPlayer = require 'lib/AudioPlayer' - -module.exports = class ChooseHeroView extends CocoView - id: 'choose-hero-view' - className: 'tab-pane' - template: template - - events: - 'slide.bs.carousel #hero-carousel': 'onHeroChanged' - 'change #option-code-language': 'onCodeLanguageChanged' - - shortcuts: - 'left': -> @$el.find('#hero-carousel').carousel('prev') if @heroes.models.length and not @$el.hasClass 'secret' - 'right': -> @$el.find('#hero-carousel').carousel('next') if @heroes.models.length and not @$el.hasClass 'secret' - - constructor: (options) -> - super options - @heroes = new CocoCollection([], {model: ThangType}) - @heroes.url = '/db/thang.type?view=heroes&project=original,name,slug,soundTriggers,featureImage,gems,heroClass,description,components' - @supermodel.loadCollection(@heroes, 'heroes') - @stages = {} - - destroy: -> - for heroIndex, stage of @stages - createjs.Ticker.removeEventListener "tick", stage - stage.removeAllChildren() - super() - - getRenderData: (context={}) -> - context = super(context) - context.heroes = @heroes.models - hero.locked = temporaryHeroInfo[hero.get('slug')].status is 'Locked' and not me.ownsHero hero.get('original') for hero in context.heroes - context.codeLanguages = [ - {id: 'python', name: 'Python (Default)'} - {id: 'javascript', name: 'JavaScript'} - {id: 'coffeescript', name: 'CoffeeScript'} - {id: 'clojure', name: 'Clojure (Experimental)'} - {id: 'lua', name: 'Lua (Experimental)'} - {id: 'io', name: 'Io (Experimental)'} - ] - context.codeLanguage = @codeLanguage = @options.session.get('codeLanguage') ? me.get('aceConfig')?.language ? 'python' - context.heroInfo = temporaryHeroInfo - context - - afterRender: -> - super() - return unless @supermodel.finished() - heroes = @heroes.models - @$el.find('.hero-indicator').each -> - heroID = $(@).data('hero-id') - hero = _.find heroes, (hero) -> hero.get('original') is heroID - $(@).find('.hero-avatar').css('background-image', "url(#{hero.getPortraitURL()})").tooltip() - _.defer => $(@).addClass 'initialized' - @canvasWidth = 313 # @$el.find('canvas').width() # unreliable, whatever - @canvasHeight = @$el.find('canvas').height() - heroConfig = @options.session.get('heroConfig') ? me.get('heroConfig') ? {} - heroIndex = Math.max 0, _.findIndex(heroes, ((hero) -> hero.get('original') is heroConfig.thangType)) - @$el.find(".hero-item:nth-child(#{heroIndex + 1}), .hero-indicator:nth-child(#{heroIndex + 1})").addClass('active') - @onHeroChanged direction: null, relatedTarget: @$el.find('.hero-item')[heroIndex] - @$el.find('.hero-stat').tooltip() - @buildCodeLanguages() - - onHeroChanged: (e) -> - direction = e.direction # 'left' or 'right' - heroItem = $(e.relatedTarget) - hero = _.find @heroes.models, (hero) -> hero.get('original') is heroItem.data('hero-id') - return console.error "Couldn't find hero from heroItem:", heroItem unless hero - heroIndex = heroItem.index() - @$el.find('.hero-indicator').each -> - distance = Math.min 3, Math.abs $(@).index() - heroIndex - size = 100 - (50 / 3) * distance - $(@).css width: size, height: size, top: -(100 - size) / 2 - heroInfo = temporaryHeroInfo[hero.get('slug')] - locked = heroInfo.status is 'Locked' and not me.ownsHero ThangType.heroes[hero.get('slug')] - hero = @loadHero hero, heroIndex - @preloadHero heroIndex + 1 - @preloadHero heroIndex - 1 - @selectedHero = hero unless locked - Backbone.Mediator.publish 'level:hero-selection-updated', hero: @selectedHero - $('#choose-inventory-button').prop 'disabled', locked - - getFullHero: (original) -> - url = "/db/thang.type/#{original}/version" - if fullHero = @supermodel.getModel url - return fullHero - fullHero = new ThangType() - fullHero.setURL url - fullHero = (@supermodel.loadModel fullHero, 'thang').model - fullHero - - preloadHero: (heroIndex) -> - return unless hero = @heroes.models[heroIndex] - @loadHero hero, heroIndex, true - - loadHero: (hero, heroIndex, preloading=false) -> - createjs.Ticker.removeEventListener 'tick', stage for stage in _.values @stages - if featureImage = hero.get 'featureImage' - $(".hero-item[data-hero-id='#{hero.get('original')}'] canvas").hide() - $(".hero-item[data-hero-id='#{hero.get('original')}'] .hero-feature-image").show().find('img').prop('src', '/file/' + featureImage) - @playSelectionSound hero unless preloading - return hero - createjs.Ticker.setFPS 30 # In case we paused it from being inactive somewhere else - if stage = @stages[heroIndex] - unless preloading - _.defer -> createjs.Ticker.addEventListener 'tick', stage # Deferred, otherwise it won't start updating for some reason. - @playSelectionSound hero - return hero - fullHero = @getFullHero hero.get 'original' - onLoaded = => - return unless canvas = $(".hero-item[data-hero-id='#{fullHero.get('original')}'] canvas") - canvas.show().prop width: @canvasWidth, height: @canvasHeight - builder = new SpriteBuilder(fullHero) - movieClip = builder.buildMovieClip(fullHero.get('actions').attack?.animation ? fullHero.get('actions').idle.animation) - movieClip.scaleX = movieClip.scaleY = canvas.prop('height') / 120 # Average hero height is ~110px tall at normal resolution - if fullHero.get('name') in ['Knight', 'Robot Walker'] # These are too big, so shrink them. - movieClip.scaleX *= 0.7 - movieClip.scaleY *= 0.7 - movieClip.regX = -fullHero.get('positions').registration.x - movieClip.regY = -fullHero.get('positions').registration.y - movieClip.x = canvas.prop('width') * 0.5 - movieClip.y = canvas.prop('height') * 0.925 # This is where the feet go. - stage = new createjs.Stage(canvas[0]) - @stages[heroIndex] = stage - stage.addChild movieClip - stage.update() - movieClip.gotoAndPlay 0 - unless preloading - createjs.Ticker.addEventListener 'tick', stage - @playSelectionSound hero - if fullHero.loaded - _.defer onLoaded - else - @listenToOnce fullHero, 'sync', onLoaded - fullHero - - playSelectionSound: (hero) -> - return if @$el.hasClass 'secret' - @currentSoundInstance?.stop() - return unless sounds = hero.get('soundTriggers')?.selected - return unless sound = sounds[Math.floor Math.random() * sounds.length] - name = AudioPlayer.nameForSoundReference sound - AudioPlayer.preloadSoundReference sound - @currentSoundInstance = AudioPlayer.playSound name, 1 - @currentSoundInstance - - buildCodeLanguages: -> - $select = @$el.find('#option-code-language') - $select.fancySelect().parent().find('.options li').each -> - languageName = $(@).text() - languageID = $(@).data('value') - blurb = $.i18n.t("choose_hero.#{languageID}_blurb") - $(@).text("#{languageName} - #{blurb}") - - onCodeLanguageChanged: (e) -> - @codeLanguage = @$el.find('#option-code-language').val() - @codeLanguageChanged = true - - onShown: -> - # Called when we switch tabs to this within the modal - - onHidden: -> - # Called when the modal itself is dismissed - - -temporaryHeroInfo = - knight: - fullName: 'Tharin Thunderfist' - weapons: 'Swords - Short Range, No Magic' - class: 'Warrior' - description: 'Beefcake! Beefcaaake!' - status: 'Available' - attack: 8 - attackFactor: 1.2 - health: 8.5 - healthFactor: 1.4 - speed: 1.5 - speedAbsolute: 6 - - captain: - fullName: 'Captain Anya Weston' - weapons: 'Swords - Short Range, No Magic' - class: 'Warrior' - description: 'Don\'t bother me, I\'m winning this fight for you.' - status: 'Available' - attack: 8 - attackFactor: 1.2 - health: 8.5 - healthFactor: 1.4 - speed: 1.5 - speedAbsolute: 6 - - thoktar: - fullName: 'Thoktar the Devourer' - weapons: 'Wands, Staffs - Long Range, Magic' - class: 'Wizard' - description: '???' - status: 'Locked' - attack: 5 - attackFactor: 2 - health: 4.5 - healthFactor: 1.4 - speed: 2.5 - speedAbsolute: 7 - skills: ['summonElemental', 'devour'] - - equestrian: - fullName: 'Rider Reynaldo' - weapons: 'Crossbows, Guns - Long Range, No Magic' - class: 'Ranger' - description: '???' - status: 'Locked' - attack: 6 - attackFactor: 1.4 - health: 7 - healthFactor: 1.8 - speed: 1.5 - speedAbsolute: 6 - skills: ['hide'] - - 'potion-master': - fullName: 'Master Snake' - weapons: 'Wands, Staffs - Long Range, Magic' - class: 'Wizard' - description: '???' - status: 'Locked' - attack: 2 - attackFactor: 0.833 - health: 4 - healthFactor: 1.2 - speed: 6 - speedAbsolute: 11 - skills: ['brewPotion'] - - librarian: - fullName: 'Hushbaum' - weapons: 'Wands, Staffs - Long Range, Magic' - class: 'Wizard' - description: '???' - status: 'Locked' - attack: 3 - attackFactor: 1.2 - health: 4.5 - healthFactor: 1.4 - speed: 2.5 - speedAbsolute: 7 - - 'robot-walker': - fullName: '???' - weapons: '???' - class: 'Ranger' - description: '???' - status: 'Locked' - attack: 6.5 - attackFactor: 1.6 - health: 5.5 - healthFactor: 1.2 - speed: 6 - speedAbsolute: 11 - skills: ['???', '???', '???'] - - 'michael-heasell': - fullName: '???' - weapons: '???' - class: 'Ranger' - description: '???' - status: 'Locked' - attack: 4 - attackFactor: 0.714 - health: 5 - healthFactor: 1 - speed: 10 - speedAbsolute: 16 - skills: ['???', '???'] - - 'ian-elliott': - fullName: '???' - weapons: 'Swords - Short Range, No Magic' - class: 'Warrior' - description: '???' - status: 'Locked' - attack: 9.5 - attackFactor: 1.8 - health: 6.5 - healthFactor: 0.714 - speed: 3.5 - speedAbsolute: 8 - skills: ['trueStrike'] diff --git a/app/views/game-menu/InventoryModal.coffee b/app/views/game-menu/InventoryModal.coffee index dc1931d87..8822f7909 100644 --- a/app/views/game-menu/InventoryModal.coffee +++ b/app/views/game-menu/InventoryModal.coffee @@ -357,35 +357,47 @@ module.exports = class InventoryModal extends ModalView @remainingRequiredEquipment = [] @$el.find('.should-equip').removeClass('should-equip') inWorldMap = $('#world-map-view').length + if heroClass = @selectedHero?.get('heroClass') + for slot, item of _.clone equipment + itemModel = @items.findWhere original: item + unless itemModel and heroClass in itemModel.classes + console.log 'Unequipping', itemModel.get('heroClass'), 'item', itemModel.get('name'), 'from slot due to class restrictions.' + @unequipItemFromSlot @$el.find(".item-slot[data-slot='#{slot}']") for slot, item of restrictedGear equipped = equipment[slot] if equipped and equipped is gear[restrictedGear[slot]] console.log 'Unequipping restricted item', restrictedGear[slot], 'for', slot, 'before level', @options.levelID @unequipItemFromSlot @$el.find(".item-slot[data-slot='#{slot}']") - for slot, item of requiredGear - #continue if item is 'leather-tunic' and inWorldMap and @options.levelID is 'the-raised-sword' # Don't tell them they need it until they need it in the level # ... when we make it so that you can buy it - equipped = equipment[slot] - continue if equipped and not ( - (item is 'builders-hammer' and equipped in [gear['simple-sword'], gear['long-sword']]) or - (item in ['simple-sword', 'long-sword'] and equipped is gear['builders-hammer']) or - (item is 'leather-boots' and equipped is gear['simple-boots']) or - (item is 'simple-boots' and equipped is gear['leather-boots']) - ) - itemModel = @items.findWhere {slug: item} - continue unless itemModel - availableSlotSelector = "#unequipped .item[data-item-id='#{itemModel.id}']" - @highlightElement availableSlotSelector, delay: 500, sides: ['right'], rotation: Math.PI / 2 - @$el.find(availableSlotSelector).addClass 'should-equip' - @$el.find("#equipped div[data-slot='#{slot}']").addClass 'should-equip' - @$el.find('#double-click-hint').removeClass('secret') - @remainingRequiredEquipment.push slot: slot, item: gear[item] + if heroClass is 'Warrior' + # After they switch to a ranger or wizard, we stop being so finicky about gear. + for slot, item of requiredGear + #continue if item is 'leather-tunic' and inWorldMap and @options.levelID is 'the-raised-sword' # Don't tell them they need it until they need it in the level # ... when we make it so that you can buy it + equipped = equipment[slot] + continue if equipped and not ( + (item is 'builders-hammer' and equipped in [gear['simple-sword'], gear['long-sword']]) or + (item in ['simple-sword', 'long-sword'] and equipped is gear['builders-hammer']) or + (item is 'leather-boots' and equipped is gear['simple-boots']) or + (item is 'simple-boots' and equipped is gear['leather-boots']) + ) + itemModel = @items.findWhere {slug: item} + continue unless itemModel + availableSlotSelector = "#unequipped .item[data-item-id='#{itemModel.id}']" + @highlightElement availableSlotSelector, delay: 500, sides: ['right'], rotation: Math.PI / 2 + @$el.find(availableSlotSelector).addClass 'should-equip' + @$el.find("#equipped div[data-slot='#{slot}']").addClass 'should-equip' + @$el.find('#double-click-hint').removeClass('secret') + @remainingRequiredEquipment.push slot: slot, item: gear[item] if hadRequired and not @remainingRequiredEquipment.length @endHighlight() @highlightElement '#play-level-button', duration: 5000 $('#play-level-button').prop('disabled', @remainingRequiredEquipment.length > 0) setHero: (@selectedHero) -> + if @selectedHero.loading + @listenToOnce @selectedHero, 'sync', => @setHero? @selectedHero + return @$el.removeClass('Warrior Ranger Wizard').addClass(@selectedHero.get('heroClass')) + @requireLevelEquipment() @render() onShown: -> @@ -418,8 +430,11 @@ module.exports = class InventoryModal extends ModalView inventory = @getCurrentEquipmentConfig() patchSession = patchMe = false patchSession ||= not _.isEqual inventory, sessionHeroConfig.inventory - patchMe ||= not _.isEqual inventory, lastHeroConfig.inventory sessionHeroConfig.inventory = inventory + if hero = @selectedHero.get('original') + patchSession ||= not _.isEqual hero, sessionHeroConfig.thangType + sessionHeroConfig.thangType = hero + patchMe ||= not _.isEqual inventory, lastHeroConfig.inventory lastHeroConfig.inventory = inventory if patchMe console.log 'setting me.heroConfig to', JSON.stringify(lastHeroConfig) diff --git a/app/views/play/WorldMapView.coffee b/app/views/play/WorldMapView.coffee index f158018b5..64c9ff629 100644 --- a/app/views/play/WorldMapView.coffee +++ b/app/views/play/WorldMapView.coffee @@ -708,21 +708,10 @@ forest = [ id: 'drop-the-flag' description: 'it is hot' disabled: not me.isAdmin() - nextLevels: - continue: 'rich-forager' - x: 77.54 - y: 50.94 - } - { - name: 'Rich Forager' - type: 'hero' - id: 'rich-forager' - description: 'if i were a rich man' - disabled: not me.isAdmin() nextLevels: continue: 'deadly-pursuit' x: 77.54 - y: 45.94 + y: 50.94 } { name: 'Deadly Pursuit' @@ -731,10 +720,21 @@ forest = [ description: 'what is a dirt nap? it is death.' disabled: not me.isAdmin() nextLevels: - continue: 'multiplayer-treasure-grove' + continue: 'rich-forager' x: 77.54 y: 40.94 } + { + name: 'Rich Forager' + type: 'hero' + id: 'rich-forager' + description: 'if i were a rich man' + disabled: not me.isAdmin() + nextLevels: + continue: 'multiplayer-treasure-grove' + x: 77.54 + y: 45.94 + } { name: 'Multiplayer Treasure Grove' type: 'hero' diff --git a/app/views/play/level/ControlBarView.coffee b/app/views/play/level/ControlBarView.coffee index aa5404b12..8eb859fe8 100644 --- a/app/views/play/level/ControlBarView.coffee +++ b/app/views/play/level/ControlBarView.coffee @@ -63,6 +63,7 @@ module.exports = class ControlBarView extends CocoView @homeLink = c.homeLink = '/' @homeViewClass = require 'views/HomeView' c.editorLink = "/editor/level/#{@level.get('slug')}" + c.homeLink = @homeLink c showGameMenuModal: -> diff --git a/app/views/play/level/LevelHUDView.coffee b/app/views/play/level/LevelHUDView.coffee index d9414eeae..669da3687 100644 --- a/app/views/play/level/LevelHUDView.coffee +++ b/app/views/play/level/LevelHUDView.coffee @@ -98,8 +98,8 @@ module.exports = class LevelHUDView extends CocoView @stage?.stopTalking() createProperties: -> - if @thang.id is 'Hero Placeholder' - name = {knight: 'Tharin', captain: 'Anya'}[@thang.type] ? 'Hero' + if @thang.id in ['Hero Placeholder', 'Hero Placeholder 1'] + name = {knight: 'Tharin', captain: 'Anya', librarian: 'Hushbaum', sorcerer: 'Pender', 'potion-master': 'Omarn', samurai: 'Hattori', ninja: 'Amara'}[@thang.type] ? 'Hero' else name = if @thang.type then "#{@thang.id} - #{@thang.type}" else @thang.id @$el.find('.thang-name').text name diff --git a/app/views/play/modal/PlayHeroesModal.coffee b/app/views/play/modal/PlayHeroesModal.coffee index dd96354e6..88a78ecbd 100644 --- a/app/views/play/modal/PlayHeroesModal.coffee +++ b/app/views/play/modal/PlayHeroesModal.coffee @@ -29,6 +29,7 @@ module.exports = class PlayHeroesModal extends ModalView @heroes = new CocoCollection([], {model: ThangType}) @heroes.url = '/db/thang.type?view=heroes' @heroes.setProjection ['original','name','slug','soundTriggers','featureImage','gems','heroClass','description','components','extendedName'] + @heroes.comparator = 'gems' @listenToOnce @heroes, 'sync', @onHeroesLoaded @supermodel.loadCollection(@heroes, 'heroes') @stages = {} diff --git a/server/levels/thangs/thang_type_handler.coffee b/server/levels/thangs/thang_type_handler.coffee index 833129c13..537058fbd 100644 --- a/server/levels/thangs/thang_type_handler.coffee +++ b/server/levels/thangs/thang_type_handler.coffee @@ -1,17 +1,6 @@ ThangType = require './ThangType' Handler = require '../../commons/Handler' -heroes = - captain: '529ec584c423d4e83b000014' - knight: '529ffbf1cf1818f2be000001' - librarian: '52fbf74b7e01835453bd8d8e' - equestrian: '52e95b4222efc8e70900175d' - 'potion-master': '52e9adf7427172ae56002172' - thoktar: '52a00542cf1818f2be000006' - 'robot-walker': '5301696ad82649ec2c0c9b0d' - 'michael-heasell': '53e126a4e06b897606d38bef' - 'ian-elliott': '53e12be0d042f23505c3023b' - ThangTypeHandler = class ThangTypeHandler extends Handler modelClass: ThangType jsonSchema: require '../../../app/schemas/models/thang_type' @@ -64,8 +53,7 @@ ThangTypeHandler = class ThangTypeHandler extends Handler query.kind = 'Item' query.tier = {$exists: true} # Items without a tier don't show up anywhere, whereas items without gems don't show up in the store else if req.query.view is 'heroes' - #query.kind = 'Hero' # TODO: when ChooseHeroView is refactored, just use this - query.original = {$in: _.values heroes} # TODO: when ChooseHeroView is refactored, don't do this + query.kind = 'Hero' else if req.query.view is 'i18n-coverage' query.i18nCoverage = {$exists: true}