Bunch of fixes to get the level editor working again.

This commit is contained in:
Scott Erickson 2014-08-25 20:34:46 -07:00
parent d392994a83
commit 1406a970ea
13 changed files with 102 additions and 37 deletions

View file

@ -209,7 +209,7 @@ module.exports = class World
@thangMap = {} @thangMap = {}
# Load new Thangs # Load new Thangs
toAdd = (@loadThangFromLevel thangConfig, level.levelComponents, level.thangTypes for thangConfig in level.thangs) toAdd = (@loadThangFromLevel thangConfig, level.levelComponents, level.thangTypes for thangConfig in level.thangs ? [])
@extraneousThangs = consolidateThangs toAdd if willSimulate # Combine walls, for example; serialize the leftovers later @extraneousThangs = consolidateThangs toAdd if willSimulate # Combine walls, for example; serialize the leftovers later
@addThang thang for thang in toAdd @addThang thang for thang in toAdd
null null

View file

@ -95,7 +95,7 @@ module.exports = class Level extends CocoModel
#console.log 'sorted systems adding', systemModel.name #console.log 'sorted systems adding', systemModel.name
sorted.push {model: systemModel, config: _.cloneDeep system.config} sorted.push {model: systemModel, config: _.cloneDeep system.config}
originalsSeen[system.original] = true originalsSeen[system.original] = true
visit system for system in levelSystems visit system for system in levelSystems ? []
sorted sorted
sortThangComponents: (thangs, levelComponents, parentType) -> sortThangComponents: (thangs, levelComponents, parentType) ->
@ -106,7 +106,7 @@ module.exports = class Level extends CocoModel
# Decision? Just special case the sort logic in here until we have more examples than these two and decide how best to handle most of the cases then, since we don't really know the whole of the problem yet. # Decision? Just special case the sort logic in here until we have more examples than these two and decide how best to handle most of the cases then, since we don't really know the whole of the problem yet.
# TODO: anything that depends on Programmable will break right now. # TODO: anything that depends on Programmable will break right now.
for thang in thangs for thang in thangs ? []
sorted = [] sorted = []
visit = (c) -> visit = (c) ->
return if c in sorted return if c in sorted
@ -137,7 +137,7 @@ module.exports = class Level extends CocoModel
# TODO DEFAULTS # TODO DEFAULTS
fillInDefaultComponentConfiguration: (thangs, levelComponents) -> fillInDefaultComponentConfiguration: (thangs, levelComponents) ->
for thang in thangs for thang in thangs ? []
for component in thang.components or [] for component in thang.components or []
continue unless lc = _.find levelComponents, {original: component.original} continue unless lc = _.find levelComponents, {original: component.original}
component.config ?= {} component.config ?= {}

View file

@ -207,7 +207,7 @@ GeneralArticleSchema = c.object {
LevelSchema = c.object { LevelSchema = c.object {
title: 'Level' title: 'Level'
description: 'A spectacular level which will delight and educate its stalwart players with the sorcery of coding.' description: 'A spectacular level which will delight and educate its stalwart players with the sorcery of coding.'
required: ['name', 'description', 'scripts', 'thangs', 'documentation'] required: ['name']
'default': 'default':
name: 'Ineffable Wizardry' name: 'Ineffable Wizardry'
description: 'This level is indescribably flarmy.' description: 'This level is indescribably flarmy.'

View file

@ -103,7 +103,7 @@ LevelComponentSchema = c.object {
title: 'Component' title: 'Component'
description: 'A Component which can affect Thang behavior.' description: 'A Component which can affect Thang behavior.'
required: ['system', 'name', 'description', 'code', 'dependencies', 'propertyDocumentation', 'codeLanguage'] required: ['system', 'name', 'description', 'code', 'dependencies', 'propertyDocumentation', 'codeLanguage']
'default': default:
system: 'ai' system: 'ai'
name: 'AttacksSelf' name: 'AttacksSelf'
description: 'This Component makes the Thang attack itself.' description: 'This Component makes the Thang attack itself.'

View file

@ -4,3 +4,6 @@
.treema-root .treema-root
border: 0 border: 0
padding: 0 5px padding: 0 5px
.is-default-component
background-color: lightgray

View file

@ -29,6 +29,9 @@
bottom: 0 bottom: 0
overflow: scroll overflow: scroll
.treema-key, .treema-description
display: none
.dependent .dependent
background-color: rgba(128, 64, 255, 0.10) background-color: rgba(128, 64, 255, 0.10)

View file

@ -1,5 +1,5 @@
.panel.panel-default .panel.panel-default
.panel-heading .panel-heading(class=isDefaultComponent ? "is-default-component" : "")
em #{component.system}. em #{component.system}.
strong.panel-title.spr= component.name strong.panel-title.spr= component.name
span#description.text-muted= component.description span#description.text-muted= component.description

View file

@ -416,7 +416,7 @@ class LevelComponentReferenceNode extends LatestVersionReferenceNode
# supermodels. # supermodels.
buildSearchURL: (term) -> "#{@url}?term=#{term}&project=name,system,original,version,dependencies,configSchema,description" buildSearchURL: (term) -> "#{@url}?term=#{term}&project=name,system,original,version,dependencies,configSchema,description"
modelToString: (model) -> model.get('system') + '.' + model.get('name') modelToString: (model) -> model.get('system') + '.' + model.get('name')
canEdit: -> not @data.original # only allow editing if the row's data hasn't been set yet canEdit: -> not @getData().original # only allow editing if the row's data hasn't been set yet
LatestVersionReferenceNode.prototype.search = _.debounce(LatestVersionReferenceNode.prototype.search, 200) LatestVersionReferenceNode.prototype.search = _.debounce(LatestVersionReferenceNode.prototype.search, 200)

View file

@ -17,6 +17,7 @@ module.exports = class ThangComponentConfigView extends CocoView
super options super options
@component = options.component @component = options.component
@config = options.config or {} @config = options.config or {}
@additionalDefaults = options.additionalDefaults
@world = options.world @world = options.world
@level = options.level @level = options.level
@callback = options.callback @callback = options.callback
@ -25,12 +26,18 @@ module.exports = class ThangComponentConfigView extends CocoView
context = super(context) context = super(context)
context.component = @component.attributes context.component = @component.attributes
context.configProperties = [] context.configProperties = []
context.isDefaultComponent = @isDefaultComponent
context context
afterRender: -> afterRender: ->
super() super()
@buildTreema() @buildTreema()
setIsDefaultComponent: (isDefaultComponent) ->
changed = @isDefaultComponent isnt isDefaultComponent
@isDefaultComponent = isDefaultComponent
@render() if changed
buildTreema: -> buildTreema: ->
thangs = if @level? then @level.get('thangs') else [] thangs = if @level? then @level.get('thangs') else []
thangIDs = _.filter(_.pluck(thangs, 'id')) thangIDs = _.filter(_.pluck(thangs, 'id'))
@ -39,6 +46,9 @@ module.exports = class ThangComponentConfigView extends CocoView
superteams = _.union(teams, superteams) superteams = _.union(teams, superteams)
config = $.extend true, {}, @config config = $.extend true, {}, @config
schema = $.extend true, {}, @component.get('configSchema') schema = $.extend true, {}, @component.get('configSchema')
schema.default ?= {}
_.merge schema.default, @additionalDefaults if @additionalDefaults
if @level?.get('type') is 'hero' if @level?.get('type') is 'hero'
schema.required = [] schema.required = []
treemaOptions = treemaOptions =

View file

@ -28,13 +28,18 @@ module.exports = class ThangComponentsEditView extends CocoView
@originalsLoaded = {} @originalsLoaded = {}
@components = options.components or [] @components = options.components or []
@components = $.extend true, [], @components # just to be sure @components = $.extend true, [], @components # just to be sure
@setThangType options.thangType
@lastComponentLength = @components.length @lastComponentLength = @components.length
@world = options.world @world = options.world
@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.
@itemThangTypes = @supermodel.loadCollection(new ItemThangTypeSearchCollection(), 'thangs').model @itemThangTypes = @supermodel.loadCollection(new ItemThangTypeSearchCollection(), 'thangs').model
setThangType: (@thangType) ->
return unless componentRefs = @thangType?.get('components')
@loadComponents(componentRefs)
loadComponents: (components) -> loadComponents: (components) ->
for componentRef in components for componentRef in components
# just to handle if ever somehow the same component is loaded twice, through bad data and alike # just to handle if ever somehow the same component is loaded twice, through bad data and alike
@ -59,26 +64,20 @@ module.exports = class ThangComponentsEditView extends CocoView
@addThangComponentConfigViews() @addThangComponentConfigViews()
buildComponentsTreema: -> buildComponentsTreema: ->
components = _.zipObject((c.original for c in @components), @components)
defaultValue = undefined
if thangTypeComponents = @thangType?.get('components', true)
defaultValue = _.zipObject((c.original for c in thangTypeComponents), thangTypeComponents)
treemaOptions = treemaOptions =
supermodel: @supermodel supermodel: @supermodel
schema: Level.schema.properties.thangs.items.properties.components schema: {
data: $.extend true, [], @components type: 'object'
default: defaultValue
additionalProperties: Level.schema.properties.thangs.items.properties.components.items
},
data: $.extend true, {}, components
callbacks: {select: @onSelectComponent, change: @onComponentsTreemaChanged} callbacks: {select: @onSelectComponent, change: @onComponentsTreemaChanged}
noSortable: true
nodeClasses:
'thang-components-array': ThangComponentsArrayNode
'point2d': nodes.WorldPointNode
'viewport': nodes.WorldViewportNode
'bounds': nodes.WorldBoundsNode
'radians': nodes.RadiansNode
'team': nodes.TeamNode
'superteam': nodes.SuperteamNode
'meters': nodes.MetersNode
'kilograms': nodes.KilogramsNode
'seconds': nodes.SecondsNode
'speed': nodes.SpeedNode
'acceleration': nodes.AccelerationNode
'item-thang-type': nodes.ItemThangTypeNode
@componentsTreema = @$el.find('#thang-components-column .treema').treema treemaOptions @componentsTreema = @$el.find('#thang-components-column .treema').treema treemaOptions
@componentsTreema.build() @componentsTreema.build()
@ -91,7 +90,7 @@ module.exports = class ThangComponentsEditView extends CocoView
componentMap[component.original] = component componentMap[component.original] = component
newComponentsList = [] newComponentsList = []
for component in @componentsTreema.data for component in _.values(@componentsTreema.data)
newComponentsList.push(componentMap[component.original] or component) newComponentsList.push(componentMap[component.original] or component)
@components = newComponentsList @components = newComponentsList
@ -204,12 +203,26 @@ module.exports = class ThangComponentsEditView extends CocoView
# Put back config views into the DOM based on the component list ordering, # Put back config views into the DOM based on the component list ordering,
# adding and registering new ones as needed. # adding and registering new ones as needed.
configsEl = @$el.find('#thang-component-configs') configsEl = @$el.find('#thang-component-configs')
for componentRef in @componentsTreema.data
componentRefs = _.merge {}, @componentsTreema.data
if thangTypeComponents = @thangType?.get('components')
thangComponentRefs = _.zipObject((c.original for c in thangTypeComponents), thangTypeComponents)
for thangTypeComponent in thangTypeComponents
if componentRef = componentRefs[thangTypeComponent.original]
componentRef.additionalDefaults = thangTypeComponent.config
else
modifiedRef = _.merge {}, thangTypeComponent
modifiedRef.additionalDefaults = modifiedRef.config
delete modifiedRef.additionalDefaults
componentRefs[thangTypeComponent.original] = modifiedRef
for componentRef in _.values(componentRefs)
subview = componentConfigViews[componentRef.original] subview = componentConfigViews[componentRef.original]
if not subview if not subview
subview = @makeThangComponentConfigView(componentRef) subview = @makeThangComponentConfigView(componentRef)
continue unless subview continue unless subview
@registerSubView(subview) @registerSubView(subview)
subview.setIsDefaultComponent(not @componentsTreema.data[componentRef.original])
configsEl.append(subview.$el) configsEl.append(subview.$el)
makeThangComponentConfigView: (thangComponent) -> makeThangComponentConfigView: (thangComponent) ->
@ -222,15 +235,29 @@ module.exports = class ThangComponentsEditView extends CocoView
world: @world world: @world
config: config config: config
component: component component: component
additionalDefaults: thangComponent.additionalDefaults
}) })
configView.render() configView.render()
@listenTo configView, 'changed', @onConfigChanged @listenTo configView, 'changed', @onConfigChanged
configView configView
onConfigChanged: (e) -> onConfigChanged: (e) ->
foundComponent = false
for thangComponent in @components for thangComponent in @components
if thangComponent.original is e.component.get('original') if thangComponent.original is e.component.get('original')
thangComponent.config = e.config thangComponent.config = e.config
foundComponent = true
break
if not foundComponent
@components.push({
original: e.component.get('original')
majorVersion: e.component.get('version').major
config: e.config
})
@onComponentsChanged()
@updateComponentsList()
@reportChanges() @reportChanges()
onSelectComponent: (e, nodes) => onSelectComponent: (e, nodes) =>
@ -239,28 +266,28 @@ module.exports = class ThangComponentsEditView extends CocoView
# find dependent components # find dependent components
dependents = {} dependents = {}
dependents[nodes[0].data.original] = true dependents[nodes[0].getData().original] = true
componentsToCheck = [nodes[0].data.original] componentsToCheck = [nodes[0].getData().original]
while componentsToCheck.length while componentsToCheck.length
componentOriginal = componentsToCheck.pop() componentOriginal = componentsToCheck.pop()
for otherComponentRef in @components for otherComponentRef in @components
continue if otherComponentRef.original is componentOriginal continue if otherComponentRef.original is componentOriginal
continue if dependents[otherComponentRef.original] continue if dependents[otherComponentRef.original]
otherComponent = @supermodel.getModelByOriginal(LevelComponent, otherComponentRef.original) otherComponent = @supermodel.getModelByOriginal(LevelComponent, otherComponentRef.original)
for dependency in otherComponent.get('dependencies') for dependency in otherComponent.get('dependencies', true)
if dependents[dependency.original] if dependents[dependency.original]
dependents[otherComponentRef.original] = true dependents[otherComponentRef.original] = true
componentsToCheck.push otherComponentRef.original componentsToCheck.push otherComponentRef.original
# highlight them # highlight them
for child in _.values(@componentsTreema.childrenTreemas) for child in _.values(@componentsTreema.childrenTreemas)
if dependents[child.data.original] if dependents[child.getData().original]
child.$el.addClass('dependent') child.$el.addClass('dependent')
# scroll to the config # scroll to the config
for subview in _.values(@subviews) for subview in _.values(@subviews)
continue unless subview instanceof ThangComponentConfigView continue unless subview instanceof ThangComponentConfigView
if subview.component.get('original') is nodes[0].data.original if subview.component.get('original') is nodes[0].getData().original
subview.$el[0].scrollIntoView() subview.$el[0].scrollIntoView()
break break

View file

@ -47,7 +47,7 @@ module.exports = class SettingsTabView extends CocoView
@settingsTreema.open() @settingsTreema.open()
getThangIDs: -> getThangIDs: ->
(t.id for t in @level.get('thangs') when t.id isnt 'Interface') (t.id for t in @level.get('thangs') ? [] when t.id isnt 'Interface')
onSettingsChanged: (e) => onSettingsChanged: (e) =>
$('.level-title').text @settingsTreema.data.name $('.level-title').text @settingsTreema.data.name

View file

@ -7,7 +7,7 @@ exports.config =
'public': 'public' 'public': 'public'
conventions: conventions:
ignored: (path) -> startsWith(sysPath.basename(path), '_') ignored: (path) -> startsWith(sysPath.basename(path), '_')
sourceMaps: false sourceMaps: true
files: files:
javascripts: javascripts:
defaultExtension: 'coffee' defaultExtension: 'coffee'

View file

@ -1,4 +1,5 @@
ThangComponentEditView = require('views/editor/component/ThangComponentsEditView') ThangComponentEditView = require('views/editor/component/ThangComponentsEditView')
ThangType = require 'models/ThangType'
responses = responses =
'/db/level.component/A/version/0': { '/db/level.component/A/version/0': {
@ -6,8 +7,13 @@ responses =
original: 'A' original: 'A'
version: { major: 0, minor: 0 } version: { major: 0, minor: 0 }
name: 'A' name: 'A'
configSchema: { type: 'object', properties: { propA: { type: 'number' }, propB: { type: 'string' }} } configSchema: {
type: 'object'
properties: {
propA: { type: 'number' }
propB: { type: 'string' }
}
}
} }
'/db/level.component/B/version/0': { '/db/level.component/B/version/0': {
system: 'System' system: 'System'
@ -22,6 +28,16 @@ responses =
version: { major: 0, minor: 0 } version: { major: 0, minor: 0 }
name: 'C (depends on B)' name: 'C (depends on B)'
dependencies: [{original:'B', majorVersion: 0}] dependencies: [{original:'B', majorVersion: 0}]
configSchema: {
type: 'object'
default: { propC: 'Default property from component config' }
}
}
'/db/level.component/D/version/0': {
system: 'System'
original: 'D'
version: { major: 0, minor: 0 }
name: 'D (comes from ThangType components)'
} }
'/db/thang.type': [] '/db/thang.type': []
@ -32,6 +48,12 @@ module.exports = ->
{ original: 'B', majorVersion: 0 } { original: 'B', majorVersion: 0 }
{ original: 'C', majorVersion: 0 } { original: 'C', majorVersion: 0 }
] ]
thangType: new ThangType({
components: [
{ original: 'C', majorVersion: 0, config: {propD: 'Default property from thang type component.'} }
{ original: 'D', majorVersion: 0 }
]
})
}) })
view.render() view.render()