TFEM (“TFE Map”) Spec [Draft]

This is an early draft and is not final, some features or elements may be missing.

The Force Engine will support the Dark Forces LEV format, Outlaws LVT and LVB formats and a super-set format called TFEM (“TFE Map”). Unfortunately the Jedi Engine reads text based data files in a fairly rigid way, meaning that there is no provision for skipping default parameters or adding new unknown parameters or structure. For this reason, the original formats are not very useful for adding new features or for the level editor. This format has some similarities to the UDMF format and to the vanilla game formats and is my attempt to “get ahead” of the format mess and allow the same format to be used while adding new features in the future and for the format to be the master format used by tools. (Full disclosure, the idea for the Comment value added to many blocks was taken directly from UDMF though that certainly wasn’t the first format with that idea. But it was a very good idea).

As a super-set format, TFEM will support the entire feature set of the Dark Forces version of Jedi and the Outlaws version as well as any new features added for The Force Engine. It will remain a text based format, though a binary “compiled” version may also be supported later. Finally it will support default values - in which case the parameter does not have to be specified and map readers must skip unknown values or blocks without giving an error - making the format more robust and making versioning easier. The plan is to use the TFEM format as the level editor format, which can then be exported to other formats as requested. If using Outlaws features in Dark Forces or using new map features not present in vanilla, the TFEM format must be used - in other words there will be no Outlaws in Dark Forces style formats. However when using the TFEM format all of the level features of both Dark Forces and Outlaws will be accessible when using the “TFE” featureset (and any addition TFE specific features).

To be clear, this format does not replace the original LEV, LVT and LVB formats - The Force Engine will continue to read the native formats without conversion. It is intended as a extensible format for new, non-vanilla, mods and as an editor format.

In order to control the features used, the header includes a FeatureSet value which must be set when creating a new map. Initially the feature sets will be Dark Forces, Outlaws and TFE though more fine grained TFE feature sets may be added in the future. Note that the “TFE” or “Outlaws” feature sets can be used with Dark Forces or Outlaws maps when using The Force Engine.

Another major change between TFEM and vanilla formats is that objects are included directly in the map, attached to their parent sectors. This allows objects to be explicitly assigned to sectors without having to uniquely name each sector and makes loading simpler.

This format is meant to be machine generated by visual tools but it remains text by default for several reasons:

There are some downsides, of course, so as larger file sizes. However The Force Engine will support reading mods directly from ZIP files, reducing the size impact. There is a possbility of adding a binary target later, similar in nature to LVB.

Basic syntax

BlockName { Name1: Value; Name2: Value; Name3: Value1, Value2, Value3; ... }

Nested blocks are allowed. Semi-colons are optional unless appending multiple name/value pairs on the same line The above example without semi-colons:

BlockName
{
  Name1: Value
  Name2: Value
  Name3: Value1, Value2, Value3
  ...
}

In some cases there are lists of values, which have the following syntax (item number comments are optional):

ListName: Count
{
  Item0  // 0
  Item1  // 1
  Item2  // 2
}

Multi-value items are also possible, such as vertices:

ListName: Count
{
  X0, Y0  // 0
  X1, Y1  // 1
  X2, Y2  // 2
}

Lists of blocks are similar:

ListName: Count
{
  BlockName
  { 
    ...
  }
  BlockName
  {
    ...
  }
}

Inline comments are allowed but may not be preserved by tools; both single line comments #, // and block comments /*...*/. Most blocks will also have an optional Comment field which will be used by tools to allow users to add comments to sectors, walls and other elements that are visible in the editor. The Force Engine text parser will be used to read the files as it is already being used to read Dark Forces and Outlaws text based assets.

Value Formats

Values may take on one of the following formats:

Level format

Header
{
  Version: <float>       // TFEM version, default = 1.0
  FeatureSet: <string>   // Possible values: "Dark Forces", "Outlaws", "TFE"
  DisplayName: <string>  // Name displayed for the player.
  SlotName: <string>     // Name of the level slot the map occupies, game specific.
  Palettes: <int>        // Palettes used by the level, ignored when using the Dark Forces featureset.
  {
    <string>
    <string>
    // ...
  }
  Colormaps: <int>       // Colormaps used by the level, ignored when using the Dark Forces featureset.
  {
    <string>
    <string>
    // ...
  }
  Music: <string>             // Main music track to use, ignored when using the Dark Forces featureset.
  Parallax: <float>, <float>  // Sky parallax for X and Y.
  Comment: <string>           // An optional comment that is visible and preserved by tools.
}

Textures: <int>  // List of textures used by the level (BM for FeatureSet: Dark Forces).
{
  <string>           // 0
  <string>           // 1
  // ...
}

Pods: <int>  // List of 3D object assets (3DO for FeatureSet: Dark Forces or Outlaws).
{
  <string>           // 0
  <string>           // 1
  // ...
}

Sprites: <int>  // List of Sprite assets (WAX for FeatureSet: Dark Forces, NWX for FeatureSet: Outlaws).
{
  <string>           // 0
  <string>           // 1
  // ...
}

Frames: <int>  // List of Frame assets (FME for FeatureSet: Dark Forces).
{
  <string>           // 0
  <string>           // 1
  // ...
}

Sounds: <int>  // List of sound assets (VOC for FeatureSet: Dark Forces, WAV for FeatureSet: Outlaws).
{
  <string>           // 0
  <string>           // 1
  // ...
}

Sectors: <int>  // List of sectors.
{
  Sector  // #0
  {
    Name: <string>         // Sector name, used for INF/scripting.
    Layer: <int>           // Sector layer, default = 0
    VerticalAdjoin: <int>  // Sector adjoined to the floor or ceiling (ignored for the Dark Forces featureset).
    Comment: <string>      // Comment should be visible and preserved by tools.
    
    Lighting
    {
      Ambient: <int>         // Sector ambient.
      MasterOnAmbient: <int> // Ambient level when "master on" message has been recieved.
      
      // Per-sector colormap/palette is ignored for the Dark Forces featureset.
      Palette: <int>         // Palette index
      Colormap: <int>        // Colormap index
    }
    
    Floor
    {
      // Texture/Overlay Angle is ignored for the Dark Forces featureset.
      // Floor texture: Index - index into the texture list, Offset - texture offset in world units.
      Texture { Index: <int>; Offset: <float>, <float>; Angle: <float>; }
      
      // Floor overlay texture (ignored for the Dark Forces featureset).
      Overlay { Index: <int>; Offset: <float>, <float>; Angle: <float>; }
      
      // Floor height in world units.
      Height: <float>
      // Offset height, used for liquids and bridges (Second height in Dark Forces).
      HeightOffset: <float>
      // Floor slope (ignored for the Dark Forces featureset).
      Slope { Sector: <int>; Wall: <int>; Angle: <int>; }
    }
    
    Ceiling
    {
      // Texture/Overlay Angle is ignored for the Dark Forces featureset.
      // Ceiling texture: Index - index into the texture list, Offset - texture offset in world units.
      Texture { Index: <int>; Offset: <float>, <float>; Angle: <float>; }
      
      // Ceiling overlay texture (ignored for the Dark Forces featureset).
      Overlay { Index: <int>; Offset: <float>, <float>; Angle: <float>; }
      
      // Ceiling height in world units.
      Height: <float>
      // Ceiling slope (ignored for the Dark Forces featureset).
      Slope { Sector: <int>; Wall: <int>; Angle: <int>; }
    }
                
    // Sector physics properties (ignored for the Dark Forces featureset).
    Physics
    {
      Friction: <float>
      Gravity: <float>
      Elasticity: <float>
      Velocity: <float>
      SoundFloor: <string>
    }
    
    // Flags are explicity named since numbers can change from game to game.
    // Default for all flags is false, so omit any unused flags.
    // See the Flags section below for an exhaustive list.
    Flags
    {
      Exterior: <bool>
      Door: <bool>
      MagSeal: <bool>
      ExteriorAdjoin: <bool>
      IceFloor: <bool>
      SnowFloor: <bool>
      // ...
      SlopedFloor: <bool>
      SlopedCeiling: <bool>
      // ...
    }
    
    Vertices: <int>  // Vertices referenced by the sector.
    {
      <float>, <float> [, <float>]  // XZ values or XYZ values depending on feature set.
      <float>, <float>
      // ...
    }
    
    Walls: <int>  // Walls used by the sector
    {
      Wall
      {
        Indices: <int>, <int>  // Vertex indices.
        
        // Middle Texture - Index: index into texture table, Offset: UV offset in world units.
        TextureMid: { Index: <int>; Offset: <float>, <float>; }
        
        // Top Texture - Index: index into texture table, Offset: UV offset in world units.
        TextureTop: { Index: <int>; Offset: <float>, <float>; }
        
        // Bottom Texture - Index: index into texture table, Offset: UV offset in world units.
        TextureBot: { Index: <int>; Offset: <float>, <float>; }
        
        // Overlay Texture (Sign in Dark Forces) - Index: index into texture table, Offset: UV offset in world units.
        TextureOverlay: { Index: <int>; Offset: <float>, <float>; }
        
        Light: <int>   // Additional wall light, omit if 0.
        Adjoin: <int>  // The sector adjoined at this wall. Omit if this is a solid wall.
        Mirror: <int>  // Wall Id of the adjoined sector. Omit if this is a solid wall.
        
        // Double adjoins are ignored for the Dark Forces featureset.
        DAdjoin: <int>  // The second sector adjoined at this wall. Omit if this is a solid wall or only has one adjoin.
        DMirror: <int>  // Wall Id of the second adjoined sector. Omit if this is a solid wall or only has one adjoin.
        
        Comment: <string>  // Optional comment.
        
        Flags
        {
          // Flags are explicity named since numbers can change from game to game.
          // Default for all flags is false, so omit any unused flags.
          // See the Flags section below for an exhaustive list.
          AdjoinMidTexture: <bool>
          FlipHorizontal: <bool>
          // ...
        }
      }
      Wall { ... }
      // ...
    }
    
    Objects: <int>
    {
      Object
      {
        Class: <string>
        Data: <int>
        Difficulty: <int>
        Comment: <string>
        
        Physics
        {
          Position: <float>, <float>, <float>
          Pitch: <float>
          Yaw: <float>
          Roll: <float>
          Radius: <float>
          Height: <float>
        }
        
        Logic
        {
          Name: <string>
          DeltaYaw: <float>
          DeltaPitch: <float>
          DeltaRoll: <float>
          // Flags are explicity named since numbers can change from game to game.
          // Default for all flags is false, so omit any unused flags.
          // See the Flags section below for an exhaustive list.
          Flag_AnimateYaw: <bool>
          Flag_AnimatePitch: <bool>
          // ...
        }
        
        Generator
        {
          Logic: <string>
          NumTerminate: <int>
          Delay: <float>
          Interval: <float>
          MaxAlive: <int>
          MaxDist: <float>
          MinDist: <float>
          Master: <bool>
        }
        
        Animation
        {
          VueAsset:  { Name: <string>; Xform: <string>; }
          VueAppend: { Name: <string>; Xform: <string>; } 
          Pause: <bool>
        }
      }
      Object { ... }
      // ...
    }
  }
}

Sector Flags

TODO

Wall Flags

TODO

Logic Flags

TODO