codecombat/app/views/editor/level/treema_nodes.coffee
Scott Erickson be78f4049c Fix adding inventory items to hero in level editor
Was only happening when HeroPlaceholder had HasPet component.
Presumably because the collection was loaded by HasPet, but the thang node
for the inventory node needed to populate its autocomplete array.
2016-06-06 15:35:10 -07:00

296 lines
11 KiB
CoffeeScript

WorldSelectModal = require './modals/WorldSelectModal'
ThangType = require '/models/ThangType'
LevelComponent = require 'models/LevelComponent'
CocoCollection = require 'collections/CocoCollection'
require 'vendor/treema'
makeButton = -> $('<a class="btn btn-primary btn-xs treema-map-button"><span class="glyphicon glyphicon-screenshot"></span></a>')
shorten = (f) -> parseFloat(f.toFixed(1))
WIDTH = 924
module.exports.WorldPointNode = class WorldPointNode extends TreemaNode.nodeMap.point2d
constructor: (args...) ->
super(args...)
console.error 'Point Treema node needs a World included in the settings.' unless @settings.world?
console.error 'Point Treema node needs a RootView included in the settings.' unless @settings.view?
buildValueForDisplay: (valEl, data) ->
super(valEl, data)
valEl.find('.treema-shortened').prepend(makeButton())
buildValueForEditing: (valEl, data) ->
super(valEl, data)
valEl.find('.treema-shortened').prepend(makeButton())
onClick: (e) ->
btn = $(e.target).closest('.treema-map-button')
if btn.length then @openMap() else super(arguments...)
openMap: ->
modal = new WorldSelectModal(world: @settings.world, dataType: 'point', default: @getData(), supermodel: @settings.supermodel)
modal.callback = @callback
@settings.view.openModalView modal
callback: (e) =>
return unless e?.point?
@data.x = shorten(e.point.x)
@data.y = shorten(e.point.y)
@refreshDisplay()
class WorldRegionNode extends TreemaNode.nodeMap.object
# this class is not yet used, later will be used to configure the Physical component
constructor: (args...) ->
super(args...)
console.error 'Region Treema node needs a World included in the settings.' unless @settings.world?
console.error 'Region Treema node needs a RootView included in the settings.' unless @settings.view?
buildValueForDisplay: (valEl, data) ->
super(valEl, data)
valEl.find('.treema-shortened').prepend(makeButton())
buildValueForEditing: (valEl, data) ->
super(valEl, data)
valEl.find('.treema-shortened').prepend(makeButton())
onClick: (e) ->
btn = $(e.target).closest('.treema-map-button')
if btn.length then @openMap() else super(arguments...)
openMap: ->
modal = new WorldSelectModal(world: @settings.world, dataType: 'region', default: @createWorldBounds(), supermodel: @settings.supermodel)
modal.callback = @callback
@settings.view.openModalView modal
callback: (e) =>
x = Math.min e.points[0].x, e.points[1].x
y = Math.min e.points[0].y, e.points[1].y
@data.pos = {x: x, y: y, z: 0}
@data.width = Math.abs e.points[0].x - e.points[1].x
@data.height = Math.min e.points[0].y - e.points[1].y
@refreshDisplay()
createWorldBounds: ->
# not yet written
module.exports.WorldViewportNode = class WorldViewportNode extends TreemaNode.nodeMap.object
# selecting ratio'd dimensions in the world, ie the camera in level scripts
constructor: (args...) ->
super(args...)
console.error 'Viewport Treema node needs a World included in the settings.' unless @settings.world?
console.error 'Viewport Treema node needs a RootView included in the settings.' unless @settings.view?
buildValueForDisplay: (valEl, data) ->
super(valEl, data)
valEl.find('.treema-shortened').prepend(makeButton())
buildValueForEditing: (valEl, data) ->
super(valEl, data)
valEl.find('.treema-shortened').prepend(makeButton())
onClick: (e) ->
btn = $(e.target).closest('.treema-map-button')
if btn.length then @openMap() else super(arguments...)
openMap: ->
# can't really get the bounds from this data, so will have to hack this solution
options = world: @settings.world, dataType: 'ratio-region'
data = @getData()
options.defaultFromZoom = data if data?.target?.x?
options.supermodel = @settings.supermodel
modal = new WorldSelectModal(options)
modal.callback = @callback
@settings.view.openModalView modal
callback: (e) =>
return unless e
target = {
x: shorten((e.points[0].x + e.points[1].x) / 2)
y: shorten((e.points[0].y + e.points[1].y) / 2)
}
@set('target', target)
bounds = e.camera.normalizeBounds(e.points)
@set('zoom', shorten(WIDTH / bounds.width))
@refreshDisplay()
module.exports.WorldBoundsNode = class WorldBoundsNode extends TreemaNode.nodeMap.array
# selecting camera boundaries for a world
dataType: 'region'
constructor: (args...) ->
super(args...)
console.error 'Bounds Treema node needs a World included in the settings.' unless @settings.world?
console.error 'Bounds Treema node needs a RootView included in the settings.' unless @settings.view?
buildValueForDisplay: (valEl, data) ->
super(valEl, data)
valEl.find('.treema-shortened').prepend(makeButton())
buildValueForEditing: (valEl, data) ->
super(valEl, data)
valEl.find('.treema-shortened').prepend(makeButton())
onClick: (e) ->
btn = $(e.target).closest('.treema-map-button')
if btn.length then @openMap() else super(arguments...)
openMap: ->
bounds = @getData() or [{x: 0, y: 0}, {x: 100, y: 80}]
modal = new WorldSelectModal(world: @settings.world, dataType: 'region', default: bounds, supermodel: @settings.supermodel)
modal.callback = @callback
@settings.view.openModalView modal
callback: (e) =>
return unless e
@set '/0', {x: shorten(e.points[0].x), y: shorten(e.points[0].y)}
@set '/1', {x: shorten(e.points[1].x), y: shorten(e.points[1].y)}
module.exports.ThangNode = class ThangNode extends TreemaNode.nodeMap.string
buildValueForDisplay: (valEl, data) ->
super(valEl, data)
valEl.find('input').autocomplete(source: @settings.thangIDs, minLength: 0, delay: 0, autoFocus: true)
valEl
module.exports.TeamNode = class TeamNode extends TreemaNode.nodeMap.string
buildValueForDisplay: (valEl, data) ->
super(valEl, data)
valEl.find('input').autocomplete(source: @settings.teams, minLength: 0, delay: 0, autoFocus: true)
valEl
module.exports.SuperteamNode = class SuperteamNode extends TreemaNode.nodeMap.string
buildValueForEditing: (valEl, data) ->
super(valEl, data)
valEl.find('input').autocomplete(source: @settings.superteams, minLength: 0, delay: 0, autoFocus: true)
valEl
module.exports.RadiansNode = class RadiansNode extends TreemaNode.nodeMap.number
buildValueForDisplay: (valEl, data) ->
super(valEl, data)
deg = data / Math.PI * 180
valEl.text valEl.text() + "rad (#{deg.toFixed(0)}˚)"
module.exports.MetersNode = class MetersNode extends TreemaNode.nodeMap.number
buildValueForDisplay: (valEl, data) ->
super(valEl, data)
valEl.text valEl.text() + 'm'
module.exports.KilogramsNode = class KilogramsNode extends TreemaNode.nodeMap.number
buildValueForDisplay: (valEl, data) ->
super(valEl, data)
valEl.text valEl.text() + 'kg'
module.exports.SecondsNode = class SecondsNode extends TreemaNode.nodeMap.number
buildValueForDisplay: (valEl, data) ->
super(valEl, data)
valEl.text valEl.text() + 's'
module.exports.MillisecondsNode = class MillisecondsNode extends TreemaNode.nodeMap.number
buildValueForDisplay: (valEl, data) ->
super(valEl, data)
valEl.text valEl.text() + 'ms'
module.exports.SpeedNode = class SpeedNode extends TreemaNode.nodeMap.number
buildValueForDisplay: (valEl, data) ->
super(valEl, data)
valEl.text valEl.text() + 'm/s'
module.exports.AccelerationNode = class AccelerationNode extends TreemaNode.nodeMap.number
buildValueForDisplay: (valEl, data) ->
super(valEl, data)
valEl.text valEl.text() + 'm/s^2'
module.exports.ThangTypeNode = class ThangTypeNode extends TreemaNode.nodeMap.string
valueClass: 'treema-thang-type'
@thangTypes: null
@thangTypesCollection: null
constructor: (args...) ->
super args...
data = @getData()
@thangType = _.find @settings.supermodel.getModels(ThangType), (m) => m.get('original') is data if data
buildValueForDisplay: (valEl) ->
@buildValueForDisplaySimply(valEl, @thangType?.get('name') or 'None')
buildValueForEditing: (valEl, data) ->
super(valEl, data)
thangTypeNames = (m.get('name') for m in @settings.supermodel.getModels ThangType)
input = valEl.find('input').autocomplete(source: thangTypeNames, minLength: 0, delay: 0, autoFocus: true)
input.val(@thangType?.get('name') or 'None')
valEl
saveChanges: ->
thangTypeName = @$el.find('input').val()
@thangType = _.find @settings.supermodel.getModels(ThangType), (m) -> m.get('name') is thangTypeName
if @thangType
@data = @thangType.get('original')
else
@data = null
module.exports.ThangTypeNode = ThangTypeNode = class ThangTypeNode extends TreemaNode.nodeMap.string
valueClass: 'treema-thang-type'
@thangTypesCollection: null # Lives in ThangTypeNode parent class
@thangTypes: null # Lives in ThangTypeNode or subclasses
constructor: ->
super(arguments...)
@getThangTypes()
unless ThangTypeNode.thangTypesCollection.loaded
ThangTypeNode.thangTypesCollection.once('sync', @refreshDisplay, @)
buildValueForDisplay: (valEl, data) ->
@buildValueForDisplaySimply(valEl, @getCurrentThangType() or '')
valEl
buildValueForEditing: (valEl, data) ->
super(valEl, data)
input = valEl.find 'input'
if @constructor.thangTypes
source = (thangType.name for thangType in @constructor.thangTypes when @filterThangType thangType)
input.autocomplete(source: source, minLength: 0, delay: 0, autoFocus: true)
input.val(@getCurrentThangType() or '')
valEl
filterThangType: (thangType) -> true
getCurrentThangType: ->
return null unless @constructor.thangTypes
return null unless original = @getData()
thangType = _.find @constructor.thangTypes, { original: original }
thangType?.name or '...'
getThangTypes: ->
if ThangTypeNode.thangTypesCollection
if not @constructor.thangTypes
@processThangTypes(ThangTypeNode.thangTypesCollection)
return
ThangTypeNode.thangTypesCollection = new CocoCollection([], {
url: '/db/thang.type'
project:['name', 'components', 'original']
model: ThangType
})
res = ThangTypeNode.thangTypesCollection.fetch()
ThangTypeNode.thangTypesCollection.once 'sync', => @processThangTypes(ThangTypeNode.thangTypesCollection)
processThangTypes: (thangTypeCollection) ->
@constructor.thangTypes = []
@processThangType thangType for thangType in thangTypeCollection.models
processThangType: (thangType) ->
@constructor.thangTypes.push name: thangType.get('name'), original: thangType.get('original')
saveChanges: ->
thangTypeName = @$el.find('input').val()
thangType = _.find(@constructor.thangTypes, {name: thangTypeName})
return @remove() unless thangType
@data = thangType.original
module.exports.ItemThangTypeNode = ItemThangTypeNode = class ItemThangTypeNode extends ThangTypeNode
valueClass: 'treema-item-thang-type'
filterThangType: (thangType) ->
@keyForParent in thangType.slots
processThangType: (thangType) ->
return unless itemComponent = _.find thangType.get('components'), {original: LevelComponent.ItemID}
@constructor.thangTypes.push name: thangType.get('name'), original: thangType.get('original'), slots: itemComponent.config?.slots ? ['right-hand']