mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2024-11-27 09:35:39 -05:00
Bunch of fixes to get the level editor working again.
This commit is contained in:
parent
d392994a83
commit
1406a970ea
13 changed files with 102 additions and 37 deletions
|
@ -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
|
||||||
|
|
|
@ -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 ?= {}
|
||||||
|
|
|
@ -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.'
|
||||||
|
|
|
@ -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.'
|
||||||
|
|
|
@ -4,3 +4,6 @@
|
||||||
.treema-root
|
.treema-root
|
||||||
border: 0
|
border: 0
|
||||||
padding: 0 5px
|
padding: 0 5px
|
||||||
|
|
||||||
|
.is-default-component
|
||||||
|
background-color: lightgray
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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 =
|
||||||
|
|
|
@ -28,6 +28,7 @@ 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
|
||||||
|
@ -35,6 +36,10 @@ module.exports = class ThangComponentsEditView extends CocoView
|
||||||
# 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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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'
|
||||||
|
|
|
@ -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()
|
||||||
|
|
Loading…
Reference in a new issue