Background
Graphics
Also see Custom Graphics
Engine Frequently Asked Questions
Each cell of the dungeon can have its own 'Skin'. A 'Skin'
consists of zero to four graphics assocated with the 'Floor', 'Middle',
'Ceiling', and 'Walls' and four masks which describe what part of the
graphics are to be displayed and where.. The Skin also can
contain graphics for each of the possible Wall Decorations and for each
of the possible Floor Decorations. The Skin number for each
Cell is stored
in the dungeon file. The definition of each Skin (the IDs of the
four graphics, the four sets of masks, and the graphics and masks for
each Decoration) is contained in the CSBgraphics.dat
file. The
graphics themselves are also stored in the CSBGraphics.dat file.
The graphic 'masks' are also
stored in CSBGraphics.dat.. All the graphics associated with
backgrounds (including masks and skin definitions) are stored as a
single 'Graphic Type' identified as 'Background Graphics' when using
CSBGraphics.exe. Finally, the 'CSBGraphics.dat' contains 'code'
for each skin that is executed and which controls the drawing of the
floors, middle, ceiling, walls, and decorations.
The dungeon can contain up to 255 'Skins'. There can be up to
65535 graphics and masks for the floors, middle, ceiling, walls, and
decorations
combined.
Each of the Floor, Middle, and Ceiling graphics is a single graphic
that is (in the simplest case) the size of the entire area. The
masks for each of these area selects the portion of the graphic to be
displayed and designates the position in the viewport where it is to be
displayed. Each Decoration is a single graphic and
collection of masks that control what pieces of the graphic are
to be displayed and where.
The Code has a separate function for each Cell position and Cell
type. For example, there is a function for a Stone Cell two steps
ahead and one to the right. There is another function for a
teleporter two steps ahead and one to the right. There is another
function for a teleporter one step aheand and one to the right..
Any one of these functions can do as it pleases. For example, the
function for a Teleporter one step ahead and one step to the left
can draw a Door two steps ahead and one to the right if it so
pleases. But a person would have to be mad to do this. Are
you there, Zyx? See Custom Graphics
Code Explained
The
cells are numbered from 0 to 20 starting at the farthest visible cell
to the far left and working to the right and closer. The drawing
functions are called in inccreasing numerical order. Here is a
diagram with the party looking north (P = party):
--------------------------
| 0 | 4 | 6 |
5 | 1 |
| | |
| | |
--------------------------
| 2 | 7 | 9 |
8 | 3 |
| | |
| | |
--------------------------
| 10 | 12 | 14 | 13 | 11 |
| | |
| | |
--------------------------
| 15 | 17 | 16 |
| | | |
----------------
| 18 | 20 | 19 |
| | P | |
----------------
The definition of the skins is stored as 'Background Graphic' with ID =
1.. Each skin is defined by a number of 16-bit values that
designate the graphicID for the floors, middle, ceiling, walls, and
decorations. Very
simple. Skin 0 existes in the definitions but is never used
because Skin 0 means to use no custom background. Skin 1 follows,
then Skin 2, etc, each occupying 16 bytes. The first four 16-bit
values
represent the Floor Graphic ID, the Middle graphic ID, the Ceiling
graphic ID, and the Wall graphic ID associated with that skin.
The second four 16-bit values identify the mask to be used for Floor,
Middle, Ceiling, and Walls. If
any of the graphic IDs (or mask ID) is zero then no custom graphic is
used for the
corresponding portion of the viewport.. The remainder of the skin
definition consists of a variable number of 16-bit values for the
floor decorations and a variable number of 16-bit words for the wall
decorations. The total structure looks like this:
struct SKINS
{
i16 number of skins = Sn
i16 offset of skin 0 ( not
used - must be zero )
i16 offset of skin 1
...
i16 offset of skin Sn-1
SKINDEF
skinDef [ Sn ] ;
};
struct SKINDEF
{
i16 floor graphic ID
i16 middle graphic ID
i16 ceiling graphic ID
i16 wall graphic ID
i16 floor mask ID
i16 middle mask ID
i16 ceiling mask ID
i16 wall mask ID
i16 code ID
i16 number of floor decorations = Fn
i16 floor decoration graphic ID [ Fn ]
i16 number of wall decorations = Wn
i16 wall decoration graphic ID [ Wn ]
};
The graphics themselves (other than the skin definition) can have any
ID you choose although it is
wasteful to use very large graphic numbers because it causes memory to
be wasted at runtime. Also, at
runtime you can generate 'Virtual' graphics such as mirrored floors,
shifted, and scaled graphics
The graphics and the masks are in the Atari 'Bit-Plane' format in which
four 16-bit words are used to represent 16 pixels. The first of
the four words contains the least significant bit of each of the 16
pixels. The second 16-bit word contains the next least
significant bits, etc. The most significant bit in each of the
four words provide the four bits for the first pixel, etc. In
this way the four words define 16 pixels. The mask need only
contain one word for the 16 pixels because the masks for each of the
four words are identical and can be constructed at runtime by
replicating just one word. So, in the mask graphic 16 pixels are
represented by one 16-bit word and in the 'picture' graphic 16 pixels
are represented by four words.
The Mask graphic has the following format:
struct index
{
i32 number of masks = Mn
i32 maskOffset [ Mn ]
};
maskOffset is the byte offset within the file of the start of each of
the masks used by the skin. If any of these is zero then any code
using that mask will not be drawn. Each mask (pointed at by
the index entry) is of the following format:
struct maskEntry
{
ui16
sourceX;
ui16
sourceY;
ui16
destinationX;
ui16
dstinationY;
ui16 width;
ui16 height;
ui16
pixels[width*height/16];
};
The 'sourceX', 'sourceY', 'destinationWidth', 'destinationHeight',
'width' and
'height' are all in units of one
pixel. Therefore, 'width' must be
a multiple
of 16; 'destinationX need not be a multiple of 16 although it is
more efficient if it is. 'destinationX' and 'destinationY' are
relative to the top
left of the
viewport. As a result of this, you can draw floor graphics on the
ceiling or anywhere else in the viewport that you choose.
Moreover, the graphics can be any size. So if you want to draw
trees or columns you can do so.
The 'picture' graphic has the following format:
struct picture
{
i32 width; //In units of pixels. Multiple of
16;
i16 pixels[#pixels in graphic/4]
};
*******************************************************************************************************
*****
*****
A DECORATION GRAPHIC ( WALL OR FLOOR ) LOOKS LIKE THIS
*****
*******************************************************************************************************
i16 number of
decorationPictures = Pn
i16 offset of decorationPicture 0
i16 offset of decorationPicture 1
.....
i16 offset of decorationPicture Pn-1
i16 number of
decorationLocations = Ln
i16
decorationLocation 0
i16
decorationLocation 1
...
i16
decorationLocation Ln-1
Here starts the actual bitmap data and mask data.
Each picture is an 'interleaved' bitmap with the mask interleaved in
the pixel data.
struct decorationPicture
{
i8 width; // In
units of pixels. Multiple of 16.
i8 height;
PICTUREGROUP
pictureData[width/4 * height];
};
struct PICTUREGROUP
{
i16 mask;
i16 bitplanes[4];
};
struct decorationLocation
{
i8 destinationX;
i8 destinationY;
};
Notice that the pictures have no sourceX nor sourceY. The source
of bitmaps all start at ( 0, 0 );
Like other graphics, the destinationX must be a multiple of 16 and the
width must be a multiple of 16.