Skip to content

Prefabs

Demiosis edited this page Jan 19, 2023 · 31 revisions

Prefabricates are user-made constructions that Obsidian can use to further add a human touch to its procedurally-generated levels.

Prefabs in Obsidian use two files: a typical Doom WAD containing models, and an accompanying LUA script to determine how the prefab is to be used in-game.

Obsidian performs CSG (binary operations), to additively combine prefabs into a generated map.

Types of Prefabs

While prefabs fulfill a lot of functions for the game, they can be best categorized by where they are positioned.

  • Edge prefabs are wall and fence sections as well as doors, windows and archways on certain occasions.

One of the vanilla door fabs that also happens to be an edge fab. It's a pretty cool arch that doesn't afraid of any room. Notice the small part of the arch where the brown surface turns green - these are markers for the fab to use which textures from either side of the room.

  • Diagonal prefabs are diagonal wall and fence sections. (currently diagonal doors and windows are not supported)

A decorative diagonal wall piece.

  • Seed prefabs are prefabs that occupy especially designated sections of a map, usually CLOSETS, JOINERS. Things such as lifts, stairs, doors (switch-locked and key-locked or otherwise), traps, and cages can all qualify as seed prefabs. Seed prefabs heavily rely on Obsidian's shape grammar system.

A simple joiner fab.

  • Point prefabs are various fabs that are placed directly on the floor, such as crates, pillars, and other freestanding decorative objects along with important points such as item/key pedestals, including start and exit fabs if a closet doesn't exist for a seed fab to spawn in place of. Point prefabs often have different sizes, and Obsidian will attempt to place them wherever it fits based on a provided size based on the Chunk is to be placed in.

A decorative tech-themed point prefab, and one of the oldest in Obsidian.

BEFORE YOU BUILD THAT THANG

There are some important caveats and paradigms to understand when building prefabs for Obsidian. Before you start building the mansion of your dreams and expect to appear inside Obsidian, you'll want to know the following:

Rely on procedural generation

The goal of Obsidian is to procedurally generate maps, which means delegating the creation of rooms and layouts to an algorithm instead of the human doing it. If you wish to alter a map's layout - it is done so via shape grammars (check the Shape Grammars page here for more info) rather than a prefab.

It is better to design a furniture-sized prefab that Obsidian can build a house from than a house-sized prefab that you'd want to force Obsidian to spawn.

Fat shaming

The size of prefabs greatly matters. Going with the above notion of designing components rather than whole structures Obsidian can use, prefabs are limited by their size as, of course, rooms receive the priority before what prefabs in them are decided and placed. Creating overly huge fabs means there must be enough incidental space and probability for such a huge fab to spawn in the map. The following is an overall size guide:

  • For edge prefabs. If you are just making walls, make it 16-units y, 128-units x. Special edges such as doors and windows can be longer than 128-units x. Edge prefabs can be of any height. Edge fabs taller than the room currently being rendered are disqualified from appearing in that room. If you are building door or window edge fabs, the depth becomes 32-units y, compensating for the standard thickness of walls for either side of the room.

  • For point prefabs. Obsidian subdivides rooms into squares called Chunks. Chunks are used for multiple important procedures such as the placement of items and traps but for the concerns of making prefabs, chunks also define a spot where a point prefab can be placed. Larger fabs will basically require more space to appear and sufficiently large fabs may never appear at all by virtue of its size. It is preferable to create fabs within a 256-unit radius. Most decor fabs in Obsidian average around 64-128 units in radius.

  • For seed prefabs. Seed prefabs are placed on especially marked spots of a map, particularly closets and joiners. Closets can contain item closets, monster cages, start closets, exit closets, or sometimes simply just have a decorative seed fab in place. Joiners are prefabs that connect one room to the other.

As of the current shape rules, the following are the current available sizes for closet fabs (as per the current Obsidian shape rules and in seeds [a seed being a single 128x128 unit grid space])

1x1

2x1, 2x2, 2x3,

3x1, 3x2,

4x2,

There are special additional sizes but only for Urban Streets mode, due to how Obsidian uses closets to build many of the building facades:

6x1, 8x1,

The following are the current available sizes for joiners:+

1x1

2x1, 2x2, 2x3,

3x1, 3x2,

4x1, 4x2,

+ L-shape joiners currently only have a 2x2 rule.

Because joiners are fabs that partially reach through either side of the room, you may also need to add an extra total depth of 32 units such that the joiner will properly align against the walls on either side.

Hands down one of the best fabs in all of Obsidian, this key-containing toilet fab exactly fits a 2x3 seed closet. If it were smaller, such as having a 2x2 size, it would have the opportunity to occupy more footprint sizes as Obsidian allows generating smaller seed fabs into larger spaces and fitting/stretching them afterwards. The fab itself actually can extend further back up the y-axis by at least 16 units because of the wall thickness the fab extrudes from.

Monkey see, monkey do

The best way to create easily usable fabs for Obsidian is to follow what already exists. You can explore how existing fabs are built in the games/doom/fabs directory. It is better to decide what fab to do based on how you want it to appear in the map, before simply just building a fab at random.

What's in Your WAD

A typical prefab WAD can contain multiple prefabs, spread over different MAPs. It is probably preferable to separate prefabs into different WAD's entirely to make it easier to add, remove, and modify what already exists.

In order to start, first create a large room - the actual size of your initial room doesn't matter as it will just house your prefab. Assign all surfaces of this room with the texture '_NOTHING'. All surfaces marked with _NOTHING are essentially ignored by Obsidian, allowing the room the prefab is to be placed in to fill in these areas instead.

Set this room's brightness to 144. This is the neutral value used by Obsidian. Brighter or darker sectors will be offset against the ambient brightness of the room the prefab is placed in.

Obsidian's uses the concept of a 'skin' whereas a 'skin' is a grouping of a flat and a texture. These groupings can be found in the respective games' themes.lua.

The following are special texture names Obsidian uses for dynamic texturing:

  • _WALL - Surfaces marked with this will use the wall skin of the room it's in.
  • _FLOOR - Surfaces marked with this will use the floor skin of the room it's in.
  • _CEIL - Surfaces marked with this will use the ceiling skin of the room it's in.
  • _OUTER - If your prefab connects to another room, _OUTER surfaces will be replaced with the wall skin of that room. (must be placed on the northern part of your prefab. Only useful with joiner/window/fence prefabs.)
  • _FLOOR2 - If your prefab connects to another room, _FLOOR2 surfaces will use the skin of that other room. (must be placed on the northern part of your prefab. Only useful with joiner/window/fence prefabs.)
  • _CEIL2 - Just like FLOOR2, surface will use the ceiling skin of the other room. (must be placed on the northern part of your prefab. Only useful with joiner/window/fence prefabs.)
  • _LIQUID - This surface will be changed to the liquid texture chosen for that room. If this room has no liquid, it will default to the default texture specified in themes.lua instead.

A perfectly usable technique is to actually set ceiling textures to use _WALL instead, so it will receive the corresponding flat assigned to that skin. Seed prefabs connecting to outdoor areas especially would rather use _WALL and _OUTER for ceiling textures as opposed to the actual _CEIL due to the tendency for the outdoor sky ceiling to be copied into the seed's ceiling.

If you are making edge or seed prefabs, the south-west bounding box corner of your prefab is the origin point ( 0,0 ). If are making a point prefab, the center of the object is the origin.

What's in Your LUA

Each prefab has its own LUA definitions that change how Obsidian will treat this prefab. If you have a box.wad prefab, for example, your script file must be box.lua. Check already existing examples of prefab LUA's as some fields are not required depending on circumstance or between types of prefabs.

A Prefab Struct

Each prefab is encapsulated by a group of fields that look like this:

PREFABS.<name_of_prefab> =
{
  <..fields..>
}

Hint: You can use an online LUA editor like this one: https://www.jdoodle.com/execute-lua-online/ To reduce the chances of code grammar errors.

Fields

File System

  • file - this field simply specifies which WAD is to be loaded. e.g. file = "decor/box.wad"
  • map - specifies which map from the referenced WAD to use for this template.

Probability/Picking

  • prob - the probability for this object to be spawned. A larger number means the fab is more likely to be picked than the other.
  • rank - gives a special priority for this object to be spawned. A larger number means this fab will always be picked over the lower-ranked. A ranked fab will always be picked over a rankless fab as well. In conjunction with other fields based on Placement, it's possible to control the picking behavior for fabs.

Placement

  • where - specifies what kind of prefab this is and where it is to be placed. Options are stated as per above as follows:"edge", "diagonal", "seed", "point"
  • group - this is a field that accepts a string. All prefabs that share the same string on this field will always be used with each other in a single room. (ex: low_gap) If this field is to be used, the group will only be recognized if it's also defined in the game's theme.lua.
  • kind - this is used to further specify what sort of prefab this is. Obsidian actually uses the folder a prefab it's in to identify its kind with this likely overwriting it if declared. (ex: "light" for light fixtures, "arch" for edge openings if the theme has archy_archs set to true in the themes tables)
  • env - specifies the kind of room this prefab should be in. Options are "cave", "park", "outdoor", and "building" with 'building' meaning interior. You can append a ! as a not-operator e.g. "!cave" means not in "caves". Not declaring this field or declaring env="any" means having the prefab spawn in any type of environment.
  • neighbor - used for seed prefabs with adjoining rooms - accepts the same options as env, limiting the acceptable adjoining room type.
  • theme - specifies what theme this prefab can be used in. The themes are "tech", "urban", "hell". Much like the env field, you can use a not-operator as well. Not declaring this field means this prefab will spawn in all themes. For Doom 1-ish games "deimos" and "flesh" are available. "Tech" and "hell" are treated as Deimos theme-compatible fabs. "urban" and "hell" fabs are treated as The Flesh theme-compatible fabs.
  • game - specifies the game this prefab can spawn in such as "doom1", "ultdoom", "doom2", etc. "doom1" and "ultdoom" fabs are considered compatible with "tnt", and "plutonia".
  • engine - specifies what sourceport this prefab can spawn in i.e. "zdoom", "boom", and so on. Not declaring this means this prefab spawns for all sourceports. (you may want to prevent ZDoom slope prefabs from being used in non-ZDoom ports for example) Obsidian currently only supports "limit" (for any standard limit-removing engine), "boom" for Boom-compatible engines, "zdoom" for ZDoom-family engines, "edge" for EDGE-Classic, and "eternity" for Eternity Engine.
  • shape - required for seed prefabs - specifies the implied layout shape of this prefab based on Obsidian's shape grammars. Known options are:
    • I - used for straight joiners.
    • L - used for L-bend joiners.
    • U - used for terminating pieces. (closets, wall cages, wall secrets) Does not actually seem necessary.

Fitting

  • long - mostly for edge fabs, specifies how long the fab is in raw Doom map units. This is largely unused because seed_w is considered a better measure.
  • deep - specifies the extra depth of this prefab south of it. (the extra y-axis length, such as on seed prefabs where it must occupy the seed space + the walls opening on either side.)
  • over - specifies extra depth of this fab north of it. (same tidbit as above, but ONLY used on joiners prefabs, they usually have something like deep=16 and over=16 to ensure that the joiner fab meets up precisely with the walls of either rooms.)

Prefabs ecology

  • seed_w - required for seed prefabs and edge prefabs that occupy more than 1 seed - specifies the width (x-axis length) in seeds (remember that 128-units is a single 'seed' in Obsidian terms) this will fill. If a prefab, for example, is defined to be 3-seeds wide but the actual model less than that, Obsidian will use the x_fit field to determine how to stretch this prefab in that space.
  • seed_h - required for seed prefabs - specifies the depth (y-axis length) in seeds. Much like seed_w, the y_fit field influences how the prefab is stretched across the specified amount of seeds.
  • height - specifies the minimum height of the room this object can spawn in.
  • size - required for point prefabs - this determines the size of your point prefab's bounding box from the point of origin. A smaller prefab will find itself easier to spawn than a larger one as large spaces are less common. Most decorative point prefabs such as crates are use 64-units of size.
  • bound_z1 - the lower bound (in z-axis) of this object's bounding box.
  • bound_z2 - the upper bound (in z-axis) of this object's bounding box.
  • [axis]_fit - Replace [axis] with x, y, or z. This determines how your prefab is stretched on the chosen axis. The following are the options:
    • stretch - this allows your prefab to be rubberband-stretched from end to end, filling the space intended for it.

    • top - for the y_fit option: your prefab will be placed from the northmost seed's top vertices and stretch down to the southmost vertices.

      • for the z_fit option: your prefab will be placed from the floor and then the topmost vertices will be pulled to the ceiling.
    • bottom - for the y_fit option: your prefab will be placed from the southmost seed's bottom vertices and stretch up to the northmost vertices.

      • for the z_fit option: your prefab will be placed at the ceiling with the bottom-most vertices pulled down to the floor.
    • frame - your prefab is placed at the dead center. Think center-aligning text. The bounds of your prefab are then pulled to meet the floors/ceilings and so on. Think, uhh, Hellraiser. It is possible to define no [axis]_fit, forcing Obsidian to allow the fab only in the same exact spaces.

    • { r1,r2, ... } - this allows you to specify a specific range around the center of your prefab to be stretched. e.g. { 48,56 }. For example, it would make sense for only the stem of your pillar to stretch, rather than the crown or the base with it. You can have define multiple ranges within the grouping. For example { 24,32 , 64,72 } means the stretching happens between those pairs i.e. useful if you have a double door fab and want the doors themselves to stretch, but not the pillars in between and so on.

  • delta - this along with sector special 997 allow Obsidian to generates sectors under floor height 0. Otherwise the generator ignore everything under 0 floor height when building a prefab.

Entities

  • item_kind - if this prefab has a thing of type 8166, item_kind can be used to limit the sort of item that spawns in place of thing 8166 i.e. a key.
  • key - used for key-locked door seed prefabs. Determines which kind of key this door opens against. Options are "k_red", "k_blue", "k_yellow", "k_ALL", "sw_metal", "barred". k_ALL means this door only opens when all three level keys/skulls are in your inventory. Barred means this door is to be locked by a switch in the same room while sw_metal is to be locked by a switch elsewhere.

Other Filters

  • door_action
  • sink_mode - only option is "never" or simply not declared. This ensures this fab will not spawn in 'sink' areas which are the little sunken sectors Obsidian makes such as with liquids on walkable floors or just fancy trims. Roads in Streets Mode are considered sinks. Because of the way sinks are rendered on the CSG side, insufficiently tall fabs will often look broken when rendered over floor sinks, or reach too high into a ceiling sink.
  • solid_ents - true or false. Specifies whether things inside this prefab should have collision or not. You'll want candelabras to block your path, but probably not corpses.
  • open_to_sky - true or false. Seed prefabs with this property will spawn not being inset into a solid stucture such as the items and exit gazebos.
  • on_liquids - accepts "never" or "only". For wall fabs only, this will cause fabs not to spawn or be limited to spawning over liquid seeds.
  • on_scenics - accepts "never". Mostly only for edge fabs, this will cause edge fabs not to spawn in scenic areas. Fake doors over a sheer cliff? That's just too fake.
  • can_be_on_roads - accepts true. Allows a decorative point fab to spawn over a road area in Streets Mode. sink_mode will override this functionality as Street Mode roads are considered sinks. (despite them not being 'sunken', they are simply technically rendered as sinks.)
  • replaces - accepts the name of another prefab. When Epic Textures module is on, this fab replaces the fab in the field based on the behavior defined in the replace_mode field. If the replace_mode field is not defined, it is assumed to be a hard replacement.
  • replace_mode - accepts "hard" or "soft". Hard replacement causes the fab to be replaced to be completely removed along with its templates. Soft replacement simply causes the fab to be replaced have a probability of zero. Soft replacement is preferred most of the time.
  • need_solid_back - accepts true. For edge fabs only, this ensures that the seed behind this edge is completely solid, a necessity for things like fake doors and windows.
  • sound - specifies the type of ambient sound to be played for the fab, as defined in ZDoom Ambient Sound Definitions (e.g. sound = "Water_Streaming"). This must be accompanied by one or more sound things (each of type #8185) placed in corresponding MAP(s) of the WAD file. Note that there is currently only one type of ambient sound that can be specified for each fab. In order to actually hear the sounds from the ambient sound things placed in an Obsidian-generated map, the ZDoom: Ambient Sounds option must be enabled from the GUI, and an ambient sounds pack (e.g. PK3 file) containing the corresponding sound files (example here) must be added to your Doom launcher.

Special Fields

  • native_hexen - This should only be set to true if needed. Specifies that this is a Hexen-formatted prefab, and not one converted from Doom format. Without this, Hexen thing TIDs and action specials will not be preserved.
  • tex_[wall texture] - this allows you to change a wall texture on your prefab. e.g. tex_BIGDOOR1 = "BIGDOOR5" means any wall with BIGDOOR1 will be changed to BIGDOOR5.
  • flat_[flat texture] - this allows you to change a floor texture on your prefab. e.g. flat_CRATTOP = "FLOOR4_8" means any floor/ceiling textured with CRATTOP will be changed to FLOOR4_8.
  • sector_[special_id] - allows you to change the sector special of all sectors with this existing special.
  • line_[special_id] - functions just like sector_#, except for linedefs.
  • thing_[type_id] - allows you to change all entities of this type to another. e.g. thing_25 = impaled_twitch changes all things with type_id 25 (impaled humans) into impaled_twitch (the animated, twitching version).
  • tag_[id] - this is used in Obsidian's quest generation (locked/switched doors). Options are ?switch_tag or ?door_tag.
    • ?switch_tag - linedef with tag [id] is treated as a switch for quest generation. This switch will open some other locked door in the level. This linedef should use reserved special 888.
    • ?door_tag - sector with tag [id] is treated as a door for quest generation.
    • ?out_tag and ?in_tag - used for teleporters

Special fields can also receive a LUA table whereas you can specify multiple options and respective probabilities. For example:

thing_25 = 
{
  impaled_twitch = 50
  gutted_torso = 50
} 

This means thing_25 can be randomly changed to become an impaled twitching human or a gutted torso with the 50:50 odds. Another example:

sector_1 = { [0]=90, [1]=15 }

This means the sector with special ID 1 can have high odds (90) of being a normal sector, or lower odds (15) to become a sector with randomly blinking lights using sector special 1.

Templates

  • template - using templates, you can create a new prefab definition based on an already existing one. This is for when you want to create, for example, a door that gets different textures but is still the same door model all throughout. For example:
PREFABS.Basic_door = {
  file = "decor/mydoor.wad"
  map = "map01"

  theme = "tech"
  <..other fields..>
}

PREFABS.Basic_door_with_different_texture = {
  template = "Basic_door"

  theme = "urban"
}

In this scenario, Basic_door_with_different_texture copies all the properties of Basic_door, and all declared fields here will overrule whatever is declared from the previous template. Both Basic_door and Basic_door_with_different_texture will both be spawned as two different prefabs by Obsidian. In this specific example, Basic_door will only spawn on tech-themed maps, while Basic_door_with_different_texture will only spawn in urban-themed maps.

Reserved Sector Specials for Obsidian

  • 992 - WADFAB_REACHABLE - this force Obsidian to generate enclosed or closed sectors.
  • 995 - this marks this sector as a MOVER (lift), and forces bottoms of sidedefs to be unpegged. (The lines composing the affected sector need the 991 action. It can also be used to force the generator into unpegging some surface the genny refuse to do itself!)
  • 996 - this marks this sector as a DOOR, and forces the tops of sidedefs to be unpegged. (The lines composing the affected sector need the 991 action. It can also be used to force the generator into unpegging some surface the genny refuse to do itself!)
  • 997 - WADFAB_DELTA - this combined with delta = X, in the prefab's lua will allow obsidian to lower sectors by the defined number under floor height 0 (otherwise sectors under height of 0 simply get booted back up to 0 and cleaned up).
  • 987 - WADFAB_LIGHT_BRUSH - even if this sector is marked _NOTHING on the floor and ceiling, Obsidian will adopt the sector's brightness setting

Reserved Thing ID's for Obsidian

  • 8166 - spot for a big item to pickup (Weapon, Key, Armor, Power-up). Can be influenced with item_kind field to limit what item this should be.
  • 8151 - spot for small pickups like armor and health bonus or small ammo drops.

Regular Monsters

  • 8102 - spot for a monster with a radius of 20 or less(Imps, Zombies, Revenants, Lost Souls, Archviles)
  • 8103 - same as above, but for monsters below a maximum radius of 32 or less(Pinkies, Cacodemons, Hell Knights, Barons, Pain Elementals)
  • 8104 - same as above, but for monsters below a maximum radius of 48 or less(Mancubi, Cyberdemons)
  • 8106 - same as above, but for monsters below a maximum radius of 64 or less(Arachnotrons)
  • 8108 - same as above, but for monsters below a maximum radius of 128 or less (Masterminds)

For Hexen-formatted fabs using the native_hexen paramter, these numbers will be 8105-8109 instead to prevent conflicting with existing Thing numbers! Note: 8102 spots that generate revenants may have their ranged attacks nullified because of the weird height of the monster combined with low ceilings.

Flying Monsters

  • 8112 - same template pattern as regular monsters, but capable of flight
  • 8113
  • 8114
  • 8116
  • 8118

Caged Monsters

  • 8122 - same template pattern as regular monsters, but spawn in cages and have projectile/missile attacks
  • 8123
  • 8124
  • 8126
  • 8128

Closet/Trap Monsters

  • 8132 - same template pattern as regular monsters, but spawn in monster closets or traps
  • 8133
  • 8134
  • 8136
  • 8138

Note: Bigger spots can be used by one or several smaller monsters than defined size but their position is going to be randomized within the spot

Lights

  • 14999 White
  • 14998 Red
  • 14997 Orange
  • 14996 Yellow
  • 14995 Blue
  • 14994 Green
  • 14993 Beige
  • 14992 Purple

Custom decorations

  • 27000 Hospital blood pack
  • 27001 Fire
  • 27002 Fire with debris

Reserved Linedef Specials for Obsidian

  • 888 - this linedef will be used as a switch for quest generation in Obsidian e.g. this switch can open some other switched door in the level.

Common Issues

Texture Alignment

Obsidian ignores wall texture pegging set in the WAD. For specific cases such as lifts, texture pegging is achieved instead by using a reserved sector special for Obsidian - i.e. 995 for lifts (bottom-unpegged), 996 for doors or crushers (top-unpegged). Any texture with a (0, 0) offset in either x or y-axis will have their alignments based on Obsidian's world space, ignoring how it looks in the map editor. Any texture with a non-0 offset will have their texture alignments obeyed by Obsidian. In order to force-align a texture that should have a naturally (0, 0) alignment, use (1, 1) instead. Obsidian will rectify the offset by subtracting 1.

Thicc Edges

Edge prefabs deeper (longer in y-axis) than 16-units will NOT actually sink into walls, but press out against them. If you want a wall that occupies space behind it, you may want to create a U-shaped seed prefab instead. Beware that if you are also intending to make thicker walls, these walls will eventually interrupt other level geometry such as stairs - make them thick enough, you might as well prevent the player from passing through this area.

Same Height Floors

Floor of the same height and texture will automatically get cleaned by the generator. to force them to generate still the designer need to add sector tags to the pieces the generator must keep

Vertex Density

Obsidian's CSG seems to explode violently if verts are in coordinates smaller than 4x4 grid spaces. Beware! 4x4 grid space is the smallest safest you can work with.

align

EVEN when working on a 4x4 grid and placing the lines points properly on it, CSG errors can still happen, its really the combination of the grid AND proper line length, if you do not have both it will most likely generate CSG errors. To alleviate this one can cut his fabs into multiples pieces reducing the chances CSG act up! (Do not worry about the extra lines, they will get automatically cleaned up by the generator if the floor height and textures are the same.) Finally, angles smaller than 45 degree do not sit well with the generator and tend to mess things up.

dividing

Line Loop Not Convex but everything else seem's fine?

Transfer the fab to a new wad file in GZDoom Builder. I personally never encounter any problem with this issue compared to peoples who worked in Slade.

Cage or picture fab with decoration in front of them do not generate?

The prefab itself need to be 2 cells wide minimum along with seed_w = that must be at least 2 for it to generate!

Prefabs with decoration in front of the wall generate weirdly in outdoors or have wrong light value?

Make sure you have a raised bit of 12 under the decorative bits and at least _floor along with _nothing, plus the raised floor bit should have empty textures. (Refer to dem_pic_fake_windows.wad for more infos.)

Error Reporting

Obsidian will sometimes be unable to peruse a prefab due to author error i.e. it tries to fit an object into a certain spot, but the actual physical object is much larger than the spot. More detailed error reporting can be enabled from the File->Options menu.