ModalView = require 'views/core/ModalView' template = require 'templates/play/modal/play-items-modal' buyGemsPromptTemplate = require 'templates/play/modal/buy-gems-prompt' ItemDetailsView = require './ItemDetailsView' BuyGemsModal = require 'views/play/modal/BuyGemsModal' AuthModal = require 'views/core/AuthModal' CocoCollection = require 'collections/CocoCollection' ThangType = require 'models/ThangType' LevelComponent = require 'models/LevelComponent' Level = require 'models/Level' Purchase = require 'models/Purchase' utils = require 'core/utils' PAGE_SIZE = 200 slotToCategory = { 'right-hand': 'primary' 'left-hand': 'secondary' 'head': 'armor' 'torso': 'armor' 'gloves': 'armor' 'feet': 'armor' 'eyes': 'accessories' 'neck': 'accessories' 'wrists': 'accessories' 'left-ring': 'accessories' 'right-ring': 'accessories' 'waist': 'accessories' 'pet': 'misc' 'minion': 'misc' 'flag': 'misc' 'misc-0': 'misc' 'misc-1': 'misc' 'programming-book': 'books' } module.exports = class PlayItemsModal extends ModalView className: 'modal fade play-modal' template: template id: 'play-items-modal' events: 'click .item': 'onItemClicked' 'shown.bs.tab': 'onTabClicked' 'click .unlock-button': 'onUnlockButtonClicked' 'click .buy-gems-prompt-button': 'onBuyGemsPromptButtonClicked' 'click #close-modal': 'hide' 'click': 'onClickedSomewhere' 'update .tab-pane .nano': 'onScrollItemPane' 'click #hero-type-select label': 'onClickHeroTypeSelect' constructor: (options) -> @onScrollItemPane = _.throttle(_.bind(@onScrollItemPane, @), 200) super options @items = new Backbone.Collection() @itemCategoryCollections = {} project = [ 'name' 'components.config' 'components.original' 'slug' 'original' 'rasterIcon' 'gems' 'tier' 'description' 'i18n' 'heroClass' ] itemFetcher = new CocoCollection([], { url: '/db/thang.type?view=items', project: project, model: ThangType }) itemFetcher.skip = 0 itemFetcher.fetch({data: {skip: 0, limit: PAGE_SIZE}}) @listenTo itemFetcher, 'sync', @onItemsFetched @stopListening @supermodel, 'loaded-all' @supermodel.loadCollection(itemFetcher, 'items') @idToItem = {} onItemsFetched: (itemFetcher) -> gemsOwned = me.gems() needMore = itemFetcher.models.length is PAGE_SIZE for model in itemFetcher.models model.owned = me.ownsItem model.get('original') continue unless (cost = model.get('gems')) or model.owned category = slotToCategory[model.getAllowedSlots()[0]] or 'misc' @itemCategoryCollections[category] ?= new Backbone.Collection() collection = @itemCategoryCollections[category] collection.comparator = (m) -> m.get('tier') ? m.get('gems') collection.add(model) model.name = utils.i18n model.attributes, 'name' model.affordable = cost <= gemsOwned model.silhouetted = not model.owned and model.isSilhouettedItem() model.level = model.levelRequiredForItem() if model.get('tier')? model.unequippable = not _.intersection(me.getHeroClasses(), model.getAllowedHeroClasses()).length model.comingSoon = not model.getFrontFacingStats().props.length and not _.size(model.getFrontFacingStats().stats) and not model.owned # Temp: while there are placeholder items @idToItem[model.id] = model if itemFetcher.skip isnt 0 # Make sure we render the newly fetched items, except the first time (when it happens automatically). @render() if needMore itemFetcher.skip += PAGE_SIZE itemFetcher.fetch({data: {skip: itemFetcher.skip, limit: PAGE_SIZE}}) getRenderData: (context={}) -> context = super(context) context.itemCategoryCollections = @itemCategoryCollections context.itemCategories = _.keys @itemCategoryCollections context.itemCategoryNames = ($.i18n.t "items.#{category}" for category in context.itemCategories) context.gems = me.gems() context afterRender: -> super() return unless @supermodel.finished() @playSound 'game-menu-open' @$el.find('.nano:visible').nanoScroller({alwaysVisible: true}) @itemDetailsView = new ItemDetailsView() @insertSubView(@itemDetailsView) @$el.find("a[href='#item-category-armor']").click() # Start on armor tab, if it's there. earnedLevels = me.get('earned')?.levels or [] if Level.levels['defense-of-plainswood'] not in earnedLevels @$el.find('#misc-tab').hide() @$el.find('#hero-type-select #warrior').click() # Start on warrior tab, if low level. onHidden: -> super() @playSound 'game-menu-close' #- Click events onItemClicked: (e) -> return if $(e.target).closest('.unlock-button').length @playSound 'menu-button-click' itemEl = $(e.target).closest('.item') wasSelected = itemEl.hasClass('selected') @$el.find('.item.selected').removeClass('selected') if wasSelected item = null else item = @idToItem[itemEl.data('item-id')] if item.silhouetted and not item.owned item = null else itemEl.addClass('selected') unless wasSelected @itemDetailsView.setItem(item) onTabClicked: (e) -> @playSound 'game-menu-tab-switch' nano = $($(e.target).attr('href')).find('.nano') nano.nanoScroller({alwaysVisible: true}) @paneNanoContent = nano.find('.nano-content') @onScrollItemPane() onScrollItemPane: -> # dynamically load visible items when the user scrolls enough to see them return console.error "Couldn't update scroll, since paneNanoContent wasn't initialized." unless @paneNanoContent items = @paneNanoContent.find('.item:not(.loaded)') threshold = @paneNanoContent.height() + 100 for itemEl in items itemEl = $(itemEl) if itemEl.position().top < threshold $(itemEl).addClass('loaded') item = @idToItem[itemEl.data('item-id')] itemEl.find('.item-silhouette, .item-img').attr('src', item.getPortraitURL()) onClickHeroTypeSelect: (e) -> value = $(e.target).closest('label').attr('id') tabContent = @$el.find('.tab-content') tabContent.removeClass('filter-wizard filter-ranger filter-warrior') tabContent.addClass("filter-#{value}") if value isnt 'all' onUnlockButtonClicked: (e) -> e.stopPropagation() button = $(e.target).closest('button') item = @idToItem[button.data('item-id')] affordable = item.affordable if not affordable @playSound 'menu-button-click' @askToBuyGems button else if button.hasClass('confirm') @playSound 'menu-button-unlock-end' purchase = Purchase.makeFor(item) purchase.save() #- set local changes to mimic what should happen on the server... purchased = me.get('purchased') ? {} purchased.items ?= [] purchased.items.push(item.get('original')) item.owned = true me.set('purchased', purchased) me.set('spent', (me.get('spent') ? 0) + item.get('gems')) #- ...then rerender key bits @renderSelectors(".item[data-item-id='#{item.id}']", "#gems-count") @itemDetailsView.render() Backbone.Mediator.publish 'store:item-purchased', item: item, itemSlug: item.get('slug') else @playSound 'menu-button-unlock-start' button.addClass('confirm').text($.i18n.t('play.confirm')) @$el.one 'click', (e) -> button.removeClass('confirm').text($.i18n.t('play.unlock')) if e.target isnt button[0] askToSignUp: -> authModal = new AuthModal supermodel: @supermodel authModal.mode = 'signup' return @openModalView authModal askToBuyGems: (unlockButton) -> @$el.find('.unlock-button').popover 'destroy' popoverTemplate = buyGemsPromptTemplate {} unlockButton.popover( animation: true trigger: 'manual' placement: 'top' content: ' ' # template has it container: @$el template: popoverTemplate ).popover 'show' popover = unlockButton.data('bs.popover') popover?.$tip?.i18n() onBuyGemsPromptButtonClicked: (e) -> @playSound 'menu-button-click' return @askToSignUp() if me.get('anonymous') @openModalView new BuyGemsModal() onClickedSomewhere: (e) -> return if @destroyed @$el.find('.unlock-button').popover 'destroy' destroy: -> @$el.find('.unlock-button').popover 'destroy' super()