2014-11-28 20:49:41 -05:00
ModalView = require ' views/core/ModalView '
2014-11-29 15:46:04 -05:00
template = require ' templates/play/menu/inventory-modal '
2014-11-24 13:51:20 -05:00
buyGemsPromptTemplate = require ' templates/play/modal/buy-gems-prompt '
2014-11-28 20:49:41 -05:00
{ me } = require ' core/auth '
2014-08-08 14:36:41 -04:00
ThangType = require ' models/ThangType '
2014-08-13 20:21:37 -04:00
CocoCollection = require ' collections/CocoCollection '
ItemView = require ' ./ItemView '
2014-09-21 02:06:28 -04:00
SpriteBuilder = require ' lib/sprites/SpriteBuilder '
2014-11-10 18:24:05 -05:00
ItemDetailsView = require ' views/play/modal/ItemDetailsView '
Purchase = require ' models/Purchase '
2014-11-24 13:51:20 -05:00
BuyGemsModal = require ' views/play/modal/BuyGemsModal '
2016-02-25 18:24:16 -05:00
CreateAccountModal = require ' views/core/CreateAccountModal '
2014-08-13 20:21:37 -04:00
2014-11-06 19:23:23 -05:00
hasGoneFullScreenOnce = false
module.exports = class InventoryModal extends ModalView
id: ' inventory-modal '
className: ' modal fade play-modal '
2014-08-08 14:36:41 -04:00
template: template
2014-10-21 16:57:49 -04:00
slots: [ ' head ' , ' eyes ' , ' neck ' , ' torso ' , ' wrists ' , ' gloves ' , ' left-ring ' , ' right-ring ' , ' right-hand ' , ' left-hand ' , ' waist ' , ' feet ' , ' programming-book ' , ' pet ' , ' minion ' , ' flag ' ] #, 'misc-0', 'misc-1'] # TODO: bring in misc slot(s) again when we have space
2015-06-29 20:25:43 -04:00
ringSlots: [ ' left-ring ' , ' right-ring ' ]
2014-11-10 18:24:05 -05:00
closesOnClickOutside: false # because draggable somehow triggers hide when you don't drag onto a draggable
2014-08-14 18:09:10 -04:00
2014-08-13 20:21:37 -04:00
events:
' click .item-slot ' : ' onItemSlotClick '
2014-11-18 16:02:01 -05:00
' click # unequipped .item ' : ' onUnequippedItemClick '
' doubletap # unequipped .item ' : ' onUnequippedItemDoubleClick '
' doubletap .item-slot .item ' : ' onEquippedItemDoubleClick '
' click button.equip-item ' : ' onClickEquipItemButton '
2014-11-06 19:23:23 -05:00
' shown.bs.modal ' : ' onShown '
' click # choose-hero-button ' : ' onClickChooseHero '
' click # play-level-button ' : ' onClickPlayLevel '
2014-11-10 18:24:05 -05:00
' click .unlock-button ' : ' onUnlockButtonClicked '
' click # equip-item-viewed ' : ' onClickEquipItemViewed '
' click # unequip-item-viewed ' : ' onClickUnequipItemViewed '
' click # close-modal ' : ' hide '
2014-11-24 13:51:20 -05:00
' click .buy-gems-prompt-button ' : ' onBuyGemsPromptButtonClicked '
' click ' : ' onClickedSomewhere '
2014-12-06 17:21:56 -05:00
' update # unequipped .nano ' : ' onScrollUnequipped '
2014-09-21 02:06:28 -04:00
2014-08-13 20:21:37 -04:00
shortcuts:
' esc ' : ' clearSelection '
2014-11-06 19:23:23 -05:00
' enter ' : ' onClickPlayLevel '
2014-11-10 19:09:41 -05:00
2014-11-10 18:24:05 -05:00
#- Setup
2014-08-14 18:09:10 -04:00
2014-08-13 20:21:37 -04:00
initialize: (options) ->
2014-12-06 17:21:56 -05:00
@onScrollUnequipped = _ . throttle ( _ . bind ( @ onScrollUnequipped , @ ) , 200 )
2014-08-13 20:21:37 -04:00
super ( arguments . . . )
2014-08-14 18:09:10 -04:00
@items = new CocoCollection ( [ ] , { model: ThangType } )
2014-11-10 18:24:05 -05:00
# TODO: switch to item store loading system?
@items.url = ' /db/thang.type?view=items '
@ items . setProjection [
2014-11-11 01:07:55 -05:00
' name '
' slug '
' components '
' original '
' rasterIcon '
2014-11-26 17:18:17 -05:00
' dollImages '
2014-11-11 01:07:55 -05:00
' gems '
' tier '
' description '
' heroClass '
2014-11-10 18:24:05 -05:00
' i18n '
]
2014-08-13 20:21:37 -04:00
@ supermodel . loadCollection ( @ items , ' items ' )
2014-11-10 19:09:41 -05:00
@equipment = { } # Assign for real when we have loaded the session and items.
2014-09-21 02:06:28 -04:00
2014-11-10 18:24:05 -05:00
onItemsLoaded: ->
2014-11-10 19:09:41 -05:00
item.notInLevel = true for item in @ items . models
@equipment = @ options . equipment or @ options . session ? . get ( ' heroConfig ' ) ? . inventory or me . get ( ' heroConfig ' ) ? . inventory or { }
@equipment = $ . extend true , { } , @ equipment
2014-11-10 18:24:05 -05:00
@ requireLevelEquipment ( )
@itemGroups = { }
2014-12-24 14:01:39 -05:00
@itemGroups.requiredPurchaseItems = new Backbone . Collection ( )
2014-11-10 18:24:05 -05:00
@itemGroups.availableItems = new Backbone . Collection ( )
@itemGroups.restrictedItems = new Backbone . Collection ( )
@itemGroups.lockedItems = new Backbone . Collection ( )
2014-11-26 12:22:30 -05:00
itemGroup.comparator = ( (m) -> m . get ( ' tier ' ) ? m . get ( ' gems ' ) ) for itemGroup in _ . values @ itemGroups
2014-11-10 18:24:05 -05:00
equipped = _ . values ( @ equipment )
@ sortItem ( item , equipped ) for item in @ items . models
2014-11-10 19:09:41 -05:00
2014-11-10 18:24:05 -05:00
sortItem: (item, equipped) ->
equipped ? = _ . values ( @ equipment )
2014-11-10 19:09:41 -05:00
2014-11-10 18:24:05 -05:00
# general starting classes
item.classes = _ . clone ( item . getAllowedSlots ( ) )
for heroClass in item . getAllowedHeroClasses ( )
item . classes . push heroClass
item . classes . push ' equipped ' if item . get ( ' original ' ) in equipped
# sort into one of the four groups
2014-11-10 19:09:41 -05:00
locked = not ( item . get ( ' original ' ) in me . items ( ) )
2014-11-21 23:36:56 -05:00
#locked = false if me.get('slug') is 'nick'
2014-11-10 18:24:05 -05:00
2015-01-02 18:44:43 -05:00
allRestrictedGear = _ . flatten ( _ . values ( @ options . level . get ( ' restrictedGear ' ) ? { } ) )
restricted = item . get ( ' original ' ) in allRestrictedGear
# TODO: make this re-use result of computation of updateLevelRequiredItems, which we can only do after heroClass is ready...
requiredToPurchase = false
if requiredGear = @ options . level . get ( ' requiredGear ' )
inCampaignView = $ ( ' # campaign-view ' ) . length
unless gearSlugs [ item . get ( ' original ' ) ] is ' tarnished-bronze-breastplate ' and inCampaignView and @ options . level . get ( ' slug ' ) is ' the-raised-sword '
for slot in item . getAllowedSlots ( )
continue unless requiredItems = requiredGear [ slot ]
2015-06-29 20:25:43 -04:00
continue if @ equipment [ slot ] and @ equipment [ slot ] not in allRestrictedGear and slot not in @ ringSlots
2015-01-02 18:44:43 -05:00
# Point out that they must buy it if they haven't bought any of the required items for that slot, and it's the first one.
if item . get ( ' original ' ) is requiredItems [ 0 ] and not _ . find ( requiredItems , (requiredItem) -> me . ownsItem requiredItem )
requiredToPurchase = true
break
2015-01-31 16:18:32 -05:00
if requiredToPurchase and locked and not item . get ( ' gems ' )
# Either one of two things has happened:
# 1. There's a bug and the player doesn't have a required item they should have.
# 2. The player is trying to play a level they haven't unlocked.
# We'll just pretend they own it so that they don't get stuck.
application . tracker ? . trackEvent ' Required Item Locked ' , level: @ options . level . get ( ' slug ' ) , label: @ options . level . get ( ' slug ' ) , item: item . get ( ' name ' ) , playerLevel: me . level ( ) , levelUnlocked: me . ownsLevel @ options . level . get ( ' original ' )
locked = false
2014-12-28 16:25:20 -05:00
placeholder = not item . getFrontFacingStats ( ) . props . length and not _ . size ( item . getFrontFacingStats ( ) . stats )
if placeholder and locked # The item is not complete, so don't put it into a collection.
null
2015-01-02 18:44:43 -05:00
else if locked and requiredToPurchase
2014-12-24 14:01:39 -05:00
item . classes . push ' locked '
@ itemGroups . requiredPurchaseItems . add item
2015-01-02 18:44:43 -05:00
else if locked
2014-11-10 18:24:05 -05:00
item . classes . push ' locked '
2014-11-30 22:45:40 -05:00
if item . isSilhouettedItem ( ) or not item . get ( ' gems ' )
2014-11-29 20:06:46 -05:00
# Don't even load/show these--don't add to a collection. (Bandwidth optimization.)
null
else
@ itemGroups . lockedItems . add ( item )
2014-12-28 16:25:20 -05:00
else if restricted
2014-11-10 18:24:05 -05:00
@ itemGroups . restrictedItems . add ( item )
item . classes . push ' restricted '
else
@ itemGroups . availableItems . add ( item )
2014-11-10 19:09:41 -05:00
2014-11-26 10:33:07 -05:00
# level to unlock
item.level = item . levelRequiredForItem ( ) if item . get ( ' tier ' ) ?
2014-08-13 20:21:37 -04:00
onLoaded: ->
2014-11-10 19:09:41 -05:00
# Both items and session have been loaded.
@ onItemsLoaded ( )
2014-08-13 20:21:37 -04:00
super ( )
2014-08-08 14:36:41 -04:00
getRenderData: (context={}) ->
context = super ( context )
2014-08-13 20:21:37 -04:00
context.equipped = _ . values ( @ equipment )
2014-08-14 18:09:10 -04:00
context.items = @ items . models
2014-11-10 18:24:05 -05:00
context.itemGroups = @ itemGroups
2014-08-13 20:21:37 -04:00
context.slots = @ slots
2014-11-10 18:24:05 -05:00
context.selectedHero = @ selectedHero
2014-12-06 17:21:56 -05:00
context.selectedHeroClass = @ selectedHero ? . get ( ' heroClass ' )
2014-08-13 20:21:37 -04:00
context.equipment = _ . clone @ equipment
2014-11-10 18:24:05 -05:00
context . equipment [ slot ] = @ items . findWhere { original: itemOriginal } for slot , itemOriginal of context . equipment
context.gems = me . gems ( )
2014-08-08 14:36:41 -04:00
context
afterRender: ->
super ( )
2014-11-10 19:09:41 -05:00
@ $el . find ( ' # play-level-button ' ) . css ( ' visibility ' , ' hidden ' )
2014-08-13 20:21:37 -04:00
return unless @ supermodel . finished ( )
2014-11-10 19:09:41 -05:00
@ $el . find ( ' # play-level-button ' ) . css ( ' visibility ' , ' visible ' )
2014-11-10 18:24:05 -05:00
@ setUpDraggableEventsForAvailableEquipment ( )
@ setUpDraggableEventsForEquippedArea ( )
@ delegateEvents ( )
@itemDetailsView = new ItemDetailsView ( )
@ insertSubView ( @ itemDetailsView )
@ requireLevelEquipment ( )
2014-11-12 21:01:35 -05:00
@ $el . find ( ' .nano ' ) . nanoScroller ( { alwaysVisible: true } )
2014-11-18 16:20:51 -05:00
@ onSelectionChanged ( )
2014-11-30 16:00:45 -05:00
@ onEquipmentChanged ( )
2014-11-10 19:09:41 -05:00
2014-11-10 18:24:05 -05:00
afterInsert: ->
super ( )
@canvasWidth = @ $el . find ( ' canvas ' ) . innerWidth ( )
@canvasHeight = @ $el . find ( ' canvas ' ) . innerHeight ( )
@inserted = true
@ requireLevelEquipment ( )
#- Draggable logic
2014-11-10 19:09:41 -05:00
2014-11-10 18:24:05 -05:00
setUpDraggableEventsForAvailableEquipment: ->
2014-11-18 16:02:01 -05:00
for availableItemEl in @ $el . find ( ' # unequipped .item ' )
2014-11-10 18:24:05 -05:00
availableItemEl = $ ( availableItemEl )
continue if availableItemEl . hasClass ( ' locked ' ) or availableItemEl . hasClass ( ' restricted ' )
dragHelper = availableItemEl . clone ( ) . addClass ( ' draggable-item ' )
do (dragHelper, availableItemEl) =>
availableItemEl . draggable
2014-09-21 02:06:28 -04:00
revert: ' invalid '
appendTo: @ $el
cursorAt: { left: 35.5 , top: 35.5 }
helper: -> dragHelper
revertDuration: 200
2014-09-21 23:19:27 -04:00
distance: 10
2014-09-21 02:06:28 -04:00
scroll: false
2014-11-10 18:24:05 -05:00
zIndex: 1100
availableItemEl . on ' dragstart ' , => @ selectUnequippedItem ( availableItemEl )
2014-11-10 19:09:41 -05:00
2014-11-10 18:24:05 -05:00
setUpDraggableEventsForEquippedArea: ->
2014-09-21 02:06:28 -04:00
for itemSlot in @ $el . find ' .item-slot '
slot = $ ( itemSlot ) . data ' slot '
2014-10-09 18:54:18 -04:00
do (slot, itemSlot) =>
2014-09-21 02:06:28 -04:00
$ ( itemSlot ) . droppable
2014-11-10 18:24:05 -05:00
drop: (e, ui) => @ equipSelectedItem ( )
2014-09-21 02:06:28 -04:00
accept: (el) -> $ ( el ) . parent ( ) . hasClass slot
activeClass: ' droppable '
hoverClass: ' droppable-hover '
2014-09-21 14:24:37 -04:00
tolerance: ' touch '
2014-10-09 18:54:18 -04:00
@ makeEquippedSlotDraggable $ ( itemSlot )
2014-08-14 18:09:10 -04:00
2014-11-10 18:24:05 -05:00
@ $el . find ( ' # equipped ' ) . droppable
drop: (e, ui) => @ equipSelectedItem ( )
2014-10-27 14:17:34 -04:00
accept: (el) -> true
activeClass: ' droppable '
hoverClass: ' droppable-hover '
tolerance: ' pointer '
2014-11-10 19:09:41 -05:00
2014-10-09 18:54:18 -04:00
makeEquippedSlotDraggable: (slot) ->
2014-11-10 21:13:04 -05:00
unequip = =>
2015-02-18 12:56:42 -05:00
itemEl = @ unequipItemFromSlot slot
selectedSlotItemID = itemEl . data ( ' item-id ' )
item = @ items . get ( selectedSlotItemID )
2014-11-10 21:13:04 -05:00
@ requireLevelEquipment ( )
2015-02-18 12:56:42 -05:00
@ showItemDetails ( item , ' equip ' )
@ onSelectionChanged ( )
@ onEquipmentChanged ( )
2014-10-09 18:54:18 -04:00
shouldStayEquippedWhenDropped = (isValidDrop) ->
pos = $ ( @ ) . position ( )
revert = Math . abs ( pos . left ) < $ ( @ ) . outerWidth ( ) and Math . abs ( pos . top ) < $ ( @ ) . outerHeight ( )
unequip ( ) if not revert
revert
# TODO: figure out how to make this actually above the available items list (the .ui-draggable-helper img is still inside .item-view and so underlaps...)
$ ( slot ) . find ( ' img ' ) . draggable
revert: shouldStayEquippedWhenDropped
appendTo: @ $el
cursorAt: { left: 35.5 , top: 35.5 }
revertDuration: 200
distance: 10
scroll: false
2014-11-10 18:24:05 -05:00
zIndex: 100
slot . on ' dragstart ' , => @ selectItemSlot ( slot )
2014-11-10 19:09:41 -05:00
2014-11-10 18:24:05 -05:00
#- Select/equip event handlers
2014-08-13 20:21:37 -04:00
onItemSlotClick: (e) ->
2014-11-05 17:17:11 -05:00
return if @ remainingRequiredEquipment ? . length # Don't let them select a slot if we need them to first equip some require gear.
2014-12-09 17:00:48 -05:00
#@playSound 'menu-button-click'
2014-11-10 18:24:05 -05:00
@ selectItemSlot ( $ ( e . target ) . closest ( ' .item-slot ' ) )
onUnequippedItemClick: (e) ->
return if @ justDoubleClicked
2015-02-18 02:12:19 -05:00
return if @ justClickedEquipItemButton
2014-11-18 16:02:01 -05:00
itemEl = $ ( e . target ) . closest ( ' .item ' )
2014-12-09 17:00:48 -05:00
#@playSound 'menu-button-click'
2014-11-10 18:24:05 -05:00
@ selectUnequippedItem ( itemEl )
onUnequippedItemDoubleClick: (e) ->
2014-11-18 16:02:01 -05:00
itemEl = $ ( e . target ) . closest ( ' .item ' )
return if itemEl . hasClass ( ' locked ' ) or itemEl . hasClass ( ' restricted ' )
2014-11-10 18:24:05 -05:00
@ equipSelectedItem ( )
@justDoubleClicked = true
_ . defer => @justDoubleClicked = false
onEquippedItemDoubleClick: -> @ unequipSelectedItem ( )
onClickEquipItemViewed: -> @ equipSelectedItem ( )
onClickUnequipItemViewed: -> @ unequipSelectedItem ( )
2014-11-10 19:09:41 -05:00
2014-11-18 16:02:01 -05:00
onClickEquipItemButton: (e) ->
2014-11-26 09:58:23 -05:00
@ playSound ' menu-button-click '
2014-11-18 16:02:01 -05:00
itemEl = $ ( e . target ) . closest ( ' .item ' )
@ selectUnequippedItem ( itemEl )
@ equipSelectedItem ( )
2015-02-18 02:12:19 -05:00
@justClickedEquipItemButton = true
_ . defer => @justClickedEquipItemButton = false
2014-11-18 16:02:01 -05:00
2014-11-10 18:24:05 -05:00
#- Select/equip higher-level, all encompassing methods the callbacks all use
2014-11-10 19:09:41 -05:00
2014-11-10 18:24:05 -05:00
selectItemSlot: (slotEl) ->
@ clearSelection ( )
slotEl . addClass ( ' selected ' )
2014-11-18 16:02:01 -05:00
selectedSlotItemID = slotEl . find ( ' .item ' ) . data ( ' item-id ' )
2014-11-10 18:24:05 -05:00
item = @ items . get ( selectedSlotItemID )
if item then @ showItemDetails ( item , ' unequip ' )
2014-08-13 20:21:37 -04:00
@ onSelectionChanged ( )
2014-08-14 18:09:10 -04:00
2014-11-10 18:24:05 -05:00
selectUnequippedItem: (itemEl) ->
@ clearSelection ( )
itemEl . addClass ( ' active ' )
showExtra = if itemEl . hasClass ( ' restricted ' ) then ' restricted ' else if not itemEl . hasClass ( ' locked ' ) then ' equip ' else ' '
@ showItemDetails ( @ items . get ( itemEl . data ( ' item-id ' ) ) , showExtra )
2014-08-13 20:21:37 -04:00
@ onSelectionChanged ( )
2014-11-10 19:09:41 -05:00
2014-11-10 18:24:05 -05:00
equipSelectedItem: ->
selectedItemEl = @ getSelectedUnequippedItem ( )
selectedItem = @ items . get ( selectedItemEl . data ( ' item-id ' ) )
return unless selectedItem
allowedSlots = selectedItem . getAllowedSlots ( )
2014-11-30 17:12:07 -05:00
firstSlot = unequippedSlot = null
for allowedSlot in allowedSlots
slotEl = @ $el . find ( " .item-slot[data-slot= ' #{ allowedSlot } ' ] " )
firstSlot ? = slotEl
unequippedSlot ? = slotEl unless slotEl . find ( ' img ' ) . length
slotEl = unequippedSlot ? firstSlot
2014-11-10 18:24:05 -05:00
selectedItemEl . effect ( ' transfer ' , to: slotEl , duration: 500 , easing: ' easeOutCubic ' )
unequipped = @ unequipItemFromSlot ( slotEl )
selectedItemEl . addClass ( ' equipped ' )
2014-11-18 16:02:01 -05:00
slotEl . append ( selectedItemEl . find ( ' img ' ) . clone ( ) . addClass ( ' item ' ) . data ( ' item-id ' , selectedItem . id ) )
2014-11-10 18:24:05 -05:00
@ clearSelection ( )
@ showItemDetails ( selectedItem , ' unequip ' )
slotEl . addClass ( ' selected ' )
selectedItem . classes . push ' equipped '
@ makeEquippedSlotDraggable slotEl
@ requireLevelEquipment ( )
2014-08-13 20:21:37 -04:00
@ onSelectionChanged ( )
2014-11-26 22:04:47 -05:00
@ onEquipmentChanged ( )
2014-08-14 18:09:10 -04:00
2014-11-10 18:24:05 -05:00
unequipSelectedItem: ->
slotEl = @ getSelectedSlot ( )
@ clearSelection ( )
itemEl = @ unequipItemFromSlot ( slotEl )
return unless itemEl
itemEl . addClass ( ' active ' )
slotEl . effect ( ' transfer ' , to: itemEl , duration: 500 , easing: ' easeOutCubic ' )
selectedSlotItemID = itemEl . data ( ' item-id ' )
item = @ items . get ( selectedSlotItemID )
item.classes = _ . without item . classes , ' equipped '
@ showItemDetails ( item , ' equip ' )
@ requireLevelEquipment ( )
2014-08-14 16:41:32 -04:00
@ onSelectionChanged ( )
2014-11-26 22:04:47 -05:00
@ onEquipmentChanged ( )
2014-11-10 19:09:41 -05:00
2014-11-10 18:24:05 -05:00
#- Select/equip helpers
clearSelection: ->
@ deselectAllSlots ( )
@ deselectAllUnequippedItems ( )
@ hideItemDetails ( )
2014-08-14 18:09:10 -04:00
2014-11-10 18:24:05 -05:00
unequipItemFromSlot: (slotEl) ->
2014-11-18 16:02:01 -05:00
itemEl = slotEl . find ( ' .item ' )
2014-11-10 18:24:05 -05:00
itemIDToUnequip = itemEl . data ( ' item-id ' )
return unless itemIDToUnequip
itemEl . remove ( )
2014-12-24 14:01:39 -05:00
item = @ items . get itemIDToUnequip
item.classes = _ . without item . classes , ' equipped '
2014-11-18 16:02:01 -05:00
@ $el . find ( " # unequipped .item[data-item-id= #{ itemIDToUnequip } ] " ) . removeClass ( ' equipped ' )
2014-08-14 18:09:10 -04:00
2014-11-10 18:24:05 -05:00
deselectAllSlots: ->
2014-09-21 02:06:28 -04:00
@ $el . find ( ' # equipped .item-slot.selected ' ) . removeClass ( ' selected ' )
2014-08-14 18:09:10 -04:00
2014-11-10 18:24:05 -05:00
deselectAllUnequippedItems: ->
2014-11-18 16:02:01 -05:00
@ $el . find ( ' # unequipped .item ' ) . removeClass ( ' active ' )
2014-08-14 18:09:10 -04:00
2014-08-14 16:41:32 -04:00
getSlot: (name) ->
@ $el . find ( " .item-slot[data-slot= #{ name } ] " )
2014-08-14 18:09:10 -04:00
2014-11-10 18:24:05 -05:00
getSelectedSlot: ->
@ $el . find ( ' # equipped .item-slot.selected ' )
2014-08-14 18:09:10 -04:00
2014-11-10 18:24:05 -05:00
getSelectedUnequippedItem: ->
2014-11-18 16:02:01 -05:00
@ $el . find ( ' # unequipped .item.active ' )
2014-08-14 18:09:10 -04:00
2014-08-13 20:21:37 -04:00
onSelectionChanged: ->
2014-11-18 16:20:51 -05:00
heroClass = @ selectedHero ? . get ( ' heroClass ' )
itemsCanBeEquipped = @ $el . find ( ' # unequipped .item.available:not(.equipped) ' ) . filter ( ' . ' + heroClass ) . length
toShow = @ $el . find ( ' # double-click-hint, # available-description ' )
if itemsCanBeEquipped then toShow . removeClass ( ' secret ' ) else toShow . addClass ( ' secret ' )
2014-08-13 20:21:37 -04:00
@ delegateEvents ( )
2014-08-14 18:09:10 -04:00
2014-11-10 19:09:41 -05:00
2014-11-10 18:24:05 -05:00
showItemDetails: (item, showExtra) ->
@ itemDetailsView . setItem ( item )
@ $el . find ( ' # item-details-extra > * ' ) . addClass ( ' secret ' )
@ $el . find ( " # #{ showExtra } -item-viewed " ) . removeClass ( ' secret ' )
2014-11-10 19:09:41 -05:00
2014-11-10 18:24:05 -05:00
hideItemDetails: ->
2014-12-06 14:05:52 -05:00
@ itemDetailsView ? . setItem ( null )
2014-11-10 18:24:05 -05:00
@ $el . find ( ' # item-details-extra > * ' ) . addClass ( ' secret ' )
2014-08-13 20:21:37 -04:00
getCurrentEquipmentConfig: ->
config = { }
for slot in @ $el . find ( ' .item-slot ' )
slotName = $ ( slot ) . data ( ' slot ' )
2014-11-18 16:02:01 -05:00
slotItemID = $ ( slot ) . find ( ' .item ' ) . data ( ' item-id ' )
2014-08-13 20:21:37 -04:00
continue unless slotItemID
item = _ . find @ items . models , { id : slotItemID }
config [ slotName ] = item . get ( ' original ' )
2014-08-14 18:09:10 -04:00
config
2014-10-09 18:54:18 -04:00
requireLevelEquipment: ->
2015-01-02 18:44:43 -05:00
# This is called frequently to make sure the player isn't using any restricted items and knows she must equip any required items.
return unless @ inserted
equipment = if @ supermodel . finished ( ) then @ getCurrentEquipmentConfig ( ) else @ equipment # Make sure we're using latest equipment.
hadRequired = @ remainingRequiredEquipment ? . length
@remainingRequiredEquipment = [ ]
@ $el . find ( ' .should-equip ' ) . removeClass ( ' should-equip ' )
@ unequipClassRestrictedItems equipment
@ unequipLevelRestrictedItems equipment
@ updateLevelRequiredItems equipment
if hadRequired and not @ remainingRequiredEquipment . length
@ endHighlight ( )
@ highlightElement ' # play-level-button ' , duration: 5000
$ ( ' # play-level-button ' ) . prop ( ' disabled ' , @ remainingRequiredEquipment . length > 0 )
unequipClassRestrictedItems: (equipment) ->
return unless @ supermodel . finished ( ) and 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 } ' ] " )
delete equipment [ slot ]
unequipLevelRestrictedItems: (equipment) ->
return unless restrictedGear = @ options . level . get ' restrictedGear '
for slot , items of restrictedGear
for item in items
2014-12-16 21:57:54 -05:00
equipped = equipment [ slot ]
2015-01-02 18:44:43 -05:00
if equipped and equipped is item
console . log ' Unequipping restricted item ' , equipped , ' for ' , slot , ' before level ' , @ options . level . get ( ' slug ' )
@ unequipItemFromSlot @ $el . find ( " .item-slot[data-slot= ' #{ slot } ' ] " )
delete equipment [ slot ]
updateLevelRequiredItems: (equipment) ->
return unless requiredGear = @ options . level . get ' requiredGear '
return unless heroClass = @ selectedHero ? . get ' heroClass '
2015-06-27 21:22:37 -04:00
2015-01-02 18:44:43 -05:00
for slot , items of requiredGear when items . length
2015-06-29 20:25:43 -04:00
if slot in @ ringSlots
validSlots = @ ringSlots
2015-06-27 21:22:37 -04:00
else
validSlots = [ slot ]
continue if validSlots . some (slot) ->
equipped = equipment [ slot ]
equipped in items
2015-06-29 20:25:43 -04:00
# Actually, just let them play if they have equipped anything in that slot (and we haven't unequipped it due to restrictions).
# Rings often have unique effects, so this rule does not apply to them (they are still required even if there is a non-restricted ring equipped in the slot).
continue if equipment [ slot ] and slot not in @ ringSlots
2015-01-02 18:44:43 -05:00
items = ( item for item in items when heroClass in ( @ items . findWhere ( original: item ) ? . classes ? [ ] ) )
continue unless items . length # If the required items are for another class, then let's not be finicky.
# We will point out the last (best) element that they own and can use, otherwise the first (cheapest).
bestOwnedItem = _ . findLast items , (item) -> me . ownsItem item
item = bestOwnedItem ? items [ 0 ]
# For the Tarnished Bronze Breastplate only, don't tell them they need it until they need it in the level, so we can show how to buy it.
slug = gearSlugs [ item ]
inCampaignView = $ ( ' # campaign-view ' ) . length
continue if slug is ' tarnished-bronze-breastplate ' and inCampaignView and @ options . level . get ( ' slug ' ) is ' the-raised-sword '
# Now we're definitely requiring and pointing out an item.
itemModel = @ items . findWhere { original: item }
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 '
@ remainingRequiredEquipment . push slot: slot , item: item
null
2014-09-22 23:44:45 -04:00
2014-11-10 18:24:05 -05:00
setHero: (@selectedHero) ->
2014-11-11 19:36:44 -05:00
if @ selectedHero . loading
@ listenToOnce @ selectedHero , ' sync ' , => @ setHero ? @ selectedHero
return
2014-11-10 21:13:04 -05:00
@ $el . removeClass ( ' Warrior Ranger Wizard ' ) . addClass ( @ selectedHero . get ( ' heroClass ' ) )
2014-11-11 19:36:44 -05:00
@ requireLevelEquipment ( )
2014-11-10 18:24:05 -05:00
@ render ( )
2014-11-26 22:04:47 -05:00
@ onEquipmentChanged ( )
2014-09-21 02:06:28 -04:00
onShown: ->
# Called when we switch tabs to this within the modal
2014-10-09 18:54:18 -04:00
@ requireLevelEquipment ( )
2014-09-21 02:06:28 -04:00
2014-08-14 18:09:10 -04:00
onHidden: ->
2014-09-21 02:06:28 -04:00
# Called when the modal itself is dismissed
2014-10-09 18:54:18 -04:00
@ endHighlight ( )
2014-11-09 19:19:18 -05:00
super ( )
2015-11-30 15:53:52 -05:00
@ playSound ' game-menu-close '
2014-11-05 23:56:17 -05:00
2014-11-06 19:23:23 -05:00
onClickChooseHero: ->
2014-11-26 09:58:23 -05:00
@ playSound ' menu-button-click '
2014-11-06 19:23:23 -05:00
@ hide ( )
@ trigger ' choose-hero-click '
onClickPlayLevel: (e) ->
return if @ $el . find ( ' # play-level-button ' ) . prop ' disabled '
2015-05-05 18:05:18 -04:00
levelSlug = @ options . level . get ( ' slug ' )
2014-11-26 09:58:23 -05:00
@ playSound ' menu-button-click '
2014-11-06 19:23:23 -05:00
@ showLoading ( )
ua = navigator . userAgent . toLowerCase ( )
2015-12-15 18:38:27 -05:00
isSafari = /safari/ . test ( ua ) and not /chrome/ . test ( ua )
isTooShort = $ ( window ) . height ( ) < 658 # Min vertical resolution needed at 1366px wide
if isTooShort and not me . isAdmin ( ) and not hasGoneFullScreenOnce and not isSafari
2014-11-06 19:23:23 -05:00
@ toggleFullscreen ( )
hasGoneFullScreenOnce = true
@ updateConfig =>
2014-11-13 13:49:37 -05:00
@ trigger ? ' play-click '
2015-05-05 18:05:18 -04:00
window . tracker ? . trackEvent ' Inventory Play ' , category: ' Play Level ' , level: levelSlug
2014-11-10 19:09:41 -05:00
2014-11-06 19:23:23 -05:00
updateConfig: (callback, skipSessionSave) ->
sessionHeroConfig = @ options . session . get ( ' heroConfig ' ) ? { }
lastHeroConfig = me . get ( ' heroConfig ' ) ? { }
inventory = @ getCurrentEquipmentConfig ( )
patchSession = patchMe = false
patchSession || = not _ . isEqual inventory , sessionHeroConfig . inventory
sessionHeroConfig.inventory = inventory
2014-12-07 17:03:11 -05:00
if hero = @ selectedHero ? . get ( ' original ' )
2014-11-11 19:36:44 -05:00
patchSession || = not _ . isEqual hero , sessionHeroConfig . thangType
sessionHeroConfig.thangType = hero
patchMe || = not _ . isEqual inventory , lastHeroConfig . inventory
2014-11-06 19:23:23 -05:00
lastHeroConfig.inventory = inventory
if patchMe
console . log ' setting me.heroConfig to ' , JSON . stringify ( lastHeroConfig )
me . set ' heroConfig ' , lastHeroConfig
me . patch ( )
if patchSession
console . log ' setting session.heroConfig to ' , JSON . stringify ( sessionHeroConfig )
@ options . session . set ' heroConfig ' , sessionHeroConfig
@ options . session . patch success: callback unless skipSessionSave
else
callback ? ( )
2014-11-05 23:56:17 -05:00
2014-11-26 09:58:23 -05:00
#- TODO: DRY this between PlayItemsModal and InventoryModal and PlayHeroesModal
2014-11-24 13:51:20 -05:00
onUnlockButtonClicked: (e) ->
e . stopPropagation ( )
button = $ ( e . target ) . closest ( ' button ' )
item = @ items . get ( button . data ( ' item-id ' ) )
affordable = item . affordable
if not affordable
2014-11-26 09:58:23 -05:00
@ playSound ' menu-button-click '
2014-11-24 13:51:20 -05:00
@ askToBuyGems button
else if button . hasClass ( ' confirm ' )
2014-11-26 09:58:23 -05:00
@ playSound ' menu-button-unlock-end '
2014-11-24 13:51:20 -05:00
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 ' ) )
me . set ( ' purchased ' , purchased )
me . set ( ' spent ' , ( me . get ( ' spent ' ) ? 0 ) + item . get ( ' gems ' ) )
#- ...then rerender key bits
@ itemGroups . lockedItems . remove ( item )
2014-12-24 14:01:39 -05:00
@ itemGroups . requiredPurchaseItems . remove ( item )
2014-11-24 13:51:20 -05:00
# Redo all item sorting to make sure that we don't clobber state changes since last render.
equipped = _ . values @ getCurrentEquipmentConfig ( )
2014-12-07 18:05:39 -05:00
@ sortItem ( otherItem , equipped ) for otherItem in @ items . models
2014-11-24 13:51:20 -05:00
@ renderSelectors ( ' # unequipped ' , ' # gems-count ' )
@ requireLevelEquipment ( )
@ delegateEvents ( )
@ setUpDraggableEventsForAvailableEquipment ( )
@ itemDetailsView . setItem ( item )
2015-01-31 17:38:54 -05:00
@ onScrollUnequipped true
2014-11-24 13:51:20 -05:00
Backbone . Mediator . publish ' store:item-purchased ' , item: item , itemSlug: item . get ( ' slug ' )
else
2014-11-26 09:58:23 -05:00
@ playSound ' menu-button-unlock-start '
2014-11-24 13:51:20 -05:00
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 ]
2015-02-11 17:09:00 -05:00
askToSignUp: ->
2016-02-25 18:24:16 -05:00
createAccountModal = new CreateAccountModal supermodel: @ supermodel
return @ openModalView createAccountModal
2015-02-11 17:09:00 -05:00
2014-11-24 13:51:20 -05:00
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) ->
2014-11-26 09:58:23 -05:00
@ playSound ' menu-button-click '
2015-02-11 17:09:00 -05:00
return @ askToSignUp ( ) if me . get ( ' anonymous ' )
2014-11-24 13:51:20 -05:00
@ openModalView new BuyGemsModal ( )
onClickedSomewhere: (e) ->
return if @ destroyed
@ $el . find ( ' .unlock-button ' ) . popover ' destroy '
2014-12-07 17:03:11 -05:00
2014-12-06 17:21:56 -05:00
#- Dynamic portrait loading
2015-01-31 17:38:54 -05:00
onScrollUnequipped: (forceLoadAll=false) ->
2014-12-06 17:21:56 -05:00
# dynamically load visible items when the user scrolls enough to see them
2014-12-08 14:09:04 -05:00
return if @ destroyed
2014-12-06 17:21:56 -05:00
nanoContent = @ $el . find ( ' # unequipped .nano-content ' )
items = nanoContent . find ( ' .item:visible:not(.loaded) ' )
threshold = nanoContent . height ( ) + 100
for itemEl in items
itemEl = $ ( itemEl )
2015-01-31 17:38:54 -05:00
if itemEl . position ( ) . top < threshold or forceLoadAll
2014-12-06 17:21:56 -05:00
itemEl . addClass ( ' loaded ' )
item = @ items . get ( itemEl . data ( ' item-id ' ) )
itemEl . find ( ' img ' ) . attr ( ' src ' , item . getPortraitURL ( ) )
2014-11-24 13:51:20 -05:00
2014-11-26 22:04:47 -05:00
#- Paper doll equipment updating
onEquipmentChanged: ->
2014-12-05 21:16:58 -05:00
heroClass = @ selectedHero ? . get ( ' heroClass ' ) ? ' Warrior '
gender = if @ selectedHero ? . get ( ' slug ' ) in heroGenders . male then ' male ' else ' female '
@ $el . find ( ' # hero-image, # hero-image-hair, # hero-image-head, # hero-image-thumb ' ) . removeClass ( ) . addClass " #{ gender } #{ heroClass } "
2014-12-04 16:18:00 -05:00
equipment = @ getCurrentEquipmentConfig ( )
2014-12-07 18:14:43 -05:00
@ onScrollUnequipped ( )
2014-12-04 16:18:00 -05:00
return unless _ . size ( equipment ) and @ supermodel . finished ( )
2014-11-26 22:04:47 -05:00
@ removeDollImages ( )
slotsWithImages = [ ]
for slot , original of equipment
item = _ . find @ items . models , (item) -> item . get ( ' original ' ) is original
continue unless dollImages = item ? . get ( ' dollImages ' )
2015-06-16 16:50:33 -04:00
didAdd = @ addDollImage slot , dollImages , heroClass , gender , item
2015-02-23 00:50:56 -05:00
slotsWithImages . push slot if didAdd if item . get ( ' original ' ) isnt ' 54ea39342b7506e891ca70f2 ' # Circlet of the Magi needs hair under it
2014-11-26 22:04:47 -05:00
@ $el . find ( ' # hero-image-hair ' ) . toggle not ( ' head ' in slotsWithImages )
@ $el . find ( ' # hero-image-thumb ' ) . toggle not ( ' gloves ' in slotsWithImages )
2014-12-04 16:18:00 -05:00
@equipment = @options.equipment = equipment
2014-11-26 22:04:47 -05:00
removeDollImages: ->
@ $el . find ( ' .doll-image ' ) . remove ( )
2015-06-16 16:50:33 -04:00
addDollImage: (slot, dollImages, heroClass, gender, item) ->
2014-11-26 22:04:47 -05:00
heroClass = @ selectedHero ? . get ( ' heroClass ' ) ? ' Warrior '
gender = if @ selectedHero ? . get ( ' slug ' ) in heroGenders . male then ' male ' else ' female '
didAdd = false
if slot is ' gloves '
if heroClass is ' Ranger '
imageKeys = [ " #{ gender } #{ heroClass } " , " #{ gender } #{ heroClass } Thumb " ]
else
imageKeys = [ " #{ gender } " , " #{ gender } Thumb " ]
2015-02-23 00:50:56 -05:00
else if heroClass is ' Wizard ' and slot is ' torso '
imageKeys = [ gender , " #{ gender } Back " ]
2015-06-16 16:50:33 -04:00
else if heroClass is ' Ranger ' and slot is ' head ' and item . get ( ' original ' ) in [ ' 5441c2be4e9aeb727cc97105 ' , ' 5441c3144e9aeb727cc97111 ' ]
# All-class headgear like faux fur hat, viking helmet is abusing ranger glove slot
imageKeys = [ " #{ gender } Ranger " ]
2014-11-26 22:04:47 -05:00
else
imageKeys = [ gender ]
for imageKey in imageKeys
imageURL = dollImages [ imageKey ]
if not imageURL
console . log " Hmm, should have #{ slot } #{ imageKey } paper doll image, but don ' t have it. "
else
imageEl = $ ( ' <img> ' ) . attr ( ' src ' , " /file/ #{ imageURL } " ) . addClass ( " doll-image #{ slot } #{ heroClass } #{ gender } #{ _ . string . underscored ( imageKey ) . replace ( /_/g , ' - ' ) } " ) . attr ( ' draggable ' , false )
@ $el . find ( ' # equipped ' ) . append imageEl
didAdd = true
didAdd
2014-11-10 18:24:05 -05:00
destroy: ->
2014-11-24 13:51:20 -05:00
@ $el . find ( ' .unlock-button ' ) . popover ' destroy '
2014-12-08 14:09:04 -05:00
@ $el . find ( ' .ui-droppable ' ) . droppable ' destroy '
@ $el . find ( ' .ui-draggable ' ) . draggable ( ' destroy ' ) . off ' dragstart '
@ $el . find ( ' .item-slot ' ) . off ' dragstart '
2014-11-10 18:24:05 -05:00
@ stage ? . removeAllChildren ( )
super ( )
2014-11-26 22:04:47 -05:00
heroGenders =
2016-06-24 17:57:21 -04:00
male: [ ' knight ' , ' samurai ' , ' trapper ' , ' potion-master ' , ' goliath ' , ' assassin ' , ' necromancer ' , ' duelist ' ]
2016-06-24 18:01:49 -04:00
female: [ ' captain ' , ' ninja ' , ' forest-archer ' , ' librarian ' , ' sorcerer ' , ' raider ' , ' guardian ' , ' pixie ' , ' master-wizard ' , ' champion ' ]
2014-11-10 18:24:05 -05:00
2014-11-05 23:56:17 -05:00
gear =
' simple-boots ' : ' 53e237bf53457600003e3f05 '
' simple-sword ' : ' 53e218d853457600003e3ebe '
2014-11-21 19:23:26 -05:00
' tarnished-bronze-breastplate ' : ' 53e22eac53457600003e3efc '
2014-11-05 23:56:17 -05:00
' leather-boots ' : ' 53e2384453457600003e3f07 '
' leather-belt ' : ' 5437002a7beba4a82024a97d '
' programmaticon-i ' : ' 53e4108204c00d4607a89f78 '
2014-11-22 01:35:03 -05:00
' programmaticon-ii ' : ' 546e25d99df4a17d0d449be1 '
2014-11-05 23:56:17 -05:00
' crude-glasses ' : ' 53e238df53457600003e3f0b '
2014-11-17 12:20:44 -05:00
' crude-builders-hammer ' : ' 53f4e6e3d822c23505b74f42 '
2014-11-08 22:33:00 -05:00
' long-sword ' : ' 544d7d1f8494308424f564a3 '
' sundial-wristwatch ' : ' 53e2396a53457600003e3f0f '
' bronze-shield ' : ' 544c310ae0017993fce214bf '
' wooden-glasses ' : ' 53e2167653457600003e3eb3 '
' basic-flags ' : ' 545bacb41e649a4495f887da '
2014-11-14 14:03:01 -05:00
' roughedge ' : ' 544d7d918494308424f564a7 '
' sharpened-sword ' : ' 544d7deb8494308424f564ab '
2014-11-22 01:35:03 -05:00
' crude-crossbow ' : ' 544d7ffd8494308424f564c3 '
' crude-dagger ' : ' 544d952b8494308424f56517 '
' weak-charge ' : ' 544d957d8494308424f5651f '
' enchanted-stick ' : ' 544d87188494308424f564f1 '
' unholy-tome-i ' : ' 546374bc3839c6e02811d308 '
' book-of-life-i ' : ' 546375653839c6e02811d30b '
2014-12-16 21:57:54 -05:00
' rough-sense-stone ' : ' 54693140a2b1f53ce79443bc '
' polished-sense-stone ' : ' 53e215a253457600003e3eaf '
2014-12-24 14:01:39 -05:00
' quartz-sense-stone ' : ' 54693240a2b1f53ce79443c5 '
' wooden-builders-hammer ' : ' 54694ba3a2b1f53ce794444d '
' simple-wristwatch ' : ' 54693797a2b1f53ce79443e9 '
2014-12-28 16:25:20 -05:00
gearSlugs = _ . invert gear