Modding Terminology

From the Oblivion ConstructionSet Wiki
Revision as of 23:14, 28 May 2008 by imported>Quetzilla (→‎Recap: typo fixed)
Jump to navigation Jump to search

Modules, Records, Formids

Modding using the TESCS means creating and editing module files (i.e. esp and esm files). There are other ways to "mod" (create/edit resource -- mesh, texture, sound, speech -- files; edit ini and xml files, etc.), but here we'll just be talking about module file editing.

Module files are collections of records. Different types of records define different types of things: sounds, races, placed objects ("references"), leveled lists, etc. While different types of records have different fields (e.g., weight of an arrow, x position of a placed object, sound id of an opening door), at the basic level they're all the same sort of thing -- a record.

All records have a unique formid. Many (but not all) records also have editor ids. Formids are eight digit hexadecimal numbers (e.g. 00030FDC). Editor ids are short text strings (letters and number only, no spaces or special characters). For records listed in the Objects window of the CS, the editor id is listed in the first column and the formid in the second column (by default this column is too narrow to actually see the formid -- drag the column divider to the right to see the formid). Same thing for both panes of the Cell View window: the left pane lists the cells by editor id and then formid. Right pane lists placed objects in the same way -- with one caveat -- if no editor id has been assigned to a particular reference, then the editor id of the reference's base object is shown instead.

Object vs. Object

This section is likely to be very confusing. You may want to skip over it. The main point is that when talking about modding, I'll largely talk about "records" instead of "objects". Mostly. Anyway, here goes...

There's a major problem with the use of the word "object" when talking about modding. The problem is that there are several different equally obvious, yet pretty much incompatible ways to use the word. I'll cover those here, and then try to resolve those conflict -- in part by discarding some of the standard terminology.

When playing you tend to think of "objects" in the game world -- a wall, a rock, a creature, a dagger that you find an chest. When working in the rendering window of the CS, you tend to think of "objects" pretty much the same way, except that you're likely to think of fog banks, collision zones, etc. as also being "objects". Unfortunately, the CS contradicts that, calling those things "references", and seems to define "objects" as things listed in the "Objects Window". But from yet another view, if you're a programmer, you tend to think of discrete chunks of data as being "objects" -- i.e. you would see the internal representations of all the records (classes, factions, base objects and references) as being objects.

So, what I'm going to do here is to use the term "object" as little as possible. When I do use it, I'll usually mean it in the game players way -- as things seen and handled in the game world. For modding, when I need to talk about data chunks in a general way, I'll say "records". Also, whenever I talk about any type of record ("cell", "faction", "weapon", "reference", etc.), that can be considered to be short for "cell record", "faction record", "weapon record", "reference record", etc.)

In line with that, I'll break with existing CS terminology in several ways:

  • Instead of "base object" I'll say "base record".
  • Instead of "reference variable", I'll say "record variable".

References

Reference are based on base records (aka "base objects" in CS). The base record for a reference is a prototype for that reference. Some bases have many references (e.g. standard containers), some have only one (e.g. most non-combatant NPCs).

Most records in the game are references. (Think of it: hundreds of cells, with wall, racks, trees, creatures, etc. -- it all adds up.) While it would be possible to assign editor ids to all of these references, thankfully this is not done. Hence, most records in Oblivion.esm do not have editor ids.

Incidentally, aside from references, there is another group of records without editor ids -- and that is base records created in-game. Editor ids are really only useful to human modders, not to the CS or game engine, both of which prefer to work with formids. So, since the alchemy potions, spells, enchanted armor and weapons created in-game aren't expected to be viewed by humans, they're not assigned editor ids.

References can also have "parent" references. Parent references are use for two purposes: 1) chaining enable/disable states across the references (the appearance of Oblivion gates is controlled by such chains), and 2) linking references in a way that scripts can use (traps use this -- a trigger rope reference might have a falling log as it's parent -- the script for the rope will then trigger will react to an actor colliding with it by triggering the parent ref). Note that for enable/disable state control the parent ref really does act as a "parent", but when used in a trap, or similar situation, the flow is reversed -- it's the "child" that controls the "parent".

Base Records

Base records also act as the "base" for items in containers. There's a complexity about such items in the game world -- sometimes they are/have references, but most of the time, they do not. More on that below.

By the way, note that not all records listed in the "Objects Window" are base records. In particular, textures, sound, water records while (mostly) used by other records, never act as bases themselves. There's really no distinction between these types of records and the other types of records (cells, regions, races, etc.) that are edited only through menus. So why are they in the "Objects" window? :shrug: No good reason. Probably partially history from previous games, and partially arbitrary programmer and modder choices at Bethsoft.

Vs. Programming Terminology

Interlude for befuddled programmers...

Programmers are likely to find CS terminology particularly confusing. The CS use of "object", "parent" and "reference" are all pretty much completely independent of the programming meaning of those words.

  • In computing, a "reference" is essentially a pointer to another data structure, but in the CS a "reference" is a complex data structure -- more of an "object" than a "reference". The closest analog to a computer reference in the CS is actually the formid.
  • In computing, an object is typically an instantiated class. The closest analog to that in the CS is... a "reference"!
  • In computing, a class is the definition of a type of data structure that can be instantiated. The closest analog to that in the CS is a base "object"!
  • In computing a "parent" of an instance would usually be the class that it's based on. But in the CS, the parent of a reference is another reference -- more like a "previous" link in a one way linked list.

So programmers, just take your CS knowledge of objects, references and parents and stick in the closet for the time being! Now, back to regularly scheduled terminology discussion...

Dynamic Content

Dynamic records and items are records and items that are generated in the course of gameplay. In vanilla Oblivion, most of these are dynamic references (spawned creatures) and dynamic items (objects from containers and actor inventories). In addition, there are pc created spells, potions and enchanted items, as well as a few special records like the npc base record for the player's statue in Kvatch. Aside from that, OBSE can generate a wide variety of dynamic base records.

Example: When actors spawn from a spawn point, each of the actors will be a dynamic reference. Their armor, weapons, inventory and death items will all be dynamic items.

Example: When the player creates an enchanted armor at an enchantment altar, two dynamic records are created -- one for the enchantment, one for the base record of armor. In addition, there will be the single instance item of that base record which will be added to the players inventory.

Dynamic records (references, enchantment records, spell records, etc.) "belong" to the save game and not a mod, and so their formids will always start with 'FF'. In other words, If you can view the formid for some record in game and it begins with FF, then it is a dynamic record.

Dynamic Items

As mentioned previously, dynamic items are not permanently associated with references, instead they have no reference while in inventory and are dynamically assigned a new record every time they are dropped into the world. Again, this is in contrast to non-dynamic items -- i.e., items that begin their existence by being placed directly into the world by a mod. Non-dynamic items will always be represented by their original, mod-defined reference, regardless of their movement between inventories, containers and cells.

So what happens to the temporary reference of an dynamic item after you pick it up? Under most circumstances, it's immediately deleted from the save game. And in fact (after the recent 1.2.0416 patch), the formid for that dynamic reference is recycled. So if you're in an isolated cell, drop and iron dagger check its formid, pick it up and then drop an iron shield, the dynamic reference for iron shield will likely be assigned the same formid that was just freed by picking up the iron dagger.

However, under some circumstances the reference will not be removed from the save game. (Non-removal seems to be associated with having script record variables either attached to the reference and/or pointing at the reference.) In these cases, the reference continues to exist as data in the savegame, but is not visible in the game world, and is no longer associated with the item that's now in inventory. If the item is dropped again, it will not be reattached to this old reference, but instead be given a new dynamic reference -- which will likely suffer the same problem. It appears that these left over references are not cleared from the save game by the normal three day respawn/garbage collection process. As a result, if there are lot (1000's) of these references they can have a significant affect on savegame size. Accordingly, these are called bloat references.

In understanding how dynamic items are moved between cells and containers and betweend different containers, it may be that they're not so much moved as copied. I.e., the original object is copied to the new container/cell, along with its state information (armor/weapon health, enchantment charge, local script variables) and the original is deleted (or for bloat references, marked as destroyed).

This copying is most evident when dropping multiple instances of identical simple items (e.g. arrows, or undamaged iron daggers). If you drop multiple such identical simple items (e.g., ten iron arrows), they won't drop as ten separate arrows, but rather as a single stacked arrow (their mouseover id will be "Iron Arrow (10)"). In this case, the game actually treats the ten arrows not as ten separate arrows but as 1 arrow x 10. (Note that this only happens for simple, dynamic items.

So to some degree the continued existence of items is a fiction -- what the player perceives as a single dagger, pulled out a chest, and then moved to another, is probably more likely a succession of copied objects. However, since this copy/deletion process copies state exactly, we may as well speak of items as continually existing entities, even if their underlying data representation comes and goes.

Scripting Terminology

When it comes to scripting, the previous discussion has two major impacts:

  • Understanding what types of records various scripting functions require.
  • Understanding record variables and what can be put into them.

Record variables hold records, or more accurately, they hold the formids of records. Since formids uniquely identify records, the short numeric formid is able to stand in place of the actual record in function calls. You can check this in a script by using a call like "message "targetRef value: %X" targetRef. This will print the formid stored in targetRef. However, in talking about functions we usually gloss over this point and rather than saying something like "getSelf returns the formid of the current reference", we say "getSelf returns the current reference". The latter is easier to say and always implies the former.

You'll often see record variables referred to as "reference variables". This is because in non-OBSE Oblivion, functions that return records return only reference records. Hence although the record variables are perfectly capable of holding non-references, nothing but references is ever stored in them. (This is also why record variables are declared with the keyword 'ref' instead of 'rec'.) Because OBSE allows the variables to store the full range of record types, it is best to think of them as "record variables" from now on. (Unfortunately, we're still stuck with the 'ref' syntax for declaration.)

When specifying records in scripts, you should use either a record variable or an editor id. When the script is compiled, the editor id will be be converted to the corresponding formid, which is what the game engine will use while processing the script. Note that while it is often possible to directly use a formid in place of an editor id, it is undesirable to so because: a) it's harder to read, b) if an additional master is ever added to the mod, the specified formid (e.g., '01012345') will likely be incorrect. (Note that the compiled formid will be automatically adjusted to it's proper new value when a master is added.)

Now, back to confused terminology. In this wiki, different terminology is used in different places. Our understanding has grown over time and OBSE has substantially changed how records are and can be used in scripts.

Accordingly the following changes to practice are now suggested:

  • In scripts, record variables should be named to match their use in the script and to not use "id". Ref and/or base should be used if necessary to clarify nature of variable. However, function descriptions should always indicate whether the argument requires a ref or a base.
  • If the function accepts either a base or a reference, use bor. (I'm not actually clear that this is any function like this -- the brief comments that I saw about OBSE functions seemed to say that if the base is specified, it has to be in a different position than a reference argument would).
  • Container as a ref variable name is (usually/always?) understood to stand for container, npc and creature references. (I don't know of any container functions that work only on container references, and not creature or npc references.)

Some scripting examples:

ref companion
ref companionCell
set companionCell to companion.getParentCell

ref dagger
set dagger to player.placeAtMe wrTestDagger 1 100 0
message "Dagger formid: %X" dagger

ref tempRecord
set tempRecord to getSelf
set tempRecord to tempRecord.getContainer
set tempRecord to tempRecord.getParentCell

ref spell
set spell to GetPlayerSpell
player.Cast spell

ref self
ref myBase
set self to getSelf
set myBase to self.getBaseObject

ref opponent
set opponent to getCombatTarget

For functions, "ref" (or "reference") and "base" should be used more often to clarify the nature of the argument.

(opponent) actorRef.getCombatTarget
(value) npcRef.getClothingValue
(bool) npcRef.getIsSex male|female
(actionRef) reference.getActionRef
(bool) [reference.]isFurniture [base]
(bool) [reference.]isLight [base]
(health) [itemRef.]GetObjectHealth [itemBase]

An alternate approach would be to specify the return value and the type of all arguments.

(opponent:rec) actorRef:rec.getCombatTarget
(value:short) npcRef:rec.getClothingValue
(bool:short) npcRef:rec.getIsSex male|female
(actionRef:rec) reference:rec.getActionRef
(bool:short) isFurniture base:rec
(bool:short) reference:rec.isFurniture
(bool:short) isLight base:rec
(bool:short) reference:rec.isLight
(health:long) [itemRef:rec.]GetObjectHealth [itemBase:rec]

Scripting Notes

Some clarifications on scripting in light of discussion here.

  • getSelf applied to a mod based item will always return the original formid of the item.
  • getSelf applied to a dynamic item will always return 0 -- even when the item is in a cell and has a reference. While you might expect it to return it's the formid of it's dynamic reference while in the cell, apparently the function is rigged to return zero for safety reasons.
  • placeAtMe creates a dynamic reference -- and returns the correct formid. (Directly contrary to behavior of getSelf.)

Recap

  • Record: Fundamental data structure of module files.
  • Field: Aspects of a record (e.g., weight of an arrow, reach of bow)
  • Formid: The fundamental eight hex digit identifier of all records.
  • Editor id: The string identifier used by many (but not most) records.
  • Reference: An in world object -- all object place into the world, and (at least some) objects in containers. (Most references do not have editor ids, but all have formids.)
  • Base Object: The record that a given reference is based on. (E.g., "BarrelFoodLow".)
  • Items are carryable objects as the player perceives them in the game world -- they have a perceived lifetime that is usually greater than the lifetime of their underlying data representations.

Deprecated Terms

  • Object: Confusing term. Try to use another term if possible. Unfortunately, it's so basic that it's hard to completely avoid.
  • True Reference: Unnecessary. Don't use it.
  • Hexid: Use "formid" instead.