Entity data¶
Entity data are split into 2 TextAssets in the game loaded on boot:
Resources/data/EntityValues- The
Resources/data/entitydatadirectory
animid data¶
The TextAsset Resources/data/EntityValues from the root of the asset tree contains data that apply to every entities of a given AnimID. Each line id of the data corresponds to the matching AnimID. The data is loaded on boot during LoadEssentials in the endata static field of MainManager. The field is an array of struct of type Entity_Data. Specifically, it's done by a method called by LoadEssentials: LoadEntityData:
public static Entity_Data[] LoadEntityData()
NOTE: This asset uses the enum form of the AnimIDs, NOT the int form which means the first line represents None.
A word on the asset's format¶
This format is very unique in the game: it is not encoded in a similar fashion than other TextAssets in the game. Here is how the data is serialiazed:
- The fields are split by
, - Every primitives types are encoded as normal (int, float, string, boolean, etc...)
- A
Vector3is NOT encoded using 3 fields. It is encoded using 1 field with the format(x, y, z)wherex,yandzare the floats of the vector. Note that the inner separators are expected: the parser will expect the parenthesizes delimiters to know this is parsed differently. - An empty string is an empty field.
TextAsset and Entity_Data layout¶
Here is the layout of the data and the fields associated with this struct (NOTE: the position is given according to the TextAsset which means Vector3 are treated as one position):
| Position | Description | Type | Additional information |
|---|---|---|---|
| 0 | shadowsize | float | The default value is 1.0 if the AnimIDs is higher than the max line id contained in the asset |
| 1 | startscale | Vector3 | The default value is Vector3.one if the AnimIDs is higher than the max line id contained in the asset |
| 2 | bleeppitch | float | The default value is 1.0 if the AnimIDs is higher than the max line id contained in the asset. See bleep to learn more |
| 3 | bleepid | int | See bleep to learn more |
| 4 | ismodel | bool | Check the section below about model animids to learn more |
| 5 | modelscale | Vector3 | For an ismodel animid, this represents the euler angles the model will have. See the section below about model animids to learn more |
| 6 | modeloffset | Vector3 | For an ismodel or Object animid, the offset to use when calling AddModel. See the section below about model animids to learn more |
| 7 | freezesize | Vector3 | |
| 8 | freezeoffset | Vector3 | |
| 9 | freezeflipoffset | Vector3 | |
| 10 | preloaddata | string[] | The strings are split by ?, can be left empty in which case, the array is left to null. The strings represents asset paths relative from the Asset directory. A $ prefix means to not preload outside of battle while a & indicate it is a sprite instead of a game object from a prefab |
| 11 | shakeondrop | bool | |
| 12 | diganim | bool | |
| 13 | dontoverridejump | bool | |
| 14 | freezenofall | bool | |
| 15 | noshadow | bool | |
| 16 | walktype | WalkType | Encoded in its int form, see below for the possible values |
| 17 | basestate | int | This field is loaded, but not used. A value must still be provided or an exception will be thrown. |
| 18 | basewalk | int | This field is loaded, but not used. A value must still be provided or an exception will be thrown. |
| 19 | minheight | float | |
| 20 | startheight | float | This field is loaded, but not used. A value must still be provided or an exception will be thrown. |
| 21 | startbobspd | float | |
| 22 | startbobfreq | float | |
| 23 | hasiceanim | bool | |
| 24 | noflyanim | bool | This field is optional, it can be omitted which leaves a default value of false |
| 25 | forceshadow | bool | This field is optional, it can be omitted which leaves a default value of false |
| 26 | Object | bool | Check the section below about model animids to learn more. This field is optional, it can be omitted which leaves a default value of false |
NOTE: Fields 24, 25 and 26 must be omitted TOGETHER if any are to be omitted. Failure to do so will result in an exception to be thrown when parsing the asset.
WalkType values¶
Here are the possible values for a WalkType enum value:
| Value | Name |
|---|---|
| 0 | Normal |
| 1 | Jump |
The only thing this field does is if it's Jump, Move will have the entity continuously jump when it can during the movement.
ismodel and Object animids¶
These fields, when set to true, acomplishes a similar goal, but executed differently. At most, one of these 2 fields can be true, it is considered invalid for both of them to be true at once (the WindUp animid has both fields set to true, but this animid is unused under normal gameplay).
What these fields have in common¶
Either of them being true means that every entities created using this animid doesn't follow the regular structure that most entities follow. The regular structure is normally enforced by CreateNewEntity where they have a SpriteRenderer that gets animated using an Animator present on the entity's root and whose controller comes from loading a RuntimeAnimatorController present in the resources tree at animationcontrollers/X/X where X is the enum value name of the animid (see the animids table for details).
Instead of having this structure, AddModel is called during CheckSpecialID, a part of the entity startup process. AddModel will change the meaning of sprite to instead be the root of an instance of a prefab that was loaded from the resources tree at prefabs/objects/X where X is the animid enum value name. This occurs after the specific animid logic have run (though some specific animid have logic that occurs after, but they aren't common). As for the animations, the anim will be set to the animator that can be present in in the object prefab's root (the controller can either come from directly referencing it in the prefab's animator or it can be loaded from the resources tree as normal if it's not assigned). However, this animator is optional: it's possible to have a model animid that cannot be animated and will have its anim left at null. This allows entity with a custom object hierarchy that are more complex than a sprite to be animated with their custom controller or to simply not have any animations done on them.
Where these fields differ¶
Setting either ismodel or Object to true will result in this model structure. Where they differ is how the animid is expected to be animated throughout the entity's lifetime. The animation system EntityControl provides and the animstate system can still be used if an Animator is present on the root prefab. Where there's a potential problem is when an animator isn't present. In that case, EntityControl would still execute logic that tends to get in the way because such an animid isn't intended to be animated.
This problem is where the nuance between ismodel and Object comes in. ismodel ONLY makes the entity a model structure, but it is expected to have an animator at its root with a controller (either loaded from the prefab's animator or from resources). Object expects to not have an animator present on the prefab and goes one step further: after AddModel is done, the entity's animid field will be set to -1.
Normally, this would result in the entity not being rendered, but because UpdateSprite doesn't actually listen for animid changes to model entities, this doesn't happen. What happens instead is it will block a significant amount of logic that UpdateSprite would have done instead. In fact, it completely disables any automatic management of the animations and animstate so it is no longer possible with an Object animid to have EntityControl manage the animations. It's not even possible to use an Animator that would be present on the object prefab's root because UpdateSprite would set it to null. Since it's expected for the entity to not have animations, this is well suited for this.
NOTE: This implies that in order to properly know the actual animid of Object entities, the
originalidfield needs to be used instead ofanimid.
A way for Object animids to be animated¶
However, there is a narrow exception where EntityControl allows an Object animid to have an animator with its own controller. If the entity is backed by an NPCControl, UpdateSprite will not set the anim's controller to null. This specifically allows those entities to still have an animator at the root of their prefab without EntityControl setting it to null due to the animid being -1. However, it ONLY allows anim to not be null: the controller MUST be provided by the prefab (they will not be automatically loaded through the resources tree) and the animation clips still cannot be automatically managed by EntityControl. The only way to play clips on such animator is to do so manually through code. It is effectively only allowing non standard animation to work.
It should also be noted that an Object animid that is used by an Enemy won't actually have their animid set to -1 and thus, it behaves more like a ismodel entity (with the exception of the additional logic mentioned in the next section). This happens because while CheckSpecialID will set the animid to -1, this will be undone by GetEnemyData when called with createnewentity since it will itself set the animid to the one from the enemy data. Since this happens after the entity was created, it overrides the main effect of the Object field, but it preserves the model structure. Under normal gameplay, this only happens with the CoiledVineGround animid used in the AcolyteVine enemy (which is actionless and only involved in the Acolyte enemy).
Other specific logic¶
There are other effects an Object animid have enforced by CheckSpecialID:
notalkis set to truehasshadowis set to false- UpdateAnimSpecific will not be called upon starting the entity
ismodel on the other hand doesn't do the above, but it does have one exclusive change: modelscale will be used to set the euler angles of the model. The field is not used for Object entities.
In summary¶
Here are criteria to choose when an animid should be ismodel or Object:
- If EntityControl should manage the animations of every entities with this animid, it should be ismodel. An animator should be present on the object prefab's root and either directly have a controller assigned or load one from the resources tree like a regular animid. The modelscale will allow to configure the euler angles of the
model - If there's no need for any animations of every entities with this animid, it should be Object (modelscale won't be used)
- If the entity is meant to be used in an NPCControl with Unity animation clips, but there's no need for those clips to be automatically managed by EntityControl, it should be Object (EntityControl will still allow an animator to exist on the object prefab's root, but it won't manage it)
Map entity data¶
The entitydata directory contains the names and details that applies to each specific entities that should be loaded upon a specific Map load. The data only gets loaded during MapControl's CreateEntities for the concerned Maps which happens on the MapControl's Start. The data ends up in the entities field of the MapControl which is an array of EntityControl and each index of that array ends up in the npcdata.mapid field of each NPCControl. The loaded data ends up in endata and it is possible to retrieve the fields later. This is how CheckSpecialID reads some of them.
Each line of an entity map asset contains one line per entity. It contains fields about its EntityControl and its NPCControl (via the npcdata field) separated by }. Each asset corresponds to each map in the game where its asset's filename is the Map's id.
The names of each entity are not contained in the data assets. They are contained in a names directory next to the data assets. An entity name asset contains one string per line that corresponds to the name of entity of the matching line in the matching data asset. The names asset filename is Xnames where X is the Map id.
This gives the following layout:
- Path to the entity data files:
Resources/data/entitydata - Path to the entity name files:
Resources/data/entitydata/names
The name of an entity can contain special strings to act as modifiers on it which means the name is both used for debugging purposes as well as to modify its behavior.
Entity Data File Fields¶
While a lot of these fields have shared meanings, a lot are dependant of the specifics of the NPCControl (for example, the data fields completely changes between them). It is recommended to check the NPCControl documentation to understand where each field is used in what circumstances. The EntityControl fields however tends to remain shared.
| Position | Description | Type | Additional information |
|---|---|---|---|
| 0 | npcdata.entitytype | NPCControl.NPCType | |
| 1 | npcdata.objecttype | NPCControl.ObjectTypes | |
| 2 | npcdata.behaviors[0] | NPCControl.ActionBehaviors | |
| 3 | npcdata.behaviors[1] | NPCControl.ActionBehaviors | |
| 4 | npcdata.interacttype | NPCControl.Interaction | If it's Shop, this tells the game to build a shop system |
| 5 | destroytype | NPCControl.DeathType | Used in Death to determine how to destroy the entity |
| 6 | startpos.x | float | |
| 7 | startpos.y | float | |
| 8 | startpos.z | float | |
| 9 | animid | int | This is the item type for an Item object |
| 10 | flip | bool | |
| 11 | ccol.height | float | |
| 12 | ccol.radius | float | Also assigned to npcdata.colliderheight |
| 13 | npcdata.radius | float | Regulates how close the player needs to be to become inrange which affects behaviors and interactions |
| 14 | npcdata.timer | float | For a lot of objects, this is a timer in frame that when expired, Death is called on the entity. -1 means to not have a timer |
| 15 | speed | float | |
| 16 | npcdata.actionfrequency[0] | float | |
| 17 | npcdata.actionfrequency[1] | float | |
| 18 | npcdata.speedmultiplier | float | |
| 19 | npcdata.radiuslimit | float | The radius the entity can be in and will not go outside of it in multiple cases |
| 20 | npcdata.wanderradius | float | For a Wander behavior or derivative, this is the radius the entity can move to between wanders |
| 21 | npcdata.teleportradius | float | For a Wander behavior or derivative, this is the radius the entity can teleport to in the case of a failsafe situation |
| 22 | NPCControl has a BoxCollider? | bool | |
| 23 | npcdata.boxcol.isTrigger | bool | Requires field at position 22 to be true |
| 24 | npcdata.boxcol.size.x | float | Requires field at position 22 to be true |
| 25 | npcdata.boxcol.size.y | float | Requires field at position 22 to be true |
| 26 | npcdata.boxcol.size.z | float | Requires field at position 22 to be true |
| 27 | npcdata.boxcol.center.x | float | Requires field at position 22 to be true |
| 28 | npcdata.boxcol.center.y | float | Requires field at position 22 to be true |
| 29 | npcdata.boxcol.center.z | float | Requires field at position 22 to be true |
| 30 | npcdata.freezetime | float | |
| 31 | freezesize.x | float | |
| 32 | freezesize.y | float | |
| 33 | freezesize.z | float | |
| 34 | freezeoffset.x | float | |
| 35 | freezeoffset.y | float | |
| 36 | freezeoffset.z | float | |
| 37 | npcdata.eventid | int | |
| 38 | Length of npcdata.requires | int | Maximum of 10 |
| 39-48 | npcdata.requires | int[] | lists of flags slots that needs to be true for the entity to exist |
| 49 | Length of npcdata.limit | int | Maximum of 10 |
| 50-59 | npcdata.limit | int[] | lists of flags slots where any needs to be false for the entity to exist |
| 60 | Length of npcdata.data | int | Maximum of 10 |
| 61-70 | npcdata.data | int[] | General purpose int array |
| 71 | Length of npcdata.vectordata | int | Maximum of 10 |
| 72-101 | npcdata.vectordata | Vector3[] (each component is a field) | General purpose triplets of floats array |
| 102 | Length of npcdata.dialogues | int | Maximum of 20 |
| 103-162 | npcdata.dialogues | Vector3[] (each component is a field) | For an NPC, consult the documentation of GetDialogue to learn how this array is managed |
| 163 | transform.eulerAngles.x | float | |
| 164 | transform.eulerAngles.y | float | |
| 165 | transform.eulerAngles.z | float | |
| 166 | Length of npcdata.battleids | int | Maximum of 4 |
| 167-170 | npcdata.battleids | int[] | For an Enemy, this is the list of enemy id the encounter consists of |
| 171 | npcdata.tagcolor.r | float | Doesn't affect gameplay, only used in the Unity editor |
| 172 | npcdata.tagcolor.g | float | Doesn't affect gameplay, only used in the Unity editor |
| 173 | npcdata.tagcolor.b | float | Doesn't affect gameplay, only used in the Unity editor |
| 174 | npcdata.tagcolor.a | float | Doesn't affect gameplay, only used in the Unity editor |
| 175 | emoticonoffset.x | float | |
| 176 | emoticonoffset.y | float | |
| 177 | emoticonoffset.z | float | |
| 178 | npcdata.insideid | int | The interior id of the map the entity starts in |
| 179-188 | npcdata.emoticonflag | Vector2[10] (components are , separated) |
See CheckEmoteFlag |
| 189 | npcdata.tattleid | int | The dialogue line id corresponding to tattling the NPCControl |
| 190 | npcdata.regionalflag | int | The Regionalflags slot bound to this entity |
| 191 | initialheight | float | |
| 192 | bobrange | float | |
| 193 | bobspeed | float | |
| 194 | npcdata.activationflag | int | The flag slot bound to this entity |
| 195 | npcdata.returntoheight | bool or int | Int if the length in string is 1, bool otherwise |