Skip to content
Melanie Dickinson edited this page Nov 21, 2019 · 1 revision

How to write a schema.json data file for an Ensemble domain

The social schema is the social 'stuff' of your world, the raw material that your characters will keep track of and reason about to determine what actions to take. It's how you define what concepts exist in the world, or your domain's 'data types'.

A simple example schema.json:

{
  "schema": [
    {
      "category" : "trait",
      "isBoolean" : true,
      "directionType" : "undirected",
      "actionable" : false,
      "defaultValue" : false,
      "types" : [ 
        "introverted",
        "sensitive",
        "mischievous"
      ],
      "comment" : "Aspect of a character’s personality"
    }
}

Let's go through each of these attributes and discuss what they represent, and the possible values that you can use for each of them. The order that the properties are defined in do not matter.

category — Possible Values: any lowercase string (no underscores)

A descriptive string for the social phenomenon you are capturing with this object. Example category names might be "relationship", "mood", or "character trait".

types — Possible Values: an array of any number of lowercase strings

An array of the names of specific instances of the category you defined. So, for example, if your category was "relationship" then possible types might be ["friends", "enemies", "dating"]. If your category was "mood" then some types that might make sense could be ["happy", "sad", "jealous", "angry"]. Again, there is nothing sacred about the types that you use--feel free to name them anything you want!

Each type defines a separately maintained state variable for each character or pair of characters which you'll be able to reference in future actions, trigger rules, volition rules, and what will be saved to history.

isBoolean — Possible Values: true and false

Specifies if this bit of social state is something that is either on or off (true) or something that is represented by a scalar (false). You might say that you would want a relationship category to be boolean (i.e., either people are or are not friends, there is no in between). If you had a concept of affinity, however, you might prefer for that to be a scalar (e.g., I like you with a score of 90, which is a lot more than 20 but not quite as much as 100).

directionType — Possible Values: directed and undirected and reciprocal

You can imagine that some of the bits of social state pertain to a single character, while others describe a relationship between two characters. The directionType attribute is how you can specify if what you are defining pertains to one or two characters, and if two, if it is reciprocated or not.

undirected should be used if you are describing something that only pertains to a single character. If you had a "character trait" category, with types such as "humble", "greedy", "rash", and "intelligent", you would likely want this category to be 'undirected' as all of these things would likely only apply to a single character.

directed is meant to be used to describe relationships that involve two people that are not (necessarily) reciprocal. Lets say you had a "directed feelings" category, with types such as "has a crush on", "angry at", and "jealous of" -- all of these would involve two characters; the person with the "directed mood" and the subject of thier feelings (i.e., the person they are angry at or jealous of). It's possible for categories with a directionType of directed to still be reciprocated (Bob can be jealous of Abe and Abe can be jealous of Bob concurrently), but either of them are allowed to harbor this feeling of jealously independant of the feelings of the other.

reciprocal is meant to describe relationshps between two people that are mutually agreed upon. Let's say you had a category called "family bond" with the types "siblings", "immediate family", "married", and "unrelated" and let's imagine that Bob and Abe are unrelated (and have the relationship "unrelated" saying as much). Now, if Abe ends up marrying Bob's sister, Carla, we're going to want to update the social structure of this family quite a bit! We'll need to make Abe and Carla married, and remove the 'unrelated' relationship from Abe and Bob. When you add or delete a social fact pertaining to something which is reciprocal, adjusting it for one person will automatically adjust it for the other as well (i.e. making Abe married to Carla is functionally exactly the same as making Carla married to Abe; you don't have to manually do it for both).

This is the key difference between reciprocal and directed; if marriage was marked as being directed, specifying Abe being married to Carla would not automatically make Carla married to Abe. If you are designing a social world revolving around a comedy of errors, you might very well desire for two characters to have contrasting views on the current state of their nuptials. This is one reason why there is no single "correct way" to author; the directionType that you use only has to make sense to you as an author.

minValue — Only used if isBoolean is false

This should be an integer that specifies the lowest value a scalar social fact can reach. If something happens that would bring the number lower than this specified minimum value, it will clamp to the value of minValue.

maxValue — Only used if isBoolean is false

This should be an integer that specifies the largest value a scalar social fact can reach. If something happens that would bring the number higher than this specified maximum value, it will calmp to the value of maxValue.

actionable — Possible values: true and false

Much of what ensemble affords the user is allowing characters to form volitions over various intents. An intent is some way that the social space can change. So, for example, an intent might be for two characters to gain the relationship 'married', or for one character to raise an affinity attribute towards another. The actionable property is what allows you to specify which social facts characters are able to form volitions over, and which ones they are not.

Using some of the above examples, if we had the category relationship with types 'freinds', 'enemies', and 'dating', this is likely a category that would have actionable set to true -- it seems reasonable that you would want characters to take actions with the goals of creating friendships and starting to date each other. Conversely, the 'character trait' category (with types such as "humble", "greedy", "intelligent", etc.) might not make as much sense; characters don't go out of their way to become greedy or humble; they just naturally are.

Even if a category of social facts has actionable equal to false, it doesn't mean that they have to be immutable; people are welcome to evolve or remain stubborn in your world as much as you'd like. Scrooge never had the intent to stop being greedy; he naturally abandonded that trait as a result of the interventions of the spirits and his subsequent actions Christmas morning. The very same could happen in a system of your own design!

Or heck, maybe you do want character traits to be something that characters can form volitions over, and that's just fine too. You're welcome to design it in any way you see fit.

defaultValue Possible values change based on vaue of isBoolean. If isBoolean is true, possible values are true and false. If isBoolean is false, possible values are integer numbers within the range specified by minValue and maxValue

This is the default value of the social fact. That is to say, if the social fact hasn't been explcitly specified yet, what should ensemble assume the value to be.

duration

Specifies the amount of time that a certain social fact in the database should exist before reverting back to its default value. Time is a construct that will likely be handled differently from game to game, but imagine you are making a game where every turn the timestep is advanced by one. This is meant to capture fleeting social facts. For example, Abe may be angry at Bob for a few turns, but if the default attitude in this world is for characters to not be angry at each other, and nothing is done to renew Abe's anger, it will evetually fade away (e.g., the social fact representing that Abe is angry at Bob will eventually change from true to false).

If duration is unspecified, then changes to the social state are permament until explicitly removed.

Special case: duration of 0 -- If you specify something as having a duration of 0, then ensemble will treat it slightly differently. Namely, the fact will enter the social facts database for exactly one timestep (i.e., the timestep that it happened), but instead of reverting to its default on the subsequent timestep, it is instead not re-entered into the database at all.

This special case is essentially allowing ensemble, and the social facts database, to dabble in some mixing of metaphor. In general, the database holds state information. However, by specifying a category as having a duration of 0, you essentially mark it as not being state information, but rather an event. This can be very useful if you want to reason over things such as "did a character do something embarrassing" (as opposed to the subtly different "was a character ever embarrassed").

Clone this wiki locally