mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2025-02-18 01:11:46 -05:00
Crude steps towards using your hero equipment in items levels.
This commit is contained in:
parent
9404c40b47
commit
0f983bdcf1
9 changed files with 108 additions and 60 deletions
app
lib
models
schemas/models
views
editor
game-menu
bin
server/levels/sessions
|
@ -31,6 +31,7 @@ module.exports = class LevelLoader extends CocoClass
|
||||||
@opponentSessionID = options.opponentSessionID
|
@opponentSessionID = options.opponentSessionID
|
||||||
@team = options.team
|
@team = options.team
|
||||||
@headless = options.headless
|
@headless = options.headless
|
||||||
|
@inLevelEditor = options.inLevelEditor
|
||||||
@spectateMode = options.spectateMode ? false
|
@spectateMode = options.spectateMode ? false
|
||||||
|
|
||||||
@worldNecessities = []
|
@worldNecessities = []
|
||||||
|
@ -83,6 +84,7 @@ module.exports = class LevelLoader extends CocoClass
|
||||||
for itemThangType in _.values(heroConfig.inventory)
|
for itemThangType in _.values(heroConfig.inventory)
|
||||||
url = "/db/thang.type/#{itemThangType}/version?project=name,components,original"
|
url = "/db/thang.type/#{itemThangType}/version?project=name,components,original"
|
||||||
@worldNecessities.push @maybeLoadURL(url, ThangType, 'thang')
|
@worldNecessities.push @maybeLoadURL(url, ThangType, 'thang')
|
||||||
|
@populateHero() if @level?.loaded
|
||||||
|
|
||||||
# Supermodel (Level) Loading
|
# Supermodel (Level) Loading
|
||||||
|
|
||||||
|
@ -96,6 +98,7 @@ module.exports = class LevelLoader extends CocoClass
|
||||||
|
|
||||||
onLevelLoaded: ->
|
onLevelLoaded: ->
|
||||||
@populateLevel()
|
@populateLevel()
|
||||||
|
@populateHero() if @session?.loaded
|
||||||
|
|
||||||
populateLevel: ->
|
populateLevel: ->
|
||||||
thangIDs = []
|
thangIDs = []
|
||||||
|
@ -149,6 +152,16 @@ module.exports = class LevelLoader extends CocoClass
|
||||||
|
|
||||||
@worldNecessities = @worldNecessities.concat worldNecessities
|
@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) ->
|
loadItemThangsEquippedByLevelThang: (levelThang) ->
|
||||||
return unless levelThang.components
|
return unless levelThang.components
|
||||||
for component in levelThang.components
|
for component in levelThang.components
|
||||||
|
@ -178,7 +191,7 @@ module.exports = class LevelLoader extends CocoClass
|
||||||
levelThang = $.extend true, {}, levelThang
|
levelThang = $.extend true, {}, levelThang
|
||||||
@level.denormalizeThang(levelThang, @supermodel)
|
@level.denormalizeThang(levelThang, @supermodel)
|
||||||
equipsComponent = _.find levelThang.components, {original: LevelComponent.EquipsID}
|
equipsComponent = _.find levelThang.components, {original: LevelComponent.EquipsID}
|
||||||
inventory = equipsComponent.config?.inventory
|
inventory = equipsComponent?.config?.inventory
|
||||||
continue unless inventory
|
continue unless inventory
|
||||||
for itemThangType in _.values inventory
|
for itemThangType in _.values inventory
|
||||||
url = "/db/thang.type/#{itemThangType}/version?project=name,components,original"
|
url = "/db/thang.type/#{itemThangType}/version?project=name,components,original"
|
||||||
|
|
|
@ -9,8 +9,7 @@ module.exports = class Level extends CocoModel
|
||||||
urlRoot: '/db/level'
|
urlRoot: '/db/level'
|
||||||
|
|
||||||
serialize: (supermodel) ->
|
serialize: (supermodel) ->
|
||||||
# o = _.cloneDeep @attributes # slow in level editor when there are hundreds of Thangs
|
o = @denormalize supermodel
|
||||||
o = $.extend true, {}, @attributes
|
|
||||||
|
|
||||||
# Figure out Components
|
# Figure out Components
|
||||||
o.levelComponents = _.cloneDeep (lc.attributes for lc in supermodel.getModels LevelComponent)
|
o.levelComponents = _.cloneDeep (lc.attributes for lc in supermodel.getModels LevelComponent)
|
||||||
|
@ -31,8 +30,10 @@ module.exports = class Level extends CocoModel
|
||||||
|
|
||||||
denormalize: (supermodel) ->
|
denormalize: (supermodel) ->
|
||||||
o = $.extend true, {}, @attributes
|
o = $.extend true, {}, @attributes
|
||||||
for levelThang in o.thangs
|
if @get('type') is 'hero'
|
||||||
@denormalizeThang(levelThang, supermodel)
|
# 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
|
o
|
||||||
|
|
||||||
denormalizeThang: (levelThang, supermodel) ->
|
denormalizeThang: (levelThang, supermodel) ->
|
||||||
|
@ -41,16 +42,38 @@ module.exports = class Level extends CocoModel
|
||||||
configs = {}
|
configs = {}
|
||||||
for thangComponent in levelThang.components
|
for thangComponent in levelThang.components
|
||||||
configs[thangComponent.original] = thangComponent
|
configs[thangComponent.original] = thangComponent
|
||||||
|
placeholders = {}
|
||||||
|
for thangComponent in levelThang.placeholderComponents ? []
|
||||||
|
placeholders[thangComponent.original] = thangComponent
|
||||||
|
|
||||||
for defaultThangComponent in thangType.get('components')
|
for defaultThangComponent in thangType.get('components')
|
||||||
if levelThangComponent = configs[defaultThangComponent.original]
|
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
|
copy = $.extend true, {}, defaultThangComponent.config
|
||||||
levelThangComponent.config = _.merge copy, levelThangComponent.config
|
levelThangComponent.config = _.merge copy, levelThangComponent.config
|
||||||
|
|
||||||
else
|
else
|
||||||
# just add the component as is
|
# Just add the Component as is
|
||||||
levelThang.components.push $.extend true, {}, defaultThangComponent
|
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) ->
|
sortSystems: (levelSystems, systemModels) ->
|
||||||
[sorted, originalsSeen] = [[], {}]
|
[sorted, originalsSeen] = [[], {}]
|
||||||
|
|
|
@ -56,9 +56,12 @@ _.extend LevelSessionSchema.properties,
|
||||||
screenshot:
|
screenshot:
|
||||||
type: 'string'
|
type: 'string'
|
||||||
|
|
||||||
heroConfig: c.object {},
|
heroConfig: c.object {description: 'Which hero the player is using, equipped with what inventory.'},
|
||||||
inventory: c.object()
|
inventory:
|
||||||
thangType: c.objectId()
|
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 {},
|
state: c.object {},
|
||||||
complete:
|
complete:
|
||||||
|
|
|
@ -12,8 +12,8 @@ nodes = require '../level/treema_nodes'
|
||||||
ThangType = require 'models/ThangType'
|
ThangType = require 'models/ThangType'
|
||||||
CocoCollection = require 'collections/CocoCollection'
|
CocoCollection = require 'collections/CocoCollection'
|
||||||
|
|
||||||
class ThangTypeSearchCollection extends CocoCollection
|
class ItemThangTypeSearchCollection extends CocoCollection
|
||||||
url: '/db/thang.type?project=original,name,version,slug,kind,components'
|
url: '/db/thang.type?view=items&project=original,name,version,slug,kind,components'
|
||||||
model: ThangType
|
model: ThangType
|
||||||
|
|
||||||
module.exports = class ThangComponentsEditView extends CocoView
|
module.exports = class ThangComponentsEditView extends CocoView
|
||||||
|
@ -32,7 +32,7 @@ module.exports = class ThangComponentsEditView extends CocoView
|
||||||
@level = options.level
|
@level = options.level
|
||||||
@loadComponents(@components)
|
@loadComponents(@components)
|
||||||
# Need to grab the ThangTypes so that we can autocomplete items in inventory based on them.
|
# 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) ->
|
loadComponents: (components) ->
|
||||||
for componentRef in components
|
for componentRef in components
|
||||||
|
|
|
@ -47,7 +47,7 @@ module.exports = class LevelEditView extends RootView
|
||||||
super options
|
super options
|
||||||
@supermodel.shouldSaveBackups = (model) ->
|
@supermodel.shouldSaveBackups = (model) ->
|
||||||
model.constructor.className in ['Level', 'LevelComponent', 'LevelSystem', 'ThangType']
|
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
|
@level = @levelLoader.level
|
||||||
@files = new DocumentFiles(@levelLoader.level)
|
@files = new DocumentFiles(@levelLoader.level)
|
||||||
@supermodel.loadCollection(@files, 'file_names')
|
@supermodel.loadCollection(@files, 'file_names')
|
||||||
|
|
|
@ -335,8 +335,8 @@ module.exports = class ThangsTabView extends CocoView
|
||||||
|
|
||||||
adjustThangPos: (sprite, thang, pos) ->
|
adjustThangPos: (sprite, thang, pos) ->
|
||||||
snap = sprite?.data?.snap or sprite?.thangType?.get('snap') or {x: 0.01, y: 0.01} # Centimeter resolution by default
|
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.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 / 2) / snap.y) * snap.y + thang.height / 2
|
pos.y = Math.round((pos.y - (thang.height ? 1) / 2) / snap.y) * snap.y + (thang.height ? 1) / 2
|
||||||
pos.z = thang.depth / 2
|
pos.z = thang.depth / 2
|
||||||
thang.pos = pos
|
thang.pos = pos
|
||||||
@surface.spriteBoss.update true # Make sure Obstacle layer resets cache
|
@surface.spriteBoss.update true # Make sure Obstacle layer resets cache
|
||||||
|
|
|
@ -9,7 +9,7 @@ module.exports = class InventoryView extends CocoView
|
||||||
id: 'inventory-view'
|
id: 'inventory-view'
|
||||||
className: 'tab-pane'
|
className: 'tab-pane'
|
||||||
template: template
|
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:
|
events:
|
||||||
'click .item-slot': 'onItemSlotClick'
|
'click .item-slot': 'onItemSlotClick'
|
||||||
|
@ -23,8 +23,8 @@ module.exports = class InventoryView extends CocoView
|
||||||
|
|
||||||
initialize: (options) ->
|
initialize: (options) ->
|
||||||
super(arguments...)
|
super(arguments...)
|
||||||
@items = new CocoCollection([], { model: ThangType })
|
@items = new CocoCollection([], {model: ThangType})
|
||||||
@equipment = options.equipment or {}
|
@equipment = options.equipment or @options.session?.get('heroConfig')?.inventory or {}
|
||||||
@items.url = '/db/thang.type?view=items&project=name,description,components,original'
|
@items.url = '/db/thang.type?view=items&project=name,description,components,original'
|
||||||
@supermodel.loadCollection(@items, 'items')
|
@supermodel.loadCollection(@items, 'items')
|
||||||
|
|
||||||
|
@ -58,7 +58,7 @@ module.exports = class InventoryView extends CocoView
|
||||||
for slottedItemStub in @$el.find('.replace-me')
|
for slottedItemStub in @$el.find('.replace-me')
|
||||||
itemID = $(slottedItemStub).data('item-id')
|
itemID = $(slottedItemStub).data('item-id')
|
||||||
item = itemMap[itemID]
|
item = itemMap[itemID]
|
||||||
itemView = new ItemView({item:item, includes:{name:true}})
|
itemView = new ItemView({item: item, includes: {name: true}})
|
||||||
itemView.render()
|
itemView.render()
|
||||||
$(slottedItemStub).replaceWith(itemView.$el)
|
$(slottedItemStub).replaceWith(itemView.$el)
|
||||||
@registerSubView(itemView)
|
@registerSubView(itemView)
|
||||||
|
@ -66,7 +66,7 @@ module.exports = class InventoryView extends CocoView
|
||||||
for availableItemEl in @$el.find('#available-equipment .list-group-item')
|
for availableItemEl in @$el.find('#available-equipment .list-group-item')
|
||||||
itemID = $(availableItemEl).data('item-id')
|
itemID = $(availableItemEl).data('item-id')
|
||||||
item = itemMap[itemID]
|
item = itemMap[itemID]
|
||||||
itemView = new ItemView({item:item, includes:{name:true}})
|
itemView = new ItemView({item: item, includes: {name: true}})
|
||||||
itemView.render()
|
itemView.render()
|
||||||
$(availableItemEl).append(itemView.$el)
|
$(availableItemEl).append(itemView.$el)
|
||||||
@registerSubView(itemView)
|
@registerSubView(itemView)
|
||||||
|
@ -236,3 +236,12 @@ module.exports = class InventoryView extends CocoView
|
||||||
config[slotName] = item.get('original')
|
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()
|
||||||
|
|
|
@ -9,7 +9,7 @@ import sys
|
||||||
current_directory = os.path.dirname(os.path.realpath(sys.argv[0]))
|
current_directory = os.path.dirname(os.path.realpath(sys.argv[0]))
|
||||||
coco_path = os.getenv("COCO_DIR",os.path.join(current_directory,os.pardir))
|
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"
|
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:
|
while True:
|
||||||
print("Starting brunch. After the first compile, it'll keep running and watch for changes.")
|
print("Starting brunch. After the first compile, it'll keep running and watch for changes.")
|
||||||
call(brunch_path + " w",shell=True,cwd=coco_path)
|
call(brunch_path + " w",shell=True,cwd=coco_path)
|
||||||
|
|
|
@ -45,7 +45,7 @@ LevelSessionSchema.pre 'save', (next) ->
|
||||||
LevelSessionSchema.statics.privateProperties = ['code', 'submittedCode', 'unsubscribed']
|
LevelSessionSchema.statics.privateProperties = ['code', 'submittedCode', 'unsubscribed']
|
||||||
LevelSessionSchema.statics.editableProperties = ['multiplayer', 'players', 'code', 'codeLanguage', 'completed', 'state',
|
LevelSessionSchema.statics.editableProperties = ['multiplayer', 'players', 'code', 'codeLanguage', 'completed', 'state',
|
||||||
'levelName', 'creatorName', 'levelID', 'screenshot',
|
'levelName', 'creatorName', 'levelID', 'screenshot',
|
||||||
'chat', 'teamSpells', 'submitted', 'submittedCodeLanguage', 'unsubscribed', 'playtime']
|
'chat', 'teamSpells', 'submitted', 'submittedCodeLanguage', 'unsubscribed', 'playtime', 'heroConfig']
|
||||||
LevelSessionSchema.statics.jsonSchema = jsonschema
|
LevelSessionSchema.statics.jsonSchema = jsonschema
|
||||||
|
|
||||||
LevelSessionSchema.index {user: 1, changed: -1}, {sparse: true, name: 'last played index'}
|
LevelSessionSchema.index {user: 1, changed: -1}, {sparse: true, name: 'last played index'}
|
||||||
|
|
Loading…
Reference in a new issue