Copyright: DF_SPECS written Y.BORCKMANS 1995


Title Page

UNOFFICIAL

SPECIFICATIONS

v 1.90

Welcome

General Description

File Formats

Cheat Codes

dark.exe command line

Author and Credits

Copyrights


Welcome Page
Welcome to the "DARK FORCES SPECS" !

Quoting DF_SPECS v. 1.00:

"As you will rapidly see, there are still a lot of unknown things in these specs. But as they are diminishing very quickly, I found it was time to write them down in a "formal" way, which can serve as a reference."

Well, things have moved a lot thanks to a lot of DF fans (especially Jereth KOK, but see the complete Credits), so it was time to update the Specs... As time was getting short for the scheduled release date for WDFUSE, part of the new information I have didn't make it to this interim (1.90) version, and some items are not presented like I really would like, so you may expect a 2.00 release of the Specs soon.

This 1.90 version is now a Windows Hypertext Help file, and should be easier to use.

As always, nothing is as good as seeing how professionals do things, so don't hesitate to go and see an example of how the LucasArts team implemented what you want to do.

I will try to explain differences with DOOM level making where applicable, so if you have experience in that domain, look for the Doom note hyper jumps in the header of some pages.

Removing the Doom notes from the body of the text will make life simpler for newcomers, who have been buried in two jargons mixed together until now.

May The Force Be With You,

Yves Borckmans

Note

I will frequently use the following abbreviations :

DF	Dark Forces
LEC	LucasArts Entertainment Company
SC	sector
WL	wall
VX	vertex
OB	object
TX	texture


General Description
Doom Note

Dark Forces contains a huge amount of files, and these are grouped by type in what are called GOB files. They serve as containers for other files which in turn contain information.

These files can be extracted from the GOB and worked upon, then be used in the game.

LucasArts made a very good job at this :

1) if you place a file in your game directory, DF will take it there before looking for it in its GOB.

2) if you launch DF with the -u option, it will look into the specified .gob.
The command line becomes : dark -umylevel.gob
This is by far the best method to distribute new levels.

The order in which DF looks for a file is as follows :

1) as a file in the installed directory

2) in the GOB specified by -u...

3) in its normal GOB in the installed directory

4) in its normal GOB on the CD

A very important file jedi.lvl contains the names of all the levels in the game. Sadly, editing it doesn't work further than the briefing because the level names are hardcoded in dfbrief.lfd .So, when creating new levels, you must patch an existing one. You cannot create a new level name and have it accepted by DF.

What you can do however is change the levels titles in jedi.lvl.

The levels themselves are each one composed of 6 files, found in dark.gob .

FILE	USE	DOOM EQUIVALENT
name.LEV	geometry (static)	SECTORS, LINEDEFS/SIDEDEFS, VERTEXES
name.INF	workings (dynamic)	none, except the TAG concept
name.GOL	goals	none
name.O	objects	THINGS
name.PAL	palette	PLAYPAL 0 (not the 'hit' palettes)
name.CMP	palette mappings	COLORMAP

Textures	are stored in .BM  files, as are the weapons display, and so on.

Sounds	are stored in .VOC files (normal Creative Labs format).

Objects	are stored in the following files depending on their type :

obj.3DO	3D	object (real 3D)
obj.FME	FRAME	(a "one view" object)
obj.WAX	SPRITE	(i.e. all the enemies)
obj.VOC	SOUND	(any sound)

3D object motions are stored in VUE files (normal 3D Studio format).

There are still many other unexplored files.


File Formats

Container/Distribution Format

  GOB

  LFD

Level Related

  JEDI.LVL

  LEV

      Sector Flags and Wall Flags

  INF

  GOL

  O

  PAL

  CMP

Graphics Related

  BM

  FME

  WAX

  3DO

  VUE

Sound Related

  VOC

  GMD

Text Messages Related

  MSG

Other Files

  DFBRIEF.LFD

  BRIEFING.LST

  CUTSCENE.LST (+ cutscenes LFD files)


CHEAT CODES

Just in case you haven't found them anywhere else !

I've also shown the equivalent cheat for DOOMers.

DF CHEAT	NOTES	DOOM CHEAT
LABUG	bug mode
LACDS	map with things	IDDT x2
LADATA	coordinates & %secret	IDMYPOS
LAIMLAME	total invincibility	IDDQD
LANTFH	teleport to a safe
LAPOGO	allow to climb any height	IDSPISPOPD/IDCLIP w/o walking thru
LAPOSTAL	get weapons and ammo	IDFA
LARANDY	weapon super charge
LAREDLITE	freeze enemies
LASKIP	finishes current level
LAUNLOCK	get all the keys	the 'K' of IDKFA
LAlevelname	jump to level	IDCLEV

Please note that (just like in Doom) these codes are directly scanned from the keyboard, and so correspond to a QWERTY keyboard disposition. So a French user on an AZERTY keyboard would have to type 'LQI,LQ,E' on his keyboard instead of 'LAIMLAME'.


dark.exe command line

Here are all the command line parameters for the Dark Forces executable:

(ordered by probable usefulness)

Parameter	Description
-ugob	use an user gob file (where gob=your gob file)
-shots	enable screen shot mode (use Print Scrn key)
-c0	disable cutscenes
-llevel	play a particular level (where level=yourlevel)
-xd	specify CD-ROM drive letter (where d=drive)
-f	don't check to see if FILES= is set high enough
-t	autotest mode (runs all levels briefly)
-g	create a text file with a list of all files that were opened during the running of the game


GOB FILES
Doom Note

GOB files are a repository for many other files. They contain a header with a signature, a data part and an index part.

Their structure follows :

Index	Item	Type	Comments
0000	GOB_MAGIC	char[4]	is 'GOB' followed by 0x0A
0004	MASTERX	long	offset to MASTERN
0008	FILE1	bytes	data for FILE1
xxxx	FILE2	bytes	data for FILE2
....			...
xxxx	FILEn	bytes	data for FILEn
yy	MASTERN	long	number of files in the GOB
yy+4	INDEX1	GOB_INDEX	index to FILE1
yy+25	INDEX2	GOB_INDEX	index to FILE2
....			...
zzzz	INDEXn	GOB_INDEX	index to FILEn

The GOB_INDEX format is :

Index	Item	Type	Comments
0000	IX	long	offset of the start of the file
0004	LEN	long	length of the file
0008	NAME	char[13]	name of the file (null terminated)


LFD Files

LFD files contain various resources, mostly sound and graphics.

You don't need them to create a new level, but for things like cutscenes and briefings.

The Dark Forces LFD format seems to be completely compatible with X-Wing and Tie Fighter LFD files.

Header

struct LFD_INDEX_ENTRY

{

 char     SECTION_TYPE[4];

 char     NAME[8];

 long     LENGTH;

}

Data

LENGTH bytes follow the header.

The first section is of type RMAP, and contains the list of all the sections in the .LFD file.

This is similar to the GOB Master Index.

The other sections can be:

Section	Description
ANIM	animation ???
DELT	used in the briefings
FILM	this is like a mini embedded LFD, can contain ANIM, VIEW, …
FONT	font
GMID	General Midi music
PLTT	palette
TEXT	text
VOIC	VOC (standard Creative Labs format)

More on LFD in next Specs.


JEDI.LVL

| LEVELS 14

This is the number of entries in JEDI.LVL

| Secret Base,        SECBASE,

\ L:\LEVELS\SECBASE\;L:\LEVELS\;L:\LEVELS\BM-GEN\;

\ L:\LEVELS\PALETTES\;L:\LEVELS\BM-IMPER\;L:\LEVELS\HOLDER\

| ...

The remainder of this file contains the names of all the levels in Dark Forces.

Note that due to the way the names are hardcoded in the briefing files, you cannot change them.

What you may change however is the description ("Secret Base"), and it will appear changed in the mission selection screen in DF.

The paths stored are unused, and were most probably referring to the LucasArts file server at development time.


LEV FILES

LEV files contain a complete level geometry. They are in a quite complex text format.

They are also huge (generally > 600K), but this isn't a problem, as you really cannot edit them as a text file, because of the many dependencies between the geometry elements.

Geometry Elements

The Adjoin/Mirror/Walk mechanism

A Quick Note on Texturing

File Format


Geometry Elements
Doom note

The basic geometry elements of a DF level are :

VERTEX	a point in a 2 dimensions projection (X and Z)
WALLS	a line joining 2 vertices
SECTOR	a collection of walls generally closed, can contain "gaps" or other sectors
As the game works with a two dimensions projection, the third (Y) dimension is coded at the sector level by a floor altitude and a ceiling altitude.

Note that this imply that floors and ceilings of a sector are always FLAT.

Sectors can however be layered on top of one another to give a "full 3D" feeling.

Each sector is coded with its walls and vertices, and is completely self contained

The relation between sectors is done at the wall level by the adjoin/mirror/walk mechanism.


Adjoin/Mirror/Walk Mechanism
Doom note

1--------2    Sector 1 has 5 vertices (0 to 4) marked 0 1 2 A B

|        |                 5 walls    including AB (wall 3)

|   S1   |

|        |    Sector 2 has 4 vertices (0 to 3) marked 0 1 B A

0---B====A                 4 walls    including BA (wall 2)

    | S2 |

    1----0

It is VERY important to note that there are 2 vertices at point A, two vertices at point B and 2 walls marked ==== . As I said earlier, sectors are self contained.

 So, to come back to the adjoin/mirror/walk mechanism, if S1 and S2 must be connected, an adjoin/mirror relation must be established.

+----1---+

|        |

0        2

| 4    3 |

+---+====+

    1  2 3

    +--0-+

This is quite simple : the adjoin is the number of the connected sector, and the mirror is the number of the connection wall.

So we would need to set:

in S1 : W3.adjoin = S2 and W3.mirror = 2

in S2 : W2.adjoin = S1 and W2.mirror = 3

For the walk values, we would need to set:

in S1 : W3.walk = S2

in S2 : W2.walk = S1

Just to be complete, if there is no adjoin/mirror/walk, the value is -1.


A Quick Note on Texturing
Doom note

When you have adjacent sectors:

TOP  is  ABOVE the ceiling of the other SC

BOT  is  BELOW the floor   of the other SC

MID  is  everywhere you can see through to the other SC

Of course, the MID texture is not shown when walls are adjoined, so that you can see through!

(Note: WL flag 3, bit 1 forces it back in place. See Wall Flags)


LEV File Format

The LEV file is composed of 3 parts:

Magic, Version number and general level info

Texture Table

Geometry Description i.e. sectors, walls, vertices data

The following comments are accepted:

# comment

DATA # comment


LEV Magic, Version number and General Level Info

Magic and version number

This is trivial.

| LEV 2.1

General Level Info

This part contains the following data (sample from secbase.lev):

| LEVELNAME SECBASE

| PALETTE   SECBASE.PAL

| MUSIC     AVENGE.GMD

| PARALLAX  1024.0000 1024.0000

After a little testing, it seems that LEVELNAME, PALETTE and MUSIC aren't used at all by DF.

(I even cannot find AVENGE.GMD !)

PARALLAX determines how much the "exterior" backgrounds scroll as you turn. 1024 1024 means as you turn around 360 degrees, you will see 1024 pixel columns of background sky. Vertical PARALLAX is similar, although of course you can't pitch 360 degrees in DF.


LEV Texture Table
LEV Textures Table

As there is a lot of TX information in a level, a texture table is created to avoid storing TX names in full at each occurrence.

Coding sample :

| TEXTURES 85        # number of textures

| TEXTURE: TEX00.BM  # texture 0

| TEXTURE: TEX01.BM  # texture 1

| ...

| TEXTURE: TEX84.BM  # texture 84

Afterwards, all the textures are referred to by their 0 based index in this texture table.

Note that changing TX names in the TX table may be an ultra fast way to relook a level !


LEV Geometry Description

The first data is the total number of sectors in the level :

| NUMSECTORS number_of_sectors

Then each sector is described, with its vertices and walls.

Please note that the wall data is on ONE line, but has been split here for visual convenience.

| SECTOR scnum

|  NAME             sector_name

|  AMBIENT          20

|  FLOOR TEXTURE    80 -0.38 -0.06 2

|  FLOOR ALTITUDE    0.00

|  CEILING TEXTURE  0  0.00  0.00 2

|  CEILING ALTITUDE -12.00

|  SECOND ALTITUDE   0.00

|  FLAGS 0 0 0

|  LAYER            1

|

| VERTICES vxnum

|  X: 252.00 Z: 224.00 # a vx

|  ...

|

| WALLS wlnum

|  WALL LEFT:  0 RIGHT:  1

\       MID:   0  0.00  0.00 0

\       TOP:   1  0.00  0.00 0

\       BOT:   2  0.17  0.00 0

\       SIGN: -1  0.00  0.00

\       ADJOIN: 57 MIRROR: 0 WALK: 57

\       FLAGS: 0 0 0

\       LIGHT: 5

|  ...

Hmmm... heavy information!

Click a section to take it apart, it's not too difficult.


LEV Geometry - Sector

| SECTOR scnum

This is the sector number, it is zero based.

|  NAME             sector_name

This is both a link to the .INF file and a useful reminder.

|  AMBIENT          20

Ambient light level in this sector.

Note that this value is used in GROMAS to indicate an amount of red fog, not a light level.

|  FLOOR TEXTURE    80 -0.38 -0.06 2

The TX to apply to the floor of the SC as an index in the TX table.

The following two floats are the X and Z offsets by which the TX must be moved before being mapped.

The third (int) value is unused.

It seems that floor textures must be 64x64, or the game engine does strange things.

|  FLOOR ALTITUDE    0.00

The altitude of the floor of this SC. Note that the Y axis goes "down", so higher altitudes have lower values.

|  CEILING TEXTURE  0  0.00  0.00 2

|  CEILING ALTITUDE -12.00

 Same as floor.

|  SECOND ALTITUDE   0.00

This is used to indicate a second "floor" altitude in a sector. For instance, a second altitude of 4 will make you "enter into the floor" 4 deep. It will in addition make the sector water like and generate a splashing sound. If you set a negative second altitude, you will be able to walk higher on the sector, provided you also enter the sector higher. This is the way platforms are created (the platform object is only a visual clue).

|  FLAGS            0 0 0

Three flags, the second of which is never used in the 14 original levels.

Change various things in the sector. See Sector Flags.

|  LAYER            1

The layer on which the SC is (positive, 0 or negative).

This value is used in the game to make different maps corresponding to zones of altitude.

Note that this is only a logical grouping, but is also used by the map in the game.


LEV Geometry - Vertices

| VERTICES vxnum

This is the number of vertices that this SC has.

|  X: 252.00 Z: 224.00 # a vx

List of the VXs. X and Z are trivial.


LEV Geometry - Walls

| WALLS wlnum

This is the number of walls that this SC has.

|  WALL LEFT:  0 RIGHT:  1

These are the origin and destination vertices for this wall.

\       MID:   0  0.00  0.00 0

The TX to apply to the middle of the WL as an index in the TX table.

The following two floats are the X and Y offsets by which the TX must be moved before being mapped (remember Y goes down).

The third (int) value is unused.

\       TOP:   1  0.00  0.00 0

\       BOT:   2  0.17  0.00 0

Same as MID

\       SIGN: -1  0.00  0.00

A sign is a second TX on the same WL, its main use is to place switches.

First is the TX to apply to a sign on the WL as an index in the TX table.

The following two floats are the X and Y offsets by which the TX must be moved before being mapped (remember Y goes down). Also note that this is relative to the texturing of the wall. So if you offset the WALL, you have to add this offset to that of the SIGN.

\       ADJOIN: 57 MIRROR: 0 WALK: 57

See The Adjoin/Mirror/Walk mechanism

\       FLAGS: 0 0 0

Three flags.

Change various things in the wall. See Wall Flags.

\       LIGHT: 5

Relative modification of the luminosity on this specific WL.


LEV Sector Flags

FLAG 1

Bit	Description	Comment
1	EXTERIOR - NO CEIL.  (SKY)
2	DOOR	instant door
4	SHOT REFLECTION / MAG.SEAL	walls reflect weapon shots
8	EXTERIOR ADJOIN	for adjacent skies with != alt.
16	ICE FLOOR        (SKATING)
32	SNOW FLOOR
64	EXPLODING WALL/DOOR	instant exploding door
128	EXTERIOR - NO FLOOR  (PIT)
256	EXTERIOR FLOOR ADJOIN	for adjacent pits with != alt.
512	CRUSHING SECTOR
1024	NO WALL DRAW / "HORIZON"	horizon moves with eye level
2048	LOW  DAMAGE
4096	HIGH DAMAGE   (both = GAS)	both can be combined for GAS
8192	NO SMART OBJECT REACTION
16384	SMART OBJECT REACTION
32768	SUBSECTOR	player won't follow moving SC
65536	SAFE SECTOR
131072	RENDERED     (do not use!)
262144	PLAYER       (do not use!)
524288	SECRET SECTOR	increments the %secret when entered

Note on the Smart Objects:

Some objects in the game may for instance open doors. There are two values, not a toggle, because Flag doors per default react to smart objects, and other things by default don't react.

FLAG 2

is unused.

FLAG 3

Only serves as an extra lighting value in TALAY. When the generator is switched on, the engine copies these values to the Ambient of the corresponding sectors.


LEV Wall Flags

FLAG 1

Bit	Description	Comment
1	ADJOINING MID TX	the MID TX is NOT removed
2	ILLUMINATED SIGN
4	FLIP TEXTURE HORIZONTALLY
8	ELEV CAN CHANGE WALL LIGHT
16	WALL TX ANCHORED
32	WALL MORPH WITH SECTOR
64	ALLOW SCROLL TOP  TX
128	ALLOW SCROLL MID  TX
256	ALLOW SCROLL BOT  TX
512	ALLOW SCROLL SIGN TX
1024	HIDE ON MAP
2048	SHOW ON MAP
4096	SIGN ANCHORED
8192	WALL DAMAGES PLAYER
16384	SHOW AS LEDGE ON MAP
32768	SHOW AS DOOR  ON MAP

FLAG 2

is unused.

FLAG 3

Bit	Description	Comment
1	CAN ALWAYS WALK
2	CANNOT WALK THROUGH WALL
4	FENCE - KEEP ENEMIES IN
8	CANNOT FIRE THROUGH WALL


INF FILES

INF files are text files written in "the INF programming language".

They accept C like  /* */  comments.

They are linked

to the SCs via the SC names

to the WLs via the SC names and WL number.

The basic items defined in the .INF are:

elevators	They receive messages and act upon them, possibly sending other messages, and so on.  They may also have slaves following their every action.
teleporter chute	A very special item used to "fall" to another sector.
triggers	They trigger something in their clients.

The variables in the language are the positions of the elevators created by the programmer (mostly in "dummy" (i.e. never accessible) sectors). Those positions are marked by stop: keywords. The values following may be absolute or relative in which case they are prepended with a '@'. Note that the stop unit is dependent of the elevator type. It may be floor altitude, ceiling altitude, ambient or wall light level, degrees ...

The whole thing works by triggering/sending messages across the items and reacting to them differently depending on the "variables". Other actions can also be executed, such as create an adjoin, play a sound (page), change a sound, display text messages on the top of the screen, ...

It is important to note that all elevators and triggers have a master switch that may be set on or off. If master is off, the elevator or trigger won't answer, period.

The programming language is a little object oriented like, as you have default items in which you override only the fields you need. It is very powerful, and lets you to a certain extent dynamically modify the level.

See also INF File Format for details.

[Descriptions by Jereth KOK]


INF Elevators

Elevators are to be taken very widely :

elevator basic

elevator change_light

elevator change_wall_light

elevator door

elevator door_mid

elevator inv

elevator morph_move1

elevator morph_move2

elevator morph_spin1

elevator morph_spin2

elevator move_ceiling

elevator move_fc

elevator move_floor

elevator rotate_wall

elevator scroll_ceiling

elevator scroll_floor

elevator scroll_wall

[Descriptions by Jereth KOK]


INF Teleporter Chutes

[by Jereth Kok]

Their function is to teleport the player to another sector.

Dark Forces teleporter chutes do not have the same intention as the teleporters in DOOM. In DOOM, teleporters are deliberate and are just another fun aspect of a level. In Dark Forces, they are not intended to even be noticed. They are intended to make it look like the player has just  fallen through a chute into a layer below, for example, in the Robotics Facility where you fall into the gas room, and Jabba's Ship where you fall into the area where you rescue Jan. These cases could only be accomplished through teleporter chutes because the other method (using a same sector in both layers) would mean making double adjoin/mirror/walks for the walls of that sector - something not possible.

Consequently, you can only teleport straight down, so the target sector MUST be directly below the teleporter chute and must totally enclose the teleporter chute or it may be possible to teleport outside of a sector.

It is also possible to teleport up, as in the demo, but this is never really necessary.

usage:

| item: sector    name: [sector name]

|   seq

|     class: teleporter chute

|      target: [target sector's name]

|   seqend


INF Messages

Here is a list of messages :

Stop	Applies to	Message	Parameters	Comments
[stop]	[sector[(wall)]]	clear_bits	flagnum bitnum
[stop]	[sector]	complete	num
[stop]	[sector[(wall)]	done
[stop]	[sector]	goto_stop	num
[stop]	[sector]	master_on	[value]
[stop]	[sector]	master_off	[value]
[stop]	[sector[(wall)]]	m_trigger	[value]
[stop]	[sector]	next_stop
[stop]	[sector]	prev_stop
[stop]	[sector[(wall)]]	set_bits	flagnum bitnum
[stop]	[sector]	wakeup
[stop]	system	lights		(in TALAY uses SC flag 3)
	trigger	??		(in FUELSTAT.INF line 391)

The [stop] is the stop at which the message will be send when send from an elevator.

Remember that when you look at an INF file and you see something like :

| class: elevator eeeee

|  stop: 0

|   message: 0 mmmmm

|  stop: 1

|   message: 1 mmmmm

it's only a visual clue, and you could group all the messages in one place and in any order.

Important : if you add a stop, you have to renumber !

Note that the elevators START at their stop 0, but the messages are send when the elevators ARRIVE at a stop.

So the messages set up at stop 0 won't be send at level startup.

[Descriptions by Jereth KOK]


INF Triggers

trigger

trigger single

trigger switch1

trigger standard

trigger toggle

The triggers react only to determinate events: this is called the event_mask.

Custom event_mask can be created by the programmer to control the different parts of multi class elevators, this requires the trigger to define a new event:

The triggers may be triggered by specific things: this is the entity_mask.

[Descriptions by Jereth KOK]


INF File Format

|  INF 1.0

|  LEVELNAME SECBASE

INF File version and level name.

|  items 1

Total number of items in the file.

Don't forget to change this value when you add or remove items an INF.

|  item: sector  name: complete

|    seq

|      class: elevator move_floor

|        stop: 1 hold

|        stop: 2 complete

|        speed: 0

|    seqend

Notes:

Each item follows the same format, structured by the seq and seqend statements.

More than one class statement is allowed.

Line items add a num: n clause to the item statement, to specify which wall it refers to.


GOL FILES

GOL files contain a description of what to do to complete the levels.

| GOL 1.0

| GOAL: 0       ITEM:   5       # DT weapon

| GOAL: 1       TRIG:   1

Each GOAL: can be a TRIG: to fire or an ITEM: to take.

Goal triggers

are fired in the .INF file with the 'complete num' message sent to the complete sector (don't get confused here!).

For instance,  " message: s complete complete 1 " will say that TRIG: 1 is complete!

Goal objects

fire internal triggers because of their logic. That internal trigger are equivalent to " message: s complete next_stop " So, be sure that complete is at a correct hold point when the object is taken.

Try to do nothing else in complete than handling the goals. If necessary, start another 'level control' elevator very early in the level life.

Object numbers are in an internal table at runtime. The broken Dark Trooper weapon for instance has the number 5 (see TALAY.GOL). If you want to make the player retrieve it in a SECBASE modification, just change the value of item to 5 in SECBASE.GOL (don't forget to put this object in your .O file).

The message on the screen will be correct because the object has LOGIC: DT_WEAPON, and your OBJectives screen will be updated.

But note that it still says 'retrieve death star plans'. Changing that means correcting the message in the LFD format (graphical !).

Here are the screen messages associated with the goal objects, you can patch

them in text.msg.

| 400  1:  "Death Star Plans"       for object 0

| 401  1:  "Phrik Metal"            for object 1

| 402  1:  "Nava Card"              for object 2

| 405  1:  "Dark Trooper Weapon"    for object 5

| 406  1:  "Data Tape"              for object 4

Object 6 is 'Your Gear' as used in the JABSHIP level and has the text

| 312  1:  "Your Gear"

in the 'Power-Up Pickup Messages' section of the same text.msg file.

Final note: don't assume the goals will happen in the .GOL order !

See also BOSS Goals


GOL BOSS Goals

[by Jereth Kok]

A boss goal is an objective completed by killing an enemy. This could be very handy in a "Terminate Boba Fett" or "Terminate General Mohc" level!

The following can be used as boss goals:

BOBAFETT.WAX	What do you think ?!?
KELL.WAX	Kell dragon
PHASE1.WAX	Phase 1 Dark Trooper
PHASE2.WAX	Phase 2 Dark Trooper
PHASE3X.WAX	Mohc

The principle behind this is similar to goal objects like the Death Star Plans. A certain element in the sequence of one of these objects will cause an elevator called "boss", (or "mohc" in the case of PHASE3X.WAX) to move to its next stop.

This is what you need to do to turn one of these sprites into a mission objective:

Edit the logic of the sprite that you want to kill in order to complete the mission. Add this to its sequence (the stuff between "Seq" and "Seqend"):

| BOSS: TRUE

Now edit your level. Add a dummy sector called "boss" if your boss goal is Boba Fett, a Kell dragon, a Phase 1 Dark Trooper or a Phase 2 Dark Trooper. If the boss goal is Mohc, call the dummy sector "mohc".

Of course, you also need to alter the "complete" sector.

Add this to the INF file of the level (add 2 to "items #"):

| item: sector    name: boss/mohc

|    seq

|      class: elevator move_floor

|       event_mask: 0

|       stop: 0 hold

|       stop: 1 hold

|        message: 1 complete complete [num]

|       speed: 0

|    seqend

| item: sector    name: complete

|    seq

|      class: elevator move_floor

|       event_mask: 0

|       stop: 0 hold

|       stop: 1 complete

|       speed: 0

|    seqend


O FILES

O files contain all the level objects. They are in text format.

There are many different object types in Dark Forces:

Type	File	Description
SPIRIT	[none]	an object not linked to a viewable file (ie invisible)  Its main use is for the PLAYER, but you can create other invisible items.
SAFE	[none]	a restart point after the player died. You should put SAFEs in your levels, to allow the player to restart not far from where he died, but also because it seems to be the best bet as 'player starting points' if LEC produces a multiplayer version. (Put 8+ of them so you'll be ... safe!)
SPRITE	WAX	fully animated objects such as enemies.
FRAME	FME	"one view" objects such as energy power ups.
3D	3DO	3D objects such as mousebots.
SOUND	VOC	an ambient sound around the object position.

File Format


O FILE FORMAT

They are composed of 3 parts :

Magic and Version number and level name

Objects Tables

Object Descriptions

Sequences and Logics

Generators

They accept C like  /* */  comments.


O Magic, Version Number and Level Name

| O 1.1

This is trivial.

| LEVELNAME SECBASE

I'm not sure this level name is used in DF !


O Object Tables

As there is a lot of OB information in a level, 4 object tables are created to avoid storing OB names in full at each occurrence.

| PODS 3             # These are the "3D" objects

|  POD: DEATH.3DO    # 00

|  ...

|

| SPRS 10            # These are the SPRITES

|  SPR: OFFCFIN.WAX  # 00

|   ...

|

| FMES 6             # These are the FRAMES

|  FME: IENERGY.FME  # 00

|  ...

|

| SOUNDS 1           # These are the SOUNDS

|  SOUND: BANG.VOC   #00

|  ...

Afterwards, all the objects are referred to by their 0 based index in the object tables. The object CLASS determines in which table to look.


O Object Descriptions

The first data is the total number of objects in the level :

| OBJECTS 185

Then each object is described.

Please note that the object data first line has been split here for visual convenience.

| CLASS: SPIRIT  DATA:  0 X: 131.00 Y:    0.00    Z: 210.00

\                       PCH:   0.00 YAW: 176.34 ROL:   0.00

\                       DIFF: 1

|  SEQ

|   LOGIC:     PLAYER

|   EYE:       TRUE

|  SEQEND

|

| CLASS: SPRITE  DATA:  0 X: 320.62  Y:  20.00   Z:  275.64

\                       PCH: 0.00  YAW: 270.00 ROL:    0.00

\                       DIFF: 1

|  SEQ

|   TYPE:      I_OFFICER

|  SEQEND

CLASS is the type of object, and DATA is the offset in the corresponding object table.

(SPIRIT and SAFE have DATA = 0).

X, Y, Z are trivial.

PCH, YAW, ROL are classic spatial orientation, but only YAW is really used (DOOM equivalent is THING orientation). It takes a value in degrees where 0 is at the "top of the screen when you look at the map". The

value increases clockwise.

DIFF is the difficulty level at which the object appears.

DIFF	EASY	MED	HARD
-3	X	X	X
-2	X	X
-1	X
0	X	X	X
1	X	X	X
2		X	X
3			X


O Object Sequences and Logics

SEQ and SEQEND are delimiters for a series of options/modifiers to apply to the object.

They determine part of its behavior ( LOGIC: ).

The same viewable file may be used to create 'different' objects. For instance, OFFCFIN.WAX may be used with a TYPE: I_OFFICER or TYPE: I_OFFICERR (note the second 'R') which will generate a red key then killed.

It seems that the keywords TYPE: and LOGIC: are freely swappable, as are UPDATE and ANIM, and that the ITEM keyword is optional, but this has to be tested fully.

Combined Logics

The first LOGIC: is in this case the primary logic, which means that to kill the object, you have to use the firepower needed to kill its first LOGIC: .

Try Mousebot + Barrel, or Player + Mousebot...

Generators

Another use of the logics is to create object generators. Here is a quite self explaining sample:

| CLASS: SPRITE  DATA:  4 X: 396.88   Y:  -2.00   Z: 217.48

\                       PCH:   0.00 YAW:   0.00 ROL:   0.00

\                         DIFF: 1

|  SEQ

|   LOGIC:     GENERATOR STORM1

|   DELAY:     0

|   INTERVAL:  20

|   MAX_ALIVE: 1

|   MAX_DIST:  200

|   MIN_DIST:  70

|   NUM_TERMINATE: 3

|  SEQEND

What is not clear is WHEN they are generated. What I know is that they are never generated while you look directly at the generator !

Also note that you can set MASTER: OFF on a generator, and activate it by sending a message: s master_on to the sector that contains it.


PAL FILES

PAL files are 768 bytes long, and store a single 256 colors palette as 256 x 3 RGB bytes.

Each level can have its own palette.


CMP FILES

CMP files store palette mappings (like DOOM COLORMAP), which are the way the colors behave under different light levels.

Each byte is an index into the palette, and the CMP file is in fact an array that can be used for shading.

Given a color value [C] and a light value [L] the correct color to draw can be determined with the following formula:

DrawColor = [start_of_CMP_file]+(C*32)+(31-L)

Since each color in the palette has a column of shades, to get the DrawColor, the game indexes over to the top of the color's column (C*32). Then to find the correct shade of the color, it indexes down the column.

The top color is full brightness where the bottom color is black - that is why it adds (31-L) to get the correct color.

In simpler terms, to create a CMP file, just build a binary file with every color of the palette spread at even 32 byte intervals starting at the first byte. Then fill in the 31 bytes after each color with progressively darker shades of that color down to black in the last entry.

There are 128 other unknown bytes at the end of the file forming a slow gradient from 0x00 to 0x1F.

I don't know what they are for.


BM FILES

Here is the C structure for the BM file header.

struct BM_header

{

char	MAGIC[4];
int	SizeX;	// if = 1 then multiple BM in the file
int	SizeY;
int	idemX;
int	idemY;
byte	Transparent;	// 0x36 for normal, 0x3E for transparent
byte	logSizeY;	// ie logSizeY = log2(SizeY)
int	Compressed;	// 0 = not compressed, 1 = compressed
long	DataSize;	// excluding header
char	filler[12];
};

Please note that BM must have a height which is a power of 2.

The data follows, encoded by COLUMNS from the bottom to the top.

See also Transparent, Multiple and Compressed BM


BM Transparent, Multiple and Compressed

Transparent BM

You can transform any BM in a transparent BM by changing its Transparent value from 0x36 to 0x3E.

The color 0 will 'disappear'.

Note that this isn't the same color as the one used in DOOM transparent textures (cyan in the DOOM palette).

Multiple BM

If SizeX = 1 BM file contains idemY BM. (EXCEPT if idemY = 1 in which case it is a 1*1 BM)

There follow two bytes, the first of which permits to distinguish between switches (= 0) and animated textures (<>0). It is the speed of animation (maybe in frames per second). You may alter this value if you want to.

The second byte is 2.

Then follows a table of offsets to the 'sub' BM composed of idemY BM.

Each 'sub' BM then has its own header WITHOUT the MAGIC field (i.e. 28 bytes).

Important note

for a multiple BM to work correctly, it must be made a SIGN, and for switches there MUST also exist a corresponding trigger in the .INF Else, switches will be displayed wrong (as a single column) and the animated will display correctly, but static. This means that you cannot do animated floors and ceilings this way !

Compressed BM

If Compressed = 1, the BM is compressed.

These existed in the DEMO (buyit.bm), but there aren't any in the full game. They used a mixed direct/RLE coding. In the demo, wait.bm also used yet another coding, but it's a perfectly normal BM in the full game.

The heart of the data is a columns starts table, with the start addresses of each of the columns. It is at the end of the file, at offset DataSize, and has one long entry per column containing this column start address.

This start address is calculated without the 32 bytes BM header (i.e. read the header in a struct, then the data in a huge buffer at offset 0).

The coding of one column follows (in pseudo code format).

while(end of data for this column not reached)

{

 if(buffer[address] <= 128)

   the FOLLOWING n bytes are direct values

 else

   the FOLLOWING byte is a color byte to repeat n-128 times

}

There was an 0xFF last byte marker per column. However, I do prefer to calculate the end of the column data by using the start of next column, as this is more like the FME and WAX reading technique, as a slight variation of this compression is used in the frames and the sprites.


FME Files

They contain the frames, which are the "one view" objects (you can turn around them, and you always see the same image).

Here are the C structures for the FME file headers.

struct FME_header1

{

long	u1;
long	u2;
long	u3;
long	header2;	// this is the absolute address of header2
char	unknown[16];
}

There are two negative longs right at the beginning of header 1 that I don't understand, but they are linked somehow to how you view the FME. This may be an insertion point.

struct FME_header2

{

int	SizeX;
int	u1;
int	SizeY;
int	u2;
int	Compressed;	// 0 = not compressed, 1 = compressed
int	u3;
long	DataSize;
char	fil[8];
};

There are at least two formats for these.

If Compressed = 0 the format it very similar to the BM format, with just the header rearranged.

The header 1 is totally unknown except for the field header2.

The header 2 contains the X and Y sizes, and the data follows, encoded by COLUMNS from the bottom to the top.

Compressed FME

With Compressed = 1, the format looks like the Compressed BM used in the demo level, also with the header modifications.

Four differences:

  the column starts table is at the top of the file, just after the header.

  you have to offset the addresses by -24 (they counted only header 1 ?)

  the end of column data marker isn't there.

  the background color is always 0 (transparent) and ISN'T CODED.

    (there was a color byte in the BM compressed format).


WAX Files

They contain the sprites.

(samples : STORMTROOPERS, BONUS LIVES ...)

These are a collection of embedded FME files. They contain a table with 32 entries (offset to first entry = 0x000000BC). Each of these entry points to a table containing addresses of a complete embedded enhanced FME file. Take care to read that one properly (i.e. using the header2 offset in header1) as there are multiple consecutive header1 ! Enjoy the structures, these will be described more fully in the next Specs.

typedef struct

{

int	width;	// width in pixels
int	height;	// height in pixels
int	compression;	// compression type
int	size;	// total size of compressed cell
dword	*aOffsets;	// column byte offset table
dword	pad1;
byte	data[];	// cell data stored in columns
} Cell;

typedef struct

{

fixed	xShift;	// x center point
fixed	yShift;	// y center point
int	flip;	// 0 = none, 1 = h, 2 = v
Cell	*pCell;	// offset to cell
fixed	unitwidth;	// cell width in feet
fixed	unitheight;	// cell height in feet
dword	pad3;
dword	pad4;
} Frame;

typedef struct

{

dword	pad1;
dword	pad2;
dword	pad3;
dword	pad4;
Frame	*apFrame[32];	// Offsets to frames
} Seq;

typedef struct

{

fixed	worldWidth;	// real-world width in fixed point (16/16)
fixed	worldHeight;	// real-world height in fixed point (16/16)
int	frameRate;	// frames per second
dword	nFrames;	// number of frames in this wax
dword	pad2;
dword	pad3;
dword	pad4;
Seq	*apSeq[32];	// View table (Offsets)
} Wax;

typedef struct

{

fixed	version;	// File version control
int	nSeqs;	// Number of seqs
int	nFrames;	// Number of frames
int	nCells;	// Number of Cells
dword	xScale;	// x wax scale
dword	yScale;	// y wax scale
dword	extraLight;	// extra lighting
dword	pad4;
Wax	*apWax[32];	// Offset from file start to each wax
} WaxHeader;


3DO Files

They contain the "3D" objects. (samples : MOUSEBOT, the DEATH STAR HOLOGRAM, ...)

They are text files containing a geometric description of a full 3D object, and are converted from 3D Studio .ASC format. They accept # comments.

[ by Michael Taylor]

| 3DO 1.2

Magic and Version Number: this is the word "3DO" followed by a version #, either 1.2, 1.20, or 1.30.

Next comes several lines of header data.  Included is the picture name, number of objects in the file, number of vertice, number of polygons, palette used, and number of textures.

| 3DONAME  cube

| OBJECTS  00001

| VERTICES 00008

| POLYGONS 00006

| PALETTE  METAL.PAL

| TEXTURES 0

or

| TEXTURES 1

|   TEXTURE: IPDTENGR.BM

The palette file doesn't appear to relate to any PAL file found in the GOB directory.

[Could this be the type of rendering (metal, phong, ...) used in 3DS ? [Yves]]

Please note that textures are a little different and will be explained below.

If any textures are used then below the TEXTURES # line is additional lines defining each texture file. It creates a zero based array of textures for later usage by the objects.

See Object Definitions


3DO Object Definitions

After the header data comes each object's definition. Each one starts with an object header and then the data. The object header is the word "OBJECT" followed by the object's name in double quotes. Next is the word "TEXTURE" followed by the texture used for this object. If no texture is used then the value of -1 is used else an index into the texture table defined in the header data is given.

| OBJECT "shuttle"

| TEXTURE 0    # Index into texture array

|              # IFOCTGR.BM

After the texture information, starts the actual geometric description of the object.

First comes the vertices. The initial line is the word VERTICES followed by the number of vertices defined. Then the vertices are listed starting with 0 and going up to the number of vertices listed on the VERTICES line. Each vertex is defined by 3 numbers; x, y, and z. They represent relative locations on a 3-D graph. They are taken to 3 decimal places.

| VERTICES 8

|    0:   0.000   2.000  -0.050

|    1: -10.000   2.000  -5.550

| ...

After the vertex information, comes the polygonal information. Each object may be made up of either triangles or quads.

The appropriate header and number of polygons defined are listed, TRIANGLES for triangles and QUADS for quadrilaterals.

The polygons are described with a number starting at 0, then the vertex number for each end point is given (3 for triangles and 4 for quadrilaterals). Then a color is given to each polygon (0 to 255). Finally comes the shading used for each polygon. The choices are PLANE, FLAT, GOURAUD, or TEXTURE. [Actually, there is another possible shading : VERTEX. This will display only the vertices, as in the Death Star hologram (death.3do) [Yves]]

Note that in order to use a texture for a polygon, you must set its shading to TEXTURE.

| TRIANGLES 12

| # Num  V1  V2  V3  Color  Shading

|   0:   1   2   3     0     PLANE

|   1:   0   1   3     0     PLANE

|   2:   5   1   0    62     FLAT

| ...

Also note that the vertices are listed in clockwise order if you are facing directly at the polygon.

[This simplifies hidden lines/surfaces algorithm, as you may determine the facet orientation with 3 of them [Yves]]

If textures are used in a file then the next section is required. Each vertex is again defined but now 2 values are given. These values seem to be offset that the texture should be moved before being applied to the polygon ??

Additionally the polygons are again listed but only with the vertex information. The vertices are always listed exactly as they were above but I don't know if this is necessary.

| TEXTURE VERTICES 8

|   0:   0.000    0.000

|   1:   0.000    0.050

| ...

| TEXTURE TRIANGLES 12

|   0:   1   2   3

|   1:   0   1   3

| ...

This ends the object definition section. This entire section is repeated until all objects are defined. The object names seem irrelevant provided they are unique within the 3DO file.


VUE Files

[Extract from VUE.TXT by Paul Nemesh]

This what a sample .VUE looks like:

| vue 1

| transform "def" #1 #2 #3 #4 #5 #6 #7 #8 #9 #10 #11 #12

| transform "def" ......

vue 1 <- always use this type of .vue.

{Note:vue 201 is the same as vue 1, except each frame is numbered, and several different .vues are combined into one file, each referenced by its own name ("def" in my example)}

"def" is the object name (referenced by the .o file, see below)

The values for #1 through #9 are the coefficients of the rotating and scaling matrix that is used by DF to determine how to draw the .3do. [...]

The formulas are:

#1: Scale x [cos(H) x cos(R)]

#2: Scale x [-sin(H) x cos(P) + cos(H) x sin(R) x sin(P)]

#3: Scale x [-sin(H) x sin(P) - cos(H) x sin(R) x cos(P)]

#4: Scale x [sin(H) x cos(R)]

#5: Scale x [cos(H) x cos(P) + sin(H) x sin(R) x sin(P)]

#6: Scale x [cos(H) x sin(P) - sin(H) x sin(R) x cos(P)]

#7: Scale x [sin(R)]

#8: Scale x [-cos(R) x sin(P)]

#9: Scale x [cos(R) x cos(P)]

The values for #10 through #12 are:

#10: X coordinate

#11: Z coordinate

#12: -Y coordinate

The .o file should have the following logic associated with the .3do:

SEQ

KEY:   TRUE	/* This always needs to be present. */
VUE:   TIEDEF.VUE "DEF"	/* This is the filename of the .VUE, with the object name in quotes. */
VUE_APPEND: TIEDEF2.VUE "DEF"	/* Same as the previous line, except this will be run directly after the first .VUE is finished. */
PAUSE: TRUE	/* If this line is used, the .VUE will run exactly once (like Kyle's ship taking off). If this line is omitted, the .VUE will continuously repeat itself. */
SEQEND

[End of extract]

Apparently, the very best way to generate VUE files is to use 3D Studio, as .VUE is a standard 3DS file format, used to describe objects motion. By the way, 3DS .ASC is the base format for the 3DOs, after which the LEC team converted them.


VOC Files

They contain the sounds.

These are standard .VOC files in the Creative Labs format.


GMD Files

They contain the musics.

The GMD files are SMF files type 2 (SMF = Standard Midi Files), but they've got a different header. If you want to convert the header, you'll have to delete everything BEFORE the string "MThd", so that the file begins with that string.

After you've done that, you can play/edit the file with any type 2 compatible MIDI sequencer program.

You can use any General Midi file (i.e. with the MIDI header) to replace musics in DF, except for the levels musics. Those must be iMuse compatible, and FIGHT-01.GMD contains a "call" to STALK-01.GMD

But you can freely replace the opening music with, for instance, a music file from Tie Fighter.


MSG Files

They contain the text messages used in the game.

local.msg	contains error messages and should be left untouched.
text.msg	however can be patched. The use of this is to create new messages that you can use in your levels (create a trigger, use text: ttt).

General format:

| MSG 1.0

|

| MSGS 119

|

| # internal game messages

| 0    0:  "Joystick Off"

| ...

| END

MSGS is the number of messages. Don't forget to update it if you add messages.

I found no problems by adding messages at 900 and more.

| 900  1:  "Hurry up !"

I don't know what the second number ( i.e. 0: ) is for. I use 1:

Here are also the 'cheat messages' from 700 onwards.

Just so you know where to insert a few 'Cheater!' and 'Chicken Mode ON' ... :-)


DFBRIEF.LFD

[by Nicola Salmoria]

Briefings are stored in DELT sections, named after the level.

A DELT section has this short header:

{

int OffsX;

int OffsY;

int SizeX;

int SizeY;

}

(actually, they don't seem to really work that way, in particular OffsY. Anyway the width of the scrollable region seems to be hardcoded in the game, so the only field we'll want to change is SizeY).

After the header, a variable number of line descriptors follow. They are composed of an header and some data.

Header:

{

int SizeAndType;

int StartX;

int StartY;

}

StartX and StartY indicate the point where to start the drawing. You can start in the middle of a line, and draw a portion of it. Lines need not be in consequential order. You can split one line in more than one section. Portions not covered are, of course, transparent.

Bits 1-15 of SizeAndType indicate the number of pixels described in this section.

If bit 0 of SizeAndType is 0, the byte following the header contains the color to put in all the pixels. If bit 0 is 1, data compressed with RLE follows. Note that, opposite to the BM files, run/copy is indicated by bit *0* of the count byte.

This info should be more than enough to create custom briefings.

All we have to do is add a new .LFD file to the GOB, containing:

  PLTTbrf-jan

  ANIMbrf-mon/brf-jan/brf-nil (one or more, as necessary)

  one DELT section for every briefing

and change BRIEFING.LST accordingly.


BRIEFING.LST

[by Nicola Salmoria]

In DARK.GOB we have the file BRIEFINGS.LST, its contents are trivial:

LEV	name of the level
LFD	name of the .LFD file to take the briefing from
ANI	name of the ANIM (in the .LFD file) to use as background
PAL	probably the palette, I didn't check


CUTSCENE.LST

[by Michael Taylor]

The cutscene.lst file contains the necessary information to display the various scenes in between missions. This includes the starting logos and the ending credits. The file is somehow integrated into the various LFD files that make up the cutscenes. Until the LFD format is interpreted, only some things in the cutscene.lst file may be changed. Following is the complete file format and the descriptive notes I have made.

File Format

Firstly, comments are denoted by the pound sign (#). Everything after the sign is ignored until the end of the line.

First comes the keyword CUT and a version number.

| CUT 1.0

Next is the keyword CUTS and the number of cutscenes defined in the file.

| CUTS 39

Then starts the cutscene information :

| 550:  gromas1.lfd  gromas1   10  0  0  3  100

Lets decipher this line. The first number is an id number used by the LFD file. It seems that each mission is given its own id number in the LFD file and therefore you cannot change the first id number but you may add id numbers which in effect adds more cutscenes to a file. More on this later.

Note: as stated in the file, don't change the ftextcra.lfd scenes id number, it is hardcoded into the program.

Next comes the resource file that contains the cutscene and following this a scenes file. You can move most of the resource files around and effectively change the scenes provided you also change the scenes file. I received an error message when I tried to change the scenes file while leaving the resource file the same. It is possible to swap files, for example you can have the credits displayed prior to the Dark Forces logo if you swap lines 41 and 40. This is not a good way to swap scenes though. I'll show you a better way later.

Next comes the speed at which the scene is showed. It must be in the range of 5-20 else an error message is displayed. 20 is the fastest. I use this to speed up the starting logos when I'm testing things in the cutscene.lst file.

The next number is the scene id that should be displayed when this one ends. Zero means that this is the last scene to display. This is by far the best way to swap scenes. For example if you want to show the credits prior to the Dark Forces logo then you would change the next scene number in line 30 to 41 and then change the next scene number in line 41 to 40. Finally, change the next scene number in line 40 to 0. If you forget to change the last scene's next scene number to 0 then it will get into a loop. By changing the next scene number, you can also add your own cutscenes once the LFD files are deciphered.

The next number is the skip scene number. This number determines which scene to be displayed if ESC is hit. In most files, it should be zero which means to go to the menu or the next mission. But it can contain an id number for a scene.

Next comes the SEQ number for the cutmuse.txt file. This links the appropriate sound file with the scenes. Since the cutmuse.txt file is still unknown, it is best to leave this alone. I do know that it seems that every scene change cues the cutmuse.txt file to go to the next sound. The sound messes up if you move the numbers around. Change this with care until more information can be gained.

Finally is the volume at which the sound is played. 100 is normal. As noted, the resource file name and the scene file name must be the same otherwise an error occurs. Until cues, SEQ, and the cutmuse.txt file can be deciphered, the sound numbers are best left alone. The sounds must also be cued by the main program because I was never able to disable the LucasArts sound at the start of the program.

Here is what is shown for each resource file.

id	resource LFD	scene
10:	logo.lfd	Lucas Arts logo
20:	swlogo.lfd	Star Wars logo
30:	ftextcra.lfd	scrolling text
40:	1e.lfd	ship flyby
41:	darklogo.lfd	Dark Forces logo and credits
200:	kflyby.lfd	flyby from planet
209:	execx.lfd	star destroyer
210:	execcomp.lfd	Darth Vader and Gen. Mohc talk
211:	arcext.lfd	Arc Hammer external
215:	tubecomp.lfd	DT loading and lauch
216:	succes.lfd	Darth Vader continues
220:	neb1.lfd	rebel fleet
225:	brief1.lfd	Mon Montha briefing 1
230:	holocu.lfd	Admiral's report
235:	brief2.lfd	Mon Montha briefing 2
240:	exitneb.lfd	Kyle takes off
500:	gromas1.lfd	flying to Gromas
550:	gromasx.lfd	leaving Gromas
600:	arcfly.lfd	Arc Hammer
605:	madine1.lfd	General's report on Madine
610:	boba.lfd	Boba Fett
800:	rob1.lfd	flying to robotics facility
850:	robotx.lfd	leaving robotics facility
1000:	jabba1.lfd	Kyle takes off
1010:	jabba2.lfd	tractor beam gets Kyle
1020:	jabba3.lfd	Jabba's ship takes off
1030:	pit.lfd	Jabba and Kyle
1050:	jabescp.lfd	escaping from Jabba
1400:	cargo1.lfd	cargo goes from Executor to Arc Hammer
1410:	cargo2.lfd	cargo docks
1450:	exp1xx.lfd	Arc Hammer explosion
1451:	exp2x.lfd	Kyle flies by
1452:	exp3xx.lfd	Darth Vader's comments
1460:	award1.lfd	rebel fleet
1470:	award2.lfd	show medal
1472:	award3.lfd	Kyle leaves hangar
1475:	award4.lfd	Kyle flies in and out of fleet
1480:	endfly.lfd	Kyle flies away
1500:	fullcred.lfd	credits


Author and Credits

Author

  Yves BORCKMANS (Yves.Borckmans@ping.be)

Credits

HELP WANTED

If you have deciphered a Dark Forces file that isn't covered in the Specs, don't hesitate to send it to me. I'll include it in the next release.

  Serge DEBROEYER (mail to Yves.Borckmans@ping.be)

    for adjoin/mirror tips, some flags, some INF tips, complex sectors errors, ...

  Don SIELKE (DSielke@aol.com)

    for the complete flags list, the -u option, ...

  Florian ENGELHARDT (100442.2012@compuserve.com)

    for information on GMD files

  Paul NEMESH (PNem@aol.com)

    for information and text on VUE files

  Randy GREENE (rgreene@val.net)

    for information on event_masks

  Carlos GOMEZ (CarGoCo@aol.com)

  Jonathan WISE (TheRedDeth@aol.com)

    for information on frames and waxes

  Jereth KOK (KOKJ4M@REDAN.stmichaels.vic.edu.au)

    for information and texts on the INF and GOL files

  Nicola SALMORIA (MC6489@mclink.it)

    for information and texts on dfbrief.lfd and briefings.lst

  Michael TAYLOR (MichaelLTa@aol.com)

    for information and texts on cutscene.lst and the 3DO description

Very special thanks to D., I. and R. who will recognize themselves.

You're the best !

May The Force Be With all of You...


Copyrights

DARK FORCES	is (c) LucasArts Entertainment Company

DOOM	is (c) iD Software

None of these have anything to do with these totally unofficial specs, and shouldn't be bothered with them in any way.

Thanks for those GREAT games...


Doom Note

GOBS are quite similar in principle to DOOM WAD files, but at a higher level.

In fact, WADS directly contain the information or resources, but GOBS also contain complex files, themselves still containing multiple resources.


Doom note :

The GOB structure is quite similar to that of a WAD file, the small difference is that in wads the MASTERN field is at the beginning of the file, between WAD_MAGIC and MASTERX.

Of course, the WAD_MAGIC is 'PWAD' or 'IWAD', not 'GOB' followed by 0x0A.

This distinction between master (IWAD) and patches (PWAD) doesn't exist in DF.


Doom note

Layers are a whole new concept for DOOM levels designers.

It is possible in DF to have many sectors one above the others.

This is completely impossible in DOOM.

The DF sectors are self-contained, by opposition to DOOM vertex and linedef sharing.


Doom note

In Doom, the equivalent to an Adjoin/Mirror is the sharing of two vertices and a linedef between two sectors.

Contrary to a DF Wall, the linedef doesn't contain texturing information, this one being coded at the sidedef level.

There is no node building (BSP) to do on these levels.

There certainly are checks at level loading time, but a few tests on complicated sectors seem to show 'errors' in texturing or HOM problems.

In fact, it seems that big non-convex sectors are problematic.

Maybe you just have to try and create a few 'more convex' sectors instead.


Doom note

This is exactly the same as DOOM texturing, just note that there are two different walls, not one linedef with two sidedefs.


elevator change_light

description:

Changing light level (ambient) in sector.

Stop values are sector light levels.

usage:

| class: elevator change_light

|  {event_mask: }

|  {master: }

|  stop:

|  speed:

|  {slave: }


elevator change_wall_light

description:

Changing light on wall.

Note that you apply this elevator to a whole sector, but make specific walls change light by giving those walls flag 1 bit 8 (allow change wall light). Walls without this flag will not change light even if their sector is an elevator change_wall_light.

Stops values are wall light.

usage:

| class: elevator change_wall_light

|  {event_mask: }

|  {master: }

|  stop:

|  speed:

|  {slave: }


elevator morph_move1

description:

A sector that moves horizontally. The player doesn't move with the sector.

IMPORTANT - each of the moving sector's walls, as well as any mirrors of those walls need to be given the flag 1 bit 32 (wall morph with sector). Otherwise, the sector would not move visually.

Stop values are distances on the X-Z (horizontal) plane.

usage:

| class: elevator morph_move1

|  {event_mask: }

|  stop:

|  {master: }

|  speed:

|  angle:

|  {key: }

|  {slave: }


elevator morph_move2

description:

A sector that moves horizontally. The player moves with the sector.

IMPORTANT - each of the moving sector's walls, as well as any mirrors of those walls need to be given the flag 1 bit 32 (wall morph with sector). Otherwise, the sector would not move visually.

Stop values are distances on the X-Z (horizontal) plane.

usage:

| class: elevator morph_move2

|  {event_mask: }

|  stop:

|  {master: }

|  speed:

|  angle:

|  {key: }

|  {slave: }


elevator morph_spin1

description:

A sector that spins around a point. The player doesn't spin with the sector.

IMPORTANT - each of the spinning sector's walls, as well as any mirrors of those walls need to be given the flag 1 bit 32 (wall morph with sector). Otherwise, the sector would not spin visually.

Stop values are angles in degrees.

usage:

| class: elevator morph_spin1

|  {event_mask: }

|  stop:

|  {master: }

|  speed:

|  center:

|  {key: }

|  {slave: }


elevator morph_spin2

description:

A sector that spins around a point. The player spins with the sector.

IMPORTANT - each of the spinning sector's walls, as well as any mirrors of those walls need to be given the flag 1 bit 32 (wall morph with sector). Otherwise, the sector would not spin visually.

Stop values are angles in degrees.

usage:

| class: elevator morph_spin2

|  {event_mask: }

|  stop:

|  {master: }

|  speed:

|  center:

|  {key: }

|  {slave: }


elevator rotate_wall

description:

The walls of a sector with this elevator will rotate around the specified center.

Stop values are angles in degrees.

usage:

| class: elevator rotate_wall

|  {event_mask: }

|  {stop: }

|  {master: }

|  speed:

|  center:

|  {key: }

|  {slave: }


elevator door

description:

Normal door.

Note, that it is easier to just use flag 1 bit 2 on a sector for a normal door. Elevator door is only really needed for a key door. Make sure that a sector with this elevator has the ceiling altitude of the door when it is OPEN!!!

usage:

| class: elevator door

|  {master: }

|  {key: }


elevator door_mid

description:

A 2 part door (opens upwards and downwards). Information for the top and bottom parts are specified individually.

"addon: 0" is the top part

"addon: 1" is the bottom part

The floor altitude of a sector with this elevator has to be the BOTTOM of the door.

The ceiling altitude has to be the TOP of the door. The door will split halfway between.

usage:

| class: elevator door_mid

|  {master: }

|  addon: 0

|   speed:

|   {key: }

|  addon: 1

|   speed:

|   {key: }


elevator basic

description:

Floor that moves up and down.

Stop values are the altitude of the floor.

usage:

| class: elevator basic

|  {event_mask: }

|  stop:

|  {master: }

|  speed:

|  {key: }

|  {slave: }


elevator inv

description:

Ceiling that moves up and down. Often used for making doors.

Stop values are the altitude of the ceiling.

usage:

| class: elevator inv

|  {event_mask: }

|  stop:

|  {master: }

|  speed:

|  {key: }

|  {slave: }


elevator move_ceiling

description:

Ceiling that moves up and down. In fact, the same as elevator inv.

Stop values are the altitude of the ceiling.

usage:

| class: elevator move_ceiling

|  {event_mask: }

|  stop:

|  {master: }

|  speed:

|  {key: }

|  {slave: }


elevator move_floor

description:

Floor that moves up and down. In fact, the same as elevator basic.

Stop values are the altitude of the floor.

usage:

| class: elevator move_floor

|  {event_mask: }

|  stop:

|  {master: }

|  speed:

|  {key: }

|  {slave: }


elevator move_fc

description:

Floor and ceiling that move up and down together.

Stop values are the altitude of the floor.

usage:

| class: elevator move_fc

|  {event_mask: }

|  stop:

|  {master: }

|  speed:

|  {key: }

|  {slave: }


elevator scroll_ceiling

description:

Ceiling texture scrolls.

Stop values are distances on the X-Z (horizontal) plane.

usage:

| class: elevator scroll_ceiling

|  {event_mask: }

|  {stop: }

|  {master: }

|  speed:

|  angle:

|  {slave: }


elevator scroll_floor

description:

Floor texture scrolls. Player moves with it. Useful for making conveyor belts, flowing water and flowing sewage. Remember, use a second altitude with water and sewage for better effect!

Stop values are distances on the X-Z (horizontal) plane.

usage:

| class: elevator scroll_floor

|  {event_mask: }

|  {stop: }

|  {master: }

|  speed:

|  angle:

|  {slave: }


elevator scroll_wall

description:

Wall texture scrolls. Useful for making waterfalls.

Note that you apply this elevator to a whole sector, but make specific walls scroll by giving those walls flag 1 bit 64, 128, 256 or 512 depending on whether you want the top, middle, bottom or sign texture to scroll. Walls without any of these flags will not scroll even if their sector is elevator scroll_wall.

Stop values are distances on the Y (vertical) axis.

usage:

| class: elevator scroll_wall

|  {event_mask: }

|  {stop: }

|  {master: }

|  speed:

|  angle:

|  {slave: }


trigger standard

description:

This can be applied to a sector (entering, leaving or nudging it) or a line (crossing it or nudging it).

usage:

| class: trigger standard

|  event_mask:

|  {entity_mask: }

|  client:


trigger

description:

This can be applied to a sector (entering, leaving or nudging it) or a line (crossing it or nudging it).

Exactly the same as trigger standard as far as I know.

usage:

| class: trigger

|  event_mask:

|  {entity_mask: }

|  client:


trigger switch1

description:

This is used specifically for switch triggers. Remember, the wall containing the switch must have a sign which is a switch texture or the trigger will NOT operate.

usage:

| class: trigger switch1

|  event_mask:

|  {entity_mask: }

|  client:


trigger single

description:

This is a trigger that can only be used once. Can only be applied to lines - sectors will be able to be triggered more than once!

usage:

| class: trigger single

|  event_mask:

|  {entity_mask: }

|  client:


trigger toggle

description:

This toggles an elevator between stops. Therefore, the client sector MUST have more than one stop.

usage:

| class: trigger toggle

|  event_mask:

|  {entity_mask: }

|  client:


event_mask:

description:

Event_mask is a value which determines under what conditions an elevator or trigger will function. For an elevator, the conditions will move it to its next stop. For a trigger, the conditions determine how it can be triggered.

These are the possible values:

Value	Event
0	Nothing
1	Cross line from front side
2	Cross line from back side
4	Enter sector
8	Leave sector
16	Nudge line from front side / Nudge sector from inside
32	Nudge line from back side /  Nudge sector from outside
64	Explosion
128	unused
256	Shoot at line (see entity_mask)
512	Touch floor of sector

If you want a sector or line to have more than one of these values, then add the desired values up.

For example, an elevator that can be operated by entering the sector or nudging in from inside has

event_mask: 20 (16 + 4).

usage:

| event_mask: [value]


event:

description:

Event creates a custom event value. A trigger can be given a custom event value, and any elevator with this event value as its event_mask value will operate when the trigger is triggered. The custom event value has to be a power of 2 that is 65536 or greater (65536, 131072, 262144, 524288, 1048576 and so on).

This is needed with multiple class elevators where each class is controlled by a separate switch. For example in the Research Facility (level 4), the sector called "Corecat" (spins around the Phrik metal) is two classes of elevator:

elevator move_fc and elevator morph_spin2. Two switches control these classes individually. If the "event" variable was not used, both switches would move both classes of elevator to its next stop at the same time. Utilising the "event" variable, it is made possible to have one switch control spinning and one control the moving floor/ceiling.

usage:

| event: [value]


entity_mask

description:

Entity_mask defines the entity that triggers certain triggers. The default is the player.

Here are the values that are used in the original 14 levels.

Value	Entity
-1	unknown, see IMPCITY.INF.  Used with event_mask: 308  Seems to work the same as * [This is all the bits set, so it IS equal to * [Yves]]
1	an enemy (e.g. an enemy crossing a line trigger)
8	one of your weapons (e.g. a "shoot" switch)
*	usually used with event_mask: 272 - you can shoot at the trigger and nudge it
2147483656	unknown, see the DTENTION.INF, RAMSHED.INF and ARC.INF.  Usually used with event_mask: 304.  Seems to work the same way as * [This is all the bits set except the 8 bit, so it should mean the player or an enemy [Yves]]

Entity_mask is always used in triggers except in the Research Facility (level 4), where it is applied to an elevator (the big one that spins around the Phrik metal and moves up and down). I don't know its function here. Removing the entity_mask from its sequence does not affect the elevator's workings!

Any explanations???

[Maybe just an error in the INF, ignored by the engine ? [Yves]]

usage:

| entity_mask: [value]


client:

description:

Client is used with triggers only. It defines which sector the trigger affects.

usage:

| client: [client sector name]


slave:

description:

Slave is optional and is used with elevators only. Slave determines which sector is a slave of the elevator it is applied to. The slave copies whatever the elevator does.

usage:

| slave: [slave sector name]


stop:

description:

A stop is a value that an elevator can arrive at. This value varies depending on the type of elevator (eg. the value for elevator move_floor is the altitude of the floor).

After the stop value, there needs to be another value.

This other value can either be the time the elevator remains at the stop in seconds, "hold", "terminate" or "complete".

Hold means that the elevator will remain at the stop until told to move to another stop.

Terminate means that the elevator will terminate at the stop (it will stay there permanently).

Complete means that the mission will be complete when the elevator arrives at the stop.

usage:

| stop: [value] [time]|hold|terminate|complete


master:

description:

A master value can either be on or off. If master is on, the trigger or elevator will function. If master is off, the trigger or elevator won't function, even if the event_mask is carried out or a message is sent, telling it to move.

usage:

| master: on|off


complete

Description:

Tells an elevator that a trigger goal has been completed.

The final num refers to the "TRIG: [num]" in the GOL file.

The corresponding goal in your Personal Data Assistant (F1) will then be shown to be complete.

Send this message to your completion elevator (the one with the "complete" stop).

This message also sends its recipent elevator to its next stop.

usage (T = Trigger, E = Elevator):

T->E | message: complete [num]

E->E | message: [stop] [elevator name] complete [value]


done

description:

Sent from an elevator to a switch trigger.

Tells the switch that it is ready to be used again (UNLESS it is a trigger single).

usage:

| message: [stop] [sector name] [trigger line number] done


goto_stop

description:

Sent from a trigger to an elevator or an elevator to an elevator.

Sends the elevator to a specified stop.

usage (T = Trigger, E = Elevator):

T->E | message:  goto_stop [stop number]

E->E | message: [stop] [elevator name] goto_stop [stop number]


next_stop

description:

Sent from a trigger to an elevator or an elevator to an elevator.

Sends the elevator to its next stop.

usage (T = Trigger, E = Elevator):

T->E | message: next_stop

E->E | message: [stop] [elevator name] next_stop


prev_stop

description:

Sent from a trigger to an elevator or an elevator to an elevator.

Sends the elevator to its previous stop.

usage (T = Trigger, E = Elevator):

T->E | message: prev_stop

E->E | message: [stop] [elevator name] prev_stop


master_on

description:

Sent from a trigger to an elevator or an elevator to an elevator.

Turns the elevator's master on.

usage (T = Trigger, E = Elevator):

T->E | message: master_on

E->E | message: [stop] [elevator name] master_on


master_off

description:

Sent from a trigger to an elevator or an elevator to an elevator.

Turns the elevator's master off.

usage (T = Trigger, E = Elevator):

T->E | message: master_off

E->E | message: [stop] [elevator name] master_off


m_trigger

description:

When sent to a trigger, this message triggers it no matter what its event_mask is.

Useful for triggering dummy trigger sectors that display messages from TEXT.MSG since "text" can only be sent when a trigger is triggered.

usage (T = Trigger, E = Elevator):

T->Sector T | message: m_trigger

E->Sector T | message: [stop] [sector name] m_trigger

E->Line   T | message: [stop] [sector name]([line number]) m_trigger


clear_bits

description:

Sent from a trigger to an sector or an elevator to a sector or wall.

Clears specified flag bits.

To clear more than one bit from a sector or a wall, add the bit values up that you want cleared.

usage (T = Trigger, E = Elevator):

T->SC	message: clear_bits [flag number] [bit number]
E->SC	message: [stop] [sector name] clear_bits [flag number][bit number]
E->WL	message: [stop] [sector name]([line number]) clear_bits [flag number][bit number]


set_bits

description:

Sent from a trigger to an sector or an elevator to a sector or wall.

Sets specified flag bits.

To set more than one flag to a sector or wall, add up the bit values that you want to be set.

usage (T = Trigger, E = Elevator):

T->SC	message: set_bits [flag number] [bit number]
E->SC	message: [stop] [sector name] set_bits [flag number][bit number]
E->WL	message: [stop] [sector name]([line number]) set_bits [flag number][bit number]


wakeup

description:

Sent from a trigger to a sector or an elevator to a sector.

Wakes up objects in that sector.

Useful for waking up generators and 3DO object animations (VUE file).

usage (T = Trigger, E = Elevator):

T->SC | message: wakeup

E->SC | message: [stop] [sector name] wakeup


page:

description:

"page" plays a sound effect when an elevator arrives at a stop.

It is placed in an elevator's sequence (between seq and seqend).

usage:

| page: [stop] [VOC file]


text:

description:

"text" displays a text message from TEXT.MSG on the top of the screen.

It is placed in a trigger's sequence.

Add your own messages on the end of TEXT.MSG if you want!

usage:

| text: [text number in text.msg]


sound:

description:

"sound" replaces the default sound effects of an elevator or switch trigger with another VOC file or silence.

It is placed in an elevator's or switch trigger's sequence.

Most elevators have 3 sound effects: leaving a stop, moving between stops, and arriving at a stop.

Switches only have one sound: when the switch is pressed.

usage (replacing a sound effect, elevators):

| sound: [sound value] [VOC file]

usage (replacing a sound effect, triggers):

| sound: [VOC file]

usage (replacing a sound effect with silence, elevators):

| sound: [sound value] 0

usage (replacing a sound effect with silence, triggers):

| sound: 0


adjoin:

description:

"adjoin" adjoins a line to another line when an elevator arrives at a stop.

It is placed in an elevator's sequence.

This is required if you need a line to remove its adjoin with one line and adjoin with another line midway through a level.

For example, in level 6 (detention center), you may notice that the 2 main lifts have a door on each layer adjoined to it on the same line. Since a line cannot be adjoined to more than one other line at once, the following occurs: midway through moving up between 2 layers, the elevator move_floor arrives at a stop which it remains at for 0 seconds. At this stop, a line of the lift sector is adjoined to a line of the door sector on the layer above, at the same time removing its adjoin with a line of the door sector on the layer below. The lift's doors all appear to be directly on top of each other.

usage:

| adjoin: [stop] [sector name] [line number] [sector name] [line number]