Merge branch 'master' into production

This commit is contained in:
Nick Winter 2014-11-10 18:19:02 -08:00
commit ce4567c868
12 changed files with 854 additions and 751 deletions

View file

@ -301,10 +301,13 @@
choose_inventory: "Equip Items"
equipped_item: "Equipped"
available_item: "Available"
restricted_title: "Restricted"
should_equip: "(double-click to equip)"
equipped: "(equipped)"
locked: "(locked)"
restricted: "(restricted in this level)"
equip: "Equip"
unequip: "Unequip"
choose_hero:
choose_hero: "Choose Your Hero"

View file

@ -1,18 +1,8 @@
@import "app/styles/mixins"
$totalWidth: 706px - 2 * 20px
$inventoryHeight: 445px
$equippedWidth: 450px
$itemSlotMargin: 5px
$itemSlotSize: ($equippedWidth - 6 * 2 * $itemSlotMargin) / 6
$itemSlotSizeWithMargin: $itemSlotSize + 2 * $itemSlotMargin
$itemSlotBorderWidth: 2px
$itemSlotInnerWidth: $itemSlotSize - 2 * $itemSlotBorderWidth
$heroContainerWidth: 4 * $itemSlotSizeWithMargin
$heroContainerHeight: $inventoryHeight - $itemSlotSizeWithMargin
$selectedAreaHeight: 150px
$stashMargin: 20px
$stashWidth: $totalWidth - $equippedWidth - $stashMargin
$itemSlotSize: 55px
$itemSlotInnerWidth: $itemSlotSize - 4
$itemSlotGridHeight: 70px
.ui-effects-transfer
outline: 2px solid #28f
@ -20,318 +10,434 @@ $stashWidth: $totalWidth - $equippedWidth - $stashMargin
z-index: 9001
#inventory-modal
//- Overall modal structure
.modal-dialog
margin: 30px auto 0 auto
width: 720px
width: 1017px
height: 660px
.modal-content
height: 100%
width: 100%
.modal-body
height: 450px
margin: 0
+user-select(none)
h3
.modal-body
height: 450px
margin: 0
.draggable-item
width: $itemSlotSize
height: $itemSlotSize
+user-select(none)
//- Background
#play-items-modal-narrow-bg
position: absolute
top: -69px
left: -8px
//- Gems count
#gems-count-container
position: absolute
left: 213px
top: 10px
width: 160px
height: 66px
@include rotate(5deg)
#gems-count
position: absolute
left: 75px
top: 17px
font-size: 25px
color: rgb(1,64,91)
//- Close modal button
#close-modal
position: absolute
left: 390px
top: 23px
width: 60px
height: 60px
color: white
text-align: center
font-size: 30px
padding-top: 7px
cursor: pointer
@include rotate(-3deg)
&:hover
color: yellow
//- Equipped area
#equipped
width: $equippedWidth
width: 330px
background: white
border: 3px solid black
position: absolute
left: 20px
top: 0
bottom: 0
//bottom: $selectedAreaHeight + 10
right: 0
top: 112px
height: 450px
overflow: hidden
.item-slot-row
//background-color: rgba(35, 112, 124, 0.5)
height: $itemSlotSizeWithMargin
clear: both
margin: 0px auto
#hero-image
@include filter(contrast(0%) brightness(0%))
opacity: 0.4
width: 225px
height: 410px
position: absolute
left: 10px
top: 20px
&.row-4
width: 4 * $itemSlotSizeWithMargin
.item-slot-column
//background-color: rgba(112, 124, 35, 0.5)
width: $itemSlotSizeWithMargin
height: 5 * $itemSlotSizeWithMargin
float: left
//margin-top: 30px
.item-slot
width: $itemSlotSize
height: $itemSlotSize
margin: $itemSlotMargin
background-color: white
float: left
margin: 5px
background-color: rgba(255,255,255,0.4)
border: 2px dashed rgb(100,100,150)
position: relative
cursor: pointer
@include transition(0.5s ease)
&.selected
.placeholder, .item-container .item-view img
border-color: #28f
@include box-shadow(0 0 10px #28f)
//&.disabled
// opacity: 0.5
&.selected
.placeholder, img.item
border-color: rgb(81,153,236)
background-color: rgb(81,153,236)
@include box-shadow(0 0 10px rgb(81,153,236))
img.item
background: rgb(81,153,236)
&.should-equip
background-color: #8fa
outline: 2px solid #8af
@include box-shadow(2px 2px 4px black)
&.droppable
outline: 2px solid blue
@include box-shadow(4px 4px 6px black)
&.droppable-hover
outline: 4px solid blue
@include box-shadow(6px 6px 8px black)
.placeholder
width: 100%
height: 100%
background-size: cover
border: $itemSlotBorderWidth solid #888
@include opacity(0.4)
@include opacity(0.7)
background-image: url(/images/pages/game-menu/slot-icons.png)
&[data-slot="misc-1"] .placeholder
background-position: (-0 * $itemSlotInnerWidth) 0px
// A terrible awful bit of styling, but will be gone or messed around with soon anyway
&[data-slot="minion"] .placeholder
background-position: (-1 * $itemSlotInnerWidth) 0px
&[data-slot="misc-1"]
display: none // hiding for now
position: absolute
left: 250px
top: 20px + ($itemSlotGridHeight * 3)
.placeholder
background-position: (-0 * $itemSlotInnerWidth) 0px
&[data-slot="misc-0"]
display: none // hiding for now
position: absolute
left: 250px
top: 20px + ($itemSlotGridHeight * 2)
.placeholder
background-position: (-4 * $itemSlotInnerWidth) 0px
&[data-slot="programming-book"] .placeholder
background-position: (-3 * $itemSlotInnerWidth) 0px
&[data-slot="minion"]
position: absolute
left: 250px
top: 20px + ($itemSlotGridHeight * 1)
.placeholder
background-position: (-1 * $itemSlotInnerWidth) 0px
&[data-slot="programming-book"]
position: absolute
left: 250px
top: 20px + ($itemSlotGridHeight * 4)
.placeholder
background-position: (-3 * $itemSlotInnerWidth) 0px
// Only for wizards...
//&[data-slot="spellbook"] .placeholder
// background-position: (-2 * $itemSlotInnerWidth) 0px
&[data-slot="misc-0"] .placeholder
background-position: (-4 * $itemSlotInnerWidth) 0px
&[data-slot="wrists"] .placeholder
background-position: (-5 * $itemSlotInnerWidth) 0px
&[data-slot="left-ring"] .placeholder
background-position: (-6 * $itemSlotInnerWidth) 0px
&[data-slot="right-ring"] .placeholder
background-position: (-7 * $itemSlotInnerWidth) 0px
&[data-slot="torso"] .placeholder
background-position: (-8 * $itemSlotInnerWidth) 0px
&[data-slot="feet"] .placeholder
background-position: (-9 * $itemSlotInnerWidth) 0px
&[data-slot="neck"] .placeholder
background-position: (-10 * $itemSlotInnerWidth) 0px
&[data-slot="waist"] .placeholder
background-position: (-11 * $itemSlotInnerWidth) 0px
&[data-slot="eyes"] .placeholder
background-position: (-12 * $itemSlotInnerWidth) 0px
&[data-slot="head"] .placeholder
background-position: (-13 * $itemSlotInnerWidth) 0px
&[data-slot="pet"] .placeholder
background-position: (-14 * $itemSlotInnerWidth) 0px
&[data-slot="gloves"] .placeholder
background-position: (-15 * $itemSlotInnerWidth) 0px
&[data-slot="left-hand"] .placeholder
background-position: (-16 * $itemSlotInnerWidth) 0px
&[data-slot="right-hand"] .placeholder
background-position: (-17 * $itemSlotInnerWidth) 0px
&[data-slot="flag"] .placeholder
//background-position: (-18 * $itemSlotInnerWidth) 0px
background-position: (-2 * $itemSlotInnerWidth) 0px
.item-container
&[data-slot="wrists"]
position: absolute
left: 0
top: 0
left: 20px
top: 20px + ($itemSlotGridHeight * 2.5)
.placeholder
background-position: (-5 * $itemSlotInnerWidth) 0px
&[data-slot="left-ring"]
position: absolute
left: 250px
top: 20px + ($itemSlotGridHeight * 2)
.placeholder
background-position: (-6 * $itemSlotInnerWidth) 0px
&[data-slot="right-ring"]
position: absolute
left: 250px
top: 20px + ($itemSlotGridHeight * 3)
.placeholder
background-position: (-7 * $itemSlotInnerWidth) 0px
&[data-slot="torso"]
position: absolute
left: 90px
top: 20px + ($itemSlotGridHeight * 3)
.placeholder
background-position: (-8 * $itemSlotInnerWidth) 0px
&[data-slot="feet"]
position: absolute
left: 90px
top: 20px + ($itemSlotGridHeight * 5)
.placeholder
background-position: (-9 * $itemSlotInnerWidth) 0px
&[data-slot="neck"]
position: absolute
left: 90px
top: 20px + ($itemSlotGridHeight * 2)
.placeholder
background-position: (-10 * $itemSlotInnerWidth) 0px
&[data-slot="waist"]
position: absolute
left: 90px
top: 20px + ($itemSlotGridHeight * 4)
.placeholder
background-position: (-11 * $itemSlotInnerWidth) 0px
&[data-slot="eyes"]
position: absolute
left: 90px
top: 20px + $itemSlotGridHeight
.placeholder
background-position: (-12 * $itemSlotInnerWidth) 0px
&[data-slot="head"]
position: absolute
left: 90px
top: 20px
.placeholder
background-position: (-13 * $itemSlotInnerWidth) 0px
&[data-slot="pet"]
position: absolute
left: 250px
top: 20px
.placeholder
background-position: (-14 * $itemSlotInnerWidth) 0px
&[data-slot="gloves"]
position: absolute
left: 160px
top: 20px + ($itemSlotGridHeight * 2.5)
.placeholder
background-position: (-15 * $itemSlotInnerWidth) 0px
&[data-slot="left-hand"]
position: absolute
left: 160px
top: 20px + ($itemSlotGridHeight * 3.5)
.placeholder
background-position: (-16 * $itemSlotInnerWidth) 0px
&[data-slot="right-hand"]
position: absolute
left: 20px
top: 20px + ($itemSlotGridHeight * 3.5)
.placeholder
background-position: (-17 * $itemSlotInnerWidth) 0px
&[data-slot="flag"]
position: absolute
left: 250px
top: 20px + ($itemSlotGridHeight * 5)
.placeholder
background-position: (-2 * $itemSlotInnerWidth) 0px
img.item
position: absolute
left: -2px
top: -2px
.item-view
img
width: $itemSlotSize
height: $itemSlotSize
border: 2px solid black
background-color: white
width: $itemSlotSize
height: $itemSlotSize
border: 2px solid black
background-color: white
.item-info
display: none
//- dragging styling
#equipped
.item-slot.disabled
opacity: 0.5
&.droppable
outline: 2px solid blue
@include box-shadow(4px 4px 6px black)
.hero-container
//background-color: rgba(31, 0, 200, 0.25)
float: left
position: relative
@include transition(0.5s ease)
&.droppable-hover
outline: 4px solid blue
@include box-shadow(6px 6px 8px black)
.draggable-item
width: $itemSlotSize * 1.2
height: $itemSlotSize * 1.2
&.droppable
outline: 2px solid blue
@include box-shadow(4px 4px 6px black)
&.droppable-hover
outline: 4px solid blue
@include box-shadow(6px 6px 8px black)
.equipped-hero-canvas, .hero-feature-image
width: $heroContainerWidth
height: $heroContainerHeight
.hero-feature-image
text-align: center
img
height: $heroContainerHeight
#available-equipment
width: $stashWidth
position: absolute
right: 20px
top: 0
bottom: 0
overflow-y: scroll
border: 2px solid #ccc
padding: 4px
background-color: white
&.Warrior .list-group-item:not(.Warrior), &.Ranger .list-group-item:not(.Ranger), &.Wizard .list-group-item:not(.Wizard)
//- Available equipment
&.Warrior #unequipped img.item:not(.Warrior), &.Ranger #unequipped img.item:not(.Ranger), &.Wizard #unequipped img.item:not(.Wizard)
// Our code hides and shows (modifies display), but we can be invisible this other way.
visibility: hidden
position: absolute
.list-group-item
padding: 4px 0
@include transition(0.5s ease)
&.active
background-color: #e0f0f5
.status-message .should-equip-message
display: inline
&.should-equip
background-color: #8fa
outline: 2px solid #8af
@include box-shadow(4px 4px 6px black)
z-index: 1
.status-message .should-equip-message
display: inline
font-weight: bold
&.equipped
background-color: #ff5
.item-view
cursor: default
.status-message .equipped-message
display: inline
&.restricted
background-color: rgba(255, 80, 67, 0.25)
.item-view
cursor: default
.status-message .restricted-message
display: inline
&.locked
background-image: url(/images/pages/game-menu/lock.png)
background-size: 25px 25px
background-repeat: no-repeat
background-position: 98% 90%
.item-view
cursor: default
h4, img
//@include filter(contrast(50%) brightness(65%))
@include opacity(0.6)
.status-message .locked-message
display: inline
&.silhouette
h4
visibility: hidden
position: absolute
h4:before
content: '???'
visibility: visible
.item-view .status-message .locked-message
display: none
img
@include filter(contrast(25%) brightness(25%))
.item-view
cursor: pointer
#selected-items
$selectedItemsContainerMargin: 20px
$selectedItemMargin: 10px
$selectedItemImageSize: 75px
#unequipped
width: 222px
position: absolute
top: $selectedItemsContainerMargin
right: $selectedItemsContainerMargin
bottom: $selectedItemsContainerMargin
left: $selectedItemsContainerMargin
text-align: center
left: 370px
top: 112px
height: 450px
border: 3px solid black
padding: 9px 0 9px 9px
background-color: white
#selected-equipped-item, #selected-available-item
text-align: left
overflow-y: scroll
margin: 0
height: 48.4%
height: -webkit-calc(50% - $selectedItemMargin / 2)
height: calc(50% - $selectedItemMargin / 2)
width: 100%
padding: 10px 5px 10px 10px
position: relative
display: none
#double-click-hint
margin: 20px 0 70px
h4
clear: both
margin-bottom: 10px
margin-top: 20px
font-size: 24px
text-transform: uppercase
font-weight: bold
img.item
float: left
border: 1px solid black
margin: 3px
padding: 1px
width: 60px
height: 60px
cursor: pointer
&.active
background-color: rgb(81,153,236)
//.status-message .should-equip-message
// display: inline
img
margin-top: 21px
width: $selectedItemImageSize
height: $selectedItemImageSize
margin-right: 10px
&.should-equip
background-color: #8fa
outline: 2px solid #8af
@include box-shadow(4px 4px 6px black)
z-index: 1
//.status-message .should-equip-message
// display: inline
// font-weight: bold
.item-info
width: 110px
width: -webkit-calc(100% - 75px - 10px)
width: calc(100% - 75px - 10px)
&.equipped
background-color: #ff5
display: none
cursor: default
//.item-view
// cursor: default
//
//.status-message .equipped-message
// display: inline
&.restricted
background-color: rgba(255, 80, 67, 0.25)
cursor: default
//.item-view
// cursor: default
//
//.status-message .restricted-message
// display: inline
&.locked
cursor: default
//background-color: gray
&.silhouette
cursor: default
pointer-events: none
@include filter(contrast(25%) brightness(25%))
opacity: 0.5
//- Hero/Play buttons
> h3
position: absolute
left: 0px
top: 0px
padding: 5px
#choose-hero-button, #play-level-button
top: 572px
position: absolute
background: url(/images/pages/play/modal/confirm-button.png)
width: 209px
height: 65px
background-size: 209px 65px
border: 0
#selected-equipped-item
margin-bottom: $selectedItemMargin
padding-bottom: 20px
background-color: #ff5
&:disabled
opacity: 1
@include filter(grayscale(100%))
#choose-hero-button
left: 20px
#play-level-button
right: 414px
#selected-available-item
padding-top: 15px
background-color: #e0f0f5
bottom: 0
//- Item details. Non-specific item-details-view styling is in item-details-view.sass.
#item-details-view
#item-title
left: 698px
top: 56px
#item-details-body
left: 650px
#selected-item-unlock-button
left: 646px
//- Equip/unequip/extra
#item-details-extra
position: absolute
left: 644px
top: 589px
& > *
width: 338px
height: 50px
.alert
text-align: center
font-weight: bold
button
border: 3px solid rgb(46,46,46)
background: white
font-size: 16px

View file

@ -0,0 +1,82 @@
#item-details-view
.nano-content
padding: 10px
#item-title
position: absolute
width: 228px
height: 50px
left: 910px
top: 60px
z-index: 2
h2
font-size: 20px
margin: 12px 20px
text-align: center
color: rgb(53,40,25)
#item-details-body
position: absolute
left: 860px
top: 126px
width: 330px
height: 449px
//background: rgba(100,100,100,0.5)
#item-container
height: 163px
width: 100%
.item-img, .item-shadow
width: 130px
height: 130px
.item-img
top: 15px
.item-shadow
top: 25px
img.hr
width: 80%
margin: 0 10% -3px
&.faded
opacity: 0.4
.stat-row
height: 24px
position: relative
font-size: 20px
font-weight: bold
.stat-label
position: absolute
left: 54px
color: rgb(93,73,52)
.stat
position: absolute
left: 150px
color: rgb(42,38,28)
#skills
margin: 25px
h3
color: rgb(41,35,25)
strong
color: rgb(50,50,30)
#selected-item-unlock-button
left: 856px
top: 594px
width: 337px
height: 41px
font-size: 16px
img
height: 16px

View file

@ -123,8 +123,8 @@
.tab-pane
height: 100%
.nano-content
padding: 26px 51px 26px 26px
.nano-content
padding: 26px 51px 26px 26px
//- Item box
@ -222,6 +222,23 @@
background: url(/images/pages/play/modal/item-box-background-selected.png)
//- Item details. Non-specific item-details-view styling is in item-details-view.sass.
#item-details-view
#item-title
left: 910px
top: 60px
#item-details-body
left: 860px
#selected-item-unlock-button
left: 856px
#play-items-modal, #inventory-modal
//- Item list scrollbar
.nano-pane
@ -236,86 +253,6 @@
margin-left: -3px
margin-right: -3px
// color: red
//- Item details
#item-title
position: absolute
width: 228px
height: 50px
left: 910px
top: 60px
z-index: 2
h2
font-size: 20px
margin: 12px 20px
text-align: center
color: rgb(53,40,25)
#item-details-body
position: absolute
left: 860px
top: 126px
width: 330px
height: 449px
//background: rgba(100,100,100,0.5)
#item-container
height: 163px
width: 100%
.item-img, .item-shadow
width: 130px
height: 130px
.item-img
top: 15px
.item-shadow
top: 25px
img.hr
width: 80%
margin: 0 10% -3px
&.faded
opacity: 0.4
.stat-row
height: 24px
position: relative
font-size: 20px
font-weight: bold
.stat-label
position: absolute
left: 54px
color: rgb(93,73,52)
.stat
position: absolute
left: 150px
color: rgb(42,38,28)
#skills
margin: 25px
h3
color: rgb(41,35,25)
strong
color: rgb(50,50,30)
#selected-item-unlock-button
left: 856px
top: 594px
width: 337px
height: 41px
font-size: 16px
//- Item icons w/shadows (both in list and details areas)
@ -355,6 +292,9 @@
opacity: 1
color: rgba(255,255,255, 0.4)
//- Use the two-column layout and background image if we are on a narrow screen.
@media only screen and (max-width: 1300px)
#play-items-modal
overflow-x: hidden

View file

@ -1,68 +1,52 @@
extends /templates/modal/modal_base
.modal-dialog
.modal-content
img(src="/images/pages/play/modal/items-background-narrow.png")#play-items-modal-narrow-bg
block modal-header-content
h1#choose-inventory-header.choose-inventory-active(data-i18n="inventory.choose_inventory") Equip Items
div#gems-count-container
span#gems-count.big-font= gems
block modal-body-content
#equipped
.item-slot-row
for slot in ['left-ring', 'neck', 'eyes', 'head', 'wrists', 'right-ring']
div#close-modal
span.glyphicon.glyphicon-remove
#equipped
if selectedHero
img(src="/file/"+selectedHero.get('featureImage'))#hero-image
for slot in ['head', 'eyes', 'neck', 'torso', 'gloves', 'wrists', 'left-hand', 'right-hand', 'waist', 'feet', 'left-ring', 'right-ring', 'minion', 'flag', 'pet', 'programming-book', 'misc-0', 'misc-1']
.item-slot(data-slot=slot)
.placeholder
.item-container
if equipment[slot]
.replace-me(data-item-id=equipment[slot].get('original'))
.item-slot-column.pull-left
// TODO: add in 'misc-0' again somehow? Used to be where 'flag' is now.
for slot in ['minion', 'torso', 'gloves', 'left-hand', 'flag']
.item-slot(data-slot=slot)
.placeholder
.item-container
if equipment[slot]
.replace-me(data-item-id=equipment[slot].get('original'))
.hero-container
canvas.equipped-hero-canvas
.hero-feature-image
img
#selected-items
#selected-equipped-item.well
h3(data-i18n="inventory.equipped_item") Equipped
.item-view-stub
#selected-available-item.well
h3(data-i18n="inventory.available_item") Available
.item-view-stub
.item-slot-column.pull-right
for slot in ['pet', 'waist', 'feet', 'right-hand', 'programming-book']
.item-slot(data-slot=slot)
.placeholder
.item-container
if equipment[slot]
.replace-me(data-item-id=equipment[slot].get('original'))
// TODO: work in misc 1 again
//hr.slot-row-separator
//
//.item-slot-row.row-4
// for slot in ['misc-1']
// .item-slot(data-slot=slot)
// .placeholder
// .item-container
// if equipment[slot]
// .replace-me(data-item-id=equipment[slot].get('original'))
#available-equipment
h4#unlocked-description
ul.list-group
for item in unlockedItems
li.list-group-item(class=item.classes, data-item-id=item.get('original'))
h4#locked-description
ul.list-group
for item in lockedItems
li.list-group-item(class=item.classes, data-item-id=item.get('original'), style="display: none")
if equipment[slot]
img.item(src=equipment[slot].getPortraitURL(), data-item-id=equipment[slot].id)
block modal-footer-content
button#choose-hero-button.btn.btn-lg.btn-primary.choose-inventory-active.pull-left(data-i18n="play.change_hero") Change Hero
button#play-level-button.btn.btn-lg.btn-success.choose-inventory-active(data-i18n="common.play") Play
#unequipped
.nano
.nano-content
if itemGroups
if itemGroups.availableItems.models.length
h4#available-description(data-i18n="inventory.available_item")
for item in itemGroups.availableItems.models
img.item(src=item.getPortraitURL(), class=item.classes, data-item-id=item.id)
.clearfix
#double-click-hint.alert.alert-info.secret(data-i18n="inventory.should_equip")
if itemGroups.restrictedItems.models.length
h4#restricted-description(data-i18n="inventory.restricted_title")
for item in itemGroups.restrictedItems.models
img.item(src=item.getPortraitURL(), class=item.classes, data-item-id=item.id)
.clearfix
if itemGroups.lockedItems.models.length
h4#locked-description(data-i18n="play.locked")
for item in itemGroups.lockedItems.models
img.item(src=item.getPortraitURL(), class=item.classes, data-item-id=item.id)
.clearfix
#item-details-view
#item-details-extra
button#equip-item-viewed.btn.secret(data-i18n="inventory.equip")
button#unequip-item-viewed.btn.secret(data-i18n="inventory.unequip")
.alert.alert-danger#restricted-item-viewed.secret(data-i18n="inventory.restricted")
button#choose-hero-button.btn.btn-lg.btn-primary.choose-inventory-active(data-i18n="play.change_hero") Change Hero
button#play-level-button.btn.btn-lg.btn-success.choose-inventory-active(data-i18n="common.play") Play

View file

@ -33,4 +33,9 @@
p Only admins will see it for now.
if item && !item.owned
button#selected-item-unlock-button.btn.big-font.unlock-button(disabled=!item.affordable, data-item-id=item.id, data-i18n="play.unlock") Unlock
button#selected-item-unlock-button.btn.big-font.unlock-button(disabled=!item.affordable, data-item-id=item.id)
span(data-i18n="play.unlock") Unlock
span.spl= '('+item.get('gems')
img(src="/images/common/gem.png")
span )

View file

@ -4,6 +4,8 @@ WizardLank = require 'lib/surface/WizardLank'
ThangType = require 'models/ThangType'
Simulator = require 'lib/simulator/Simulator'
InventoryModal = require 'views/game-menu/InventoryModal'
{me} = require '/lib/auth'
module.exports = class HomeView extends RootView
@ -34,4 +36,8 @@ module.exports = class HomeView extends RootView
e.preventDefault()
e.stopImmediatePropagation()
window.tracker?.trackEvent 'Homepage', Action: 'Play'
window.open '/play', '_blank'
window.open '/play', '_blank'
afterInsert: ->
super(arguments...)
@openModalView(new InventoryModal({levelID: 'the-raised-sword'}))

View file

@ -5,6 +5,8 @@ ThangType = require 'models/ThangType'
CocoCollection = require 'collections/CocoCollection'
ItemView = require './ItemView'
SpriteBuilder = require 'lib/sprites/SpriteBuilder'
ItemDetailsView = require 'views/play/modal/ItemDetailsView'
Purchase = require 'models/Purchase'
LevelOptions = require 'lib/LevelOptions'
hasGoneFullScreenOnce = false
@ -14,101 +16,130 @@ module.exports = class InventoryModal extends ModalView
className: 'modal fade play-modal'
template: template
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
closesOnClickOutside: false # because draggable somehow triggers hide when you don't drag onto a draggable
events:
'click .item-slot': 'onItemSlotClick'
'click #available-equipment .list-group-item:not(.equipped)': 'onAvailableItemClick'
'dblclick #available-equipment .list-group-item:not(.equipped)': 'onAvailableItemDoubleClick'
'doubletap #available-equipment .list-group-item:not(.equipped)': 'onAvailableItemDoubleClick'
'dblclick .item-slot .item-view': 'onEquippedItemDoubleClick'
'doubletap .item-slot .item-view': 'onEquippedItemDoubleClick'
'click #unequipped img.item': 'onUnequippedItemClick'
'doubletap #unequipped img.item': 'onUnequippedItemDoubleClick'
'doubletap .item-slot img.item': 'onEquippedItemDoubleClick'
'shown.bs.modal': 'onShown'
'click #choose-hero-button': 'onClickChooseHero'
'click #play-level-button': 'onClickPlayLevel'
'click .unlock-button': 'onUnlockButtonClicked'
'click #equip-item-viewed': 'onClickEquipItemViewed'
'click #unequip-item-viewed': 'onClickUnequipItemViewed'
'click #close-modal': 'hide'
shortcuts:
'esc': 'clearSelection'
'enter': 'onClickPlayLevel'
#- Setup
initialize: (options) ->
super(arguments...)
@items = new CocoCollection([], {model: ThangType})
@items.url = '/db/thang.type?view=items&project=name,slug,components,original,rasterIcon,gems,description,heroClass'
# TODO: switch to item store loading system?
@items.url = '/db/thang.type?view=items'
@items.setProjection [
'name',
'slug',
'components',
'original',
'rasterIcon',
'gems',
'description',
'heroClass',
'i18n'
]
@supermodel.loadCollection(@items, 'items')
@equipment = {} # Assign for real when we have loaded the session
@equipment = {} # Assign for real when we have loaded the session and items.
destroy: ->
@stage?.removeAllChildren()
super()
onLoaded: ->
onItemsLoaded: ->
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
@requireLevelEquipment()
@itemGroups = {}
@itemGroups.availableItems = new Backbone.Collection()
@itemGroups.restrictedItems = new Backbone.Collection()
@itemGroups.lockedItems = new Backbone.Collection()
itemGroup.comparator = 'gems' for itemGroup in _.values @itemGroups
equipped = _.values(@equipment)
@sortItem(item, equipped) for item in @items.models
sortItem: (item, equipped) ->
equipped ?= _.values(@equipment)
# 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
locked = not (item.get('original') in me.items())
if locked and item.get('slug') isnt 'simple-boots'
@itemGroups.lockedItems.add(item)
item.classes.push 'locked'
item.classes.push 'silhouette' if item.isSilhouettedItem()
else if item.get('slug') in _.values(LevelOptions[@options.levelID]?.restrictedGear ? {})
@itemGroups.restrictedItems.add(item)
item.classes.push 'restricted'
else
@itemGroups.availableItems.add(item)
onLoaded: ->
# Both items and session have been loaded.
@onItemsLoaded()
super()
getRenderData: (context={}) ->
context = super(context)
context.equipped = _.values(@equipment)
context.items = @items.models
for item in @items.models
item.classes = item.getAllowedSlots()
item.classes.push 'equipped' if item.get('original') in context.equipped
locked = not (item.get('original') in me.items())
item.classes.push 'locked' if locked and item.get('slug') isnt 'simple-boots'
for heroClass in item.getAllowedHeroClasses()
item.classes.push heroClass
item.classes.push 'silhouette' if item.isSilhouettedItem()
item.classes.push 'restricted' if item.get('slug') in _.values(LevelOptions[@options.levelID]?.restrictedGear ? {})
@items.models.sort (a, b) ->
lockScore = 90019001 * (('locked' in a.classes) - ('locked' in b.classes))
gemScore = a.get('gems') - b.get('gems')
lockScore + gemScore
context.unlockedItems = []
context.lockedItems = []
for item in @items.models
(if 'locked' in item.classes then context.lockedItems else context.unlockedItems).push item
context.itemGroups = @itemGroups
context.slots = @slots
context.selectedHero = @selectedHero
context.equipment = _.clone @equipment
for slot, itemOriginal of context.equipment
item = _.find @items.models, (item) -> item.get('original') is itemOriginal
context.equipment[slot] = item
context.equipment[slot] = @items.findWhere {original: itemOriginal} for slot, itemOriginal of context.equipment
context.gems = me.gems()
context
afterRender: ->
super()
@$el.find('.modal-footer button').css('visibility', 'hidden')
@$el.find('#play-level-button').css('visibility', 'hidden')
return unless @supermodel.finished()
@$el.find('.modal-footer button').css('visibility', 'visible')
@$el.find('#play-level-button').css('visibility', 'visible')
keys = (item.get('original') for item in @items.models)
itemMap = _.zipObject keys, @items.models
@setUpDraggableEventsForAvailableEquipment()
@setUpDraggableEventsForEquippedArea()
@delegateEvents()
@itemDetailsView = new ItemDetailsView()
@insertSubView(@itemDetailsView)
@requireLevelEquipment()
@$el.find('.nano').nanoScroller()
# Fill in equipped items
for slottedItemStub in @$el.find('.replace-me')
itemID = $(slottedItemStub).data('item-id')
item = itemMap[itemID]
itemView = new ItemView({item: item, includes: {}})
itemView.render()
$(slottedItemStub).replaceWith(itemView.$el)
@registerSubView(itemView)
afterInsert: ->
super()
@canvasWidth = @$el.find('canvas').innerWidth()
@canvasHeight = @$el.find('canvas').innerHeight()
@inserted = true
@requireLevelEquipment()
for availableItemEl in @$el.find('#available-equipment .list-group-item')
itemID = $(availableItemEl).data('item-id')
item = itemMap[itemID]
itemView = new ItemView({item: item, includes: {name: true}})
itemView.render()
$(availableItemEl).append(itemView.$el)
@registerSubView(itemView)
continue if $(availableItemEl).hasClass('locked') or $(availableItemEl).hasClass('restricted')
dragHelper = itemView.$el.find('img').clone().addClass('draggable-item')
do (dragHelper, itemView) =>
itemView.$el.draggable
#- Draggable logic
setUpDraggableEventsForAvailableEquipment: ->
for availableItemEl in @$el.find('#unequipped img.item')
availableItemEl = $(availableItemEl)
continue if availableItemEl.hasClass('locked') or availableItemEl.hasClass('restricted')
dragHelper = availableItemEl.clone().addClass('draggable-item')
do (dragHelper, availableItemEl) =>
availableItemEl.draggable
revert: 'invalid'
appendTo: @$el
cursorAt: {left: 35.5, top: 35.5}
@ -116,43 +147,32 @@ module.exports = class InventoryModal extends ModalView
revertDuration: 200
distance: 10
scroll: false
zIndex: 10000
itemView.$el.on 'dragstart', =>
@onAvailableItemClick target: itemView.$el.parent() unless itemView.$el.parent().hasClass 'active'
zIndex: 1100
availableItemEl.on 'dragstart', => @selectUnequippedItem(availableItemEl)
setUpDraggableEventsForEquippedArea: ->
for itemSlot in @$el.find '.item-slot'
slot = $(itemSlot).data 'slot'
do (slot, itemSlot) =>
$(itemSlot).droppable
drop: (e, ui) => @onAvailableItemDoubleClick()
drop: (e, ui) => @equipSelectedItem()
accept: (el) -> $(el).parent().hasClass slot
activeClass: 'droppable'
hoverClass: 'droppable-hover'
tolerance: 'touch'
@makeEquippedSlotDraggable $(itemSlot)
@$el.find('.hero-container').droppable
drop: (e, ui) => @onAvailableItemDoubleClick()
@$el.find('#equipped').droppable
drop: (e, ui) => @equipSelectedItem()
accept: (el) -> true
activeClass: 'droppable'
hoverClass: 'droppable-hover'
tolerance: 'pointer'
@$el.find('#selected-items').hide() # Hide until one is selected
@delegateEvents()
if @selectedHero and not @startedLoadingFirstHero
@loadHero()
@requireLevelEquipment()
afterInsert: ->
super()
@canvasWidth = @$el.find('canvas').innerWidth()
@canvasHeight = @$el.find('canvas').innerHeight()
@inserted = true
makeEquippedSlotDraggable: (slot) ->
unequip = => @unequipItemFromSlot slot
unequip = =>
@unequipItemFromSlot slot
@requireLevelEquipment()
shouldStayEquippedWhenDropped = (isValidDrop) ->
pos = $(@).position()
revert = Math.abs(pos.left) < $(@).outerWidth() and Math.abs(pos.top) < $(@).outerHeight()
@ -166,179 +186,158 @@ module.exports = class InventoryModal extends ModalView
revertDuration: 200
distance: 10
scroll: false
zIndex: 10000
zIndex: 100
slot.on 'dragstart', => @selectItemSlot(slot)
clearSelection: ->
@$el.find('.item-slot.selected').removeClass 'selected'
@$el.find('.list-group-item').removeClass('active')
@onSelectionChanged()
#- Select/equip event handlers
onItemSlotClick: (e) ->
return if @remainingRequiredEquipment?.length # Don't let them select a slot if we need them to first equip some require gear.
slot = $(e.target).closest('.item-slot')
wasActive = slot.hasClass('selected')
@unselectAllSlots()
@unselectAllAvailableEquipment() if slot.hasClass('disabled')
if wasActive
@hideSelectedSlotItem()
@unselectAllAvailableEquipment()
@selectItemSlot($(e.target).closest('.item-slot'))
onUnequippedItemClick: (e) ->
return if @justDoubleClicked
itemEl = $(e.target).closest('img.item')
@selectUnequippedItem(itemEl)
onUnequippedItemDoubleClick: (e) ->
item = $(e.target).closest('img.item')
return if item.hasClass('locked') or item.hasClass('restricted')
@equipSelectedItem()
@justDoubleClicked = true
_.defer => @justDoubleClicked = false
onEquippedItemDoubleClick: -> @unequipSelectedItem()
onClickEquipItemViewed: -> @equipSelectedItem()
onClickUnequipItemViewed: -> @unequipSelectedItem()
onUnlockButtonClicked: (e) ->
button = $(e.target).closest('button')
if button.hasClass('confirm')
item = @items.get($(e.target).data('item-id'))
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
@requireLevelEquipment()
@itemGroups.lockedItems.remove(item)
@sortItem(item)
@renderSelectors("#unequipped", "#gems-count")
@delegateEvents()
@setUpDraggableEventsForAvailableEquipment()
@itemDetailsView.setItem(item)
else
@selectSlot(slot)
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]
#- Select/equip higher-level, all encompassing methods the callbacks all use
selectItemSlot: (slotEl) ->
@clearSelection()
slotEl.addClass('selected')
selectedSlotItemID = slotEl.find('img.item').data('item-id')
item = @items.get(selectedSlotItemID)
if item then @showItemDetails(item, 'unequip')
@onSelectionChanged()
onAvailableItemClick: (e) ->
itemContainer = $(e.target).closest('.list-group-item')
return if itemContainer.hasClass('locked') or itemContainer.hasClass('restricted')
wasActive = itemContainer.hasClass 'active'
@unselectAllAvailableEquipment()
@selectAvailableItem(itemContainer) unless wasActive
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)
@onSelectionChanged()
onAvailableItemDoubleClick: (e) ->
if e
itemContainer = $(e.target).closest('.list-group-item')
return if itemContainer.hasClass('locked') or itemContainer.hasClass('restricted')
@selectAvailableItem itemContainer
@onSelectionChanged()
slot = @getSelectedSlot()
slot = @$el.find('.item-slot:not(.disabled):first') if not slot.length
$(e.target).effect('transfer', to: slot, duration: 500, easing: 'easeOutCubic') if e
@unequipItemFromSlot(slot)
@equipSelectedItemToSlot(slot)
equipSelectedItem: ->
selectedItemEl = @getSelectedUnequippedItem()
selectedItem = @items.get(selectedItemEl.data('item-id'))
return unless selectedItem
allowedSlots = selectedItem.getAllowedSlots()
slotEl = @$el.find(".item-slot[data-slot='#{allowedSlots[0]}']")
selectedItemEl.effect('transfer', to: slotEl, duration: 500, easing: 'easeOutCubic')
unequipped = @unequipItemFromSlot(slotEl)
selectedItemEl.addClass('equipped')
slotEl.append(selectedItemEl.clone())
@clearSelection()
@showItemDetails(selectedItem, 'unequip')
slotEl.addClass('selected')
selectedItem.classes.push 'equipped'
@makeEquippedSlotDraggable slotEl
@requireLevelEquipment()
@onSelectionChanged()
onEquippedItemDoubleClick: (e) ->
@unselectAllAvailableEquipment()
slot = $(e.target).closest('.item-slot')
@selectAvailableItem(@unequipItemFromSlot(slot))
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()
@onSelectionChanged()
getSelectedSlot: ->
@$el.find('#equipped .item-slot.selected')
unselectAllAvailableEquipment: ->
@$el.find('#available-equipment .list-group-item').removeClass('active')
#- Select/equip helpers
unselectAllSlots: ->
clearSelection: ->
@deselectAllSlots()
@deselectAllUnequippedItems()
@hideItemDetails()
unequipItemFromSlot: (slotEl) ->
itemEl = slotEl.find('img.item')
itemIDToUnequip = itemEl.data('item-id')
return unless itemIDToUnequip
itemEl.remove()
@$el.find("#unequipped img.item[data-item-id=#{itemIDToUnequip}]").removeClass('equipped')
deselectAllSlots: ->
@$el.find('#equipped .item-slot.selected').removeClass('selected')
selectSlot: (slot) ->
slot.addClass('selected')
deselectAllUnequippedItems: ->
@$el.find('#unequipped img.item').removeClass('active')
getSlot: (name) ->
@$el.find(".item-slot[data-slot=#{name}]")
getSelectedAvailableItemContainer: ->
@$el.find('#available-equipment .list-group-item.active')
getSelectedSlot: ->
@$el.find('#equipped .item-slot.selected')
getAvailableItemContainer: (itemID) ->
@$el.find("#available-equipment .list-group-item[data-item-id='#{itemID}']")
selectAvailableItem: (itemContainer) ->
itemContainer?.addClass('active')
unequipItemFromSlot: (slot) ->
itemIDToUnequip = slot.find('.item-view').data('item-id')
return unless itemIDToUnequip
slot.find('.item-view').detach()
for el in @$el.find('#available-equipment .list-group-item')
itemID = $(el).find('.item-view').data('item-id')
if itemID is itemIDToUnequip
unequipped = $(el).removeClass('equipped')
break
if unequipped
@clearSelection()
@requireLevelEquipment()
return unequipped
equipSelectedItemToSlot: (slot) ->
selectedItemContainer = @getSelectedAvailableItemContainer()
newItemHTML = selectedItemContainer.html()
selectedItemContainer.addClass('equipped')
slotContainer = slot.find('.item-container')
slotContainer.html(newItemHTML)
slotContainer.find('.item-view').data('item-id', selectedItemContainer.find('.item-view').data('item-id'))
@$el.find('.list-group-item').removeClass('active')
@makeEquippedSlotDraggable slot
@requireLevelEquipment()
getSelectedUnequippedItem: ->
@$el.find('#unequipped img.item.active')
onSelectionChanged: ->
@$el.find('.item-slot').show()
selectedSlot = @$el.find('.item-slot.selected')
selectedItem = @$el.find('#available-equipment .list-group-item.active')
if selectedSlot.length
@$el.find('#available-equipment .list-group-item').hide()
unlockedCount = @$el.find("#available-equipment .list-group-item.#{selectedSlot.data('slot')}:not(.locked)").show().length
lockedCount = @$el.find("#available-equipment .list-group-item.#{selectedSlot.data('slot')}.locked").show().length
@$el.find('#unlocked-description').text("#{unlockedCount} #{selectedSlot.data('slot')} items owned").toggle unlockedCount > 0
@$el.find('#locked-description').text("#{lockedCount} #{selectedSlot.data('slot')} items locked").toggle lockedCount > 0
selectedSlotItemID = selectedSlot.find('.item-view').data('item-id')
if selectedSlotItemID
item = _.find @items.models, {id: selectedSlotItemID}
@showSelectedSlotItem(item)
else
@hideSelectedSlotItem()
else
unlockedCount = @$el.find('#available-equipment .list-group-item:not(.locked)').show().length
@$el.find('#available-equipment .list-group-item.locked').hide()
@$el.find('#unlocked-description').text("#{unlockedCount} items owned").toggle unlockedCount > 0
@$el.find('#locked-description').text("#{lockedCount} items locked").hide()
#@$el.find('#available-equipment .list-group-item.equipped').hide()
@$el.find('.item-slot').removeClass('disabled')
if selectedItem.length
item = _.find @items.models, {id:selectedItem.find('.item-view').data('item-id')}
# update which slots are enabled
allowedSlots = item.getAllowedSlots()
for slotEl in @$el.find('.item-slot')
slotName = $(slotEl).data('slot')
if slotName not in allowedSlots
$(slotEl).addClass('disabled')
@showSelectedAvailableItem(item)
else
@hideSelectedAvailableItem()
@delegateEvents()
showSelectedSlotItem: (item) ->
if not @selectedEquippedItemView
@selectedEquippedItemView = new ItemView({
item: item, includes: {name: true, stats: true, props: true}})
@insertSubView(@selectedEquippedItemView, @$el.find('#selected-equipped-item .item-view-stub'))
else
@selectedEquippedItemView.$el.show()
@selectedEquippedItemView.item = item
@selectedEquippedItemView.render()
@$el.find('#selected-items').show()
@$el.find('#selected-equipped-item').show()
hideSelectedSlotItem: ->
@selectedEquippedItemView?.$el.hide().parent().hide()
@$el.find('#selected-items').hide() unless @selectedEquippedItemView?.$el?.is(':visible')
showItemDetails: (item, showExtra) ->
@itemDetailsView.setItem(item)
@$el.find('#item-details-extra > *').addClass('secret')
@$el.find("##{showExtra}-item-viewed").removeClass('secret')
showSelectedAvailableItem: (item) ->
if not @selectedAvailableItemView
@selectedAvailableItemView = new ItemView({
item: item, includes: {name: true, stats: true, props: true}})
@insertSubView(@selectedAvailableItemView, @$el.find('#selected-available-item .item-view-stub'))
else
@selectedAvailableItemView.$el.show()
@selectedAvailableItemView.item = item
@selectedAvailableItemView.render()
@$el.find('#selected-items').show()
@$el.find('#selected-available-item').show()
hideSelectedAvailableItem: ->
@selectedAvailableItemView?.$el.hide().parent().hide()
@$el.find('#selected-items').hide() unless @selectedEquippedItemView?.$el?.is(':visible')
hideItemDetails: ->
@itemDetailsView.setItem(null)
@$el.find('#item-details-extra > *').addClass('secret')
getCurrentEquipmentConfig: ->
config = {}
for slot in @$el.find('.item-slot')
slotName = $(slot).data('slot')
slotItemID = $(slot).find('.item-view').data('item-id')
slotItemID = $(slot).find('img.item').data('item-id')
continue unless slotItemID
item = _.find @items.models, {id:slotItemID}
config[slotName] = item.get('original')
@ -371,10 +370,13 @@ module.exports = class InventoryModal extends ModalView
(item is 'leather-boots' and equipped is gear['simple-boots']) or
(item is 'simple-boots' and equipped is gear['leather-boots'])
)
availableSlotSelector = "#available-equipment li[data-item-id='#{gear[item]}']"
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()
@ -382,54 +384,12 @@ module.exports = class InventoryModal extends ModalView
$('#play-level-button').prop('disabled', @remainingRequiredEquipment.length > 0)
setHero: (@selectedHero) ->
@loadHero()
@$el.removeClass('Warrior Ranger Wizard').addClass(@selectedHero.get('heroClass'))
loadHero: ->
return unless @supermodel.finished() and @selectedHero and not @$el.hasClass 'secret'
@startedLoadingFirstHero = true
@stage?.removeAllChildren()
if featureImage = @selectedHero.get 'featureImage'
@$el.find(".equipped-hero-canvas").hide()
@$el.find(".hero-feature-image").show().find('img').prop('src', '/file/' + featureImage)
return
if @selectedHero.loaded and movieClip = @movieClips?[@selectedHero.get('original')]
@stage.addChild(movieClip)
@stage.update()
return
onLoaded = =>
return unless canvas = @$el.find(".equipped-hero-canvas")
@canvasWidth ||= canvas.width()
@canvasHeight ||= canvas.height()
canvas.prop width: @canvasWidth, height: @canvasHeight
builder = new SpriteBuilder(@selectedHero)
movieClip = builder.buildMovieClip(@selectedHero.get('actions').attack?.animation ? @selectedHero.get('actions').idle.animation)
movieClip.scaleX = movieClip.scaleY = canvas.prop('height') / 120 # Average hero height is ~110px at normal resolution
if @selectedHero.get('name') in ['Knight', 'Robot Walker'] # These are too big, so shrink them.
movieClip.scaleX *= 0.7
movieClip.scaleY *= 0.7
movieClip.regX = -@selectedHero.get('positions').registration.x
movieClip.regY = -@selectedHero.get('positions').registration.y
movieClip.x = canvas.prop('width') * 0.5
movieClip.y = canvas.prop('height') * 0.95 # This is where the feet go.
movieClip.gotoAndPlay 0
@stage ?= new createjs.Stage(canvas[0])
@stage.addChild movieClip
@stage.update()
@movieClips ?= {}
@movieClips[@selectedHero.get('original')] = movieClip
if @selectedHero.loaded
if @selectedHero.isFullyLoaded()
_.defer onLoaded
else
console.error 'Hmm, trying to render a hero we have not loaded...?', @selectedHero
else
@listenToOnce @selectedHero, 'sync', onLoaded
@render()
onShown: ->
# Called when we switch tabs to this within the modal
@requireLevelEquipment()
@loadHero()
onHidden: ->
# Called when the modal itself is dismissed
@ -471,6 +431,13 @@ module.exports = class InventoryModal extends ModalView
else
callback?()
destroy: ->
@stage?.removeAllChildren()
super()
gear =
'simple-boots': '53e237bf53457600003e3f05'
'simple-sword': '53e218d853457600003e3ebe'

View file

@ -0,0 +1,76 @@
CocoView = require 'views/kinds/CocoView'
template = require 'templates/play/modal/item-details-view'
CocoCollection = require 'collections/CocoCollection'
LevelComponent = require 'models/LevelComponent'
utils = require 'lib/utils'
module.exports = class ItemDetailsView extends CocoView
id: "item-details-view"
template: template
constructor: ->
super(arguments...)
@propDocs = {}
setItem: (@item) ->
if @item
@item.name = utils.i18n @item.attributes, 'name'
@item.affordable = me.gems() >= @item.get('gems')
@item.owned = me.ownsItem @item.get('original')
@item.comingSoon = not @item.getFrontFacingStats().props.length and not _.size @item.getFrontFacingStats().stats # Temp: while there are placeholder items
stats = @item.getFrontFacingStats()
props = (p for p in stats.props when not @propDocs[p])
if props.length > 0
docs = new CocoCollection([], {
url: '/db/level.component?view=prop-doc-lookup'
model: LevelComponent
project: [
'propertyDocumentation.name'
'propertyDocumentation.description'
'propertyDocumentation.i18n'
]
})
docs.fetch({ data: {
componentOriginals: [c.original for c in @item.get('components')].join(',')
propertyNames: props.join(',')
}})
@listenToOnce docs, 'sync', @onDocsLoaded
@render()
@$el.find('.nano:visible').nanoScroller()
onDocsLoaded: (levelComponents) ->
for component in levelComponents.models
for propDoc in component.get('propertyDocumentation')
@propDocs[propDoc.name] = propDoc
@render()
getRenderData: ->
c = super()
c.item = @item
if @item
stats = @item.getFrontFacingStats()
c.stats = _.values(stats.stats)
_.last(c.stats).isLast = true if c.stats.length
c.props = []
progLang = (me.get('aceConfig') ? {}).language or 'python'
for prop in stats.props
description = utils.i18n @propDocs[prop] ? {}, 'description'
if _.isObject description
description = description[progLang] or _.values(description)[0]
if _.isString description
description = description.replace(/#{spriteName}/g, 'hero')
if fact = stats.stats.shieldDefenseFactor
description = description.replace(/#{shieldDefensePercent}%/g, fact.display)
description = $(marked(description)).html()
c.props.push {
name: prop
description: description or '...'
}
c

View file

@ -1,8 +1,6 @@
ModalView = require 'views/kinds/ModalView'
CocoView = require 'views/kinds/CocoView'
template = require 'templates/play/modal/play-items-modal'
itemDetailsTemplate = require 'templates/play/modal/item-details-view'
ItemDetailsView = require './ItemDetailsView'
CocoCollection = require 'collections/CocoCollection'
ThangType = require 'models/ThangType'
@ -139,7 +137,7 @@ module.exports = class PlayItemsModal extends ModalView
$($(e.target).attr('href')).find('.nano').nanoScroller({alwaysVisible: true})
onUnlockButtonClicked: (e) ->
button = $(e.target)
button = $(e.target).closest('button')
if button.hasClass('confirm')
item = @idToItem[$(e.target).data('item-id')]
purchase = Purchase.makeFor(item)
@ -161,67 +159,3 @@ module.exports = class PlayItemsModal extends ModalView
@$el.one 'click', (e) ->
button.removeClass('confirm').text($.i18n.t('play.unlock')) if e.target isnt button[0]
class ItemDetailsView extends CocoView
id: "item-details-view"
template: itemDetailsTemplate
constructor: ->
super(arguments...)
@propDocs = {}
setItem: (@item) ->
@render()
if @item
stats = @item.getFrontFacingStats()
props = (p for p in stats.props when not @propDocs[p])
return if props.length is 0
docs = new CocoCollection([], {
url: '/db/level.component?view=prop-doc-lookup'
model: LevelComponent
project: [
'propertyDocumentation.name'
'propertyDocumentation.description'
'propertyDocumentation.i18n'
]
})
docs.fetch({ data: {
componentOriginals: [c.original for c in @item.get('components')].join(',')
propertyNames: props.join(',')
}})
@listenToOnce docs, 'sync', @onDocsLoaded
@$el.find('.nano:visible').nanoScroller()
onDocsLoaded: (levelComponents) ->
for component in levelComponents.models
for propDoc in component.get('propertyDocumentation')
@propDocs[propDoc.name] = propDoc
@render()
getRenderData: ->
c = super()
c.item = @item
if @item
stats = @item.getFrontFacingStats()
c.stats = _.values(stats.stats)
_.last(c.stats).isLast = true if c.stats.length
c.props = []
progLang = (me.get('aceConfig') ? {}).language or 'python'
for prop in stats.props
description = utils.i18n @propDocs[prop] ? {}, 'description'
if _.isObject description
description = description[progLang] or _.values(description)[0]
if _.isString description
description = description.replace(/#{spriteName}/g, 'hero')
if fact = stats.stats.shieldDefenseFactor
description = description.replace(/#{shieldDefensePercent}%/g, fact.display)
description = $(marked(description)).html()
c.props.push {
name: prop
description: description or '...'
}
c

View file

@ -4,6 +4,6 @@ log = require 'winston'
{handlers} = require '../commons/mapping'
PurchaseSchema = new mongoose.Schema({status: String}, {strict: false})
PurchaseSchema.index({recipient: 1, 'purchase.original': 1}, {unique: true, name: 'unique purchase'})
PurchaseSchema.index({recipient: 1, 'purchased.original': 1}, {unique: true, name: 'unique purchase'})
module.exports = mongoose.model('purchase', PurchaseSchema)

View file

@ -40,7 +40,7 @@ PurchaseHandler = class PurchaseHandler extends Handler
return @sendDatabaseError(res, err) if err
return @sendNotFoundError(res) unless purchasedItem
return @sendBadInputError(res, 'This cannot be purchased.') if not cost = purchasedItem.get('gems')
return @sendForbiddenError(res, 'Not enough gems.') if cost > req.user.get('gems')
return @sendForbiddenError(res, 'Not enough gems.') if cost > req.user.gems()
req.purchasedItem = purchasedItem # for safekeeping
criteria = {
@ -69,7 +69,7 @@ PurchaseHandler = class PurchaseHandler extends Handler
when 'Hero' then 'heroes'
else 'levels'
original = item.get('original')
original = item.get('original') + ''
purchased[group] ?= []
unless original in purchased[group]
#- add the purchase to the list of purchases