Crude steps towards using your hero equipment in items levels.

This commit is contained in:
Nick Winter 2014-08-14 15:09:10 -07:00
parent 9404c40b47
commit 0f983bdcf1
9 changed files with 108 additions and 60 deletions

View file

@ -31,6 +31,7 @@ module.exports = class LevelLoader extends CocoClass
@opponentSessionID = options.opponentSessionID
@team = options.team
@headless = options.headless
@inLevelEditor = options.inLevelEditor
@spectateMode = options.spectateMode ? false
@worldNecessities = []
@ -83,6 +84,7 @@ module.exports = class LevelLoader extends CocoClass
for itemThangType in _.values(heroConfig.inventory)
url = "/db/thang.type/#{itemThangType}/version?project=name,components,original"
@worldNecessities.push @maybeLoadURL(url, ThangType, 'thang')
@populateHero() if @level?.loaded
# Supermodel (Level) Loading
@ -96,6 +98,7 @@ module.exports = class LevelLoader extends CocoClass
onLevelLoaded: ->
@populateLevel()
@populateHero() if @session?.loaded
populateLevel: ->
thangIDs = []
@ -149,6 +152,16 @@ module.exports = class LevelLoader extends CocoClass
@worldNecessities = @worldNecessities.concat worldNecessities
populateHero: ->
return if @inLevelEditor
return unless @level.get('type') is 'hero' and hero = _.find @level.get('thangs'), id: 'Hero Placeholder'
heroConfig = @session.get('heroConfig')
hero.thangType = heroConfig.thangType
hero.inventory = heroConfig.inventory # Will take effect in Level's denormalizeThang
hero.placeholderComponents = hero.components # Will be replaced in Level's denormalizeThang
hero.components = []
#hero.id = ... ? # What do we want to do about this?
loadItemThangsEquippedByLevelThang: (levelThang) ->
return unless levelThang.components
for component in levelThang.components
@ -178,7 +191,7 @@ module.exports = class LevelLoader extends CocoClass
levelThang = $.extend true, {}, levelThang
@level.denormalizeThang(levelThang, @supermodel)
equipsComponent = _.find levelThang.components, {original: LevelComponent.EquipsID}
inventory = equipsComponent.config?.inventory
inventory = equipsComponent?.config?.inventory
continue unless inventory
for itemThangType in _.values inventory
url = "/db/thang.type/#{itemThangType}/version?project=name,components,original"

View file

@ -9,8 +9,7 @@ module.exports = class Level extends CocoModel
urlRoot: '/db/level'
serialize: (supermodel) ->
# o = _.cloneDeep @attributes # slow in level editor when there are hundreds of Thangs
o = $.extend true, {}, @attributes
o = @denormalize supermodel
# Figure out Components
o.levelComponents = _.cloneDeep (lc.attributes for lc in supermodel.getModels LevelComponent)
@ -31,8 +30,10 @@ module.exports = class Level extends CocoModel
denormalize: (supermodel) ->
o = $.extend true, {}, @attributes
for levelThang in o.thangs
@denormalizeThang(levelThang, supermodel)
if @get('type') is 'hero'
# TOOD: figure out if/when/how we are doing this for non-Hero levels that aren't expecting denormalization.
for levelThang in o.thangs
@denormalizeThang(levelThang, supermodel)
o
denormalizeThang: (levelThang, supermodel) ->
@ -41,16 +42,38 @@ module.exports = class Level extends CocoModel
configs = {}
for thangComponent in levelThang.components
configs[thangComponent.original] = thangComponent
placeholders = {}
for thangComponent in levelThang.placeholderComponents ? []
placeholders[thangComponent.original] = thangComponent
for defaultThangComponent in thangType.get('components')
if levelThangComponent = configs[defaultThangComponent.original]
# take the thang type default components and merge level-specific component config into it
# Take the ThangType default Components and merge level-specific Component config into it
copy = $.extend true, {}, defaultThangComponent.config
levelThangComponent.config = _.merge copy, levelThangComponent.config
else
# just add the component as is
levelThang.components.push $.extend true, {}, defaultThangComponent
# Just add the Component as is
levelThangComponent = $.extend true, {}, defaultThangComponent
levelThang.components.push levelThangComponent
if placeholderComponent = placeholders[defaultThangComponent.original]
placeholderConfig = placeholderComponent.config ? {}
if placeholderConfig.pos # Pull in Physical pos x and y
levelThangComponent.config.pos ?= {}
levelThangComponent.config.pos.x = placeholderConfig.pos.x
levelThangComponent.config.pos.y = placeholderConfig.pos.y
else if placeholderConfig.team # Pull in Allied team
levelThangComponent.config.team = placeholderConfig.team
else if placeholderConfig.programmableMethods
# Take the ThangType default Programmable and merge level-specific Component config into it
copy = $.extend true, {}, placeholderConfig
levelThangComponent.config = _.merge copy, levelThangComponent.config
if levelThang.inventory and equips = _.find levelThang.components, {original: LevelComponent.EquipsID}
# inventory is assigned from the LevelSession in LevelLoader's populateHero
equips.config.inventory = $.extend true, {}, levelThang.inventory
sortSystems: (levelSystems, systemModels) ->
[sorted, originalsSeen] = [[], {}]

View file

@ -55,10 +55,13 @@ _.extend LevelSessionSchema.properties,
screenshot:
type: 'string'
heroConfig: c.object {},
inventory: c.object()
thangType: c.objectId()
heroConfig: c.object {description: 'Which hero the player is using, equipped with what inventory.'},
inventory:
type: 'object'
description: 'The inventory of the hero: slots to item ThangTypes.'
additionalProperties: c.objectId(description: 'An item ThangType.')
thangType: c.objectId(links: [{rel: 'db', href: '/db/thang.type/{($)}/version'}], title: 'Thang Type', description: 'The ThangType of the hero.', format: 'thang-type')
state: c.object {},
complete:

View file

@ -12,8 +12,8 @@ nodes = require '../level/treema_nodes'
ThangType = require 'models/ThangType'
CocoCollection = require 'collections/CocoCollection'
class ThangTypeSearchCollection extends CocoCollection
url: '/db/thang.type?project=original,name,version,slug,kind,components'
class ItemThangTypeSearchCollection extends CocoCollection
url: '/db/thang.type?view=items&project=original,name,version,slug,kind,components'
model: ThangType
module.exports = class ThangComponentsEditView extends CocoView
@ -32,7 +32,7 @@ module.exports = class ThangComponentsEditView extends CocoView
@level = options.level
@loadComponents(@components)
# Need to grab the ThangTypes so that we can autocomplete items in inventory based on them.
@thangTypes = @supermodel.loadCollection(new ThangTypeSearchCollection(), 'thangs').model
@itemThangTypes = @supermodel.loadCollection(new ItemThangTypeSearchCollection(), 'thangs').model
loadComponents: (components) ->
for componentRef in components

View file

@ -47,7 +47,7 @@ module.exports = class LevelEditView extends RootView
super options
@supermodel.shouldSaveBackups = (model) ->
model.constructor.className in ['Level', 'LevelComponent', 'LevelSystem', 'ThangType']
@levelLoader = new LevelLoader supermodel: @supermodel, levelID: @levelID, headless: true
@levelLoader = new LevelLoader supermodel: @supermodel, levelID: @levelID, headless: true, inLevelEditor: true
@level = @levelLoader.level
@files = new DocumentFiles(@levelLoader.level)
@supermodel.loadCollection(@files, 'file_names')

View file

@ -335,8 +335,8 @@ module.exports = class ThangsTabView extends CocoView
adjustThangPos: (sprite, thang, pos) ->
snap = sprite?.data?.snap or sprite?.thangType?.get('snap') or {x: 0.01, y: 0.01} # Centimeter resolution by default
pos.x = Math.round((pos.x - thang.width / 2) / snap.x) * snap.x + thang.width / 2
pos.y = Math.round((pos.y - thang.height / 2) / snap.y) * snap.y + thang.height / 2
pos.x = Math.round((pos.x - (thang.width ? 1) / 2) / snap.x) * snap.x + (thang.width ? 1) / 2
pos.y = Math.round((pos.y - (thang.height ? 1) / 2) / snap.y) * snap.y + (thang.height ? 1) / 2
pos.z = thang.depth / 2
thang.pos = pos
@surface.spriteBoss.update true # Make sure Obstacle layer resets cache

View file

@ -9,32 +9,32 @@ 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"]
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'
'click #swap-button': 'onClickSwapButton'
shortcuts:
'esc': 'clearSelection'
initialize: (options) ->
super(arguments...)
@items = new CocoCollection([], { model: ThangType })
@equipment = options.equipment or {}
@items = new CocoCollection([], {model: ThangType})
@equipment = options.equipment or @options.session?.get('heroConfig')?.inventory or {}
@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
context.items = @items.models
for item in @items.models
item.classes = item.getAllowedSlots()
@ -50,7 +50,7 @@ module.exports = class InventoryView extends CocoView
afterRender: ->
super()
return unless @supermodel.finished()
keys = (item.id for item in @items.models)
itemMap = _.zipObject keys, @items.models
@ -58,7 +58,7 @@ module.exports = class InventoryView extends CocoView
for slottedItemStub in @$el.find('.replace-me')
itemID = $(slottedItemStub).data('item-id')
item = itemMap[itemID]
itemView = new ItemView({item:item, includes:{name:true}})
itemView = new ItemView({item: item, includes: {name: true}})
itemView.render()
$(slottedItemStub).replaceWith(itemView.$el)
@registerSubView(itemView)
@ -66,13 +66,13 @@ module.exports = class InventoryView extends CocoView
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 = 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')
@ -85,7 +85,7 @@ module.exports = class InventoryView extends CocoView
@unselectAllAvailableEquipment() if slot.hasClass('disabled')
@selectSlot(slot) unless wasActive and not $(e.target).closest('.item-view')[0]
@onSelectionChanged()
onAvailableItemClick: (e) ->
itemContainer = $(e.target).closest('.list-group-item')
@unselectAllAvailableEquipment()
@ -98,7 +98,7 @@ module.exports = class InventoryView extends CocoView
@unequipItemFromSlot(slot)
@equipSelectedItemToSlot(slot)
@onSelectionChanged()
onEquippedItemDoubleClick: (e) ->
@unselectAllAvailableEquipment()
slot = $(e.target).closest('.item-slot')
@ -115,31 +115,31 @@ module.exports = class InventoryView extends CocoView
@selectAvailableItem(itemContainer)
@selectSlot(slot)
@onSelectionChanged()
getSelectedSlot: ->
getSelectedSlot: ->
@$el.find('#equipped .item-slot.panel-info')
unselectAllAvailableEquipment: ->
unselectAllAvailableEquipment: ->
@$el.find('#available-equipment .list-group-item').removeClass('active')
unselectAllSlots: ->
@$el.find('#equipped .panel').removeClass('panel-info')
selectSlot: (slot) ->
slot.addClass('panel-info')
getSlot: (name) ->
@$el.find(".item-slot[data-slot=#{name}]")
getSelectedAvailableItemContainer: ->
@$el.find('#available-equipment .list-group-item.active')
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
@ -157,22 +157,22 @@ module.exports = class InventoryView extends CocoView
slotContainer.html(newItemHTML)
slotContainer.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}
@showSelectedSlotItem(item)
else
@hideSelectedSlotItem()
@ -183,7 +183,7 @@ module.exports = class InventoryView extends CocoView
@$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')
@ -192,12 +192,12 @@ module.exports = class InventoryView extends CocoView
$(slotEl).addClass('disabled')
@showSelectedAvailableItem(item)
else
@hideSelectedAvailableItem()
@delegateEvents()
showSelectedSlotItem: (item) ->
if not @selectedEquippedItemView
@selectedEquippedItemView = new ItemView({
@ -208,10 +208,10 @@ module.exports = class InventoryView extends CocoView
@selectedEquippedItemView.$el.show()
@selectedEquippedItemView.item = item
@selectedEquippedItemView.render()
hideSelectedSlotItem: ->
@selectedEquippedItemView?.$el.hide()
showSelectedAvailableItem: (item) ->
if not @selectedAvailableItemView
@selectedAvailableItemView = new ItemView({
@ -222,7 +222,7 @@ module.exports = class InventoryView extends CocoView
@selectedAvailableItemView.$el.show()
@selectedAvailableItemView.item = item
@selectedAvailableItemView.render()
hideSelectedAvailableItem: ->
@selectedAvailableItemView?.$el.hide()
@ -234,5 +234,14 @@ module.exports = class InventoryView extends CocoView
continue unless slotItemID
item = _.find @items.models, {id:slotItemID}
config[slotName] = item.get('original')
config
config
onHidden: ->
inventory = @getCurrentEquipmentConfig()
heroConfig = @options.session.get('heroConfig') ? {}
unless _.isEqual inventory, heroConfig.inventory
heroConfig.inventory = inventory
heroConfig.thangType ?= '529ffbf1cf1818f2be000001' # Temp: assign Tharin as the hero
@options.session.set 'heroConfig', heroConfig
@options.session.patch()

View file

@ -9,7 +9,7 @@ import sys
current_directory = os.path.dirname(os.path.realpath(sys.argv[0]))
coco_path = os.getenv("COCO_DIR",os.path.join(current_directory,os.pardir))
brunch_path = coco_path + os.sep + "node_modules" + os.sep + ".bin" + os.sep + "brunch"
subprocess.Popen("ulimit -n 100000",shell=True)
subprocess.Popen("ulimit -n 10000", shell=True)
while True:
print("Starting brunch. After the first compile, it'll keep running and watch for changes.")
call(brunch_path + " w",shell=True,cwd=coco_path)

View file

@ -45,7 +45,7 @@ LevelSessionSchema.pre 'save', (next) ->
LevelSessionSchema.statics.privateProperties = ['code', 'submittedCode', 'unsubscribed']
LevelSessionSchema.statics.editableProperties = ['multiplayer', 'players', 'code', 'codeLanguage', 'completed', 'state',
'levelName', 'creatorName', 'levelID', 'screenshot',
'chat', 'teamSpells', 'submitted', 'submittedCodeLanguage', 'unsubscribed', 'playtime']
'chat', 'teamSpells', 'submitted', 'submittedCodeLanguage', 'unsubscribed', 'playtime', 'heroConfig']
LevelSessionSchema.statics.jsonSchema = jsonschema
LevelSessionSchema.index {user: 1, changed: -1}, {sparse: true, name: 'last played index'}