509 lines
14 KiB
Plaintext
Executable File
509 lines
14 KiB
Plaintext
Executable File
|
|
Rise of the Triad
|
|
|
|
Version 1.1
|
|
|
|
Hacker Info
|
|
|
|
RTL & RTC File format:
|
|
----------------------
|
|
|
|
Rise of the Triad (ROTT) uses two file extensions for levels data, RTL
|
|
and RTC. RTC indicates that the file is for Comm-bat (multiplayer) play
|
|
only and does not contain any enemies or exits. RTL indicates the file
|
|
can can be used for both Comm-bat and standard game levels. In Comm-bat,
|
|
the enemies in RTL maps in standard play are not present during Comm-bat
|
|
games and the exit and entrance arches behave like teleporters. Other than
|
|
these differences, the two files are alike.
|
|
|
|
The RTL/RTC file format changed with the release of ROTT version 1.1.
|
|
Since the shareware version of ROTT cannot use alternate levels, this
|
|
should not be a problem for map designers. The new format is much more
|
|
formal. If any changes are made in the format in the future, the first 8
|
|
bytes of the file will inform you if it is compatible with your editor/viewer.
|
|
|
|
The RTL/RTC file is broken into three sections: Version info, Header block,
|
|
and Data block.
|
|
|
|
|
|
RTL/RTC version info
|
|
|
|
This 8 byte block of data indicates what type of file it is and which
|
|
version of the RTL/RTC file format it is.
|
|
|
|
Offset Size Description
|
|
-------------------------------------------------------------
|
|
0 4 Format signature
|
|
4 4 Version number
|
|
|
|
Format signature :
|
|
|
|
This is used to indicate what type of levels are contained within the
|
|
file. This is a null-terminated string containing either "RTL" or "RTC".
|
|
|
|
Version number :
|
|
|
|
0101h for version 1.1. If this value is higher, it indicates that the file
|
|
format has changed. This is NOT the ROTT version.
|
|
|
|
|
|
RTL/RTC Header block
|
|
|
|
The header block contains an array of 100 structures with the following
|
|
format:
|
|
|
|
Offset Size Explanation
|
|
-------------------------------------------------------------
|
|
0 4 Used flag
|
|
4 4 CRC
|
|
8 4 RLEWtag
|
|
12 4 MapSpecials
|
|
12 4 Offset in file of Wall plane
|
|
16 4 Offset in file of Sprite plane
|
|
20 4 Offset in file of Info plane
|
|
24 4 Length of Wall plane
|
|
28 4 Length of Sprite plane
|
|
32 4 Length of Info plane
|
|
36 24 Name of level
|
|
|
|
|
|
Used flag :
|
|
|
|
This is non-zero if a map exists at this position.
|
|
|
|
CRC :
|
|
|
|
This value is used to determine if all the players in a multiplayer game
|
|
are using the same maps. You can use any method you like to calculate this
|
|
value.
|
|
|
|
RLEWtag :
|
|
|
|
This is the run-length encoding tag used for compressing and decompressing
|
|
the map data. The use of this will be described below.
|
|
|
|
MapSpecials :
|
|
|
|
This is used for flags that describe special conditions for the level.
|
|
Currently only one flag is used. If Bit 0 is set, then all the pushwalls
|
|
will be activated in Comm-bat mode. This is done in case there are player
|
|
start locations within hidden areas and the player would be trapped until
|
|
a pushwall was activated.
|
|
|
|
Offsets :
|
|
|
|
The Wall, Sprite, and Info plane offsets are each absolute offsets of the
|
|
data from the beginning of the file.
|
|
|
|
Lengths :
|
|
|
|
The Wall, Sprite, and Info plane lengths are each lengths of the
|
|
uncompressed data.
|
|
|
|
Name of level :
|
|
|
|
This is a null-terminated string containing the name of the level.
|
|
Although there is 24 bytes available, level names should be at most 22
|
|
bytes long.
|
|
|
|
RTL/RTC Data block
|
|
|
|
When expanded, ROTT maps contain 3 planes of 128 by 128 word sized data.
|
|
They are stored in the RTL/RTC files as 3 blocks of run-length encoded
|
|
data. The procedure for decompressing them is as follows:
|
|
|
|
1) Allocate 128 * 128 words of memory (32768 bytes)
|
|
2) Read one word from compressed block
|
|
3) If word is equal to RLEWTag, then the next two words are a compressed
|
|
run of data. The first word is the number of words to write.
|
|
The second word is the value to write map.
|
|
If word was not equal to RLEWTag, then simply write that word
|
|
to the map.
|
|
4) Go back to 2 until all data is written.
|
|
|
|
Here's an example of the procedure in C.
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: RLEW_Expand
|
|
|
|
Run-length encoded word decompression.
|
|
---------------------------------------------------------------------*/
|
|
|
|
void RLEW_Expand
|
|
(
|
|
unsigned short *source,
|
|
unsigned short *dest,
|
|
long length,
|
|
unsigned short rlewtag
|
|
)
|
|
|
|
{
|
|
unsigned short value;
|
|
unsigned short count;
|
|
unsigned short *end;
|
|
|
|
end = dest + length;
|
|
|
|
while( dest < end );
|
|
{
|
|
value = *source;
|
|
source++;
|
|
|
|
if ( value != rlewtag )
|
|
{
|
|
//
|
|
// uncompressed data
|
|
//
|
|
*dest = value;
|
|
dest++;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// compressed string
|
|
//
|
|
count = *source;
|
|
source++;
|
|
|
|
value = *source;
|
|
source++;
|
|
|
|
//
|
|
// expand the data
|
|
//
|
|
while( count > 0 )
|
|
{
|
|
*dest = value;
|
|
dest++;
|
|
count--;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
Here is sample code for loading a ROTT map.
|
|
|
|
|
|
#include <stdlib.h>
|
|
#include <fcntl.h>
|
|
#include <stdio.h>
|
|
#include <io.h>
|
|
|
|
/*---------------------------------------------------------------------
|
|
Map constants
|
|
---------------------------------------------------------------------*/
|
|
|
|
#define MAXLEVELNAMELENGTH 23
|
|
#define ALLOCATEDLEVELNAMELENGTH 24
|
|
#define NUMPLANES 3
|
|
#define NUMHEADEROFFSETS 100
|
|
#define MAPWIDTH 128
|
|
#define MAPHEIGHT 128
|
|
#define MAP_SPECIAL_TOGGLE_PUSHWALLS 0x0001
|
|
|
|
#define WALL_PLANE 0
|
|
#define SPRITE_PLANE 1
|
|
#define INFO_PLANE 2
|
|
|
|
/*---------------------------------------------------------------------
|
|
Type definitions
|
|
---------------------------------------------------------------------*/
|
|
|
|
typedef struct
|
|
{
|
|
unsigned long used;
|
|
unsigned long CRC;
|
|
unsigned long RLEWtag;
|
|
unsigned long MapSpecials;
|
|
unsigned long planestart[ NUMPLANES ];
|
|
unsigned long planelength[ NUMPLANES ];
|
|
char Name[ ALLOCATEDLEVELNAMELENGTH ];
|
|
} RTLMAP;
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Global variables
|
|
---------------------------------------------------------------------*/
|
|
|
|
unsigned short *mapplanes[ NUMPLANES ];
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Macros
|
|
---------------------------------------------------------------------*/
|
|
|
|
#define MAPSPOT( x, y, plane ) \
|
|
( mapplanes[ plane ][ MAPWIDTH * ( y ) + ( x ) ] )
|
|
|
|
#define WALL_AT( x, y ) ( MAPSPOT( ( x ), ( y ), WALL_PLANE ) )
|
|
#define SPRITE_AT( x, y ) ( MAPSPOT( ( x ), ( y ), SPRITE_PLANE ) )
|
|
#define INFO_AT( x, y ) ( MAPSPOT( ( x ), ( y ), INFO_PLANE ) )
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: ReadROTTMap
|
|
|
|
Read a map from a RTL/RTC file.
|
|
---------------------------------------------------------------------*/
|
|
|
|
void ReadROTTMap
|
|
(
|
|
char *filename,
|
|
int mapnum
|
|
)
|
|
|
|
{
|
|
char RTLSignature[ 4 ];
|
|
unsigned long RTLVersion;
|
|
RTLMAP RTLMap;
|
|
int filehandle;
|
|
long pos;
|
|
long compressed;
|
|
long expanded;
|
|
int plane;
|
|
unsigned short *buffer;
|
|
|
|
filehandle = open( filename, O_RDONLY | O_BINARY );
|
|
|
|
//
|
|
// Load RTL signature
|
|
//
|
|
read( filehandle, RTLSignature, sizeof( RTLSignature ) );
|
|
|
|
//
|
|
// Read the version number
|
|
//
|
|
read( filehandle, &RTLVersion, sizeof( RTLVersion ) );
|
|
|
|
//
|
|
// Load map header
|
|
//
|
|
lseek( filehandle, mapnum * sizeof( RTLMap ), SEEK_CUR );
|
|
read( filehandle, &RTLMap, sizeof( RTLMap ) );
|
|
|
|
if ( !RTLMap.used )
|
|
{
|
|
//
|
|
// Exit on error
|
|
//
|
|
printf( "ReadROTTMap: Tried to load a non existent map!" );
|
|
exit( 1 );
|
|
}
|
|
|
|
//
|
|
// load the planes in
|
|
//
|
|
expanded = MAPWIDTH * MAPHEIGHT * 2;
|
|
|
|
for( plane = 0; plane <= 2; plane++ )
|
|
{
|
|
pos = RTLMap.planestart[ plane ];
|
|
compressed = RTLMap.planelength[ plane ];
|
|
buffer = malloc( compressed );
|
|
|
|
lseek( filehandle, pos, SEEK_SET );
|
|
read( filehandle, buffer, compressed );
|
|
|
|
mapplanes[ plane ] = malloc( expanded );
|
|
|
|
RLEW_Expand( buffer, mapplanes[ plane ], expanded >> 1, RTLMap.RLEWtag );
|
|
|
|
free( buffer );
|
|
}
|
|
|
|
close( filehandle );
|
|
}
|
|
|
|
|
|
MAP WEIRDNESS
|
|
-------------
|
|
You can pretty much figure out most of the map data easily, but there are
|
|
a few things in the map which are a little oddly set up. Here's a few
|
|
helpful items.
|
|
|
|
|
|
THE UPPER CORNER
|
|
The first row of a map contains vital information to setting up a map.
|
|
|
|
In the first plane (WALLS) are these values:
|
|
|
|
0,0 FLOOR # (0xB4 through 0xC3, though we might cut some)
|
|
1,0 CEILING # (0xC6 through 0xD5, or skies: 0xEA to 0xEE)
|
|
2,0 BRIGHTNESS LEVEL (0xD8 to 0xDF, from dark to light)
|
|
3,0 RATE AT WHICH LIGHT FADES OUT WITH DISTANCE
|
|
(0xFC to 0x010B, fast to slow)
|
|
|
|
In the second plane (SPRITES) are these:
|
|
|
|
0,0 Height of level
|
|
(1-8 ranges from 0x5A to 0x61, 9-16 is from 0x01C2 to 0x01C9)
|
|
1,0 Height that sky is at relative to level (with same 1-16 arrangement)
|
|
(not needed for level with a ceiling)
|
|
2,0 Icon for NO FOG (0x68) or FOG (0x69)
|
|
3,0 Light sourcing icon (0x8B: if present, lights illuminate walls)
|
|
|
|
Optional items in the upper corner are:
|
|
|
|
Second Plane
|
|
Lightning icon (0x0179)
|
|
Timer icon (0x79: third plane points 0xXXYY to X,Y location of
|
|
timed thing--time in minutes/seconds there is MMSS in decimal
|
|
digits, so 0130 is 1 minute thirty seconds--and to one side of
|
|
that timed thing is the end time in the same format. This, for
|
|
instance, would say when to shut the door that opened at the
|
|
start time)
|
|
|
|
Third Plane (INFO)
|
|
Song number: 0xBAnn, where nn is song number. If not present,
|
|
the game will choose song 0. If greater than the number of
|
|
level songs (18 in shareware), the game will blow out.
|
|
|
|
|
|
DISKS
|
|
Gravitational Anomaly Disks (GADS) are set up with a GAD icon in the
|
|
second plane and a height in the third plane. The actual graphic has a
|
|
disk in the top quarter, so to put one on the floor, you sort of have to
|
|
put the object IN the floor, so the disk will be at the right height.
|
|
Heights for objects start with 0xB0 and have that last byte as a
|
|
tiles-off-the-floor nybble and sixteenths-of-a-tile fraction.
|
|
|
|
So 0xB000 is, for normal sprites, resting on the floor.
|
|
|
|
For disks, that would be a disk you could stand on to be one story
|
|
(eight feet) in the air. The heights of disks usually go by sixes (that's
|
|
the maximum they can be apart and you can still climb them like stairs) or
|
|
fours (for a more gradual ascension). Here are three sets of height
|
|
values. The values of 0xB0F1-$B0FE are into the floor, and $B0F6 is right
|
|
about floor height.
|
|
|
|
by 6 by 4 by 2
|
|
B0F6 B0F6 B0F6
|
|
B0FC B0FA B0F8
|
|
B002 B0FE B0FA
|
|
B008 B002 B0FC
|
|
B00E B006 B0FE
|
|
B014 B00A B010
|
|
B01A B00E B012
|
|
B020 B012 B014
|
|
B026 B016 ...
|
|
B02C B01A
|
|
B032 B01E
|
|
B038 B022
|
|
B03E B026
|
|
B044 B02A
|
|
B04A B02E
|
|
B050 B032
|
|
B056 B036
|
|
B05C B03A
|
|
B062 B03E
|
|
B068 B042
|
|
B06E B046
|
|
B074 B04A
|
|
B07A B04E
|
|
|
|
If you need higher ones, calculate them yourself, man.
|
|
|
|
|
|
SWITCHES AND TOUCHPLATES
|
|
|
|
Everything activated by a switch or touchplates points to the switch or
|
|
touchplate that activates it, with the standard 0xXXYY format. This way
|
|
tons of things can be activated by one switch. To make a door open with
|
|
multiple switches/touchplates, make it a few tiles wide and have different
|
|
parts of the door point to the different switches.
|
|
|
|
LOCKED DOORS
|
|
Locked doors are normal doors with a key sprite icon placed on them.
|
|
|
|
|
|
============================================================================
|
|
|
|
The ROTT WAD Format
|
|
-------------------
|
|
|
|
Most of you out there are probably very familiar with the WAD file
|
|
format developed by Id Software. We borrowed the format with their
|
|
consent and use it for all the data in Rise of the Triad.
|
|
|
|
The WAD structure itself is identical to that of other WAD's,
|
|
where the WAD header is as follows:
|
|
|
|
typedef struct
|
|
{
|
|
char identification[4];
|
|
long numlumps;
|
|
long infotableofs;
|
|
} wadinfo_t;
|
|
|
|
and the WAD directory is made up of [numlumps] of:
|
|
|
|
typedef struct
|
|
{
|
|
long filepos;
|
|
long size;
|
|
char name[8];
|
|
} lumpinfo_t;
|
|
|
|
|
|
ROTT Specific Data
|
|
------------------
|
|
|
|
WALLS - Walls are stored in the WAD between the two labels "WALLSTRT" and
|
|
"WALLSTOP". The format of each wall is a 4,096 block of data with no
|
|
header. The bitmaps are grabbed in vertical posts so that drawing in
|
|
modex is more straight format. All walls must be 64 x 64. The walls must
|
|
be the first few lumps in the WAD.
|
|
|
|
MASKED OBJECTS - Masked objects in the wad comprise all actors and
|
|
sprites. They can be found as weapons, objects, actors etc. They use the
|
|
following headers and structures:
|
|
|
|
typedef struct
|
|
{
|
|
short origsize; // the orig size of "grabbed" gfx
|
|
short width; // bounding box size
|
|
short height;
|
|
short leftoffset; // pixels to the left of origin
|
|
short topoffset; // pixels above the origin
|
|
unsigned short collumnofs[320]; // only [width] used, the [0] is &collumnofs[width]
|
|
} patch_t;
|
|
|
|
These are extremely similar to the patches used in another game, except
|
|
for the addition of the origsize parameter.
|
|
|
|
typedef struct
|
|
{
|
|
short origsize; // the orig size of "grabbed" gfx
|
|
short width; // bounding box size
|
|
short height;
|
|
short leftoffset; // pixels to the left of origin
|
|
short topoffset; // pixels above the origin
|
|
short translevel;
|
|
short collumnofs[320]; // only [width] used, the [0] is &collumnofs[width]
|
|
} transpatch_t;
|
|
|
|
Certain objects in the game like masked walls and touch plates will use
|
|
the second type of patch which acts like a translucent patch.
|
|
|
|
SKYS, FLOORS and CEILINGS - Skys are larger than the screen and are made
|
|
up of two 256X200 grabs in posts similar to the walls. The first grab
|
|
represents the bottom part of the sky and the second part the top of the
|
|
sky. The skys are denoted by the labels SKYSTRT and SKYSTOP. Floors and
|
|
ceilings use the following structure:
|
|
|
|
typedef struct
|
|
{
|
|
short width,height;
|
|
short orgx,orgy;
|
|
byte data;
|
|
} lpic_t;
|
|
|
|
They can be found between the labels UPDNSTRT and UPDNSTOP.
|
|
|
|
|
|
Okay, enough hints! Have fun figuring stuff out.
|
|
|
|
--THE DEVELOPERS OF INCREDIBLE POWER
|