If you're familiar with Skyrim's engine, you'll probably want to know what you're putting into your game. Here are the details on how Atronach Crossing works.
Data
Atronach Crossing defines decorations, homes, and default objects in special files and reads them with a DLL. Default objects fill the same role here as they do in Bethesda's own systems: they feed forms to Atronach Crossing's DLL for use internally and externally. Decorations and homes are defined on a per-base-form basis.
The files are located here; the double-extension is required.
Data/SKSE/AtronachCrossing/SameFileNameAsTheEspOrEsm.ac.cobb
Atronach Crossing's DLL exists solely to read the contents of all such files above, build a data store, and provide Papyrus scripts with access to that data store. Atronach Crossing otherwise doesn't make any permanent changes, except through the same mechanisms as vanilla content (e.g. adding items to a leveled list the same way the game would; more on that later).
Rationale
If we defined this data in Papyrus, every placed decoration would get its own copy, and all of that redundant data would be baked into the savegame. We wouldn't be able to remove or update any of the fields for already-placed decorations. Moving the data out of Papyrus and into a secondary data store allows us to avoid those issues: decorations maintain their state in Papyrus, but pull from the data store to get basic information.
Think of it this way: if there were Decoration and Home base forms in the Creation Kit, those forms would define all of the data that we put in our data store. It's... surrogate base forms, if that makes any sense.
Format
I'm not going to put a full spec here (yet); this is a much more basic overview. The *.AC.COBB files are binary and consist of: a sentinel string ("ATRONACH CROSSING", not null terminated), a "master list" record consisting of "file" subrecords, and after that, all other content records. A "record" looks like this:struct Record {
UInt32 signature
UInt32 length
char[length] data
}
The record signature defines the type. If you open an *.AC.COBB file in a hex editor, you will see little bits like 'DECO' and 'HOME'; these are read (producing 'OCED' and 'EMOH') and bswapped (producing 'DECO' and 'HOME' again). The whole thing is set up so that if we encounter a record type that we don't understand, we can skip right over the whole thing. For example, right now, we always spawn houses in the same rotation; in the future, I could add an 'FYAW' subrecord to 'HOME' records and use that to always spawn houses facing the player, and it wouldn't break compatibility with older versions of the mod. That kind of thing is important because in the future, I hope to give other modders the ability to produce this data, and I don't want to force them to stay in sync with the latest AC version.
Master list records are as follows:struct MLST : Record {
struct FILE : Record {
UInt8 formIDPrefix;
UInt8 length;
char[length] fileName; // not null-terminated
}
UInt32 version;
FILE[...] files;
}
This is literally just a load order -- similar to what you'd find in an ESP's file header. We match load order indices to ESP/ESM filenames so that we can resolve form IDs in the data store. For example, Atronach Crossing has two masters, so all its forms in the data store are 02xxxxxx; we use MLST data to convert all of that to AC's actual load order in a user's mod list.
There may be a way to pull this data from Skyrim itself, but for now, we just specify it ourselves. This does have the effect that if you tamper with Atronach Crossing's masters, you'll probably break the mod completely.
Cobb Positioner compatibility
Homes and some decorations need to be informed about when they're edited, and when they move. Cobb Positioner is coded to call functions on them to inform them of this. Older versions of Cobb Positioner shipped with dummy versions of Atronach Crossing scripts, so that Cobb Positioner could be compiled to call functions on Atronach Crossing objects; this led to some shenanigans with load order requirements and so on. Newer versions of Cobb Positioner and Atronach Crossing share an "interface script" that allows Atronach Crossing to listen for editing events at its leisure, without the need for a specific load order.
Miscellaneous
Generating the Atronach Crossing merchant inventory
Because we store all Atronach Crossing decorations and homes in our own data store, we can keep track of them and perform group operations on them. For example, we can maintain a list of all decoration and home items. This makes it possible to dynamically generate a merchant inventory for them all -- no need to manually fill container details in the Creation Kit.
The basic mechanism is as follows: fill two form lists with all such items (these form lists are used elsewhere anyway) and use the lists to empty out the merchant inventory. Then, fill a leveled list with all such items and use that to fill the merchant inventory. Simple. The AtronachCrossing.ac.cobb file defines default-object entries that allow the DLL to identify the form lists, leveled list, merchant actor, and merchant container defined in AtronachCrossing.esp.
There is one hitch, though. A leveled list can only contain up to 255 entries at a time. Anything more, and you're gonna have a problem. We work around this by adding the items in batches. Simple.
2 comments