SpecificArticleSchema.properties.body = {type: 'string',title: 'Content',description: 'The body content of the article, in Markdown.',format: 'markdown'}
SpecificArticleSchema.properties.i18n = {type: 'object',format: 'i18n',props: ['name','body'],description: 'Help translate this article'}
eventPrereqValueTypes = ['boolean','integer','number','null','string']# not 'object' or 'array'
EventPrereqSchema = c.object{title: 'Event Prerequisite',format: 'event-prereq',description: 'Script requires that the value of some property on the event triggering it to meet some prerequisite.','default':{eventProps: []},required: ['eventProps']},
eventProps: c.array{'default':['thang'],format: 'event-value-chain',maxItems: 10,title: 'Event Property',description: 'A chain of keys in the event, like "thang.pos.x" to access event.thang.pos.x.'},c.shortString(title: 'Property',description: 'A key in the event property key chain.')
equalTo: c.object{type: eventPrereqValueTypes,title: '==',description: 'Script requires the event\'s property chain value to be equal to this value.'}
notEqualTo: c.object{type: eventPrereqValueTypes,title: '!=',description: 'Script requires the event\'s property chain value to *not* be equal to this value.'}
greaterThan: {type: 'number',title: '>',description: 'Script requires the event\'s property chain value to be greater than this value.'}
greaterThanOrEqualTo: {type: 'number',title: '>=',description: 'Script requires the event\'s property chain value to be greater or equal to this value.'}
lessThan: {type: 'number',title: '<',description: 'Script requires the event\'s property chain value to be less than this value.'}
lessThanOrEqualTo: {type: 'number',title: '<=',description: 'Script requires the event\'s property chain value to be less than or equal to this value.'}
containingString: c.shortString(title: 'Contains',description: 'Script requires the event\'s property chain value to be a string containing this string.')
notContainingString: c.shortString(title: 'Does not contain',description: 'Script requires the event\'s property chain value to *not* be a string containing this string.')
containingRegexp: c.shortString(title: 'Contains Regexp',description: 'Script requires the event\'s property chain value to be a string containing this regular expression.')
notContainingRegexp: c.shortString(title: 'Does not contain regexp',description: 'Script requires the event\'s property chain value to *not* be a string containing this regular expression.')
GoalSchema = c.object{title: 'Goal',description: 'A goal that the player can accomplish.',required: ['name','id']},
name: c.shortString(title: 'Name',description: 'Name of the goal that the player will see, like \"Defeat eighteen dragons\".')
i18n: {type: 'object',format: 'i18n',props: ['name'],description: 'Help translate this goal'}
id: c.shortString(title: 'ID',description: 'Unique identifier for this goal, like \"defeat-dragons\".')# unique somehow?
worldEndsAfter: {title: 'World Ends After',description: 'When included, ends the world this many seconds after this goal succeeds or fails.',type: 'number',minimum: 0,exclusiveMinimum: true,maximum: 300,default: 3}
howMany: {title: 'How Many',description: 'When included, require only this many of the listed goal targets instead of all of them.',type: 'integer',minimum: 1}
hiddenGoal: {title: 'Hidden',description: 'Hidden goals don\'t show up in the goals area for the player until they\'re failed. (Usually they\'re obvious, like "don\'t die".)','type':'boolean',default: false}
killThangs: c.array{title: 'Kill Thangs',description: 'A list of Thang IDs the player should kill, or team names.',uniqueItems: true,minItems: 1,'default':['ogres']},thang
saveThangs: c.array{title: 'Save Thangs',description: 'A list of Thang IDs the player should save, or team names',uniqueItems: true,minItems: 1,'default':['humans']},thang
getToLocations: c.object{title: 'Get To Locations',description: 'Will be set off when any of the \"who\" touch any of the \"targets\"',required: ['who','targets']},
who: c.array{title: 'Who',description: 'The Thangs who must get to the target locations.',minItems: 1},thang
targets: c.array{title: 'Targets',description: 'The target locations to which the Thangs must get.',minItems: 1},thang
getAllToLocations: c.array{title: 'Get all to locations',description: 'Similar to getToLocations but now a specific \"who\" can have a specific \"target\", also must be used with the HowMany property for desired effect',required: ['getToLocation']},
c.object{title: '',description: ''},
getToLocation: c.object{title: 'Get To Locations',description: 'TODO: explain',required: ['who','targets']},
who: c.array{title: 'Who',description: 'The Thangs who must get to the target locations.',minItems: 1},thang
targets: c.array{title: 'Targets',description: 'The target locations to which the Thangs must get.',minItems: 1},thang
keepFromLocations: c.object{title: 'Keep From Locations',description: 'TODO: explain',required: ['who','targets']},
who: c.array{title: 'Who',description: 'The Thangs who must not get to the target locations.',minItems: 1},thang
targets: c.array{title: 'Targets',description: 'The target locations to which the Thangs must not get.',minItems: 1},thang
keepAllFromLocations: c.array{title: 'Keep ALL From Locations',description: 'Similar to keepFromLocations but now a specific \"who\" can have a specific \"target\", also must be used with the HowMany property for desired effect',required: ['keepFromLocation']},
c.object{title: '',description: ''},
keepFromLocation: c.object{title: 'Keep From Locations',description: 'TODO: explain',required: ['who','targets']},
who: c.array{title: 'Who',description: 'The Thangs who must not get to the target locations.',minItems: 1},thang
targets: c.array{title: 'Targets',description: 'The target locations to which the Thangs must not get.',minItems: 1},thang
leaveOffSides: c.object{title: 'Leave Off Sides',description: 'Sides of the level to get some Thangs to leave across.',required: ['who','sides']},
who: c.array{title: 'Who',description: 'The Thangs which must leave off the sides of the level.',minItems: 1},thang
sides: c.array{title: 'Sides',description: 'The sides off which the Thangs must leave.',minItems: 1},side
keepFromLeavingOffSides: c.object{title: 'Keep From Leaving Off Sides',description: 'Sides of the level to keep some Thangs from leaving across.',required: ['who','sides']},
who: c.array{title: 'Who',description: 'The Thangs which must not leave off the sides of the level.',minItems: 1},thang
sides: side,{title: 'Sides',description: 'The sides off which the Thangs must not leave.',minItems: 1},side
collectThangs: c.object{title: 'Collect',description: 'Thangs that other Thangs must collect.',required: ['who','targets']},
who: c.array{title: 'Who',description: 'The Thangs which must collect the target items.',minItems: 1},thang
targets: c.array{title: 'Targets',description: 'The target items which the Thangs must collect.',minItems: 1},thang
keepFromCollectingThangs: c.object{title: 'Keep From Collecting',description: 'Thangs that the player must prevent other Thangs from collecting.',required: ['who','targets']},
who: c.array{title: 'Who',description: 'The Thangs which must not collect the target items.',minItems: 1},thang
targets: c.array{title: 'Targets',description: 'The target items which the Thangs must not collect.',minItems: 1},thang
ResponseSchema = c.object{title: 'Dialogue Button',description: 'A button to be shown to the user with the dialogue.',required: ['text']},
text: {title: 'Title',description: 'The text that will be on the button','default':'Okay',type: 'string',maxLength: 30}
channel: c.shortString(title: 'Channel',format: 'event-channel',description: 'Channel that this event will be broadcast over, like "level-set-playing".')
event: {type: 'object',title: 'Event',description: 'Event that will be broadcast when this button is pressed, like {playing: true}.'}
buttonClass: c.shortString(title: 'Button Class',description: 'CSS class that will be added to the button, like "btn-primary".')
i18n: {type: 'object',format: 'i18n',props: ['text'],description: 'Help translate this button'}
SpriteCommandSchema = c.object{title: 'Thang Command',description: 'Make a target Thang move or say something, or select/deselect it.',required: ['id'],default: {id: 'Captain Anya'}},
select: {title: 'Select',description: 'Select or deselect this Thang.',type: 'boolean'}
say: c.object{title: 'Say',description: 'Make this Thang say a message.',required: ['text']},
blurb: c.shortString(title: 'Blurb',description: 'A very short message to display above this Thang\'s head. Plain text.',maxLength: 50)
mood: c.shortString(title: 'Mood',description: 'The mood with which the Thang speaks.','enum':['explain','debrief','congrats','attack','joke','tip','alarm'],'default':'explain')
text: {title: 'Text',description: 'A short message to display in the dialogue area. Markdown okay.',type: 'string',maxLength: 400}
sound: c.object{title: 'Sound',description: 'A dialogue sound file to accompany the message.',required: ['mp3','ogg']},
preload: {title: 'Preload',description: 'Whether to load this sound file before the level can begin (typically for the first dialogue of a level).',type: 'boolean','default':false}
responses: c.array{title: 'Buttons',description: 'An array of buttons to include with the dialogue, with which the user can respond.'},ResponseSchema
i18n: {type: 'object',format: 'i18n',props: ['blurb','text'],description: 'Help translate this message'}
move: c.object{title: 'Move',description: 'Tell the Thang to move.',required: ['target'],default: {target: {x: 20,y: 20},duration: 500}},
duration: {title: 'Duration',description: 'Number of milliseconds over which to move, or 0 for an instant move.',type: 'integer',minimum: 0,default: 500,format: 'milliseconds'}
NoteGroupSchema = c.object{title: 'Note Group',description: 'A group of notes that should be sent out as a result of this script triggering.',displayProperty: 'name'},
name: {title: 'Name',description: 'Short name describing the script, like \"Anya greets the player\", for your convenience.',type: 'string'}
dom: c.object{title: 'DOM',description: 'Manipulate things in the play area DOM, outside of the level area canvas.'},
focus: c.shortString(title: 'Focus',description: 'Set the window focus to this DOM selector string.')
highlight: c.object{title: 'Highlight',description: 'Highlight the target DOM selector string with a big arrow.'},
target: c.shortString(title: 'Target',description: 'Target highlight element DOM selector string.')
delay: {type: 'integer',minimum: 0,title: 'Delay',description: 'Show the highlight after this many milliseconds. Doesn\'t affect the dim shade cutout highlight method.'}
rotation: {type: 'number',minimum: 0,title: 'Rotation',description: 'Rotation of the pointing arrow, in radians. PI / 2 points left, PI points up, etc.'}
sides: c.array{title: 'Sides',description: 'Which sides of the target element to point at.'},{type: 'string','enum':['left','right','top','bottom'],title: 'Side',description: 'A side of the target element to point at.'}
lock: {title: 'Lock',description: 'Whether the interface should be locked so that the player\'s focus is on the script, or specific areas to lock.',type: ['boolean','array'],items: {type: 'string',enum: ['surface','editor','palette','hud','playback','playback-hover','level']}}
letterbox: {type: 'boolean',title: 'Letterbox',description: 'Turn letterbox mode on or off. Disables surface and playback controls.'}
script: c.object{title: 'Script',description: 'Extra configuration for this action group.'},
duration: {type: 'integer',minimum: 0,title: 'Duration',description: 'How long this script should last in milliseconds. 0 for indefinite.',format: 'milliseconds'}
skippable: {type: 'boolean',title: 'Skippable',description: 'Whether this script shouldn\'t bother firing when the player skips past all current scripts.'}
beforeLoad: {type: 'boolean',title: 'Before Load',description: 'Whether this script should fire before the level is finished loading.'}
sound: c.object{title: 'Sound',description: 'Commands to control sound playback.'},
suppressSelectionSounds: {type: 'boolean',title: 'Suppress Selection Sounds',description: 'Whether to suppress selection sounds made from clicking on Thangs.'}
music: c.object{title: 'Music',description: 'Control music playing'},
id: c.shortString(title: 'ID',description: 'A unique ID that other scripts can rely on in their Happens After prereqs, for sequencing.')# uniqueness?
channel: c.shortString(title: 'Event',format: 'event-channel',description: 'Event channel this script might trigger for, like "world:won".')
eventPrereqs: c.array{title: 'Event Checks',description: 'Logical checks on the event for this script to trigger.',format: 'event-prereqs'},EventPrereqSchema
repeats: {title: 'Repeats',description: 'Whether this script can trigger more than once during a level.',enum: [true,false,'session'],'default':false}
scriptPrereqs: c.array{title: 'Happens After',description: 'Scripts that need to fire first.'},
c.shortString(title: 'ID',description: 'A unique ID of a script.')
notAfter: c.array{title: 'Not After',description: 'Do not run this script if any of these scripts have run.'},
c.shortString(title: 'ID',description: 'A unique ID of a script.')
noteChain: c.array{title: 'Actions',description: 'A list of things that happen when this script triggers.'},NoteGroupSchema
# TODO: split thangType into 'original' and 'majorVersion' like the rest for consistency
thangType: c.objectId(links: [{rel: 'db',href: '/db/thang.type/{($)}/version'}],title: 'Thang Type',description: 'A reference to the original Thang template being configured.',format: 'thang-type')
components: c.array{title: 'Components',description: 'Thangs are configured by changing the Components attached to them.',uniqueItems: true,format: 'thang-components-array'},ThangComponentSchema# TODO: uniqueness should be based on 'original', not whole thing
majorVersion: {title: 'Major Version',description: 'Which major version of the System is being used.',type: 'integer',minimum: 0,default: 0,format: 'hidden'}
original: c.objectId(title: 'Original',description: 'A reference to the original Article.')#, format: 'hidden') # hidden?
majorVersion: {title: 'Major Version',description: 'Which major version of the Article is being used.',type: 'integer',minimum: 0}#, format: 'hidden'} # hidden?
description: {title: 'Description',description: 'A short explanation of what this level is about.',type: 'string',maxLength: 65536,'default':'This level is indescribably flarmy!',format: 'markdown'}
documentation: c.object{title: 'Documentation',description: 'Documentation articles relating to this level.',required: ['specificArticles','generalArticles'],'default':{specificArticles: [],generalArticles: []}},
specificArticles: c.array{title: 'Specific Articles',description: 'Specific documentation articles that live only in this level.',uniqueItems: true,'default':[]},SpecificArticleSchema
generalArticles: c.array{title: 'General Articles',description: 'General documentation articles that can be linked from multiple levels.',uniqueItems: true,'default':[]},GeneralArticleSchema
scripts: c.array{title: 'Scripts',description: 'An array of scripts that trigger based on what the player does and affect things outside of the core level simulation.','default':[]},ScriptSchema
thangs: c.array{title: 'Thangs',description: 'An array of Thangs that make up the level.','default':[]},LevelThangSchema
systems: c.array{title: 'Systems',description: 'Levels are configured by changing the Systems attached to them.',uniqueItems: true,default: []},LevelSystemSchema# TODO: uniqueness should be based on 'original', not whole thing
victory: c.object{title: 'Victory Screen',default: {},properties: {'body':{type: 'string',format: 'markdown',title: 'Body Text',description: 'Inserted into the Victory Modal once this level is complete. Tell the player they did a good job and what they accomplished!'},i18n: {type: 'object',format: 'i18n',props: ['body'],description: 'Help translate this victory message'}}}
i18n: {type: 'object',format: 'i18n',props: ['name','description'],description: 'Help translate this level'}