Eye of the Beholder Decorations Format
! Wikify formatting
! Add infobox
This document describes the decoration rectangle data files (.dat) that are bundled with each decoration image file. In EOB2 they have the filename extension .dec
All unsigned short are big endian! They are little endian in the PC version.
File Format
struct Decoration { unsigned short nbrDecorations; struct Decoration decorations[nbrDecorations]; /* Indexed into by the WallMapping in the [[eob.inf|.inf]] file. */ unsigned short nbrDecorationRectangles; struct DecorationRectangle rectangles[nbrDecorationRectangles]; };
Sub structures
struct Decoration { unsigned char rectangleIndices[10]; /* indices into DecorationData.rectangles */ unsigned char linkToNextDecoration; /* index into DecorationData.decorations */ unsigned char flags; unsigned short xCoords[10]; /* coordinate in the game view, where to render the overlay */ unsigned short yCoords[10]; };
If rectangle index is 0xFF, there isn't a decoration for that view position. Each of these 10 indices in the arrays corresponds to the 10 possible overlay graphics positions in the viewport. TODO: Describe linkToNextDecoration, flags and the coordinate system. (JackAsser will fix this)
struct DecorationRectangle { unsigned short x; /* Multiply by 8 to get actual screen coord */ unsigned short y; unsigned short w; /* Multiply by 8 to get actual width */ unsigned short h; };
Each rectangle describes an area in the .cps graphics file stated by the 0xec command code in the .inf file.
Drawing a decoration + Pseudocode(EOB2)
(what about EOB1?)
decoration wall positions (0..9): 9 7 3 7 9 8 6 2 6 8 8 5 1 5 8 4 0 4 ^=party pos.
The following table contains drawing information for each view position: xflip: for side walls: 0=left, 1=right wall: decoration position number, -1=none available for this position xdelta: horizontal shift (decoration on wall only) in render window for this wall position (multiply with 8)
CDecPos: array [0 .. 25] of TPos = ( (XFlip: 0; Wall: -1; XDelta: 0), (XFlip: 0; Wall: 9; XDelta: 0), (XFlip: 0; Wall: 7; XDelta: 0), (XFlip: 1; Wall: 7; XDelta: 0), (XFlip: 1; Wall: 9; XDelta: 0), (XFlip: 0; Wall: -1; XDelta: 0), (XFlip: 0; Wall: 3; XDelta: -12), (XFlip: 0; Wall: 3; XDelta: -6), (XFlip: 0; Wall: 3; XDelta: 12), (XFlip: 0; Wall: 3; XDelta: 6), (XFlip: 0; Wall: 3; XDelta: 0), //middle front wall (XFlip: 0; Wall: 8; XDelta: 0), (XFlip: 0; Wall: 6; XDelta: 0), (XFlip: 1; Wall: 6; XDelta: 0), (XFlip: 1; Wall: 8; XDelta: 0), (XFlip: 0; Wall: 2; XDelta: -10), (XFlip: 0; Wall: 2; XDelta: 10), (XFlip: 0; Wall: 2; XDelta: 0), //middle front wall (XFlip: 0; Wall: 5; XDelta: 0), (XFlip: 1; Wall: 5; XDelta: 0), (XFlip: 0; Wall: 1; XDelta: -16), (XFlip: 0; Wall: 1; XDelta: 16), (XFlip: 0; Wall: 1; XDelta: 0), //middle front wall (XFlip: 0; Wall: 4; XDelta: 0), (XFlip: 1; Wall: 4; XDelta: 0), (XFlip: 0; Wall: 0; XDelta: 0) );
Decorations can consist of more than one rectangle, which are drawn together. They are given as a list:
procedure DrawCompleteDecoration(GFXIndex, DecNumber, Position:longint; isAtWall:boolean); begin repeat DrawDecorationPart(GFXIndex, DecNumber, Position, isAtWall); // draw a part of the decoration DecNumber:= Decoration[DecNumber].NextDecoration; // go to next part until DecNumber = 0; end;
GFXIndex points to the data file where the decoration resides: for example mezz2.cps DecNumber is the number of the Decoration. Position (range 0..25) is the wall position (side and front walls). isAtWall tells if the decoration is at a wall, this is true if the walltype in the decoration mapping definition in the .inf file is unequal zero.
procedure DrawDecorationPart(GFXIndex, DecNumber, Position:longint; isAtWall:boolean); var i,j, //pos in cps graphics s,t:longint; //pos on screen q:byte; dx,pos : longint; mirrored : boolean; begin //get the horizontal shift for the decoration according to its position. //if the decoration is drawn at a wall, we use the table above. //otherwise the decoration is placed in the middle of a field, // for example a pit or pressure plate... dx := 0; if isAtWall then dx := 8*CDecPos[Position].XDelta else case Position of 6 : dx := -88; 7 : dx := -40; 8 : dx := 88; 9 : dx := 40; 15 : dx := -59; 16 : dx := 59; 20 : dx := -98; 21 : dx := 98; end; //translate view positions (0..25) to decoration view position (0..9) pos := CDecPos[Position].Wall; if pos >= 0 then begin //get the number of the decoration rectangle for the corresponding view position q := Decoration[DecNumber].RectangleIndices[pos]; //if rectangle index is $FF, then there doesn't exist a decoration for this view position if q <> $FF then begin //Bit 0 in Flags: for front walls (and not for side walls): // for example, a decoration consists of two parts, // and the second part is the first one, but mirrored.. mirrored := false; case Position of 6..10, 15..17, 20..22, 25: mirrored := boolean(Decoration[DecNumber].Flags and $01); end; t:= Decoration[DecNumber].YCoords[pos]; for j:= Rectangles[q].y to Rectangles[q].y+Rectangles[q].Height-1 do begin if mirrored then s := 22*8 - Decoration[DecNumber].XCoords[pos] - 1 else s := Decoration[DecNumber].XCoords[pos]; for i := Rectangles[q].x*8 to Rectangles[q].x*8+Rectangles[q].Width*8-1 do begin if mirrored then begin PutPixel(s+dx, t, Graphics[GFXIndex][320*j + i]); Dec(s); end else begin if CDecPos[Position].XFlip = 0 then PutPixel(s+dx, t, Graphics[GFXIndex][320*j + i]) //left side walls; front walls else PutPixel(22*8-(s+dx), t, Graphics[GFXIndex][320*j + i]); //right side walls only Inc(s); end; end; //for i Inc(t); end; //for j end; // q<>$FF end; //pos>0 end;