mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2025-03-14 07:00:01 -04:00
Working on the inventory view. Added a way to get the current equipment config from the inventory view.
This commit is contained in:
parent
7286d069a0
commit
c31a509472
11 changed files with 409 additions and 13 deletions
|
@ -15,6 +15,7 @@ module.exports = class CocoRouter extends Backbone.Router
|
|||
|
||||
routes:
|
||||
'': go('HomeView')
|
||||
'items': go('game-menu/InventoryView')
|
||||
|
||||
'about': go('AboutView')
|
||||
|
||||
|
|
|
@ -944,6 +944,7 @@
|
|||
candidate_sessions: "Candidate Sessions"
|
||||
user_remark: "User Remark"
|
||||
versions: "Versions"
|
||||
items: "Items"
|
||||
|
||||
delta:
|
||||
added: "Added"
|
||||
|
|
|
@ -5,6 +5,8 @@ module.exports = class LevelComponent extends CocoModel
|
|||
@schema: require 'schemas/models/level_component'
|
||||
|
||||
@EquipsID: '53e217d253457600003e3ebb'
|
||||
@ItemID: '53e12043b82921000051cdf9'
|
||||
@AttacksID: '524b7ba57fc0f6d519000016'
|
||||
urlRoot: '/db/level.component'
|
||||
|
||||
set: (key, val, options) ->
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
CocoModel = require './CocoModel'
|
||||
SpriteBuilder = require 'lib/sprites/SpriteBuilder'
|
||||
LevelComponent = require './LevelComponent'
|
||||
|
||||
buildQueue = []
|
||||
|
||||
|
@ -262,3 +263,55 @@ module.exports = class ThangType extends CocoModel
|
|||
@wizardType.url = -> url
|
||||
@wizardType.fetch()
|
||||
@wizardType
|
||||
|
||||
getPortraitURL: -> "/file/db/thang.type/#{@get('original')}/portrait.png"
|
||||
|
||||
# Item functions
|
||||
|
||||
getAllowedSlots: ->
|
||||
itemComponentRef = _.find(
|
||||
@get('components') or [],
|
||||
(compRef) -> compRef.original is LevelComponent.ItemID)
|
||||
return itemComponentRef?.config?.slots or []
|
||||
|
||||
getFrontFacingStats: ->
|
||||
stats = []
|
||||
for component in @get('components') or []
|
||||
continue unless config = component.config
|
||||
if config.attackDamage
|
||||
stats.push { name: 'Attack Damage', value: config.attackDamage }
|
||||
if config.attackRange
|
||||
stats.push { name: 'Attack Range', value: "#{config.attackRange}m" }
|
||||
if config.cooldown
|
||||
stats.push { name: 'Cooldown', value: "#{config.cooldown}s" }
|
||||
if config.maxSpeed
|
||||
stats.push { name: 'Speed', value: "#{config.maxSpeed}m/s" }
|
||||
if config.maxAcceleration
|
||||
stats.push { name: 'Acceleration', value: "#{config.maxAcceleration}m/s^2" }
|
||||
if config.stats
|
||||
for stat, value of config.stats
|
||||
if value.factor
|
||||
value = "x#{value.factor}"
|
||||
if value.addend and value.addend > 0
|
||||
value = "+#{value.addend}"
|
||||
if value.addend and value.addend < 0
|
||||
value = "#{value.addend}"
|
||||
if value.setTo
|
||||
value = "=#{value.setTo}"
|
||||
if stat is 'maxHealth'
|
||||
stats.push { name: 'Health', value: value }
|
||||
if stat is 'healthReplenishRate'
|
||||
stats.push { name: 'Regen', value: value }
|
||||
if config.programmableProperties
|
||||
props = config.programmableProperties
|
||||
if props.length
|
||||
stats.push { name: 'Allows', value: props.join(', ') }
|
||||
if config.visualRange
|
||||
value = config.visualRange
|
||||
if value is 9001 then value is "Infinite"
|
||||
stats.push { name: 'Visual Range', value: "#{value}m"}
|
||||
if config.programmableSnippets
|
||||
snippets = config.programmableSnippets
|
||||
if snippets.length
|
||||
stats.push { name: 'Snippets', value: snippets.join(', ') }
|
||||
stats
|
|
@ -1,3 +1,97 @@
|
|||
$selected-area-height: 150px
|
||||
|
||||
#inventory-view
|
||||
position: relative
|
||||
height: 600px
|
||||
background-color: white
|
||||
user-select: none
|
||||
|
||||
h3
|
||||
text-decoration: underline
|
||||
margin: 0
|
||||
|
||||
#equipped
|
||||
padding: 10px
|
||||
width: 74%
|
||||
position: absolute
|
||||
left: 0
|
||||
top: 0
|
||||
bottom: $selected-area-height + 10
|
||||
right: 0
|
||||
overflow: scroll
|
||||
|
||||
.item-slot.disabled
|
||||
opacity: 0.5
|
||||
|
||||
.panel
|
||||
text-align: center
|
||||
width: 31%
|
||||
margin: 5px 2% 5px 0
|
||||
float: left
|
||||
cursor: pointer
|
||||
|
||||
.panel-heading
|
||||
padding: 5px
|
||||
|
||||
.panel-info .panel-body
|
||||
background-color: #e0f0f5
|
||||
|
||||
.panel-body
|
||||
height: 50px
|
||||
padding: 5px
|
||||
overflow: scroll
|
||||
font-size: 12px
|
||||
|
||||
#available-equipment
|
||||
width: 24%
|
||||
padding: 10px
|
||||
position: absolute
|
||||
right: 1%
|
||||
top: 0
|
||||
bottom: $selected-area-height + 10
|
||||
overflow: scroll
|
||||
user-select: none
|
||||
|
||||
h3
|
||||
margin-bottom: 5px
|
||||
|
||||
.list-group-item.active
|
||||
background-color: #e0f0f5
|
||||
|
||||
.list-group-item.equipped
|
||||
display: none
|
||||
|
||||
.item-mixin
|
||||
&.active
|
||||
background-color: lightblue
|
||||
.well
|
||||
padding: 5px
|
||||
cursor: pointer
|
||||
|
||||
#selected-items
|
||||
position: absolute
|
||||
height: $selected-area-height
|
||||
left: 0
|
||||
right: 0
|
||||
bottom: 0
|
||||
|
||||
#selected-equipped-item, #selected-available-item
|
||||
position: absolute
|
||||
bottom: 0
|
||||
top: 0
|
||||
overflow: scroll
|
||||
padding: 10px
|
||||
|
||||
img
|
||||
width: 100px
|
||||
height: 100px
|
||||
|
||||
.item-info
|
||||
margin-left: 110px
|
||||
|
||||
#selected-equipped-item
|
||||
left: 0
|
||||
width: 48.5%
|
||||
|
||||
#selected-available-item
|
||||
right: 2%
|
||||
width: 48.5%
|
||||
|
|
14
app/styles/game-menu/item-view.sass
Normal file
14
app/styles/game-menu/item-view.sass
Normal file
|
@ -0,0 +1,14 @@
|
|||
.item-mixin, .item-view
|
||||
width: 100%
|
||||
|
||||
img
|
||||
float: left
|
||||
width: 40px
|
||||
height: 40px
|
||||
|
||||
.item-info
|
||||
margin-left: 45px
|
||||
|
||||
ul
|
||||
margin-top: 5px
|
||||
padding-left: 20px
|
|
@ -1,11 +1,27 @@
|
|||
img(src="/images/pages/game-menu/inventory-stub.png")
|
||||
|
||||
div(data-i18n="inventory.temp") Temp
|
||||
|
||||
h3 Interactions
|
||||
ul
|
||||
li Click an item slot. It is highlighted and items that can go in that slot show up on the right with short descriptions. Full info about it shows up on the lower left.
|
||||
li Click an item on the menu. It shows up on the lower right.
|
||||
li To equip an item: drag (if ipad), double click (if web), click swap button (either)
|
||||
li Click an item on the menu. It swaps the item into the slot.
|
||||
li If the equipment changed and the player clicks done, a new LevelSession is created with this new equipment. If “use current code” is selected, the code for the current session is copied over to the new session. Modal closes, data is loaded, world runs, etc.
|
||||
div#equipped
|
||||
h3 Equipped
|
||||
- for (var i=0; i < slots.length; i += 3) {
|
||||
div
|
||||
- for (var j=i; j < slots.length && j < i + 3; j++) {
|
||||
- var slot = slots[j];
|
||||
div.panel.panel-default.item-slot(data-slot=slot)
|
||||
.panel-heading
|
||||
.panel-title.slot-name= slot
|
||||
.panel-body.slot-item
|
||||
if equipment[slot]
|
||||
- var item = equipment[slot]
|
||||
.replace-me(data-item-id=item.id)
|
||||
- }
|
||||
.clearfix
|
||||
- }
|
||||
|
||||
div#available-equipment
|
||||
h3 Available
|
||||
for item in items
|
||||
.list-group-item(class=item.classes, data-item-id=item.id)
|
||||
|
||||
div#selected-items
|
||||
#selected-equipped-item.well
|
||||
.item-view-stub
|
||||
#selected-available-item.well
|
||||
.item-view-stub
|
||||
|
|
12
app/templates/game-menu/item-view.jade
Normal file
12
app/templates/game-menu/item-view.jade
Normal file
|
@ -0,0 +1,12 @@
|
|||
img(src=item.getPortraitURL()).img-thumbnail
|
||||
div.item-info
|
||||
if includes.name
|
||||
strong= item.get('name')
|
||||
if includes.stats
|
||||
- var stats = item.getFrontFacingStats()
|
||||
ul
|
||||
for stat in stats
|
||||
li #{stat.name}: #{stat.value}
|
||||
.clearfix
|
||||
|
||||
|
|
@ -2,15 +2,197 @@ CocoView = require 'views/kinds/CocoView'
|
|||
template = require 'templates/game-menu/inventory-view'
|
||||
{me} = require 'lib/auth'
|
||||
ThangType = require 'models/ThangType'
|
||||
CocoCollection = require 'collections/CocoCollection'
|
||||
ItemView = require './ItemView'
|
||||
|
||||
DEFAULT_EQUIPMENT = {
|
||||
'right-hand': '53e21249b82921000051ce11'
|
||||
'feet':'53e214f153457600003e3eab'
|
||||
'eyes': '53e2167653457600003e3eb3'
|
||||
'left-hand': '53e22aa153457600003e3ef5'
|
||||
}
|
||||
|
||||
module.exports = class InventoryView extends CocoView
|
||||
id: 'inventory-view'
|
||||
className: 'tab-pane'
|
||||
template: template
|
||||
slots: ["head","eyes","neck","torso","wrists","gloves","left-ring","right-ring","right-hand","left-hand","waist","feet","spellbook","programming-book","pet","minion","misc-0","misc-1","misc-2","misc-3","misc-4"]
|
||||
|
||||
events:
|
||||
'click .item-slot': 'onItemSlotClick'
|
||||
'click #available-equipment .list-group-item': 'onAvailableItemClick'
|
||||
'dblclick #available-equipment .list-group-item': 'onAvailableItemDoubleClick'
|
||||
'dblclick .item-slot .item-view': 'onEquippedItemDoubleClick'
|
||||
|
||||
shortcuts:
|
||||
'esc': 'clearSelection'
|
||||
|
||||
initialize: (options) ->
|
||||
super(arguments...)
|
||||
@items = new CocoCollection([], { model: ThangType })
|
||||
@equipment = options.equipment or DEFAULT_EQUIPMENT
|
||||
@items.url = '/db/thang.type?view=items&project=name,description,components,original'
|
||||
@supermodel.loadCollection(@items, 'items')
|
||||
|
||||
onLoaded: ->
|
||||
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
|
||||
|
||||
context.slots = @slots
|
||||
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
|
||||
|
||||
afterRender: ->
|
||||
super()
|
||||
return unless @supermodel.finished()
|
||||
|
||||
keys = (item.id for item in @items.models)
|
||||
itemMap = _.zipObject keys, @items.models
|
||||
|
||||
# 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:{name:true}})
|
||||
itemView.render()
|
||||
$(slottedItemStub).replaceWith(itemView.$el)
|
||||
@registerSubView(itemView)
|
||||
|
||||
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)
|
||||
|
||||
@delegateEvents()
|
||||
|
||||
clearSelection: ->
|
||||
@$el.find('.panel-info').removeClass('panel-info')
|
||||
@$el.find('.list-group-item').removeClass('active')
|
||||
@onSelectionChanged()
|
||||
|
||||
onItemSlotClick: (e) ->
|
||||
slot = $(e.target).closest('.panel')
|
||||
wasActive = slot.hasClass('panel-info')
|
||||
@$el.find('#equipped .panel').removeClass('panel-info')
|
||||
@$el.find('#available-equipment .list-group-item').removeClass('active') if slot.hasClass('disabled')
|
||||
slot.addClass('panel-info') # unless wasActive
|
||||
@onSelectionChanged()
|
||||
|
||||
onAvailableItemClick: (e) ->
|
||||
itemEl = $(e.target).closest('.list-group-item')
|
||||
@$el.find('#available-equipment .list-group-item').removeClass('active')
|
||||
itemEl.addClass('active')
|
||||
@onSelectionChanged()
|
||||
|
||||
onAvailableItemDoubleClick: ->
|
||||
slot = @$el.find('#equipped .item-slot.panel-info')
|
||||
slot = $('.panel:not(.disabled):first') if not slot.length
|
||||
@unequipItemFromSlot(slot)
|
||||
@equipSelectedItemToSlot(slot)
|
||||
@onSelectionChanged()
|
||||
|
||||
onEquippedItemDoubleClick: (e) ->
|
||||
slot = $(e.target).closest('.item-slot')
|
||||
@unequipItemFromSlot(slot)
|
||||
@onSelectionChanged()
|
||||
|
||||
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
|
||||
$(el).removeClass('equipped')
|
||||
|
||||
equipSelectedItemToSlot: (slot) ->
|
||||
selectedItemContainer = @$el.find('#available-equipment .list-group-item.active')
|
||||
newItemHTML = selectedItemContainer.html()
|
||||
@$el.find('#available-equipment .list-group-item.active').addClass('equipped')
|
||||
container = slot.find('.panel-body')
|
||||
container.html(newItemHTML)
|
||||
container.find('.item-view').data('item-id', selectedItemContainer.find('.item-view').data('item-id'))
|
||||
@$el.find('.list-group-item').removeClass('active')
|
||||
|
||||
onSelectionChanged: ->
|
||||
@$el.find('.item-slot').show()
|
||||
|
||||
selectedSlot = @$el.find('.panel.panel-info')
|
||||
selectedItem = @$el.find('#available-equipment .list-group-item.active')
|
||||
|
||||
if selectedSlot.length
|
||||
@$el.find('#available-equipment .list-group-item').hide()
|
||||
@$el.find("#available-equipment .list-group-item.#{selectedSlot.data('slot')}").show()
|
||||
|
||||
selectedSlotItemID = selectedSlot.find('.item-view').data('item-id')
|
||||
if selectedSlotItemID
|
||||
item = _.find @items.models, {id:selectedSlotItemID}
|
||||
|
||||
if not @selectedEquippedItemView
|
||||
@selectedEquippedItemView = new ItemView({
|
||||
item: item, includes: {name: true, stats: true}})
|
||||
@insertSubView(@selectedEquippedItemView, @$el.find('#selected-equipped-item .item-view-stub'))
|
||||
|
||||
else
|
||||
@selectedEquippedItemView.$el.show()
|
||||
@selectedEquippedItemView.item = item
|
||||
@selectedEquippedItemView.render()
|
||||
|
||||
else
|
||||
@selectedEquippedItemView?.$el.hide()
|
||||
|
||||
else
|
||||
@$el.find('#available-equipment .list-group-item').show()
|
||||
@$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')
|
||||
|
||||
# updated selected item view
|
||||
if not @selectedAvailableItemView
|
||||
@selectedAvailableItemView = new ItemView({
|
||||
item: item, includes: {name: true, stats: true}})
|
||||
@insertSubView(@selectedAvailableItemView, @$el.find('#selected-available-item .item-view-stub'))
|
||||
|
||||
else
|
||||
@selectedAvailableItemView.$el.show()
|
||||
@selectedAvailableItemView.item = item
|
||||
@selectedAvailableItemView.render()
|
||||
|
||||
else
|
||||
@selectedAvailableItemView?.$el.hide()
|
||||
|
||||
@delegateEvents()
|
||||
|
||||
getCurrentEquipmentConfig: ->
|
||||
config = {}
|
||||
for slot in @$el.find('.item-slot')
|
||||
slotName = $(slot).data('slot')
|
||||
slotItemID = $(slot).find('.item-view').data('item-id')
|
||||
continue unless slotItemID
|
||||
item = _.find @items.models, {id:slotItemID}
|
||||
config[slotName] = item.get('original')
|
||||
|
||||
config
|
21
app/views/game-menu/ItemView.coffee
Normal file
21
app/views/game-menu/ItemView.coffee
Normal file
|
@ -0,0 +1,21 @@
|
|||
CocoView = require 'views/kinds/CocoView'
|
||||
template = require 'templates/game-menu/item-view'
|
||||
|
||||
module.exports = class ItemView extends CocoView
|
||||
className: 'item-view'
|
||||
|
||||
template: template
|
||||
|
||||
initialize: (options) ->
|
||||
super(arguments...)
|
||||
@item = options.item
|
||||
@includes = options.includes or {}
|
||||
|
||||
getRenderData: ->
|
||||
c = super()
|
||||
c.item = @item
|
||||
c.includes = @includes
|
||||
c
|
||||
|
||||
afterRender: ->
|
||||
@$el.data('item-id', @item.id)
|
|
@ -31,7 +31,7 @@ ThangTypeHandler = class ThangTypeHandler extends Handler
|
|||
projection = {}
|
||||
if req.query.project
|
||||
projection[field] = 1 for field in req.query.project.split(',')
|
||||
ThangType.find({ 'kind': 'Item' }, projection).exec (err, documents) =>
|
||||
ThangType.find({ 'kind': 'Item', slug: { $exists: true } }, projection).exec (err, documents) =>
|
||||
return @sendDatabaseError(res, err) if err
|
||||
documents = (@formatEntity(req, doc) for doc in documents)
|
||||
@sendSuccess(res, documents)
|
||||
|
|
Loading…
Reference in a new issue