// "Build Engine & Tools" Copyright (c) 1993-1997 Ken Silverman
// Ken Silverman's official web site: "http://www.advsys.net/ken"
// See the included license file "BUILDLIC.TXT" for license info.

BUILD engine Notes (8/14/95):

BUILD programmed by Ken Silverman

┌───────────────────────────────────────────────────────────────────────────┐
│                               GAME KEYS:                                  │
└───────────────────────────────────────────────────────────────────────────┘
       ESC = Quit
     Mouse = Movement
    Arrows = Movement
 Lt. Enter = Single-player: Play back game.
             Multi-player: View from other player's eyes.
 Rt. Enter = Switch between 3D / 2D modes
   Lt. +/- = Zoom in 2D mode
       A/Z = Move up and down
 Left-Ctrl = Shoot
       1/2 = Select weapon
Left-Shift = Run
         T = Reset Timing (sets totalclock = 0)
         V = Change visibility.  In BUILD.H there is a visiblity variable.
             It is initialized to 13.  It can range from around 8 (darker)
             to about 15 (lighter).
         P = Change parallaxing sky mode. (0, 1, and 2 (Default = 2)
       F12 = Screen capture (saves image as a *.BMP file, starting as file
             name CAPTUR00.BMP and incrementing by 1 each time F12 is
             pressed.

┌───────────────────────────────────────────────────────────────────────────┐
│                         BUILD EDITOR INTRODUCTION:                        │
└───────────────────────────────────────────────────────────────────────────┘

There are 2 modes in the BUILD editor:
   3D EDIT MODE (Same as PLAY MODE but with a mouse cursor)
   2D EDIT MODE (The overhead map of the board with an arrow showing your
      position and angle and a mouse cursor)

It is essential that you use both editor modes:

  Use the 3D EDIT MODE to change the attributes of a sector, wall, or sprite
     such as:

     Tile number - Tells which picture in the artwork file goes on the
                      object.  Press V to change.
     Shade       - The shade of the object.  Press -/+ to change.
     Repeating   - The "smooshiness" of a wall.  Also for sprites.  Press
                       keypad 2,4,6,8 to change.
     Panning     - The starting offset into the tile graphics.  Press
                       Shift + keypad 2,4,6,8 to change.
     Height      - For ceilings, floors, and sprites.  Press PGUP or PGDN
                   to change.
     and there are a few other special attributes.

  Use the 2D EDIT MODE to add, delete, or change the shape of sectors.

  To switch between the two EDIT MODES, press the keypad enter key.

┌───────────────────────────────────────────────────────────────────────────┐
│                          3D EDIT MODE KEYS:                               │
└───────────────────────────────────────────────────────────────────────────┘
         ESC = Quit
keypad ENTER = Flip to the 2D overhead editor

       Mouse = Move mouse cursor
      Arrows = Move you in the appropriate directions
   Caps Lock = There are 3 different Z coordinate modes in BUILD.
                  Mode 0:  Game mode (default)
                  Mode 1:  Height lock mode
                  Mode 2:  Float mode
               Press Caps Lock to switch between the 3 modes.
               A and Z move up and down for all 3 modes.


Note: For the following keys, it is important to move the mouse cursor to
the right position before using them.  Also, if you hold down the first mouse
button, then the item under the mouse cursor will be locked as the
highlighted object while the button is held down.  This is useful for the
PGUP/DN keys, when different objects come into view even without moving your
coordinates or the mouse cursor.
      There are 4 basic types of objects that can be worked with in this part
of the editor:  WALLS, CEILINGS, FLOORS, and SPRITES.

     PGUP/DN = Raise or Lower a ceiling or floor.  (If a wall is selected,
               the ceiling of that sector will move)  Also, if you did a
               sector highlight in 2D EDIT MODE, you can raise / lower
               multiple sectors at a time.
Ctrl-PGUP/DN = For sprites only, puts sprites exactly on the floor or exactly
               on the ceiling.

           V = Tile selection - use arrow keys to move around.  Press ENTER
               to change the object to the highlighted piece of artwork, or
               press ESC to cancel without changes made.
       ALT-V = Height selection - works just like V, but this selects the
               groudraw height map.

2,4,6,8 (keypad) = Repeat values (smooshiness of the sizes of pixels) -
               think of these keys as arrow keys controlling the bottom
               right corner of the bitmap.  Normally, this is used for walls
               and sprites.  If you select a floor or ceiling, all the walls
               in that sector will be affected.  Perhaps this can be used
               to make sprites grow and shrink as they get healthy or hurt.
Shift +
2,4,6,8 (keypad) = Panning values (offset into the tile) -  These keys are
               useful when the you want a long wall to look continuous when
               they normally would not look continuous.
           / = Use this key to reset the panning values (if you're lost!)
  5 (keypad) = If you hold down this key down in addition to the 2,4,6,8
               keys (keypad),the values will align at multiples of 8.
          .> =  This key attempts to match up all the tiles along a wall.  It
               scans along the walls towards the right as long as the picture
               number of the next wall is the same as the original picture
               number.  Note that some walls may not work right, especially
               floor / ceiling steps.

           F = Flip an object.  For sprites and walls, this flips the object
               x-wise.  For ceilings and floors, the objects are flipped in
               8 possible ways.  Just keep pressing 'F' to go through
               the 8 ways.
       ALT-F = When you use relative alignment mode on ceiling and floor
               textures, you can press Alt-F on the ceiling or floor to
               choose a new wall to align to.  It actually rotates the walls
               of a sector by 1.
           O = Wall orientation (whether it starts from the top or bottom)
               Normally, walls are oriented from the top.  For example, if
               you hold down 2/8 on the keypad in 3D EDIT MODE, the wall
               always starts from the top.  Orientation works differently
               for white lines and red lines, so if a wall doesn't look
               right, just press 'O' anyway to see if it get fixed.

COPY & PASTE
         TAB = COPY. Copy the attibutes of the highlighted objects into a
               temporary place.  The attributes it remembers are:
               tile, shade, x-repeat, y-repeat, and cstat values.
  Left ENTER = PASTE. Paste the stored attributes over the highlighted
               object.  Whenever you press ENTER, the y-repeat values stay
               the same, and the x-repeat values are adjusted so the pixels
               of the bitmaps have a square-aspect ratio.
Ctrl+L.ENTER = Left ENTER with the ctrl key will paste the attribtues to
                every wall in a loop (if a wall is highlighted).
Shft+L.ENTER = Left ENTER with the shift key also pressed copies the shade
               only.
Ctrl+Shft+L.ENTER = Auto-shade a sector.  First make any wall of the loop
               as light as the lightest shade you want.  Then make any other
               wall of the loop as dark as the darkest shade you want.
               Finally press Ctrl-Shift Enter on the wall that should be
               lightest.  Now the loop should be smoothly shaded.  If it
               is not smoothly shaded, you may need to insert more points
               on the walls.

SECTOR FLAGS:

           P = Make the ceiling of the given sector have a Parallaxing sky
               or just a normal ceiling.
           G = Make the floor of the given sector have a Groudraw
               (floor with height mapping).  I do not recommend using this
               attribute very extensively yet. (See the H key for selecting
               the height map)
           E = An option for ceilings and floors.  If for some reason, you
               want a tile to be smooshed into the normal 64*64 area, press
               E to unExpand the tile.  Press E again, and the tile will be
               expanded, so the pixel size is the same as the normal 64*64
               ceiling/floor.
           R = Relative alignment - switch between relative alignment mode
               and normal mode.  Allows floor / ceiling textures to align
               to the first 2 points of a sector.  Textures will rotate/pan
               with moving sectors properly.  Notice that bit 6 of both
               sector[].ceilingstat and sector[].floorstat are relative
               alignment bits.

WALL FLAGS:

           B = Make an invisible wall, such as a window, block you from
               going through.  Since the wall is invisible, you can also
               highlight the ceiling right above the window or floor
               right below the window.  You can block either the front or
               the back of the window.  If you block the back only, you
               will be able to go onto the window sill.
           T = Press to make a maskable wall 50/50 transluscent.  Press T
               again to put the masked wall back to normal mode.
           M = Make a maskable wall.  Press in the same place you press 'B'.
               The masking wall takes all its attributes from the front of
               the wall, so it must have the same repeat, panning, and cstat
               values as the walls above or below it (if you have a step).
               The masking picture number is stored in overpicnum.  Also,
               the masking walls is also automatically added the the other
               side of the wall, with the picture flipped.  (see the 'F'
               key descripte above)
   Shift + M = Make a maskable wall just like 'M' described above, but only
               on the front side.

           1 = Make a 1-way wall.
           2 = Some walls have two different sections.  One step on the
               ceiling and one step on the floor.  Normally they always
               have the same attributes.  It is possible though, to give
               both the top and bottom different attributes by pressing 2 on
               that wall.  2 simply makes the bottom wall's attributes
               separately editable.  Press 2 on either the top or bottom
               wall.
           O = Wall orientation (whether it starts from the top or bottom)
               Normally, walls are oriented from the top.  For example, if
               you hold down 2/8 on the keypad in 3D EDIT MODE, the wall
               always starts from the top.  Orientation works differently
               for white lines and red lines, so if a wall doesn't look
               right, just press 'O' anyway to see if it get fixed.
           H = Toggle hitscan pass through bit.  Default is pass through.

SPRITE FLAGS:

           B = When the mouse cursor is on a sprite, this makes a sprite
               block you from walking through.  Also makes the sprite
               sensitive to hitscan.  Sprites with the 'B' attribute will
               appear pink in 2D EDIT MODE
           T = Press to make a sprite 50/50 transluscent.  Press T again
               to put sprite back to normal mode.

┌───────────────────────────────────────────────────────────────────────────┐
│                          2D EDIT MODE KEYS:                               │
└───────────────────────────────────────────────────────────────────────────┘
               ESC = Show a menu that says, "(N)ew, (L)oad, (S)ave, (Q)uit"
                     Press ESC again to cancel the menu.
      keypad ENTER = Flip back to the 3D edit mode

             Mouse = Move mouse cursor
 Left mouse button = If you hold down the left mouse button, you can drag
                     existing points.  To drag multiple points you can use
                     the right shift key to first select a rectangular
                     region of points to highlight, then just drag any of
                     the highlighted points and the rest move with it.
Right mouse button = Moves the player's positions to the mouse cursor.
                     This is useful when you accidently get stuck somewhere
                     in the board, or when you need to edit some part of
                     a sector that is hard to access.
       Right Shift = Select a bunch of points for use with dragging around.
                        Selects all points inside a box.  (Use the left mouse
                        button to drag)
  Ctrl+Right Shift = Select a bunch of points for use with dragging around.
                        Selects all points on a loop.  (Use the left mouse
                        button to drag)
         Right Alt = Select a bunch of sectors for either duplication or
                        dragging around.  (see left mouse button for
                        dragging and the insert key for duplication).

            Arrows = Move player position in the appropriate directions.
                     The player will be clipped.  To jump to a different part
                     of the board, use the right mouse button.
             Space = Press the space bar when drawing new sectors.  There
                     are several ways of drawing new sectors.  The following
                     three ways of drawing sectors can all be done by only
                     using the space bar.  The computer is smart enough to
                     decide which method you are using.

                     1.  Drawing a FULL LOOP - that is, whenever the new
                         sector meets the old sector, draw over that line
                         again.  In full loop mode the new sector must not
                         already be in another sector.  The loop is done
                         when you press the space bar at the first point
                         again.
                     2.  SPLITTING a sector - press space bar to draw points
                         at which you want to split a sector.  The computer
                         knows you are done splitting when you end at
                         another point that's on the edge of the sector you
                         are splitting.
                     3.  Drawing a sector COMPLETELY INSIDE another sector.
                         (for example, columns) To do this, just press space
                         bar at every point in the loop.  The loop is done
                         when you press the space bar at the first point
                         again.
         Backspace = When plotting points with the space bar, you can use
                     backspace to get rid of the last point plotted.  You
                     can press the backspace to get rid of all the points
                     if you didn't want to start a sector at all.

            Insert = Inserts a new point at the midpoint of the highlighted
                     line.  Then you can drag the point to wherever you like.
                     (If you insert on a red line, the point will be inserted
                     on both sides of the sector line.)
                     If a bunch of sectors are selected (see right ALT) then
                     instead of inserted points, the selected sector bunch
                     will be duplicated (stamped).  Don't forget to drag
                     the selected sectors after stamping.

            Delete = Use this to delete sprites (blue circles).  To delete
                     points of a sector border, don't press delete.  Instead,
                     drag the point into one of its 2 neighbor points on the
                     sector.  This is easist done if grid locking is on
                     (mouse cursor is pink).  If 2 neighbor points are equal,
                     one will automatically be deleted.
 Right Ctrl-Delete = This deletes the whole sector that the mouse cursor is
                     in.  Note the right ctrl for protection.
   (Note: to delete a point of a sector, just drag that point into the next
      point and it will automatically be deleted.  You should do this with
      grid-locking on)
                 J = Use to join two neighboring sectors.  Press J when mouse
                     cursor is over the first sector.  Then press J again
                     when the mouse cursor is over the neighboring sector.
                     The attributes of the combined sector will be taken from
                     the first sector selected.
             ALT-S = When you have a white loop inside a sector, you can
                     press ALT-S on it (highlight any of its lines) to turn
                     the whole loop red.
                 S = Places a sprite at the location under the mouse cursor.
                     A sprite looks like a blue circle in the overhead map.

                 B = Blocks / unblocks you from going through a wall or
                     sprites.  A blocked wall or sprite will appear pink
                     in 2D EDIT MODE.  See the description for 'B' in
                     the 3D EDIT MODE section for more details.

                 C = Turn a line into a circle defined by short line
                     segments.  First press 'C' on a highlighted wall.  Then
                     move the mouse to the right place and press '+' or '-'
                     if you want to change the number of points on the
                     circle.  Press 'C' again to cancel the circle drawing or
                     press the Space bar to actually change the map.
               +/- = Increase / Decrease the number of points on the circle.

                 T = Type in a LO-tag for a sector.  Move the mouse cursor to
                     the inside of a sector that you want to tag first.
             ALT-T = Just like 'T' but for walls and sprites.
                 H = Type in a HI-tag for a sector.  Move the mouse cursor to
                     the inside of a sector that you want to tag first.
             ALT-H = Just like 'H' but for walls and sprites.
                 E = Change a sprite's status list number.
           < and > = Changes angle of sprite.  Move the mouse cursor to a
                     sprite first.  You can hold down shift with the < and >
                     to get more precise angles.  If you did a sector
                     highlight, then the selected sector will be rotated
                     instead.
            CTRL-T = Turn tag boxes on/off.

               TAB = Move the mouse cursor to the inside of a sector that you
                     you want to see the attributes of.  It will show them
                     at the bottom of the status bar.  To clear it, press
                     TAB again at somewhere in the board that is not part
                     of any sector.  This is a useful key for debugging.
           ALT-TAB = Works just like TAB, but here, you can see the
                     attributes of highlighted walls or sprites.  For red
                     lines, the side the mouse cursor is on the line affects
                     which line is highlighted, since red lines are actually
                     defined as 2 walls (1 wall for each sector).

       Scroll Lock = Set starting position (brown arrow) to your current
                     position (white arrow).

               A,Z = Zoom in and out.  This is useful for choosing whether
                     you want to edit finely or not.
                 G = Change grid resolution.  The grid resolution cycles
                     through this sequence:
                        (off, 1x, 2x, 4x (default), 8x, 16x)
                 L = Turns grid locking on or off.  If the mouse cursor is
                     pink then grid locking is on.  If it is white then
                     grid locking is off.  There is no grid locking
                     if the grid is turned off.  Also, grid locking will
                     lock to nearby points.

┌───────────────────────────────────────────────────────────────────────────┐
│                              EDITART KEYS:                                │
└───────────────────────────────────────────────────────────────────────────┘

   ---- Keys you will need to know if you want to select from a section of
   a 320*200*256 picture file (.BMP and .PCX only) and put it into the BUILD
   engine.

   U - Use this to import a section of a 320*200*256 .BMP, .PCX, or .GIF.
     │  Enter - Convert the image that is inside the rectangular selection
     │          rectangle to the BUILD palette.
     │  Space - Convert the image that is inside the rectangular selection
     │          rectangle without remapping the palette.
     │  P - If in the picture selecting screen (after pressing U and loading
     │      the picture), you press P, then the palette of BUILD can be
     └──    replaced by the palette of the displayed picture.
   PGUP/PGDN - Select tile to edit (4096 tile maximum right now).
   G - GOTO a tile by typing in the tile number.
   S - Re-size tile.  The X and Y sizes can be any unsigned short integer.
       X ranges from 0 to 1024, and Y ranges from 0 to 240.
   Delete - short cut key to set both the X and Y sizes to 0.
   +,- Change the animation setting.  (Default: NoAnm = 0.)
         To change the animation type, press - when the value is 0.
         Ex: If you want an object to have 4 tiles of animation, you can
            animate it in 4 different sequences: (0 is the current tile)
               NoAnm=4 sequence: 0,0,0,0,0,0,0,0,0,0,0,... (no animation)
               Oscis=4 sequence: 0,1,2,3,2,1,0,1,2,3,2,... (oscillate)
               AnmFD=4 sequence: 0,1,2,3,0,1,2,3,0,1,2,... (forwards)
               AnmBK=4 sequence: 0,-1,-2,-3,0,-1,-2,-3,... (backwards)
   A - Set the animation speed of the tile.  Press + and - to change the
       animation speed.  There are 16 different animation speeds.  The
       animation speed set here set the speed for BUILD and your GAME also.
                    (Speed is proportional to (totalclock>>animspeed))
  ~' - This key (located just above the TAB key) allows you to center a
       sprite.  Simply use the arrow keys to get to the desired position.
   N - Name a tile.  Naming a tile simply changes the #define statement in
       NAMES.H.  You should include NAMES.H when compiling so you can easily 
       refer to sprites by name rather than by number.
   O - Optimize the size of an individual piece of artwork.  Use this for
       tiles with invisible pixels on the sides.
   V - View and select a tile to edit.
     │   Space - To swap 2 tiles simply press space bar on the first tile,
     │           then space bar on the second.
     │   1,2,3 - To swap a group of tiles, press 1 on the first tile,
     │             press 2 to remember the region between where you pressed
     │             1 and 2.  Press 3 at the place to where you want to swap
     └──           all the tiles.
ALT+U- Re-grab artwork from original pictures according to the CAPFIL.TXT
       file.  If you press ALT-U in the main screen, everything will be
       re-grabbed.  If you press ALT-U in 'V' mode, then you should first
       select the range by pressing '1' and '2' on the range boundaries.
ALT+R- Generate a Tile frequency report by scanning all maps in directory.
       Use in 'V' mode only.
F12 - Screen capture (saves image as a *.BMP file, starting as file
       name CAPTUR00.BMP and incrementing by 1 each time F12 is
       pressed.

   ESC - Quit.



   ---- Extra features: (if you actually want to do the artwork in EDITART
                         or if you want to touch-up some imported art.)

      C - Change all pixels on the tile having the same color under the
         graphics cursor to to selected color.
      Arrows / Mouse - Move graphics cursor.
      Shift + Arrows - Select color. (on bottom right corner of screen)
      Space - Plot a pixel with the selected color.
      T - Turn drawing trail on / off.
      Tab - Select the color under the graphics cursor.
      BACKSPACE - Set the color to color 255 (transparent color).
      F - Floodfill a region with the current color and with the current
          color as a boundary.
      M,P - Use M to back up a tile into a temporary buffer in memory and P
            to restore it.  It may be wise to press M before a floodfill (F)
            (because sometimes you miss encapsulating the region by 1 pixel,
            and the whole picture gets killed, etc...)
      J - Randomly plots dots of current color over any pixels having the
          same color as the color under the tile cursor.
      [ - Random antialias of colors in color band under graphics cursor.
      ] - Non-random antialias of colors in color band under graphics cursor.
      ; - 3-Dimentionalize an image.  Makes colors in different rows of the
          color bar either appear to stick out or stick in to the wall.
      ' - 3-Dimentionalize the other way.
      R - Rotate the tile in a specified direction.

      1 - Mark the first corner of a rectangle for a copy/paste operation.
      2 - Mark the other corner of a rectangle for a copy/paste operation.
      3 - Paste the selected rectangle (Note: You must press 1 and 2 in that
          order first before pressing 3.  Pretty simple 1-2-3 for copy&paste)

      4 - Flip the copied rectangular region x-wise.
      5 - Flip the copied rectangular region y-wise.
      6 - Swap the x and y coordinates of the copied rectangular region.

      ,.<> - Change the shade of the selected region.
      \ - Move the cursor to the center or the tile.
      | - Get the coordinates of the cursor.

╔═╦═╦═╦═╦═╦═╦═╦═╦═╦═╦═╦═╦═╦═╦═╦═╦═╦═╦═╦═╦═╦═╦═╦═╦═╦═╦═╦═╦═╦═╦═╦═╦═╦═╦═╦═╦═╦═╗
╠═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╣
╠═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╣
╠═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╣
╠═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╣
╠═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╣
╠═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╣
╠═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╣
╠═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╣
╚═╩═╩═╩═╩═╩═╩═╩═╩═╩═╩═╩═╩═╩═╩═╩═╩═╩═╩═╩═╩═╩═╩═╩═╩═╩═╩═╩═╩═╩═╩═╩═╩═╩═╩═╩═╩═╩═╝

┌───────────────────────────────────────────────────────────────────────────┐
│                        IMPORTANT ENGINE FUNCTIONS:                        │
└───────────────────────────────────────────────────────────────────────────┘

initengine(char vidoption, long xdim, long ydim)
   Sets up interrupt vectors for keyboard, and initializes many variables
   for the BUILD engine.  You should call this once before any other
   functions of the BUILD engine are used.

   vidoption can be anywhere from 0-6
   xdim,ydim can be any mode x resolution if vidoption = 0
   xdim,ydim can be any vesa resolution if vidoption = 1
   xdim,ydim must be 320*200 for any other mode.
      (see graphics mode selection in my setup program)
uninitengine();
          Restores interrupt vectors for keyboard and timer, and frees
          buffers.  You should call this once at the end of the program
          before quitting to dos.
loadboard(char *filename, long *posx, long *posy, long *posz, short *ang, short *cursectnum)
          Loads the given board file into memory for the BUILD engine.
          Returns -1 if file not found.  If no extension is given, .MAP will
          be appended to the filename.
saveboard(char *filename, long *posx, long *posy, long *posz, short *ang, short *cursectnum)
          Saves the given board from memory inro the specified filename.
          Returns -1 if unable to save.  If no extension is given, .MAP will
          be appended to the filename.
loadpics(char *filename);
          Loads the given artwork file into memory for the BUILD engine.
          Returns -1 if file not found.  If no extension is given, .ART will
          be appended to the filename.
setgamemode();
          This function sets the video mode to 320*200*256color graphics.
          Since BUILD supports several different modes including mode x,
          mode 13h, and other special modes, I don't expect you to write
          any graphics output functions.  (Soon I have all the necessary
          functions)  If for some reason, you use your own graphics mode,
          you must call this function again before using the BUILD drawing
          functions.

drawrooms(long posx, long posy, long posz, short ang, long horiz, short cursectnum)
          This function draws the 3D screen to the current drawing page,
          which is not yet shown.  This way, you can overwrite some things
          over the 3D screen such as a gun.  Be sure to call the drawmasks()
          function soon after you call the drawrooms() function.  To view
          the screen, use the nextpage() function.  The nextpage() function
          should always be called sometime after each draw3dscreen()
          function.
drawmasks();
          This function draws all the sprites and masked walls to the current
          drawing page which is not yet shown.  The reason I have the drawing
          split up into these 2 routines is so you can animate just the
          sprites that are about to be drawn instead of having to animate
          all the sprites on the whole board.  Drawrooms() prepares these
          variables:  spritex[], spritey[], spritepicnum[], thesprite[],
          and spritesortcnt.  Spritesortcnt is the number of sprites about
          to be drawn to the page.  To change the sprite's picnum, simply
          modify the spritepicnum array  If you want to change other parts
          of the sprite structure, then you can use the thesprite array to
          get an index to the actual sprite number.

engineinput();
          This function allows the engine to adjust your position depending
          on the status of the arrow keys, and other control keys.  It
          handles timing and clipping.
nextpage();
          After a screen is prepared, use this function to view the screen.

draw2dscreen(long posxe, long posye, short ange, long zoome,
             short gride)
          Draws the 2d screen - this function is a direct replacement
          for the drawrooms() and drawmasks() functions.  Be sure
          to call either qsetmode640350() or qsetmode640480()
          first.  When switching back to 3d mode, be sure to call
          qsetmode320200().
            IMPORTANT NOTES:
            1.  The overwritesprite function should only be called in
                3D mode.  If you do this in 2D mode, junk will be
                written to the 2D screen and a crash is possible.
            2.  When you switch back to 3D mode, you should call the
                permanentwritesprite functions to draw the status bar,
                or whatever else you have to draw.
            3.  You must call the nextpage() function in both 2D and
                3D modes.
qsetmode320200();
          Set to the game mode and load palette (320*200*256)
qsetmode640350();
          Set to the 2D map mode #1 (640*350*16)
qsetmode640480();
          Set to the 2D map mode #2 (640*480*16)

doanimations(long numtics);
      This function animates anything you use setanimation for (like doors).
   You should call it for every frame.  Pass the number of tics (lockspeed)
   as a parameter to it to tell how much everything should animate.

kenchaintimer(void (__interrupt __far *datimerchainaddress)(),
              short dachainpersecond)
      This function makes the engine's timerhandler chain to another timer
   handler at any specified interrupt rate.  This function forces IRQ0 to
   point to my engine's timerhandler.  Clockspeed and totalclock will
   be fixed at counting 120 per second regardless of the chaining interrupt
   rate.  If you call this function with a NULL pointer, then the engine's
   timerhandler will not chain anymore.

   Here's how you should structure your code if you use this function:

      main()
      {
         initengine();

         musicon();              //Turn music on after engine
         kenchaintimer(yourtimerhandleraddress,yourtimerrate);
                                 //When IRQ0 goes off, it will now go to
                                 //Ken's timer handler.  Then, Ken's timer
         (main loop)             //handler will make yourtimerhandler
                                 //interrupt yourtimerrate times per second

         kenchaintimer(0,0);     //Stop chaining BEFORE music handler dies!
         musicoff();

         uninitengine();
      }

┌───────────────────────────────────────────────────────────────────────────┐
│                           OTHER ENGINE FUNCTIONS:                         │
└───────────────────────────────────────────────────────────────────────────┘

overwritesprite (long thex, long they, short tilenum,
                   signed char shade, char orientation, char dapalnum)

      Use this function to draw any sprites that must be drawn to the screen
   for every single frame, such as a gun or a menu system.

   If Bit 0 of orientation = 0: (thex, they) is top-left corner
   If Bit 0 of orientation = 1: (thex, they) is middle
   If Bit 1 of orientation = 0: no relation to viewing window
   If Bit 1 of orientation = 1: scale and clip to viewing window
   If Bit 2 of orientation = 0: normal
   If Bit 2 of orientation = 1: 50/50 transluscent!
   If Bit 3 of orientation = 0: normal
   If Bit 3 of orientation = 1: x-flipped
   If Bit 4 of orientation = 0: normal
   If Bit 4 of orientation = 1: y-flipped

   * If it works at full screen, simply set bit 1 of orientation
                   to 1, and it should automatically scale properly!

          Use this function to write sprites over the 3d view.  For example,
          you can make a menu system with this function.  Be sure
          that you call this function for every single frame after the 3d
          view is drawn or else it will be flashed on for only 1 frame.
          If you want x and y to be the top left corner, set the orientation
          to 0.  If you want x and y to be the middle of the sprite, set the
          orientation to 1.  The reason I included the orienation = 1 option
          is so that if you want a sprite centered and the size of the tile
          changes, you don't need to recompile and guess where the new top
          left corner is.  Oh yeah, and I forget to mention that if shade is
          greater than 32, than overwritesprite does transluscence.  (Try it
          out!)  This function will clip the sprite to the startumost and
          startdmost arrays.  Dapalnum refers to a palette lookup list
          (normally 0).

rotatesprite (long sx, long sy, long z, short a, short picnum,
              signed char dashade, char dapalnum, char dastat,
              long cx1, long cy1, long cx2, long cy2)
   (sx, sy) is the center of the sprite to draw defined as
       screen coordinates shifted up by 16.
   (z) is the zoom.  Normal zoom is 65536.
       Ex: 131072 is zoomed in 2X and 32768 is zoomed out 2X.
   (a) is the angle (0 is straight up)
   (picnum) is the tile number
   (dashade) is 0 normally but can be any standard shade up to 31 or 63.
   (dapalnum) can be from 0-255.
   if ((dastat&1) == 0) - no transluscence
   if ((dastat&1) != 0) - transluscence
   if ((dastat&2) == 0) - don't scale to setview's viewing window
   if ((dastat&2) != 0) - scale to setview's viewing window (windowx1,etc.)
   if ((dastat&4) == 0) - nuttin' special
   if ((dastat&4) != 0) - y-flip image
   if ((dastat&8) == 0) - clip to startumost/startdmost
   if ((dastat&8) != 0) - don't clip to startumost/startdmost
   if ((dastat&16) == 0) - use Editart center as point passed
   if ((dastat&16) != 0) - force point passed to be top-left corner
   if ((dastat&32) == 0) - nuttin' special
   if ((dastat&32) != 0) - use reverse transluscence
   if ((dastat&64) == 0) - masked drawing (check 255's) (slower)
   if ((dastat&64) != 0) - draw everything (don't check 255's) (faster)

   Note:  As a special case, if both ((dastat&2) != 0) and ((dastat&8) != 0)
      then rotatesprite will scale to the full screen (0,0,xdim-1,ydim-1)
      rather than setview's viewing window. (windowx1,windowy1,etc.)  This
      case is useful for status bars, etc.

      Ex: rotatesprite(160L<<16,100L<<16,65536,totalclock<<4,
                       DEMOSIGN,2,50L,50L,270L,150L);
          This example will draw the DEMOSIGN tile in the center of the
          screen and rotate about once per second.  The sprite will only
          get drawn inside the rectangle from (50,50) to (270,150)

permanentwritesprite (long thex, long they, short tilenum, signed char shade,
                      long cx1, long cy1, long cx2, long cy2, char dapalnum)
         - Added permanentwritesprite function for status bars or other
              sections of the screen that will not be overwritten by the
              engine.  The format of this function is like overwritesprite
              except that the x and y are always top left corner, no
              orientation variable, and no translucence.
                 The 4 last parameters (cx1, cy1) - (cx2, cy2) define a
              rectangular clipping window of where permanentwritesprite
              can draw to.   Dapalnum refers to a palette lookup list
              (normally 0).

printext(long x, long y, char buffer[42], short tilenum, char invisiblecol);
          Use this function to print text anywhere on the screen from a font
          that you can create in EDITART.  Please see my example font in
          TILES.ART to see how I lay out the user-defined font.  X ranges
          from 0-319. Y ranges from 0-199.  The buffer is the string to
          print.  Tilenum specifies which font to use.  Invisiblecol tells
          printext what color to draw the transparent pixels.  If
          invisiblecol is 255 then the transpararent pixels are still
          transparent.
printnum(long x, long y, long num, short tilenum, char invisiblecol);
          Printnum is a function call that will print a long integer (num)
          starting at top left corner x, y.  Please look at the documentation
          for printext, since internally, printnum simply prepares a buffer
          and calls the printext function.

setvmode(long videomode);
          If you look at the top of GAME.C, you will see something like this:
          #pragma aux setvmode =\...  This is how you do in-line assembler in
          WATCOM C.  All this function is doing is setting the video mode.
showengineinfo();
          Use this function after setting to text mode to view some statics
          about the engine, such as frame rate.
resettiming();
          Resets timing, such as setting totalclock = 0.  Also resets other
          timers.  This is for use with the showengineinfo function above.

ksqrt(long num);  returns (long)square root
          A square root function optimized for integers.  Use this function
          only if you want to.
krand()
      This simply returns a random number.  You can easily set the random
   seed by externing the randomseed variable as a long.  This is useful
   for keeping the random seed the same on multiple computers when playing
   multi-player mode.

getangle(long xvect,long yvect);    returns (short)angle;
          Use this function call to determine the angle between two points.
          For example, if you want a monster to shoot a bullet towards you,
          you would get the bullet's angle this way:
   sprite[bullet].ang = getangle(posx-sprite[monst].x,posy-sprite[monst].y);

lastwall(short point);
      Use this function as a reverse function of wall[].point2.  In order
   to save memory, my walls are only on a single linked list.
   
rotatepoint(long xpivot, long ypivot, long x, long y,
            short daang, long *x2, long *y2);
      This function is a very convenient and fast math helper function.
   Rotate points easily with this function without having to juggle your
   cosines and sines.  Simply pass it: 
       
       Input:   1. Pivot point     (xpivot,ypivot)
                2. Original point  (x,y)
                3. Angle to rotate (0 = nothing, 512 = 90° CW, etc.)
       Output:  4. Rotated point   (*x2,*y2)

clipmove(long *x, long *y, long *z, short *sectnum, long xvect, long yvect,
         long walldist, long ceildist, long flordist, char cliptype)
      Moves any object (x, y, z) in any direction at any velocity and will
   make sure the object will stay a certain distance from walls (walldist)
       Pass the pointers of the starting position (x, y, z).  Then
   pass the starting position's sector number as a pointer also.
   Also these values will be modified accordingly.  Pass the
   direction and velocity by using a vector (xvect, yvect).
   If you don't fully understand these equations, please call me.
         xvect = velocity * cos(angle)
         yvect = velocity * sin(angle)
      Walldist tells how close the object can get to a wall.  I use
    128L as my default.  If you increase walldist all of a sudden
    for a certain object, the object might leak through a wall, so
    don't do that!
       If cliptype is 0, then the clipping is normal (Use 0 to clip you
    and monsters).  If the cliptype is 1, then the object is clipped to
    the same things that hitscan is clipped to (use 1 for all bullets).

     Clipmove can either return 0 (touched nothing)
                                32768+wallnum (wall first touched)
                                49152+spritenum (sprite first touched)

getzrange(long x, long y, long z, short sectnum,
                  long *ceilz, long *ceilhit,
                  long *florz, long *florhit,
                  long walldist, char cliptype)

      Use this in conjunction with clipmove.  This function will keep the
   player from falling off cliffs when you're too close to the edge.  This
   function finds the highest and lowest z coordinates that your clipping
   BOX can get to.  It must search for all sectors (and sprites) that go
   into your clipping box.  This method is better than using
   sector[cursectnum].ceilingz and sector[cursectnum].floorz because this
   searches the whole clipping box for objects, not just 1 point.
      Pass x, y, z, sector normally.  Walldist can be 128.  Cliptype can be
   0, 1, or 2. (just like movesprite and clipmove)  This function returns
   the z extents in ceilz and florz. It will return the object hit in ceilhit
   and florhit.
     Ceilhit and florhit will also be either:
                                 16384+sector (sector first touched) or
                                 49152+spritenum (sprite first touched)

updatesector(long x, long y, &sectnum);
   This function updates the sector number according to the x and y values
   passed to it.  Be careful when you use this function with sprites because
   remember that the sprite's sector number should not be modified directly.
   If you want to update a sprite's sector, I recomment using the setsprite
   function described below.

inside(long x, long y, short sectnum);
   Tests to see whether the overhead point (x, y) is inside sector (sectnum)
   Returns either 0 or 1, where 1 means it is inside, and 0 means it is not.

copytilepiece(long tilenume1, long sourcex1, long sourcey1,
                              long xsiz, long ysiz,
              long tilenume2, long destx1, long desty1)

   This function simply copies any section of a source tile
      to any part of a destination tile.  It will automatically
      skip transparent pixels.  It will wrap-around in the
      source but not the destination.  If for some reason
      the destination tile gets removed from the cache, the
      destination tile will be reset to original form.  This
      is why I had to add this second function:

allocatepermanenttile(short tilenume, long xsiz, long ysiz)
   This function allocates a place on the cache as permanent.
      Right now, I reset the cache every time you call this
      function so I would recommend calling this function
      right after loadpics.

makepalookup(long palnum, char *remapbuf,
             signed char r, signed char g, signed char b,
             char dastat)
   This function allows different shirt colors for sprites.  First prepare
   remapbuf, which is a 256 byte buffer of chars which the colors to remap.
   Palnum can be anywhere from 1-15.  Since 0 is where the normal palette is
   stored, it is a bad idea to call this function with palnum=0.
   In BUILD.H notice I added a new variable, spritepal[MAXSPRITES].
   Usually the value of this is 0 for the default palette.  But if you
   change it to the palnum in the code between drawrooms() and drawmasks
   then the sprite will be drawn with that remapped palette.  The last 3
   parameters are the color that the palette fades to as you get further
   away.  This color is normally black (0,0,0).  White would be (63,63,63).
   if ((dastat&1) == 0) then makepalookup will allocate & deallocate
   the memory block for use but will not waste the time creating a palookup
   table (assuming you will create one yourself)

copytilepiece(long walnume1, long x1, long y1, long xsiz, long ysiz,
 long walnume2, long x2, long y2, char shadeoffs);
   Copies section of tile 1 (walnume1) with top-left corner (x1,y1) and
   rectangular size (xsiz, ysiz) to top-left corner (x2, y2) of tile 2
   (walnume).  You can animate tiles with this function.  For example, with
   this function, you can make a slot machine like in Ken's Labyrinth or an
   electronic sign with text sliding from right to left.

loadtile(short tilenume);
      This function will load the tile, tilenum, into the artwork cache.  A
   tile is not in the cache if (waloff[tilenum] == -1).  If 
   (waloff[tilenum] >= 0) then it is in the cache, and you don't need to call
   this function.

precache();
      This function will go through the tilenums of all sectors, walls, and
   sprites and call loadtile() on them.  This function will not cache in some 
   tiles of animations since their tilenums may not all be in the structures.

hitscan(long xstart, long ystart, long zstart, short startsectnum,
        long vectorx, long vectory, long vectorz,
        short *hitsect, short *hitwall, short *hitsprite,
        long *hitx, long *hity, long *hitz);

   Pass the starting 3D position:
        (xstart, ystart, zstart, startsectnum)
   Then pass the 3D angle to shoot (defined as a 3D vector):
        (vectorx, vectory, vectorz)
   Then set up the return values for the object hit:
        (hitsect, hitwall, hitsprite)
   and the exact 3D point where the ray hits:
        (hitx, hity, hitz)

   How to determine what was hit:
      * Hitsect is always equal to the sector that was hit (always >= 0).

      * If the ray hits a sprite then:
           hitsect = thesectornumber
           hitsprite = thespritenumber
           hitwall = -1

       * If the ray hits a wall then:
           hitsect = thesectornumber
           hitsprite = -1
           hitwall = thewallnumber

       * If the ray hits the ceiling of a sector then:
           hitsect = thesectornumber
           hitsprite = -1
           hitwall = -1
           vectorz < 0
           (If vectorz < 0 then you're shooting upward which means
              that you couldn't have hit a floor)

       * If the ray hits the floor of a sector then:
           hitsect = thesectornumber
           hitsprite = -1
           hitwall = -1
           vectorz > 0
           (If vectorz > 0 then you're shooting downard which means
              that you couldn't have hit a ceiling)

neartag(long x, long y, long z, short sectnum, short ang,  //Starting position & angle
        short *neartagsector,   //Returns near sector if sector[].tag != 0
        short *neartagwall,     //Returns near wall if wall[].tag != 0
        short *neartagsprite,   //Returns near sprite if sprite[].tag != 0
        long *neartaghitdist,   //Returns actual distance to object (scale: 1024=largest grid size)
        long neartagrange,      //Choose maximum distance to scan (scale: 1024=largest grid size)
        char tagsearch)         //1-lotag only, 2-hitag only, 3-lotag&hitag
      Neartag works sort of like hitscan, but is optimized to
   scan only close objects and scan only objects with
   tags != 0.  Neartag is perfect for the first line of your space bar code.
   It will tell you what door you want to open or what switch you want to
   flip.

cansee(long x1, long y1, long z1, short sectnum1,
       long x2, long y2, long z2, short sectnum2);  returns 0 or 1
   This function determines whether or not two 3D points can "see" each
   other or not.  All you do is pass it the coordinates of a 3D line defined
   by two 3D points (with their respective sectors)  The function will return
   a 1 if the points can see each other or a 0 if there is something blocking
   the two points from seeing each other.  This is how I determine whether a
   monster can see you or not. Try playing DOOM1.DAT to fully enjoy this
   great function!

setanimation(long *animptr, long thegoal, long thevel);
   This is a function for your convenience that will animate a long
   variable, such as sector[].floorz for platforms, or sector[].ceilingz
   for doors.  All you do is pass it the long pointer into memory, specifying
   which long variable is to be animated; you also pass the goal (long value
   to animate towards), and the velocity at which the variable is animated.
   Velocity = 128 is a normal speed door.  You may also modify the animation
   arrays directly if you wish:

   The animation arrays are as follows:
      long *animateptr[MAXANIMATES], animategoal[MAXANIMATES];
      long animatevel[MAXANIMATES], animatecnt;

getanimationgoal(long animptr);
   Check to see if a certain variable in memory is already being animated
   by the engine.  If so, an index into the animation arrays is returned,
   else -1 is returned.  This is function is useful when you are press
   space bar near a door, and it is already animating, you simply want
   to reverse its direction.

dragpoint(short wallnum, long newx, long newy);
   This function will drag a point in the exact same way a point is dragged
   in 2D EDIT MODE using the left mouse button.  Simply pass it which wall
   to drag and then pass the new x and y coordinates for that point.
   Please use this function because if you don't and try to drag points
   yourself, I can guarantee that it won't work as well as mine and you
   will get confused.  Note:  Every wall of course has 2 points.  When you
   pass a wall number to this function, you are actually passing 1 point,
   the left side of the wall (given that you are in the sector of that wall)
   Got it?

nextsectorneighborz(short sectnum, long thez, short topbottom, short direction);
   This function searches z-coordinates of neighboring sectors to find the
   closest (next) ceiling starting at the given z-coordinate (thez).
   For example, if you want to find the goal z-coordinate when opening a
   door, you might want the door to stop at the next closest neighboring
   ceiling z-coordinate.  You can get the z-coordinate this way:

      newz = sector[nextsectorneighborz(sectnum,startz,-1,-1)].ceilingz

   topbottom (3rd parameter)  -1 = search ceilings
                               1 = search floors
   direction (4th parameter)  -1 = search upwards
                               1 = search downwards

screencapture(char *filename)
   Capture the screen and save it as a .BMP file.  I don't know why my
   .BMP format isn't compatible with other programs.

┌───────────────────────────────────────────────────────────────────────────┐
│                             SPRITE FUNCTIONS:                             │
└───────────────────────────────────────────────────────────────────────────┘

insertsprite(short sectnum, short statnum);   //returns (short)spritenum;
   Whenever you insert a sprite, you must pass it the sector
   number, and a status number (statnum).  The status number can be any
   number from 0 to MAXSTATUS-1.  Insertsprite works like a memory
   allocation function and returns the sprite number.

deletesprite(short spritenum);
   Deletes the sprite.

changespritesect(short spritenum, short newsectnum);
   Changes the sector of sprite (spritenum) to the
   newsector (newsectnum).  This function may become
   internal to the engine in the movesprite function.  But
   this function is necessary since all the sectors have
   their own doubly-linked lists of sprites.

changespritestat(short spritenum, short newstatnum);
   Changes the status of sprite (spritenum) to status
   (newstatus).  Newstatus can be any number from 0 to MAXSTATUS-1.
   You can use this function to put a monster on a list of active sprites
   when it first sees you.

setsprite(short spritenum, long newx, long newy, long newz);
      This function simply sets the sprite's position to a specified
   coordinate (newx, newy, newz) without any checking to see
   whether the position is valid or not.  You could directly
   modify the sprite[].x, sprite[].y, and sprite[].z values, but
   if you use my function, the sprite is guaranteed to be in the
   right sector.

movesprite(short spritenum, long xchange, long ychange, long zchange,
           long ceildist, long flordist, char cliptype, long numtics)
      This function moves the sprite given by spritenum by the 3
   increments, xchange, ychange, and zchange.  If cliptype is 0, then
   the clipping is normal (Use 0 to clip you and monsters).  If the
   cliptype is 1, then the object is clipped to the same things that
   hitscan is clipped to (use 1 for all bullets).
      Movesprite can either return 0 (touched nothing)
                               16384+sectnum (ceiling/floor first touched)
                               32768+wallnum (wall first touched)
                               49152+spritenum (sprite first touched)

getspritescreencoord(short spritesortnum, long *scrx, long *scry)
      This function returns the actual screen coordinates of a sprite.  It
   is useful for locking on to a target.  Use this function between
   drawrooms and drawmasks.  Note that spritesortnum is the index into the
   spritesortcnt arrays, NOT the normal sprite arrays.  Scrx and scry are
   actual screen coordinates ranging from 0-319 and 0-199 respectively.

┌───────────────────────────────────────────────────────────────────────────┐
│                       MULTIPLAYER FUNCTIONS (multi.obj)                   │
└───────────────────────────────────────────────────────────────────────────┘

   initmultiplayers(char option[4], char option[5], char priority);
         Call this right after initengine.  Pass option[4] and option[5]
      exactly the way I have it written here.  (option[4] is the COM1-COM4,
      network option and option[5] is the com speed selection option)
      Priority can be used to decide who becomes master.  Lower is more
      towards the master.

   uninitmultiplayers();
         Call this right before uninitengine.

   sendlogon();
         Use this function after everything's initialized, but before you
      go into the main game loop.  Right after you call sendlogon(), you
      should run a loop that will wait until a specified number of players.
      Here's some example code:

      sendlogon();
      while (numplayers < waitplayers)
      {
         getpackets();
      }
      screenpeek = myconnectindex;

         Getpackets reserves the packet header range from 200-255.  If you
      keep calling getpackets after sendlogon, the numplayers variable will
      automatically be incremented when other people log on.

   sendlogoff();
         Call this before leaving, before uninitializing the multiplayer
      code.

   sendpacket (short otherconnectindex, char *bufptr, short bufleng)
         For COM(modem) communications, the otherconnectindex doesn't matter.
         For network communcations, you can specify which computer to send
      to by setting otherconnectindex to the proper index number.  You
      can also do a broadcast by setting otherconnectindex to -1.
         Also pass the buffer and length parameters.

   short getpacket (short *otherconnectindex, char *bufptr) returns bufleng
         When using getpacket, first check the value it returns.
      If the value is 0, then the buffer length is 0 which means there
      are no packets available.  If the buffer length is greater than
      0, then use that value as the length of the buffer.  Getpacket also
      tells you what computer the message was received from -
      (otherconnectindex).

┌───────────────────────────────────────────────────────────────────────────┐
│                       DIGITIZED SOUND FUNCTIONS:                          │
└───────────────────────────────────────────────────────────────────────────┘
Note: If you want to write your own digitized sound driver, simply delete
   these functions from GAME.C.

initsb();
          Initializes the digitized sound routines.  You need to call this
          only once at the beginning of the program.  Currently, the
          sample rate is 11,025 Hz.

wsay(char *filename, long freq, char volume);
          Play the sound file at the given frequency and volume.  If you
          set freq = 4096, the sound will play at normal frequency (given
          the sound was also recorded at 11025 Hz)  To play the sound an
          octave higher, for example, set freq = 8192.  Volume ranges from
          0 (silent) to 255 (full volume).  Ex: wsay("blowup.wav",4096L,255);

uninitsb();
          Turns the speaker off, so sounds don't continue playing while
          your back in DOS.

┌───────────────────────────────────────────────────────────────────────────┐
│                            MUSIC FUNCTIONS:                               │
└───────────────────────────────────────────────────────────────────────────┘
Note: If you want to write your own music driver, simply delete these
   functions from GAME.C.  The BUILD engine uses interrupt vector 0x8 (IRQ0)
   and that may complicate things.  If you like my music, perhaps I can send
   you my MIDI sequencer program (It requires MPU-401, and TSENG-ET4000 SVGA,
   but could be standardized if there's enough demand).

loadmusic(char *filename);
          Loads the given song into memory.  Be sure INSTS.DAT is in the
          current directory.  If no extension is given, then .KSM will be
          appended to the filename.  You should use this function only when
          the music is off.
musicon();
          Enable the playing of music.  Use this only after loadmusic has
          been called.
musicoff();
          Disable the playing of music.  Be sure to call this before quitting
          to DOS.

╔═╦═╦═╦═╦═╦═╦═╦═╦═╦═╦═╦═╦═╦═╦═╦═╦═╦═╦═╦═╦═╦═╦═╦═╦═╦═╦═╦═╦═╦═╦═╦═╦═╦═╦═╦═╦═╦═╗
╠═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╣
╠═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╣
╠═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╣
╠═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╣
╠═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╣
╠═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╣
╠═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╣
╠═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╣
╚═╩═╩═╩═╩═╩═╩═╩═╩═╩═╩═╩═╩═╩═╩═╩═╩═╩═╩═╩═╩═╩═╩═╩═╩═╩═╩═╩═╩═╩═╩═╩═╩═╩═╩═╩═╩═╩═╝

┌───────────────────────────────────────────────────────────────────────────┐
│                       BUILD Revision History:                             │
└───────────────────────────────────────────────────────────────────────────┘
2/6/94   - Added this revision thing.
         - Setup program.
         - A faster new mode for standard VGAs called chain mode.
──────────────────────────────────────────────────────────────────────────────
2/8/94   - Fixed chain mode with groudraws.
         - Added sector joining in 2D EDIT MODE - press J on first sector.
              Then press J again on sector to join with.  Attributes will be
              taken from first sector.  Possible bugs.
         - Improved controls for slower computers.
         - Made timer interrupt rate 120/second instead of 240/second.
──────────────────────────────────────────────────────────────────────────────
2/9/94   - Started to work on status bar in 2D EDIT MODE.
         - Added special optimization for the Western Digitial (Paradise)
              chipset just like with TSENG ET4000.
──────────────────────────────────────────────────────────────────────────────
2/10/94  - Added a door attribute to sectors.  To make a sector a door,
              press "D" on the ceiling of the sector in 3D EDIT MODE.  For
              the door to work, the ceiling of the door sector must be a
              tiny bit lower than lowest neighboring ceiling.  To open/close
              the door, you may press the space bar.  Note: Space Bar is also
              used for copy and paste in 3D EDIT MODE.  You may have to go
              into game mode to test out the doors for now.
         - Added a wall orientation attribute to walls.  This attribute is
              used especially in conjunction with doors.  So far, you have
              been working with walls that are start from the top.  For
              example, if you hold down 2/8 on the keypad in 3D EDIT MODE,
              the wall always starts from the top.  But for doors, you
              sometimes need walls to start from the bottom.  To do this,
              press "O" (for orientation) on the wall, and it will now
              start from the bottom.
──────────────────────────────────────────────────────────────────────────────
2/12/94  - Fixed some bugs for sprites.  Sprites don't disappear anymore.
              Now you will pick up all sprites when running over them.
              Monsters will not stop shooting anymore.
         - Made the sound blaster sounds have a 1-sound cache.  That is, if
              the same sound is to be played several times in a row, it will
              only be loaded once.
         - Added *.GIF format support to EDITART for 320*200 size images.
         - Changed the PASTE key in 3D EDIT MODE from Space bar to Left
              Enter because the Space bar conflicted with opening doors.
         - Added music as an option in the setup program.  To run the music,
              you need INSTS.DAT and a song file with a .KSM extension.
         - Split the editor from the game.
              BUILD is now the map editor - you don't need BUILD.C anymore.
              GAME is now the actual game that you will be programming.
                 I am giving you GAME.C to work with.  Its code is simpler
                 than before because there are no more editing function
                 calls in it.
         - Sprites move in a straight line now until they hit walls.  When
              they hit walls, they pick a new angle to travel in.
         - Remember that tiny little guy with the "Al" on his shirt?
              Next time you're in DOOM1.DAT, try shooting him.  Then look
              at the code in GAME.C.
         - In EDITART, when you press, "u", you now select filenames with
              the arrow keys and enter rather than having to type in
              the exact filename.
         - Board maps now have extension .MAP and artwork files now have
              extension .ART to help distinguish between the different types
              of files.
──────────────────────────────────────────────────────────────────────────────
2/22/94  - Fixed small bug with EDITART not loading right when names.h
              existed with no names defined.
         - You can now edit new boards by typing:
              BUILD <file that doesn't exist> like "BUILD NEWBOARD"
         - Added ALT-S in 2D EDIT Mode to change a whole white loop inside a
              sector to a red loop.
         - When viewing the tiles (pressing "v") you can now use PGUP/PGDN.
         - Added a message bar in 2D EDIT Mode.
         - Added TAB and L.ALT-TAB keys to view all the attributes of a
              sector, wall, or sprite (see above documentation).
         - Debugged and bullet-proofed ALT-S.
         - Fixed a bug that doesn't allow you to drag points with number
              greater than 1023. (There can actually be 4096 walls and 1024
              sectors, and 4096 sprites currently, an easy thing to increase)
         - Changed overwritesprite function paramaters.  If you are using it
              in your c file, you must change it!  Now it looks like this:

                 overwritesprite (long x, long y, short tilenum, char shade,
                    char orientation);

              If orientation = 0: x and y are the top left corner.
              If orientation = 1: x and y are the middle (like before).
         - Added permanentwritesprite function for status bars or other
              sections of the screen that will not be overwritten by the
              engine.  When using this function, you may want to modify the
              STARTUMOST / STARTDMOST arrays. The format of this function is
              like overwritesprite except that the x and y are always top
              left corner, no orientation variable, and no translucence.

            permanentwritesprite (long x, long y, short tilenum, char shade);

         - Added an attribute to the printext and printnum functions.  Please
              change these function if you use them in your program.  The
              current formats are:

                 printext(long x, long y, char buffer[42], short tilenum,
                    char invisiblecol)
                 printnum(long x, long y, long num, short tilenum,
                    char invisiblecol);

          Invisiblecol tells printext what color to draw the transparent
          pixels.  If invisiblecol is 255 then the transpararent pixels are
          still transparent.
──────────────────────────────────────────────────────────────────────────────
2/26/94  - Completely reprogrammed EDITART.
     IMPORTANT: PLEASE RUN CONVART.EXE ON ALL ARTWORK FILES! (If you have not
              already).  It will convert PICS.ART in the old format to
              TILES.ART in the new format, so you may have to do some
              renaming when converting artwork and also rename the loadpics
              line in your C file.  BUILD WILL NOT RUN UNLESS YOU DO THIS!
              With the new artwork file, the tiles can now be any size from
              1*1 to 1024*240 and they no longer have to be powers of 2.
              This feature allows parallaxing skies that are 1024 pixels wide
              to cover all 360°, or 320 pixel wide status bars.  It can also
              be used to display 320*200 title screens.
         - When pressing 'U' in EDITART, you can now change directories
              and the current loading directory will be remembered.
         - When pressing 'U' in EDITART, you can press the mouse button to
              readjust the size of the tile.
         - When pressing 'U' in EDITART, press ENTER for an automatic
              palette conversion or press SPACE BAR for no conversion.
         - Walls that are Blocked ('B') in BUILD are now shown in pink.
              If the wall does not show pink in your board, just press
              'B' twice to permanently fix it.
         - Made smooshiness values easier to work with.  Hold down 5 on the
              keypad with 2/4/6/8 to align the walls to multiples of the
              tile that is used.  Also, when you do Tab and L.Enter for
              Copy&Paste, the y-repeat values stay the same, and the
              x-repeat values are adjusted so the pixels of the bitmaps
              have a square-aspect ratio.
         - Added masked walls.  But since they don't quite work perfectly
              yet, and they are extremely hard to edit now, so for now, just
              enjoy the technologiy in DOOM1.MAP. Don't worry - you'll get
              your hands on this soon!
         - Fixed the "earthquake" effect on really high walls.  The walls
              will not move up and down, but the pixel fuzzyness bug is
              still in there.
         - Put door variables and tile information into BUILD.H for you to
              play around with.
         - Made walls, ceilings, and floors also work with the animation
              attribute in EDITART.
──────────────────────────────────────────────────────────────────────────────
3/2/94   - Made power of 2 size tiles work with ceilings & floors.
         - When using different size ceilings & floors, made an option in
              3D EDIT MODE.  If you want a tile to be smooshed into the normal
              64*64 area, press 'E' to unExpand the tile.
         - Groudraws now look more cubical than before. (But be careful of
              crashing with them for now)
         - IMPORTANT: Changed the Board format.  If you tried running BUILD
              before reading this, and it didn't work, shame on you!  You
              should always look at the history section of BUILD.TXT first!
              Since from now on, I am writing Board/Art converters, I am
              giving you CONVMAP.EXE to convert maps.  You can type either:
                 C:\BUILD>convmap house.map
              to convert an individual map or:
                 C:\BUILD>for %i in (*.map) do convmap %i
              to convert all the maps in a directory.

         - IMPORTANT: Sprites are now on 2 different sets of doubly-linked
              lists. The big advantage of this method is that when sprites
              are deleted, there can be holes in the sprite list with
              no slow down.  Before I used to have linked lists for each
              sector pointing to all the sprites in it.  Well, I have updated
              that and added more linked lists that tell whether sprites
              are active or inactive (not a fun thing to progam)  But not
              to worry, you don't have to deal with changing the linked
              lists, thanks to my very easy function calls.  YOU WILL
              HAVE TO CHANGE YOUR CODE TO SUPPORT THE NEW LINKED LISTS.
              You should know how to search through each list.  See BUILD.H
              for a detailed description of how the linked lists work, and
              see the top of this file for description of the new sprite
              function calls.

                 insertsprite(short sectnum, short statnum);
                 deletesprite(short spritenum);
                 changespritesect(short spritenum, short newsectnum);
                 changespritestat(short spritenum, short newstatnum);

              In your code, you will have to change the following:
                 firstsprite[spritenum]    -->  headspritesect[spritenum]
                 sprite[spritenum].point2  -->  nextspritesect[spritenum]

              Also, you should change the part of your main loop that scans
                 through all the active sprites to look more like this:


      i = headspritestat[1];          //head of active sprite list
      while (i != -1)
      {
         nexti = nextspritestat[i];   //back up next sprite in case current
                                      //one is deleted.

         //your code goes here (use i as the sprite number)

spriteisdeletedskip:
         i = nexti;                   //go to next sprite
      }



         - Added tagging (naming) to any sector, sprite, or wall.  That means
              you can change a tag in the BUILD editor by pressing T with the
              mouse cursor on the highlighted sector.  And in GAME.C, you
              can access the tag this way:
                 sector[sectnum].tag;
                 sprite[spritenum].tag;
                 wall[wallnum].tag;
              Note: All 3 types of tags are long integers, but in BUILD.C
                 you can only make them short integers.  I am planning to
                 let you use the upper 16 bits of the tag byte as use for
                 you only.  (like permanently unreserved bits)
         - Added serial link.  But it doesn't work very well yet.  You
              may have to try going into GAME several times before it works.
──────────────────────────────────────────────────────────────────────────────
3/15/94  - Fixed maskable walls - now sort out with sprite better.  Easier
           to edit.  Simply press 'M' at a place where a maskable wall
           should be (like where you normally press 'B' for blocking)
         - Can flip walls, masked walls, and sprites x-wise by pressing
           'F' in 3D EDIT MODE.  Also, it is possible to save space in
           sprite rotations, by flipping it x-wise by programming one of
           the bits in sprite[].cstat.
         - A few other minor things, but I forgot what they were.
──────────────────────────────────────────────────────────────────────────────
3/19/94  - Fixed one of the many crashing bugs.
              For Duke Nukem III team:
                 CITY.MAP will not crash anymore with new version.  The
                 reason it crashed was because in your TILES.ART, you had
                 either ANMFD:0, ANMBK:0, or OSCI:0.  That caused a MOD 0
                 in my animation routine. I bullet-proofed that.  How about
                 fixing tiles #34, #112, or #114 in your TILES.ART.
         - Wrote divide by zero handler.  Now if there is a divide by zero,
                BUILD goes back into text mode and fills the screen with
                /0/0/0/0/..., then gives a DOS prompt. (In other words, you
                don't have to type "MODE CO80" anymore for /0 crashes)
         - Optimized routines in assembly for 486's using my great new
                Intel 32-bit optimizing techniques book that dad got for me.
         - Fixed ALT-S in BUILD so ANY white loop can be converted into a
                red loop except for the 1 and only 1 outermost white loop.
         - FINALLY!  Splitsector bug is fixed!  You can now split a very
                complicated sector with tons of sectors inside it or with
                plenty of sprites.  Also sprites will not get deleted
                spontaneously with split sector any more.
         - Ctrl-Enter in 3D EDIT MODE is like the normal Enter paste key,
                but Ctrl-Enter pastes the attributes to an entire loop of
                walls (if a wall is highlighted).
──────────────────────────────────────────────────────────────────────────────
3/22/94  - Added doors that open left and right.  See sector[].tag #9 in the top
                of this file for details - also look at my board, NUKELAND.MAP,
                in BUILD to see how to make this type of door.
         - Increased the number of status lists.  You may recall my active /
                inactive sprite lists called headspritestat, prevspritestat,
                and nextspritestat.  Don't worry - you will NOT have to
                change any of your code.  The only thing I changed is now
                you can have 1024 different lists of sprites rather than just
                2 lists if you want to use them.  For example, you might want
                to run through all the brown monsters on the board.  Instead
                of putting all different types of monsters on the active
                list, it is faster to have a separate list just for brown
                monsters.  Use your imagination!
──────────────────────────────────────────────────────────────────────────────
3/31/94  - Fixed some palette problems with EDITART.  Now when you press 'P'
                in 'U' mode, the palette of all the existing tiles are
                converted to the new palette.  Also the text colors should
                be similar colors on different palettes now.
         - Changed bit 0 of sprite[].cstat to the hitscan checking / ignoring
                bit.  You should set this bit to 1 for monsters, 0 for
                bullets, 0 for pick-upable objects like coins, and 1 for
                objects like columns.  Right now, (Since it's not in the
                BUILD editor), you must set these bits in your code right
                after you load the board.
         - I did not document the neartagsector, neartagwall, and
                neartagsprite very well, so if you don't know how to use
                them alreay, please read on.  These 3 variables are modified
                by the engine every time you call draw3dscreen().  These
                variables are usually -1.  These variables will be >= 0 if
                all of these cases are true:
                   1.  You are looking at a wall, sprite, or sector.
                   2.  You are close enough to the wall, sprite, or sector
                       to be able to use the action key with it.
                   3.  The tag of the wall, sprite, or sector is greater or
                       equal to 1.
                As you can see, neartagsector makes a perfect variable for
                detecting whether or not you are opening a door.  You can
                also use neartagwall for switches on walls.  Also, you can
                use neartagsprite for sprites that are switches.
         - PROGRAMMERS: PLEASE MAKE THESE EASY CODE MODIFICATIONS:
                I reversed the way variables are stored in BUILD.H.
                Now the variables will be local to the ENGINE and externed
                to GAME.  Before they were local to GAME and externed to the
                engine.  All you have to do is this:
                   1.  You can remove the #define MAIN if you want to.
                   2.  Delete the externs declarations that I used to have
                       in the beginning of GAME.C, because I put them in
                       BUILD.H.  I will try not to put externs in the GAME
                       in the future to save you the time of copying and
                       pasting code.  Here is a list of externs I moved:

                EXTERN short neartagsector, neartagwall, neartagsprite;
                EXTERN long *animateptr[MAXANIMATES], animategoal[MAXANIMATES];
                EXTERN long animatevel[MAXANIMATES], animatecnt;
                EXTERN short tilesizx[MAXTILES], tilesizy[MAXTILES];
                EXTERN long numtiles, picanm[MAXTILES], waloff[MAXTILES];
         - Added a global parallaxing sky type variable.  Now I have 3
                different types of parallaxing skies.  Here they are:

             0 - Totally flat parallaxing sky.
             1 - X-only stretching parallaxing sky (This is what DOOM uses).
             2 - X- and Y- stretching parallaxing sky. (This is what BUILD
                                                        uses by default)
             A good place to set the parallaxing sky is right after you
             call initengine().
         - Added local tile selection to the BUILD editor.  This means
               that when you press V on a sprite, you will see only the
               sprites that are on the level so far.  If you want to use
               a sprite that is not already used on the current board, press
               V again and you will be able to select from the huge list
               that you are used to from before.  The local tile list will
               be sorted according to how much each tile is used on the
               current board.  Note that the local tile list will be
               different depending where the mouse cursor was left when you
               pressed V (or H).
──────────────────────────────────────────────────────────────────────────────
4/4/94   - Changed the way ART files work.  YOU MUST READ THIS:
               There are some two easy things that you must do 2 things before
               you can use the new engine:

               1. Type CONVART1.EXE to convert your ARTWORK to the new
               format.  (If I forgot to send this file to you, please call
               and torture me.)   Now, instead of everything being stored
               in one huge file, TILES.ART, I am splitting the artwork into
               several files each holding 256 tiles in it.  This will allow
               you to make infinitely large artwork files in EDITART.

               2. In GAME.C, you must change the line,
                     loadpics("tiles.art");
                  to:
                     loadpics("tiles000.art");
                  All you have to give loadpics is the first filename of your
                  artwork, and the engine will automatically know how to
                  modify the 3 digits at the end and load all the artwork
                  files properly.
──────────────────────────────────────────────────────────────────────────────
4/5/94   - Fixed the "fuzzy" pixels on high walls.  Now you make walls as
                  high as skyscrapers, and when you look at it up close, the
                  pixels will still look like perfect rectangles.  Note: This
                  fix may require you to press 'O' in 3D EDIT MODE to put
                  your boards back to normal.
         - When you pressed ENTER on weird-sized tiles, sometimes in the old
                  version the pixels would not be square aspect ratio.  Now
                  when you press ENTER, they will be.
         - You can now set the starting position in 2D EDIT MODE with the
                  Scroll Lock key.  The starting position will look like a
                  brown arrow, and your current position will look like a
                  white arrow.  Now you don't have to keep returning to start
                  when you save your board.
         - Added some new things to the structures - guess what this means?
                  CONVMAP3!  THE NEW OBJ'S and BUILD will NOT work until you
                             run CONVMAP3. (Please call if I forgot to give
                                            you this file)
                  Notice that I added these things to the structures:

                  sectortype:
                     bit 2 of ceilingstat&floorstat:
                        1 = North/South panning, 0 = East/West panning
                     char ceilingpanning, floorpanning;
                  walltype:
                     bit 6 of cstat
                        1 = Vertical panning, 0 = Horizontal panning
                     char panning;
                     short overpicnum;
                  spritetype:
                     char *extra;

                  Now an explanation of each:
                     To the sector structure, I added the capability to pan
                  the floors and ceilings.  Usually ceilingpanning and
                  floorpanning are set to 0.  For example, a good way to make
                  an earthquake would be to randomly increment or decrement
                  to panning values.  To allow you to pan the ceilings or
                  floors at any of the standard 90 degree angles, I made
                  bit 2 of sectortype's ceilingstat/floorstat byte choose
                  whether the ceilingpanning/floorpanning byte should move
                  the ceiling/floor North/South or East/West.
                     To the wall structure, I added panning also.  Bit 6
                  of cstat and the panning values work in the same as the
                  ceilings and floors.
                     To the sprite structure, I added only 1 variable.
                  The "extra" pointer is intended for your use only, so you
                  can have the sprite's structure extended if you so desire.
──────────────────────────────────────────────────────────────────────────────
4/6/94   - In 2D EDIT MODE, you can now press ESC to get a message in the
                  message bar that says this:

                  (N)ew, (L)oad, (S)ave, (Q)uit

                  And it all works too.  (Press ESC to cancel)
                  Loading lets you select the file just like 'U' in EDITART.
         - The old version of BUILD had the coordinate system all screwed
                  up in 2D EDIT MODE.  I fixed this.  Included with
                  CONVMAP3.EXE is a conversion utility that will make your
                  boards have the same orientation as in the older BUILD
                  editors.  If you don't care if the whole map gets rotated
                  in the 2D editor, then press N.
──────────────────────────────────────────────────────────────────────────────
4/7/94   - Changed the way Orientation works ('O' in 3D EDIT MODE)

              Now, when you draw a normal door in BUILD, you do NOT have to
              press 'O' everywhere to make the walls of the door move right.
              Now, the default is that everything moves right.  Here's a
              detailed description of how the new orientation works:

              For white lines (no neighboring sector):

                 orientation = 0 (default) means picture is aligned with
                            sector[].ceilingz.
                 orientation = 1 means picture aligned with sector[].floorz.

              For red lines (has a neighboring sector):

                  orientation = 0 (default) means picture is aligned with
                           either the ceilingz or floorz of the next sector.
                  orientation = 1 means picture aligned with
                           sector[].ceilingz.

              Don't worry if you don't understand the detailed description.
              Just know that it's better this way.  I have included the
              proper conversions in CONVMAP3.EXE.
         - Added wall&ceiling x-panning keys to the 3D EDIT MODE.  Press
              the , or . keys to pan a wall or ceiling left or right.  You
              can hold down the 5 key on the keypad to align at every eighth
              panning value.  (Just like with 2,4,6,8).
         - Made TAB&ENTER in 3D EDIT MODE also copy the CSTAT byte of
              the wall's attributes.  This means that attributes such as
              the block attribute, 1-way wall attribute, orientation,
              and x-flipping attribute are also copied & pasted.
         - Added a new function, cansee.

              cansee(long x1, long y1, long z1, short sectnum1,
                     long x2, long y2, long z2, short sectnum2)

              All you do is pass it the coordinates of a 3D line
                  and the respective sectors of each point of the line.
                  The function will return a 1 if the points can see each
                  other or a 0 if there is something blocking the two points
                  from seeing each other.  This is how I determine whether
                  a monster can see you or not. Try playing DOOM1.DAT with
                  digitized sound enabled to fully enjoy this great new
                  function!
──────────────────────────────────────────────────────────────────────────────
4/12/94   - Fixed HITSCAN function.  Since it works a little differently
               and the parameters are different, let me describe how the
               new hitscan works:

          hitscan(long xstart, long ystart, long zstart, short startsectnum,
                  long vectorx, long vectory, long vectorz,
                  short *hitsect, short *hitwall, short *hitsprite,
                  long *hitx, long *hity, long *hitz);

               Pass the starting 3D position:
                   (xstart, ystart, zstart, startsectnum)
               Then pass the 3D angle to shoot (defined as a 3D vector):
                   (vectorx, vectory, vectorz)
               Then set up the return values for the object hit:
                   (hitsect, hitwall, hitsprite)
               and the exact 3D point where the ray hits:
                   (hitx, hity, hitz)

               How to determine what was hit:
               * Hitsect is always equal to the sector that was hit
                   (always >= 0).

               * If the ray hits a sprite then:
                   hitsect = thesectornumber
                   hitsprite = thespritenumber
                   hitwall = -1

               * If the ray hits a wall then:
                   hitsect = thesectornumber
                   hitsprite = -1
                   hitwall = thewallnumber

               * If the ray hits the ceiling of a sector then:
                   hitsect = thesectornumber
                   hitsprite = -1
                   hitwall = -1
                   vectorz < 0
               (If vectorz < 0 then you're shooting upward which means
                   that you couldn't have hit a floor)

               * If the ray hits the floor of a sector then:
                   hitsect = thesectornumber
                   hitsprite = -1
                   hitwall = -1
                   vectorz > 0
               (If vectorz > 0 then you're shooting downard which means
                   that you couldn't have hit a ceiling)

          - Added a position window to the bottom of the menu in 2D EDIT
              MODE.  It shows the posx, posy, and ang variables.
──────────────────────────────────────────────────────────────────────────────
4/14/94   - Overwritesprite now clips to the startumost / startdmost arrays.
            Now the only way to write to the status bar is with
            the permanentwritesprite function.

          - ATTENTION PROGRAMMERS! I Split draw3dscreen() into 2 separate
            function calls.

            old:  draw3dscreen();    //Draws walls, ceilings, floors, p-skies
                                     // groudraws, sprites, and masked walls.

            new:  drawrooms();       //Draws walls, ceilings, floors, p-skies
                                     // and groudraws.
                  drawmasks();       //Draws sprites and masked walls.

            The reason I split draw3dscreen was so you could manipulate only
            the sprites that the engine is going to draw to the screen
            before the sprites are actually drawn.

          - I think I may have fixed that darn sector line bug!  Before, the
            bug usually appeared when you were on a (red) sector line where
            the ceiling and floor of the sector were very far from each
            other.  This overflowed some really high positive values into
            the negative range and vice versa.
──────────────────────────────────────────────────────────────────────────────
4/18/94   - Wall clipping now works much better! (You can still VERY RARELY
                                              sneak through walls, however)
            ATTENTION PROGRAMMERS:
               MOVESPRITE and CLIPMOVE have been changed.  They now look
            like this:

               clipmove (long *x, long *y, long *z, short *sectnum,
                         long xvect, long yvect, long walldist,
                         char cliptype);

               movesprite(short spritenum,
                          long xchange, long ychange, long zchange,
                          long walldist, char cliptype);

            To use the new clipmove:
               Pass the pointers of the starting position (x, y, z).  Then
            pass the starting position's sector number as a pointer also.
            Also these values will be modified accordingly.  Pass the
            direction and velocity by using a vector (xvect, yvect).
            If you don't fully understand these equations, please call me.
               xvect = velocity * cos(angle)
               yvect = velocity * sin(angle)
            Walldist tells how close the object can get to a wall.  I use
            128L as my default.  If you increase walldist all of a sudden
            for a certain object, the object might leak through a wall, so
            don't do that!

            To use the new movesprite:
               Works like before, but you also pass walldist (How close the
            sprite can get to a wall)

          - New function for sprites:
             setsprite(short spritenum, long newx, long newy, long newz);

             This function simply sets the sprite's position to a specified
             coordinate (newx, newy, newz) without any checking to see
             whether the position is valid or not.  You could directly
             modify the sprite[].x, sprite[].y, and sprite[].z values, but
             if you use my function, the sprite is guaranteed to be in the
             right sector.

          - You can now change the angle in 2D EDIT MODE with the
              < and > keys.  Move the mouse cursor to a sprite first.
              Hold down shift with the < and > to get more precise angles.
          - You can now press 'B' on sprites in 3D EDIT MODE.  This in effect
              is xoring bit 0 of sprite[].cstat, which will not only be
              sensitive to hitscan, but will also block you from walking
              through the sprite.  Sprites with the 'B' attribute will
              appear pink in 2D EDIT MODE.
          - You can now edit the Hi 16 bits of the tag in 2D EDIT MODE.
              Just like T and ALT-T, you press H and ALT-H to edit the high
              16-bits.  When a structure's attributes are displayed, the tag
              will be displayed as 2 unsigned shorts.  The first number
              represents the hi 16 bits and the second number represents the
              lo 16 bits.
──────────────────────────────────────────────────────────────────────────────
4/23/94   - Added 'G' in Editart to GOTO a tile by typing in the tile number.
          - Changed the way masking walls work a little bit.  Now the
              masking walls use the picture wall[].overpicnum rather than
              wall[].picnum.
          - Made hitscan hit maskable walls.
──────────────────────────────────────────────────────────────────────────────
4/24/94   - Made palette.dat support any number of shades up to 64.  There
               is no change to the palette.dat format.  The palette.dat
               format is:

                  First the palette (768 bytes)  Values range from 0-63
                  Then the lookup shades (256 * number_of_shades)  The
                     shades are stored in groups of 256 bytes.

          - If you use Mark Dochtermann's palette program, then you can
               convert his data files (such as palette.pal and colormap.lmp)
               to my palette format, palette.dat, with my great Qbasic
               program, PALMP.BAS.  To run it, first copy the basic program
               into the same directory as the 2 source palette files are in.
               Then type: "QBASIC PALMP" and then press SHIFT+F5 (to run it).

          - Added a global variable, HORIZ.  Horiz usually equals 100.
               Modifying horiz will move the whole screen up / down.  I
               added 2 keys, +/- in my own game.c to demonstrate this.  You
               can use this variable for looking up and down if you want.
               (Even though this is not "truly" looking up and down, if
               you're lucky, you might be able to fake out some people!)

          - New Key in 3D EDIT MODE, the forward slash. Use / with the
               tile panning keys (, and .) to flip the horizontal / vertical
               orientation bits for walls, ceilings or floors.
──────────────────────────────────────────────────────────────────────────────
4/28/94   - NEW BOARD VERSION!  You must run CONVMAP4.EXE on all your boards.
               Here's what I changed in the structures:

               Sectors:
                  I got rid of:
                     char sector[].ceilingpanning, sector[].floorpanning;
                     sector[].ceilingstat/floorstat bit 2 zeroed out
                  and added:
                     char sector[].ceilingxpanning, sector[].ceilingypanning;
                     char sector[].floorgxpanning, sector[].floorypanning;

               Walls:

                  I got rid of:
                     char wall[].panning;
                     wall[].cstat bit 6 zeroed out
                  and added:
                     char wall[].xpanning;
                     char wall[].ypanning;

               Sprites:

                  I got rid of:
                     short sprite[].vel;
                  and added:
                     short sprite[].xvel;
                     short sprite[].yvel;
                  The reason I did this was so sprites could be moved in
                  different directions than sprite[].ang.  Please make
                  use of both xvel and yvel.  Here's the equation I would
                  use to convert from the old to new.  For example:

                  old:
                     sprite[i].vel = 256;
                  new:
                     sprite[i].xvel = (sintable[(sprite[i].ang+512)&2047]>>6);
                     sprite[i].yvel = (sintable[sprite[i].ang&2047]>>6);

                  The reason I am shifting the sines right by 6 is becuase
                     my sintable ranges from -16384 to 16384.  Dividing by
                     64 gives a maximum range of -256 to 256.

               CONVMAP4.EXE also attempts to convert the masked walls so
                  overpicnum is the masked wall's picnum.

          - Changed 3D EDIT MODE panning keys:
                 Since panning is now all 4 directions, I got rid of the
              , and . keys and put the keys on the keypad using 2,4,6,8.
              Usually 2,4,6,8 are for x- and y-repeats, but if you hold
              down either shift key, then they act as x- and y- panning
              values.
                 The / key now resets the panning values to 0.

          - Please read the updated descriptions of how to make masking walls
               in the 3D EDIT MODE KEYS in the top of build.txt.  See
               the 'M' and 'Shift + M' keys.

          - Also look at the top of this file for a description of dragpoint(), a
               great new function that makes it easy to morph sectors.
──────────────────────────────────────────────────────────────────────────────
4/29/94   - Added some minor keys to 3D EDIT MODE.  Alt. +/- change the
               visibility variable.  It ranges from 5 (darkest) to 17
               (lightest) and it starts at 13.

          - Made 1-way walls have more options.  Now, for example, you can
               make rectangular switches on walls.  Please look at my
               nukeland.map for an example of how to do switches.  (The
               switches are close to start - just go through the door and
               bear left.)

         - For those who asked, here's how some sample bobbing code:
            First, calculate the distance actually moved - because if you
               are not moving then you shouldn't bob:

                  CODE:
               oposx = posx; oposy = posy;
               clipmove(&posx,&posy,&posz,&cursectnum,xvect,yvect,128L);
               dist = ksqrt((posx-oposx)*(posx-oposx)+(posy-oposy)*(posy-oposy));

            Then modify the horizon value by multiplying the distance
               actually moved by a sine wave.  (default is horiz = 100).

                  CODE:
               horiz = 100 + ((dist*sintable[(totalclock<<5)&2047])>>19);

         - Remember that moving block in the slime in NUKELAND.MAP?  It
              works a lot better now.  I am now modifying the floor panning
              values to make the floor match up correctly with the block.

         - Fixed the screen capturing (F12) to save captures as PCX files.
              This is for BUILD, GAME, and EDITART.

         - Fixed bug in EDITART when you press 'U' on a blank tile.  It
              should not crash any more.

         - Made Tab & Enter a little smarter in copying the right attributes
              to different types of objects.

         - Added ceiling / floor 90° rotation attributes.  All 8
              rotations are possible. 3 bits have been added to both
              the sector[].ceilingstat and sector[].floorstat flags.
              If you look in BUILD.H, you will see the new flags
              descriptions.  You can use the 'F' key in 3D EDIT MODE on
              a ceiling or floor to change the rotation.

──────────────────────────────────────────────────────────────────────────────
4/30/94  - ATTENTION PROGRAMMERS:
              I removed the function, getsector, and added a more expandable
              function, updatesector.  Also, updatesector is smart enough
              to check to see if you are in the same sector OR neighboring
              sectors first before going through every single sector.
              Getsector used to go though all the sectors every time you
              crossed a sector line.

              getsector();   (no arguments, returns nothing - REMOVED)
              * used to make sure that CURSECTNUM matched up with
                the right sector according to the POSX and POSY values.
                updatesector(posx,posy,&cursectnum);
              * the new way of writing getsector.  You can do a
                search&replace on your code.  Be careful when you use this
                function with sprites because remember that the sprite's
                sector number should not be modified directly.  For example,
                you might want to code it this way:

                    tempsectnum = sprite[i].sectnum;
                    updatesector(sprite[i].x,sprite[i].y,&tempsectnum);
                    if (tempsectnum != sprite[i].sectnum)
                       changespritesect(i,tempsectnum);

                Actually, I think the setsprite function is better for
                updating a sprite's sector number.

         - Added swinging doors!  Look at NUKELAND.MAP in my GAME.EXE for
              an example.  For swinging doors, I used a new math-helper
              function, rotatepoint.

              rotatepoint(long xpivot, long ypivot,
                          long x, long y,
                          short deltaang,
                          long *x2, long *y2);

              Rotatepoint will rotate point(x,y) around point(xpivot,ypivot)
              by the deltang value.  The resultant point will be s

         - Fixed crashing bug in BUILD when a wall's tile doesn't exist.
──────────────────────────────────────────────────────────────────────────────
5/02/94  - I improved the pre-level check on the swinging doors, so now
             you can have multiple doors in a sector that open any way.
             This is how I do my double swinging doors (demonstrated in
             NUKELAND.MAP)

         - I added a revolving door!  See NUKELAND.MAP for an example.  (The
             revolving door is beyond the bouncing platforms in the slime
             pit)

         - Fixed bug with joining sectors in 2D EDIT MODE.  The sprites
             don't get deleted any more.
──────────────────────────────────────────────────────────────────────────────
5/04/94  - Optimized the masked walls.

         - Cleaned up the sprite drawing code (and many bugs that that came
             along with it.)
──────────────────────────────────────────────────────────────────────────────
5/05/94  - Made cylindrical walls with any number of points VERY easy to
              make in BUILD.  Here's how you do it:  Highlight a wall and
              press 'C' on it.  Then move the mouse to the proper position
              to make a nice circle.  You can press '+' or '-' to change
              the number of points on the circle.  Press 'C' again to cancel
              or press space bar to actually change the map.
──────────────────────────────────────────────────────────────────────────────
5/07/94  - Made a fully-operational subway!  Look at subway.map.

         - Made a new type of door - a sliding door where the door doesn't
              get "x-smooshed" when it is opened.  (See description of
              sector tag 16 & wall tag 6)

         - Also note that I my first subroutine in game.c!  It is:
                 operatesector(short dasector)
              All the door code is now in this function (with a few
              exceptions, such as parts of the swinging doors and revolving
              door)
──────────────────────────────────────────────────────────────────────────────
5/08/94  - Made EDITART and BUILD support different tile sizes in 'V' mode.
              Here are the three possible sizes:
                 0. 64*64 - 5*3 grid - views 15 tiles (old default)
                 1. 32*32 - 10*6 grid - views 60 tiles (new default)
                 2. 16*16 - 20*12 grid - views 240 tiles
              Press the '*' and '/' keys in 'V' mode if you want to change
                 the grid resolution.

         - Added/fixed up a new key in 3D EDIT MODE, the dot key (. or >)
             This key attempts to match up all the tiles along a wall.  It
             scans along the walls towards the right as long as the picture
             number of the next wall is the same as the original picture
             number.  Note that some walls may not work right, especially
             floor / ceiling steps.

         - Made 2D EDIT MODE default to this when inserting sprites:
             1.  Sprite clipping = on for sprites taller than 32 pixels
             2.  Sprite clipping = off for sprites shorter than 32 pixels.

         - Fixed sprite clipping with Z-coordinates
──────────────────────────────────────────────────────────────────────────────
5/10/94  - Fixed some sprite dragging bugs

         - Made neartag? maximum sensing distance a global variable called
              neartagdist.  See build.h.
──────────────────────────────────────────────────────────────────────────────
5/11/94  - Added sector / groups of sectors selection.  When selecting
              sectors, hold down the right ALT key.  It works just like
              right shift.  You must completely surround any sector you
              wish to select.  Once selected, you can drag the mess of
              sectors with the mouse.  This is great for separating sectors
              from each other.

         - Added sector duplication.  First select a bunch of sectors.  Then
              press the insert key. (with the select sectors flashing)  Then
              This duplication is like stamping.  So, you must drag
              the sector bunch somewhere else after stamping to see your
              great accomplishments.
──────────────────────────────────────────────────────────────────────────────
5/12/94  - Added group-sector PGUP / PGDN.  Here's a neat trick that will
              let you move a whole section of a board up / down quickly.
              First you select a bunch of sectors with the right ALT key in
              2D mode.  Then flip to 3D mode and if you press PGUP / PGDN
              on any of the highlighted sectors, the rest of the highlighted
              sectors will move also.

         - Added group-sector rotation.  Once you do a sector select (right
              ALT), you can press the , or . keys to rotate the selected
              sectors.  Hold down the shift key with the keys to get fine
              angle rotation (WARNING:  You will get distortion with fine
              angle rotation in this version)

         - Added sector count and wall count information at the bottom of
              the 2D edit mode status bar.

         - Fixed some stupid bug that you probably wouldn't have found if
              you made boards with BUILD for the rest of your life.

         - You can now press 'B' to block / unblock a wall or sprite in
              2D edit mode.

         - Made Ctrl-Shift Enter auto-shade a sector.  First make any
              wall of the loop as light as the lightest shade you want.
              Then make any other wall of the loop as dark as the darkest
              shade you want.  Finally press Ctrl-Shift Enter on the wall
              that should be lightest.  Now the loop should be smoothly
              shaded.  If it is not smoothly shaded, you may need to insert
              more points on the walls.

         - Fixed my .PCX format bug.  My screen captures now load properly
             in Deluxe programs.
──────────────────────────────────────────────────────────────────────────────
5/13/94  - Made my serial code in Game.c work better.  I fixed the bullet
             shooting and made all types of doors work.  Unfortunately, you
             still may have to run at slower COM speeds.
──────────────────────────────────────────────────────────────────────────────
5/14/94  - Fixed deadly bug with sector copy & sprites.

         - Added hitscan sensitivity bit.  It is bit 6 (64) of wall[].cstat.
             In 3D edit mode, you can press 'H' to toggle the bit.  (H used
             to be like 'V' for choosing height tile numbers for groudraws -
             I change the 'H' key to Alt-V since I don't think anybody was
             using it anyway)  By default, hitscan CAN go through maskable
             walls.  If a hitscan is set to not go through a maskable wall,
             The wall will appear BRIGHT pink in 2D edit mode.
──────────────────────────────────────────────────────────────────────────────
5/15/94  - Made more subroutines in game.c and moved documentation from the
             end of game.c into this text file.   Added some more comments
             also.  Game.c is so much easier to read now!  The main loop is
             less than 50 lines long!

         - Made some optimizations to my serial code now that everything
             is in functions.

         - ATTENTION PROGRAMMERS:
             I added one new paramater (cliptype) to both the movesprite
                and clipmove functions.  If the new parameter is a 0, then
                the clipping is normal (Use 0 to clip you and monsters).
                If the new parameter is a 1, then the object is clipped to
                the same things that hitscan is clipped to (use 1 for all
                bullets).
             See the above documentation for a detailed description of the
                parameters.  (Remember that I moved the documentation from
                GAME.C into the middle BUILD.TXT)
             Finally, you can have sprites and bullets going where you
                want them to go!
──────────────────────────────────────────────────────────────────────────────
5/18/94  - Added support for the Spaceplayer (6-degree of freedom Space ball)
              Try out the SETUP program now.

         - Fixed some bugs with the 'E' key in 3D EDIT MODE.  As long as both
             dimensions of the tile are powers of 2, then the picture should
             be visible.  It should not be a solid color with a few weird
             lines any more.

         - Fixed some bugs with the Z control in 3D EDIT MODE.  In normal
             operation,  BUILD attempts to keep your Z constant even if
             you cross sector lines.  Then I added a special new mode that
             locks your heightofffloor to a certain value.  You Z will
             change instantly if you cross sector lines.  To use this mode,
             press the Caps Lock key.  Press Caps Lock key again for normal
             operation.

         - ATTENTION PROGRAMMERS!  I put all of engineinput into GAME.C
             This means you have absolutely total control of the Z's.  (The
             only thing left is some key code I still have in my timer
             handler)

             OK, now read carefully!  Here's how to convert to the new OBJ's:

             1. First of all, the Spaceball code is now also in game.c.
                A.  You will need to put this line in game.c:
                     include "spw_int.h"
                B.  And make sure to add spwint.obj your makefile.
             2. Heightoffceiling, heightofffloor, and gravity have been
                removed from BUILD.H.  If you still want to use them
                then you should defined them as globals in your own code.
             3. You should go through every single key of my gameinput()
                function in game.c and copy any code you want.
──────────────────────────────────────────────────────────────────────────────
5/23/94  - GREAT NEW STUFF!

            Be sure to check out GAME.EXE and try out these new keys!

          Rt. Enter = Switch between 3D / 2D modes
            Lt. +/- = Zoom in 2D mode

             Added 2D map display with auto-mapping!  The 2D maps use a
             higher resolution 16-color mode.  You have a choice of either
             using a 640*350*16 screen or a 640*480*16 screen with a 144-high
             status bar (remember that 640*480*16 cannot fit in 256K with 2
             screen pages so I have to cheat with a status bar).

                NEW FUNCTIONS:

             draw2dscreen(long posxe, long posye, short ange, long zoome,
                          short gride)
                Draws the 2d screen - this function is a direct replacement
                for the drawrooms() and drawmasks() functions.  Be sure
                to call either qsetmode640350() or qsetmode640480()
                first.  When switching back to 3d mode, be sure to call
                qsetmode320200().

                   IMPORTANT NOTES:
                   1.  The overwritesprite function should only be called in
                       3D mode.  If you do this in 2D mode, junk will be
                       written to the 2D screen and a crash is possible.
                   2.  When you switch back to 3D mode, you should call the
                       permanentwritesprite functions to draw the status bar,
                       or whatever else you have to draw.
                   3.  You must call the nextpage() function in both 2D and
                       3D modes.

             qsetmode320200();
                Set to the game mode and load palette (320*200*256)
             qsetmode640350();
                Set to the 2D map mode #1 (640*350*16)
             qsetmode640480();
                Set to the 2D map mode #2 (640*480*16)

                NEW VARIABLES (see description in build.h):

             EXTERN char show2dwall[MAXWALLS>>3];
             EXTERN char show2dsprite[MAXSPRITES>>3];
             EXTERN char automapping;

         - Added parallaxing floors!  Works like the parallaxing skies, but
              the other side.  In 3D EDIT MODE, press P on ceiling for
              parallaxing sky, or press P on floor for parallaxing floor.

──────────────────────────────────────────────────────────────────────────────
5/24/94  - There is a new function that I forgot to document in the last
              version.  Oops!

                doanimations() is this function.
             It's not really new, but I split it off of the
           drawrooms/drawmasks functions.  This function animates anything
           that you use setanimation with.  Please stick it in your code
           somewhere after you draw the screen.
──────────────────────────────────────────────────────────────────────────────
5/26/94  - You can now do TRANSLUSCENT sprites!  I am using bit 1 of
              sprite[].cstat to detemine whether or not the sprite is
              transluscent or not.  In 3D EDIT MODE, you can press 'T' to
              toggle the transluscence bit.
                 IMPORTANT:  Transluscence WILL NOT WORK until you run my
              TRANSPAL.EXE program.  On the command line, simply type:
                 C:\BUILD>transpal [filename]
              If you do not specify a filename, the default will be
              palette.dat.  If your palette.dat file is now around 40K long,
              then you are ready for transluscence!

         - Added TRANSLUSCENCE to masked walls.  See bit 7 of wall[].cstat.
              Press 'T' on wall to toggle the transluscence bit.

         - In this BUILD update, I have collected many different palettes
             for comparison purposes.  Try running TRANSPAL.EXE on each of
             them and see for yourself which palettes work the best with
             transluscence!

──────────────────────────────────────────────────────────────────────────────
5/29/94  - ATTENTION PROGRAMMERS:  I completely rewrote the neartag code.
             Here's what you have to do to convert your game.c files:

                1.  You should move the neartag variables that I used to
                    have in BUILD.H into your C code.
                2.  You must call the neartag function yourself if you want
                    to see if you're near a tagged object.  Neartag is NOT
                    automatically updated by the engine any more.
                3.  I highly recommend that you put your neartag function
                    call at the first line in your space bar code.  This way,
                    you can optimize the neartag calculations by only doing
                    them when you press the space bar.  (Exception:  For
                    ladders, I think you'll have to call neartag every single
                    frame)

                 Here's a description of the new neartag function:
                 Neartag works sort of like hitscan, but is optimized to
                 scan only close objects and scan only objects with
                 tags != 0.

             neartag(long x, long y, long z, short sectnum, short ang,  //Starting position & angle
                    short *neartagsector,   //Returns near sector if sector[].tag != 0
                    short *neartagwall,     //Returns near wall if wall[].tag != 0
                    short *neartagsprite,   //Returns near sprite if sprite[].tag != 0
                    long *neartaghitdist,   //Returns actual distance to object (scale: 1024=largest grid size)
                    long neartagrange)      //Choose maximum distance to scan (scale: 1024=largest grid size)

──────────────────────────────────────────────────────────────────────────────
5/31/94  - Added a function to get a sprite's screen coordinates.  I put
              some sample code in my GAME in the analyzesprites function.
              Simply hold down the CAPS LOCK key in my GAME.EXE, and the
              screen will be centered around the sprite closest to the
              center of the screen.

           Here's a new function in the engine I used to do this:

          getspritescreencoord(short spritesortnum, long *scrx, long *scry)

              Note that spritesortnum is the index into the spritesortcnt
          arrays, NOT the normal sprite arrays.  Scrx and scry are actual
          screen coordinates ranging from 0-319 and 0-199 respectively.
──────────────────────────────────────────────────────────────────────────────
6/6/94   - Made EDITART support sprite centering and animation speed.

              Now when you press 'A' in EDITART, the animation speed is
                 actually saved to disk and runs the same speed in BUILD
                 and your GAME.  Press + and - to change the animation speed.
                 There are 16 different animation speeds.
                    (Speed is proportional to (totalclock>>animspeed))

              To center a sprite, press the weird ~` key (located just above
                 the TAB key).  You will see some cross hairs.  Simply use
                 the arrow keys to the desired position.

              For both the 'A' and ~` keys, you can press Enter to accept
                 the new values or ESC to cancel.

         - Added a variable to BUILD.H, lockclock.  Lockclock is to
              totalclock as lockspeed is to clockspeed.
──────────────────────────────────────────────────────────────────────────────
6/7/94   - Made 3 different Z coordinate modes in BUILD.

            Mode 0:  Game mode (default)
            Mode 1:  Height lock mode
            Mode 2:  Float mode

           Press Caps Lock to switch between the 3 modes.
           A and Z move up and down for all 3 modes.
──────────────────────────────────────────────────────────────────────────────
6/10/94  - Added a new function that should solve all your sound problems.

           kenchaintimer(void (__interrupt __far *datimerchainaddress)(),
                 short dachainpersecond)

           Please look at the IMPORTANT ENGINE FUNCTIONS section above for
              a full description.
──────────────────────────────────────────────────────────────────────────────
6/20/94  - Got rid of the comsend / comgetchar functions.  Replaced them
              with sendpacket and getpacket.

         - Added network support.

         - Got rid of qsetmode320200(). Just replace it with setgamemode().
             The 2 are exactly the same.

         - Got rid of clockspeed variables.  Just replace it with lockspeed.

         - Adjusted my z directions of shooting depending on the horizon.
             You may want to readjust your code for this.

         - You now pass the number of tics as a long to doanimations.
            doanimations(long numtics);

         - FIXED DEADLY BUG!  This may have been causing bigtime crash-city
            bugs!  The problem was with show2dsprite and show2dwall.  In
            my engine.c I forgot that they were BIT arrays, not BYTE arrays.
            When I initialized the arrays to 0, I initialized 8 times the
            length, possibly overwriting your precious data!  I initialize
            these arrays in the initengine and loadboard functions.

         - ATTENTION PROGRAMMERS:  In order to get rid of the posx, posy,
            posz, ang, posz, and horiz variables from BUILD.H, I added some
            parameters to a few functions.  YOU MUST REWRITE YOUR CODE
            TO SUPPORT THESE:
               loadboard(char *filename, long *posx, long *posy, long *posz,
                         short *ang, short *cursectnum)
               saveboard(char *filename, long *posx, long *posy, long *posz,
                         short *ang, short *cursectnum)
               drawrooms(long posx, long posy, long posz,
                         short ang, long horiz, short *cursectnum)

           THESE VARIABLES SHOULD BE MOVED INTO GAME.C:
              long posx, posy, posz, horiz;
              short ang, cursectnum;
              long hvel;
──────────────────────────────────────────────────────────────────────────────
6/22/94  - ATTENTION PROGRAMMERS:  Added 1 parameter to movesprite, the
              number of tics (such as lockspeed).

           movesprite(short spritenum, long xchange, long ychange,
                      long zchange, long walldist,
                      char cliptype, long numtics)

         - ATTENTION PROGRAMMERS:  At one time, I used to have 4 timing
              variable in the engine. (totalclock, clockspeed,
              lockclock, lockspeed).  I got rid of all of them except
              totalclock.  If you want to use the other 3 variables, you
              must simulate them yourself.  Here's how you do it:

              Step 1.  Define clockspeed, lockclock, and lockspeed as global
                       longs in your code.

              Step 2.  Be sure to zero out all 3 variables just before
                       starting to play each level.  (You could zero out
                       totalclock also.)

              Step 3.  Right after every time you call the nextpage()
                       function, insert this code line for line:

                 lockspeed = totalclock-lockclock;
                 lockclock += lockspeed;
                 clockspeed = lockspeed;

              You really don't need clockspeed if you have lockspeed.
              You should replace all clockspeed's with lockspeed's.
              Before, I had both because both totalclock and clockspeed used
                 to be incremented in the timer handler and could be changed
                 at any time.

         - I added my own optional random function, krand() which returns
              a pseudo-random number as a long from 0 to 65535.  Notice that
           I put a randomseed variable in BUILD.H called randomseed.

──────────────────────────────────────────────────────────────────────────────
6/24/94  - I just want to try to explain how all of my multiplayer code
              works.  Theory:  Think of the whole program as 2 functions:

                 Function 1. Draw the screen (could be 2D / 3D)
                 Function 2. Move things. (sprites, walls, sectors, and you!)

                 My communications in GAME.C work on a MASTER/SLAVE
              system where it would be nice if the faster computer was the
              MASTER.  (Right now, the first computer in is the MASTER).
              The big trick to keeping everything in sync (and I do mean
              everything- even moving platforms, revolving doors, & subways)
              is to call function #2 with the exact same parameters as input
              on all computers the same number of times and in the same
              order.   Now this might seem like a lot of information but it
              really isn't!  Let me explain the role of the MASTER and SLAVE:

              The MASTER's job:
                 1.  Read in own input changes
                 2.  Read in any slave player input changes
                     (getpackets function - reads in foreign vel, svel,
                      angvel, & the bits)

                 3.  Just before calling the movethings function, send the
                     input to the movethings function:
                    A.  Master's tic cnt (synctics)
                    B.  Every player's velocities (syncvel, svel, angvel)
                    C.  Every player's status bits (Space, Ctrl, Shift, etc.)
                 4.  Call the movethings function
                 5.  Draw screen

              The SLAVE's job:
                 1.  Read in own input changes
                 2.  Send own input changes to master.
                 3.  Read in all master movethings input parameters and
                     call movethings function for each packet.  This may
                     mean calling the movethings function more than once
                     between calling the drawscreen functions. This is
                     waste, but it's the price you have to pay to keep
                     things in sync.
                 4.  Draw screen


                 You may ask how do monsters stay in sync even if you are
              not sending any monster information over the line.  The
              answer to this is simple.
                 1.  You make sure that a random seed starts the same on
                     both computers.
                 2.  Only call the rand() or krand() function in the
                     movethings function.

                 Before you try my demo, I wanted to say that, I have
              ABSOLUTELY NO ERROR CORRECTION in my code at this point.  It
              is most certainly my next project.  For serial link, I would
              recommend a rate of about 14400 baud.  In the future, I think
              I can optimize the code enough to work at rates as low at 2400
              baud!  Let me explain how well certain setups should work
              without error correction:
                 Ken's guesses on how badly it will crash WITH NO ERROR
              CORRECTION (Build the way it is now):

              1.  Serial link using 8250 chips - out of sync after 15
                     seconds because a few bytes were missed.
              2.  Serial link using 16550 chips - out of sync after 10
                     minutes because 16550 chips have a 16 byte FIFO and
                     therefore don't lose bytes as often as the 8250.
              3.  Modem - out of sync after less than 5 seconds.
              4.  Network - out of sync after 1 minute.  Networks love to
                     lose full packets here and there.

                 I will be working on error correction for all of the above
              situations.  Any error correction techniques I use will be
              internal to the engine and completely transparent to you (so
              you can start coding now!)  If the demo actually works multi-
              player, then you may want to look at my GAME keys at the top
              of this file again.  There are some significant changes.

            * I replaced the old COM functions section at the top of
               BUILD.TXT with a COMMUNICATIONS FUNCTIONS section.  Please
               look through it for descriptions of at least these 2
               functions:

              sendpacket(short otherconnectnum, char *bufptr, short bufleng)
              getpacket(short *otherconnectnum, char *bufptr)

            * Another rule to keep in mind while testing: (as if there
               aren't enough already)  Always make sure you have the
               same EXE's and MAP'S or else the games will get out of sync.

            * Connection numbers and Index numbers:
               For networks, each computer has a given connection number.
               Connection numbers range from 1 to 100.  Player number 1
               might have connection number 5 and player number 2 might have
               connection number 17.  Since a player's connection number
               can be anything between 1 and 100, I don't want you to
               allocate 100 of every variable such as posx[100].  My
               solution was to make index numbers.  So in this case:

                  connectnum[0] = 5;  connectindex[5] = 0;
                  connectnum[1] = 17; connectindex[17] = 1;

            * Now I'll will describe some new variables at the top of GAME.C

               myconnectnum - connection number of your computer
               myconnectindex - index number of your computer
               masterconnectnum - connection number of the MASTER computer
               screenpeek - index number of which player's eyes you are
                            looking through.
               numplayers - if numplayers >= 2 then multiplayer mode is
                            enabled, else single player game.

               connecthead, connectpoint2[MAXPLAYERS] - These 2 variables
                  form a linked list of all the index numbers.
                  Here's some example code that traverses the list of
                  players:

               p = connecthead;
               while (p != -1)
               {
                  printf("Player index %d has connection number %d\n",p,connectnum[p]);
                  p = connectpoint2[p];
               }

            * These variables are used to record / playback a demo. (This
                is like POSCAPT.DAT, but with everything in sync!)  In my
                GAME, i single player mode, I have it so if you press the
                Left ENTER, then it will playback everything.  These are
                the only variables necessary to store a demo run:

                   static long reccnt;                //Number of frames
                   static short recsyncvel[16384];    //Vel for each frame
                   static short recsyncsvel[16384];   //Svel for each frame
                   static short recsyncangvel[16384]; //Angvel for each frame
                   static short recsyncbits[16384];   //Bits for each frame
                   static short recsynctics[16384];   //Tics for each frame

            * These variables are used for communications syncing:

                   static short syncvel[MAXPLAYERS+1];    //Vel of each player
                   static short syncsvel[MAXPLAYERS+1];   //Svel of each player
                   static short syncangvel[MAXPLAYERS+1]; //Angvel of each player
                   static short syncbits[MAXPLAYERS+1];   //Bits for each player
                   static short synctics;                 //Number of tics of MASTER ONLY

            * Unless I suddenly make BUILD a 6-degree of freedom engine
            tomorrow, this should be the most difficult update in a long
            time!  Good Luck in programming!  I'm sure I left a lot of
            things out, so I'll be expecting plenty of phone calls!
──────────────────────────────────────────────────────────────────────────────
6/27/94  - I scaled the vel, svel and angvel variabile down 1 bit so they
           fit into a signed char.  (Before they ranged from -256 to 256)
           Now they range from (-128 to 127).
──────────────────────────────────────────────────────────────────────────────
6/28/94  - Optimized the size of my COM(modem)/network packets in game.c.
           I made the tics into a char.  I also use some new buffers called
           osyncvel, osyncsvel, osyncangvel, and osyncbits.  Now I send
           extra byte with bit fields that tell whether I need to update
           the syncvel, syncsvel, syncangvel, or syncbits variables.  If
           the bit field of the extra byte is off then I don't send the
           byte(s) it represents.  You can now play my GAME at 4800 baud.

      static signed char syncvel[MAXPLAYERS+1], osyncvel[MAXPLAYERS+1];
      static signed char syncsvel[MAXPLAYERS+1], osyncsvel[MAXPLAYERS+1];
      static signed char syncangvel[MAXPLAYERS+1], osyncangvel[MAXPLAYERS+1];
      static short syncbits[MAXPLAYERS+1], osyncbits[MAXPLAYERS+1];
      static unsigned char synctics;

         - There was an crashing error with transluscence palette on low
              memory configurations.  When you exit the BUILD editor,
              you normally get some numbers like this.
                 Memory status: 788979(788979) bytes
              If the first number (art memory cache size) was less than the
              second number (art file size), then the whole art file did
              not fit in memory.  The reason for the crashing was because
              loadpics() sucks all memory up to the size of the art file.
              The 64K transluscent palette is malloc'd when you call the
              setgamemode() function for the first time.  The was no memory
              left to allocate the palette.  I fixed this by making the
              transluscent palette take 64K of the art memory cache if
              it could not malloc the 64K.

         - I programmed some error correction for the COM(modem).  Here is
             my new of Ken's guesses on how badly it will crash (assuming
             that it will always crash eventually.)  You will be seeing new
             and better correction methods in the future.

             ERROR CORRECTION METHOD #1:
              1.  Serial link using 8250 chips - out of sync after 5 minutes.
              2.  Serial link using 16550 chips - out of sync after 10 minutes.
              3.  Modem - out of sync after 3 minutes - try it!
              4.  Network - out of sync after 1 minute.  Networks love to
                     lose full packets here and there. (not changed)
──────────────────────────────────────────────────────────────────────────────
6/29/94  - Added some code to my GAME.C that will let you type in messages
              and send them to all remote players.  Press Tab (or T for
              those people who play that other, very bad, game too much)
              to start typing in a message.  Press either Enter key to send
              the message to the other player.
──────────────────────────────────────────────────────────────────────────────
6/30/94  - ATTENTION PROGRAMMERS:  I changed the values that clipmove and
              movesprite return!  Here's how they work now:
                 If you did not hit anything, then they return 0.
                 Movesprite only:  If the object hits a ceiling / floor
                    then movesprite returns 16384+(sectornum hit).
                 If the first object you hit before sliding was a wall,
                    then they return the 32768+(wallnum hit).
                 If the first object you hit before sliding was a sprite,
                    then they return the 49152+(spritenum hit).

              Be careful when adding these changes to your code since the
                 return value are sort of reversed:

           return values: ║ Hit nothing: │ Hit something:
           ═══════════════╬══════════════╪══════════════════════════
           Old functions: ║      1       │       0
           ───────────────╫──────────────┼─────────────────────────-
                          ║              │    16384+sectnum (Movesprite only)
           New functions: ║      0       │ or 32768+wallnum
                          ║              │ or 49152+spritenum

──────────────────────────────────────────────────────────────────────────────
7/1/94   - Made getangle always return an angle from 0 to 2047.  Before I
              forgot to and it 2047 and sometimes the angle was negative.

         - Made overwritesprite when using the centering option be sensitive
              to the Editart centering tool.  (The ~` key.)

         - Made Ctrl+Rt.Shift highlight points on a loop rather than points
              inside a rectangle.
──────────────────────────────────────────────────────────────────────────────
7/2/94   - Added some neat features to my GAME.C to make multi-player mode
              even more fun.  I added a portable bowling ball/bomb-thrower.
              kills instantly! (similar to rocket launcher)  I also added
              water fountains.  If you hold the space bar down on them,
              you slowly get more life.

         - O.K.  I think I actually got the syncronization working perfectly
              for COM(modem) play.  I haven't had all day to test it out yet!
              (Still no error correction with networks)

              Here are some steps in playing over the modem:
                 1.  Be sure that both people have the exact same version of
                        the EXEs, ART, and MAPs.
                 2.  Then go into SETUP and set the COM port to the com port
                        of your modem.  Even though 9600 baud should work, I
                        would recommend going at 4800 baud because:
                           A.  It works fine at 4800 baud so there is really
                               no need to go any faster.
                           B.  Fewer errors over the modem so less time
                               spent re-sending packets.
                 3.  Then type:   MODEM [mapname] (must be same mapname!)
                     This will bring you into my terminal program.  You can
                        set the modem initialization string in MODEM.BAT by
                        changing the first command line option of the TERM
                        line.  Please disable modem compression and
                        correction for least jerky play.  Then connect with
                        the other modem using various methods, such as one
                        person typing ATA and the other typing ATD or one
                        person typing ATS0=1 and the other typing
                        ATDT(phone #).
                 4.  Wait through the beeps and buzzes until it says CONNECT
                        on the bottom window.
                 5.  If you can now chat with each other then things are
                        going well.  When the first person presses ESC, both
                        computers will automatically quit to DOS and go right
                        into the GAME.
                 6.  Play!
──────────────────────────────────────────────────────────────────────────────
7/5/94   - Serial moder over COM(modem) should work perfectly now.

         - Added scaredfallz to BUILD.H.  It is a global variable that
             tells monsters what height is too high to fall through.  You
             can set cliptype parameter to 2 for movesprite and clipmove.
──────────────────────────────────────────────────────────────────────────────
7/6/94   - Fixed clipping bug that used to let you go through certain concave
             corners.

         - Added new function which works well with clipmove.  Ever notice
             was when you're at the edge of a cliff and you go just a tiny
             bit over, you fall, but shouldn't yet?  Unlike what you have
             been doing, this new function finds the highest and lowest z
             coordinates that your clipping BOX can get to.  It must search
             for all sectors (and sprites) that go into your clipping box.
             Currently, you were searching the z's at the center point only
             by simply using the sector[].ceilingz and sector[].floorz
             variables.

             getzrange(long x, long y, long z, short sectnum,
                       long *ceilz, long *florz,
                       long walldist, char cliptype)

             Pass x, y, z, sector normally.  Walldist can be 128.  Cliptype
                can be 0, 1, or 2. (just like movesprite and clipmove)
                This function returnes the 2 z maxes in ceilz and florz.
                See GAME.C for an example.

         - Fixed bug with weird vertical lines in transluscent masked walls
              in chain mode.
──────────────────────────────────────────────────────────────────────────────
7/10/94  - Made screen capture (F12) work in 2D modes also.  It always
              saves to a 256 color PCX file.

         - Made screen re-sizeable.  Use this easy new function to re-size
              the screen:

              setview(long scrx1, long scry1, long scrx2, long scry2);

              It is TOO easy to use.  You simply pass is the Upper-left hand
           corner and the bottom-right corner in screen coordinates of the
           rectangular region you want the engine to draw to.  The engine
           automatically centers the horizon at the middle of the window and
           scales everything properly.

              Notes:
                 - Since the engine does extra scaling calculations for
                      window sizes that are not 320 pixels wide, I do not
                      recommend making the default mode with a window size
                      just under 320 pixels wide since the engine can
                      actually run a little slower. (such as 312-319
                      pixels wide)  Keep them 320 wide.
                 - Feel free to modify the startumost / startdmost arrays
                      AFTER a setview call if you want weird window shapes.
                      Keep in mind that startumost[0] and startdmost[0] are
                      always refer to the left edge of the viewing window.
                      (NOT left edge of screen.)
──────────────────────────────────────────────────────────────────────────────
7/19/94  - Finally got around to writing the number-of-tiles-per-file
              resizer.  Simply type rsizeart.  All instructions will be
              displayed when running the program before the actual
              conversion.

         - Fixed a few bugs in Editart.  I hope I fixed those evil bugs that
              rarely come by.  I'm pretty sure I fixed the bug that made the
              screen go blank in 'V' mode, and the bug where a few pixels
              in the top left corners of tiles sometimes get overwritten.

         - Added a key in Build 2D edit mode.  Press 'E' on a sprite to
              change its status list number.

         - Fixed those lousy Editart subdirectory colors in 'U' for those
              teams with great color palettes, but don't have my ugly shade
              of pink.

         - Fixed bug with non-200 high P-skies.  The p-skies are now (by
              default) centered on the horizon no matter what the height is.

         - Added multiple size PCX and GIF support in Editart.  You can now
              load pictures up to 1024*256. (That's all that can fit in VGA
              video memory)  If you need to load a bigger picture than that,
              it will be chopped off at the bottom, but will still load the
              top piece.

         - I know that I have introduced some wonderful new bugs with the
              sprite drawing.  I know why they're happening, but I am
              looking for a good solution so as to not make you change
              any code.  I will put another upload with these bugs fixed
              soon.
──────────────────────────────────────────────────────────────────────────────
7/20/94  - Added TRUE ornamented walls.  Right now the actual ornamentation
              must be programmed by you with 2 simple functions:

              copytilepiece(long tilenume1, long sourcex1, long sourcey1,
                                            long xsiz, long ysiz,
                            long tilenume2, long destx1, long desty1)

              * This function simply copies any section of a source tile
                    to any part of a destination tile.  It will automatically
                    skip transparent pixels.  It will wrap-around in the
                    source but not the destination.  If for some reason
                    the destination tile gets removed from the cache, the
                    destination tile will be reset to original form.  This
                    is why I had to add this second function:

              allocatepermanenttile(short tilenume, long xsiz, long ysiz)

               * This function allocates a place on the cache as permanent.
                    Right now, I reset the cache every time you call this
                    function so I would recommend calling this function
                    right after loadpics.

              I have an example of both of these functions in GAME.C.  Try
                 playing GAME DOOM1.MAP and go into the secret room with
                 the pictures of Ken.  Shoot some walls there.  The pictures
                 with Ken and the Explosion over it were done with
                 copytilepiece and allocated permanently at tile 4095.  A
                 good idea for allocating permanent tiles would be to start
                 at 4095 and decrement.  You can also allocate a permanent
                 tile over the original tile itself.

         - Tile panning matching keys work a lot better.  They now work
              well with bottom steps.  Also, it works well with matching
              up windows.
──────────────────────────────────────────────────────────────────────────────
7/25/94  - A week ago, I uploaded a version of BUILD which unfortunately
              had more bugs than the previous version.  This upload should
              have those bugs fixed - especially the sprite bugs. (I hope)

         - I think I may actually have the sprite feet behind stairs bug
              working perfectly.

         - The engine should now be even faster the before last week.  (A
              week ago, I temporarily changed the parallaxing sky algorithm,
              making the engine slower in those areas)

         - Added a variable, parallaxyoffs in BUILD.H.  It defaults to 0.
              If you set it to 100, then all parallaxing skies will be
              properly moved 100 pixels higher.

         - Fixed some weird drawing bug related to parallaxing skies and
              sprites.
──────────────────────────────────────────────────────────────────────────────
7/26/94  - Made sprite's sectors dependent on the z coordinates in the BUILD
              editor.  (Helpful if you use overlapping in your maps)
──────────────────────────────────────────────────────────────────────────────
7/29/94  - Made precache function.  Simply call:
               precache()        (no parameters)
              right after you call loadboard(?,?,...) and it will load
              all tiles in the current board into the cache.  Note that
              animations of sprites after the first one will not be
              precached.  I have included my precacheing code at the
              end of game.c for those interested in making the precaching
              fancy.

         - Fixed bug in editart which made it sometimes save the art files
              and names.h in the wrong directory (the last directory you
              were in when you were in 'U' mode).  You may want to search
              your artwork directories to see if you were a victim of this
              bug.
──────────────────────────────────────────────────────────────────────────────
7/30/94  - Made Ctrl-Enter paste parallaxing sky tiles to all neighboring
              parallaxing sky areas also.
──────────────────────────────────────────────────────────────────────────────
7/31/94  - Fixed (most) crashing bugs - Doesn't even crash on the highest
              skyscrapers now!  Since I can never be SURE that the crashing
              is gone, I need you to test it for me - compare the number
              of times it crashes per second since the last version.  It
              should be much better.

         - Fixed sector line bug!  Get close to a step, hold down Shift+Z
              and the edges of the step will NOT jitter like before AND you
              will not get random walls going through the screen.
──────────────────────────────────────────────────────────────────────────────
8/1/94   - Got rid of the OLD transluscent stuff from overwritesprite and
              permanentwritesprite.  (Before it used the 8K shading table
              which didn't work very well)

         - Added new true 50/50 transluscence option to overwritesprite.
              See bit 2 in the next comment.

         - Fixed overwritesprite so it works with different screen sizes.
              You don't need to do any scaling calculations!  Should be
              compatible with old function.  Added new bit 1 to orientation
              parameter to determine whether or not the sprite should be
              scaled and clipped to the viewing window.

               overwritesprite (long thex, long they, short tilenum,
                                     signed char shade, char orientation)

               If Bit 0 of orientation = 0: (thex, they) is top-left corner
               If Bit 0 of orientation = 1: (thex, they) is middle
               If Bit 1 of orientation = 0: no relation to viewing window
               If Bit 1 of orientation = 1: scale and clip to viewing window
               If Bit 2 of orientation = 0: normal
               If Bit 2 of orientation = 1: 50/50 transluscent!

             * If it works at full screen, simply set bit 1 of orientation
                   to 1, and it should automatically scale properly!

         - Made it so 2/4/6/8 keys in 3D EDIT MODE now only move the objects
              1 step rather than continuously, giving more control over the
              repeat/panning values of things.

         - Made the / key not only reset the repeats of walls, but also for
              sprites.  It will set both repeats of the sprite to the default
              size of 64.  If you hold down shift with / on a sprite, it
              will give the sprite a square aspect ratio by setting the
              xrepeat to equal the yrepeat value.
──────────────────────────────────────────────────────────────────────────────
8/2/94   - Doubled the board size from: (0 to 65536, 0 to 65536) to
              (-65536 to 65536, -65536 to 65536)  Notice that the grid in
              2D EDIT MODE is much bigger than before.  Since it is very easy
              to double the maximum board size, just tell me if you need
              a larger board.  The only bad thing about allowing larger
              boards is that the map designer is more likely to make some
              sectors too large and cause overflow bugs.  32 bits can only
              go so far you know!

         - I think I MAY have fixed a bug in BUILD that used to make it crash
              when quitting.  It had something to do with the vertical grid
              lines in 2D EDIT MODE when zoomed way in around the upper left
              corner of the map (I think).
──────────────────────────────────────────────────────────────────────────────
8/3/94   - Optimized Editart loading and saving.

         - Made 'V' screen in Editart 320*400 instead of 320*200.

         - Before I said I fixed the bug in EDITART where a few pixels in the
             top left corners of tiles sometimes got overwritten.  Well I
             lied.  This time I really fixed it.  (I hope)
──────────────────────────────────────────────────────────────────────────────
8/7/94   - Made x&y repeats, x&y pannings easier to adjust.  It now works
             like the default keyboard handler.  It moves once when you
             first press the key.  A little later, it starts to move fast.
──────────────────────────────────────────────────────────────────────────────
8/11/94  - Added great new sprite rotation function which works like
              overwritesprite (like 2DRAW sprites)

             rotatesprite(long sx, long sy, long z, short a, short picnum)

             (sx, sy) is the center of the sprite to draw defined as
                 screen coordinates shifted up by 16.
             (z) is the zoom.  Normal size is 65536.
                 Ex: 131072 is zoomed in 2X and 32768 is zoomed out 2X.
             (a) is the angle (0 is straight up)
             (picnum) is the tile number

             Ex: rotatesprite(160L<<16,100L<<16,65536,lockclock<<4,DEMOSIGN);
                This example will draw the DEMOSIGN tile in the center of the
                screen and rotate about once per second.

             Rotatesprite clips to the same area as overwritesprite but does
                not scale or do transluscence yet.

         - Please look at the new permanentwritesprite documentation at the
              top of this file!

         - Network should now work again - It now works great on my network
              with 3 players!  It may possibly also work with 4 or 5 players
              too, but I didn't feel like running between 2 rooms to test it!
              I never thought the day would come when I would get NET, COM,
              and MODEM all working respectably!

         - Changed setup program so you can select 2, 3, 4, or 5 players in
              a network game. (The 5 is just to annoy people who like that
              other lousy game)

         - ATTENTION BUILDERS!  Added TILE MOVING to EDITART!  For now, it
              will only work WITHIN THE SAME ART FILE.  Do the tile moving
              all in 'V' mode.  Here are the new keys in 'V' mode:

                 To swap 2 tiles:
                    Simply press space bar on the first tile, then space
                       bar on the second.
                 To swap a group of tiles:
                    Press 1 on the first tile, press 2 to remember the region
                       between where you pressed 1 and 2.  Press 3 at the
                       place to where you want to swap all the tiles.

              Don't forget that this is all SWAPPING, tiles will (should)
                 NOT be overwritten using these keys.

         - ATTENTION PROGRAMMERS!  Added ceildist and flordist parameters to
              both clipmove and movesprite.  I always had them all set to
              (4<<8) by default.

              clipmove(long *x, long *y, long *z, short *sectnum,
                        long xvect, long yvect,
                        long walldist, long ceildist, long flordist,
                        char cliptype)

              movesprite(short spritenum, long xchange, long ychange, long zchange,
                         long walldist, long ceildist, long flordist,
                         char cliptype, long numtics)

         - Moved some com/network code from the getpackets function in game
              into the engine.
──────────────────────────────────────────────────────────────────────────────
8/15/94  - Fixed some network initialization code and it now works with 4
              players.  (I tested it.)
──────────────────────────────────────────────────────────────────────────────
8/16/94  - Added different shirt color support.  Here's how you do it:

            allocatespritepalookup(long palnum, char *remapbuf)

            See more documentation of allocatespritepalookup at the top of
               this file.
──────────────────────────────────────────────────────────────────────────────
8/18/94  - I made it so you can redefine the keys in the setup program under
              the input devices menu.

         - ATTENTION EVERYONE - for this new version, you MUST go into my
              new SETUP program, and SAVE CHANGES AND QUIT once.  The arrow
              key code will not work if you don't do this!  If you are
              one of those people who actually read BUILD.TXT, give yourself
              1 point.  I wonder who will get "caught" for not reading this!
──────────────────────────────────────────────────────────────────────────────
8/22/94  - Added pixel height and wall length information to 2D EDIT MODE
              when you press Tab or Alt-Tab on a sector or wall.

         - Now show the actual number of sprites in 2D EDIT MODE.

         - Improved digitized sound routines.  Just thought I'd mention it.

         - Added a "save As" feature to 2D EDIT MODE.

         - Now show all lo and hi tags in 2D EDIT MODE on the map itself!
              Note that position of the sector tag's text is put at the
              average point of all the points of the sector, so if you have
              a weird shape tagged sector, the text might show up at an
              inconvenient place.

         - You can turn the tag boxes on or off by pressing CTRL-T or by
              zooming out.

         - ATTENTION EVERYONE - for the new BUILD.EXE and OBJ's, you will
              need to copy my new TABLES.DAT over your old one.  Also, you
              must go into SETUP and SAVE&QUIT.  I have split the TABLES.DAT
              file into 2 separate files.  They are:

              TABLES.DAT - 10880 bytes - sin tables, fonts, etc.
              SETUP.DAT  - 23 bytes    - (6 options) + (17 custom keys)

              The SETUP.DAT file is the only file SETUP.EXE accesses now.
           This means that from now on, if I want to add something to
           TABLES.DAT, you won't have to go into the setup program and reset
           the options again.
──────────────────────────────────────────────────────────────────────────────
8/24/94  - Relative alignment now fully works with ROTATION!  Relative
              alignment will allow ceilings and floors to align with the
              first 2 points of a sector.  This will relieve you of
              programming special ceiling and floor panning code for moving
              sectors.
           In 3D EDIT MODE, simply press 'R' on a ceiling / floor to switch
              between relative alignment mode and normal mode.  Notice that
              bit 6 of both sector[].ceilingstat and sector[].floorstat are
              relative alignment bits.
           I have an example of relative alignment in nukeland.map in the
              high blue room off the main octagonal room.  Also note that my
              subways and dragsectors now use relative alignment for panning.
──────────────────────────────────────────────────────────────────────────────
8/25/94  - I added a new parameter to printext256 and printext16.  I need
              to document this stuff!
──────────────────────────────────────────────────────────────────────────────
8/27/94  - Made it so you can't shrink sprites so much you can't grab them
              any more in 3D EDIT MODE.

         - Added (G)oto feature into Build 'V' mode.  Simply press G, type
              the tile number and it will go there.
──────────────────────────────────────────────────────────────────────────────
9/7/94   - Fixed relative alignment bugs I noticed.  My subway.map won't
              crash anymore and the relatively aligned ceilings and floors
              should not pan crazily anymore when looking from a far
              distance.
──────────────────────────────────────────────────────────────────────────────
9/9/94   - ATTENTION PROGRAMMERS:  I added a new parameter to
              overwritesprite.  Dapalnum can be from 0-15 depending on
              what palette lookup table is being used.  Dapalnum is normally
              0.  Overwritesprite now looks like this:

              overwritesprite (long thex, long they, short tilenum,
                   signed char shade, char orientation, char dapalnum)
──────────────────────────────────────────────────────────────────────────────
9/13/94   - ATTENTION PROGRAMMERS:  I changed the last parameter of drawrooms
              (sector number) to be passed as a value, NOT a pointer anymore.

              drawrooms(long daposx, long daposy, long daposz,
                       short daang, long dahoriz, short dacursectnum)
──────────────────────────────────────────────────────────────────────────────
9/15/94   - ATTENTION PROGRAMMERS:  I took out the COM(modem)/network code
              from my engine.obj and put it into a separate .obj called
              multi.obj.  (I also removed the sound from my engine and stuck
              it in kdmeng.obj)  Please include it in your makefile.  Here is
              a list of ALL of the variables and functions you will need to
              know to program for multiple players:

              VARIABLES:  (You should extern these in your game.c)
                 extern short numplayers, myconnectindex;
                 extern short connecthead, connectpoint2[MAXPLAYERS];
                 extern long *lastpacket2clock;

              FUNCTIONS:
                 initmultiplayers(option[4],option[5]);
                 uninitmultiplayers();

                 sendlogon();
                 sendlogoff();

                 sendpacket(connecthead,tempbuf,j);
                 sendpacket(-1,tempbuf,j);
                 leng = getpacket(&otherconnectindex,tempbuf);

              Please see detailed descriptions of these functions at the top
                 of this file.

          - Multiplayer code is now MUCH cleaner!

          - Please try my game on your networks again!  My game now works
               perfectly with 3 players on a network that used to crash at
               the DOS4GW prompt just a week ago!  My game needs only IPX,
               no server to run.
──────────────────────────────────────────────────────────────────────────────
9/16/94   - ATTENTION PROGRAMMERS:  CONVMAP5!!!  YOU MUST RUN CONVMAP5 ON
               ALL OF YOUR MAPS IF YOU WANT THEM TO WORK WITH THE NEW
               BUILD.EXE OR OBJ'S!  Following are the exact changes I made
               to the new map format:

               * Added numextras
               * Added sector[].ceilingpal
               * Added sector[].floorpal
               * Added sector[].visibility
               * Split sector[].tag to sector[].lotag and sector[].hitag
               * Added sector[].extra
               * Expanded wall[].cstat to a short
               * Split wall[].tag to wall[].lotag and wall[].hitag
               * Added wall[].extra
               * Split sprite[].tag to sprite[].lotag and sprite[].hitag
               * Got rid of sprite[].extra (the void * mess)
               * Added sprite[].extra

               The only thing programmers have to worry about when converting
                  are the tags.  I split them into lo and hi tags.
                  (See BUILD.H for new structure formats)

               I got rid of the (void *)extra thing from the sprite
                  structure.  I know I may have made some promises it
                  wouldn't change and it was for your use only, but what
                  can I say - it's not needed anymore (in other words,
                  if you're already using it, TOUGH LUCK).  I have my
                  own, new, method for extending sector, wall, or
                  sprite structures.  You will be able to extend any
                  structure as much as you want AND be able to edit it
                  all in the BUILD editor to be saved in the permanent
                  map format.  Notice I added a (short)extra to all 3
                  main structures.  They default to -1.  But if they
                  are >= 0 then they form a linked list out of the
                  extra structure.   NOTE:  THIS EXTRA STUFF IS NOT
                  PROGRAMMED YET!  I'm just mentioning it becuase the
                  new map format has this extendability.  I'll try to
                  get it done soon though.  (9/21/94) - Actually just
                  ignore the fact that this paragraph ever existed.
                  I'm just keeping it here for history purposes.

          - Renamed my allocatespritepalookup function to makepalookup since
               it now also applies to walls, ceilings, floors, p-skies and
               masked walls.
──────────────────────────────────────────────────────────────────────────────
9/20/94   - Added rotated sprites.  These new rotated sprites rotate in the
               same way as masked walls.  Sounds like a waste since the
               engine already has masked walls?  NOT AT ALL!  With rotated
               sprites, you can EASILY do TRUE ornamented walls FULLY inside
               the BUILD editor with even MORE versatility than that other
               game out there.  For example, you can place the ornamentation
               anywhere on the wall, with sizing control using 2,4,6,8 on the
               keypad, and even ornament with transluscence!  In 3D EDIT
               MODE, simply press 'R' on a sprite to make it a rotated
               sprite (Programmers see bit 4 of sprite[].cstat)

          - Fixed crashing bug with sector (Rt. ALT) copy/paste in 2D EDIT
               MODE.

          - You can now copy groups of sectors from 1 map to another!  Here's
               how you do it:

               Step 1:  Capture a bunch of sectors with the Rt. ALT selection
                        tool.
               Step 2:  With the sectors still highlighted, you can now load
                        another map and the highlighted sectors will
                        automatically be inserted into the new map.  (They
                        will still be highlighted)
──────────────────────────────────────────────────────────────────────────────
9/21/94   - Fixed bug with cursectnum not always being right after loading
               the board.  I now check it and make sure it's right when
               saving in BUILD now.

          - Added 'O' key in 2D/3D EDIT MODES.  It will push a rotated sprite
               backwards (using hitscan) into the first wall and
               automatically adjust the angle to make the sprite appear
               as if it was part of the wall.

          - You can now press 'S' to insert a sprite in 3D EDIT MODE.  Press
               'S' on a ceiling or floor.

          - You can now press delete to delete a sprite in 3D EDIT MODE.
──────────────────────────────────────────────────────────────────────────────
9/22/94   - Programmed Nick & Peter's BUILD stub to their EXACT
               specifications.  See WSTUB.C for code & documentation.
──────────────────────────────────────────────────────────────────────────────
9/27/94   - Fixed Rt. Alt block copying nextsector1 pointer bug.
──────────────────────────────────────────────────────────────────────────────
9/29/94   - Added special bitmapped effect which I either call lava or
               boiling slime.  See my initlava and movelava functions
               inside GAME.C.

          - I added a bit array call gotpic.  The engine will set the
               respective gotpic bit for each picnum drawn to the screen,
               including ceilings, floors, walls, sprites, masked walls -
               even overwritesprites, etc.   This array is mainly for
               making the game only calculate special bitmapped effects
               when that certain picnum is on the screen.

               Note 1:  The engine does NOT automatically clear the gotpic
                   bits for you.  If you want to test gotpic for a certain
                   picnum, you should clear it if you want to test it again.

               Note 2:  It is not necessary to use permanentwritesprite
                   for bitmapped special effects - after all, if you see it,
                   it MUST be in the cache.

               Added to BUILD.H:
                  EXTERN char gotpic[MAXTILES>>3];

               Example code in GAME.C:
                  if ((gotpic[SLIME>>3]&(1<<(SLIME&7))) > 0)  //test bit
                  {
                     gotpic[SLIME>>3] &= ~(1<<(SLIME&7));     //clear bit

                     if (waloff[SLIME] != -1)                 //if in cache
                        movelava((char *)waloff[SLIME]);      //calculate!
                  }

          - Added what some people call gamma correction.  I think
               brightness is a better description though.

               setbrightness(char brightness);

               Simply call this function where brightness ranges from
             0 to 4.  Brightness defaults to 0.  Levels 1-4 are all brighter
             than 0.  If you switch between 2D & 3D modes, the engine will
             remember the current brightness level.
──────────────────────────────────────────────────────────────────────────────
9/30/94   - Added a few keys to fake a multiplayer game all on 1 computer.
               In my game, press Insert to add a new player at the starting
               position, and Delete to delete the last player.  Press
               scroll lock to get control of the other players.  In the same
               way Lt. Enter lets you view other players, Scroll lock will
               let you control other players (single player game only)
──────────────────────────────────────────────────────────────────────────────
10/2/94   - Moved all doanimations code from engine into game.c.  If you
               are using the doanimations code, here's exactly what you
               need to do to make it work with your code again:

            Step 1:  Copy these 3 functions which you should find at
                     the end of my Game.c:

                doanimations(long numtics)
                getanimationgoal(long animptr)
                setanimation(long *animptr, long thegoal, long thevel)

            Step 2:  Move these variables out of BUILD.H and into your
                     game.c:

             #define MAXANIMATES 512
             static long *animateptr[MAXANIMATES], animategoal[MAXANIMATES];
             static long animatevel[MAXANIMATES], animatecnt = 0;

             * If you copied my door code, you will probably have to convert
                 some parameters to longs. (oops!)

          - Got rid of printnum from the engine.  It is an outdated function.
               Use either printext256 or printext16 instead.

          - Added y-flipping for both normal and rotated sprites.  (see bit
               3 of sprite[].cstat in BUILD.H.

          - Added y-flipping for walls.  (see bit 8 of wall[].cstat in
               BUILD.H.

          - Fixed bug with initialization of Master and Slave for
               COM(Modem) only.  If both computers went in at the same
               time, it used to sometimes think both were Masters.
──────────────────────────────────────────────────────────────────────────────
10/4/94   - Alt-C in 3D EDIT MODE changes all picnums on the whole map
               from the picnum in tab to the picnum under the mouse cursor.
──────────────────────────────────────────────────────────────────────────────
10/6/94   - Fixed y-flipping bug on walls and masked walls.
──────────────────────────────────────────────────────────────────────────────
10/16/94  - Fixed bug in editart that didn't convert the maps right after
               swapping tiles.

          - Added excellent function to my multi.obj.

               long getoutputcirclesize();
                  This function returns the number of bytes that have not
               yet been copied.  If there are still more than say, 16 bytes,
               then you may be sending too many bytes per second.  This can
               happen if the frame rate of a computer is faster than the
               speed of the serial mode (Ex: Try 2400 baud with a Pentium 90!)
               this function will tell you how many bytes are left to copy
               In other words, if getoutputcirclesize() < 16 then it is safe
               to send a packet.  If you already have serial mode working
               all you have to do to update your code is to copy the lines
               in my sync() function the deal with the getoutputcirclesize
               function.  Everything else in sync() and getpackets() is
               pretty much the same.

          - Programmed some example code in my game that will allow players
               to change masters and slaves during the game without losing
               sync.  Simply press 'M' in my multiplayer game, and that
               computer will become the master!  It is interesting how
               the frame rate and controllability changes.
──────────────────────────────────────────────────────────────────────────────
10/31/94  - Added basic scripting to EDITART.  Don't expect it to be
               everything you ever dreamed of (yet).

            Scripts are saved in a file called CAPFIL.TXT.  You can edit the
               text file, but be careful not to screw it up too badly (such
               as extra commas, spaces in weird places, etc.) since I am
               parsing it in EDITART.

            Whenever you select a box in 'U' mode, a line will be appended to
               the CAPFIL.TXT file.

                WRITING THE CAPFIL.TXT FILE:
            In 'U' mode, you can press 'O' instead of ENTER to select a tile.
               What 'O' does that is different from ENTER is that it takes
               the center point of the highlighted rectangle, and searches
               outward until a total rectangle of transparent pixels (255)
               is reached.  This is useful for grabbing sprites - not only
               will you not have fine adjust to the exact borders of a
               sprite now, but when you re-grab from the pcx's you can
               change the size of the sprite freely.

               READING THE CAPFIL.TXT FILE:
            There are 2 ways to re-grab from the CAPFIL.TXT file.  If you
               press ALT-U in the main screen, everything will be re-grabbed.
               If you press ALT-U in 'V' mode, then you should first select
               the range by pressing '1' and '2' on the range boundaries.

            Format of CAPFIL.TXT lines:
               Tile #, Full path/file name, x1, y1, xsize, ysize

               Note: If xsize and ysize are 0, then that means you did
               an 'O' grab and EDITART will search from point (x1, y1)
               when you do a re-grab.

            Example CAPFIL.TXT file:
               31,D:\CAPTUR00.PCX,220,98,64,64
               110,D:\CAPTUR00.PCX,49,72,0,0

               The first line says that tile #31 is a 64*64 tile and the
               second line says that tile #110 is unknown size tile (grabbed
               with the 'O' key)

            Note: You can only do 1 grab per tile with my scripting system.
               You may have done your parallaxing skies with several grabs.
               If so then try to make the tile into 1 large PCX and do 1
               grab.  (The largest grabbing size right now is 1024*256)

         -------------------------------------------------------------------

          - Made Editart's screen capture (F12) save to PCX's the exact size
               of the tile and not include the status bar at the bottom.  It
               will also save large tiles to large PCX's - Now it's easy and
               lossless to extract a tile from Editart!

          - ATTENTION PROGRAMMES!  Added 2 new parameters to getzrange for
               returning the objects hit on top and bottom.  
               (sectors / sprites)  See my updated documentation at the top 
               of this file.

          - Fixed clipping bugs with sprites near sector lines. (I hope)

          - Got 3D Red-Blue glasses mode working for all VGA cards.  First
               set the graphics mode to red-blue mode in the setup program.
               The left eye is red and the right eye is blue.  There are
               4 keys that let you adjust the 3D view:
                       [,] = Adjust width between eyes (3D width)
                 Shift [,] = Adjust width of parallax (2D width)
──────────────────────────────────────────────────────────────────────────────
11/1/94   - Guess what?  I turned 19 today.  Doesn't that just suck.  Now,
               if you play my build game on my birthday, all the extemely
               evil and scary brown monsters will be replaced with super
               happy fun dogs that throw smiley red jelly coconuts at you.
               Also, my incredibly evil and scary music will be replaced
               with super happy music.  Actually this whole paragraph is
               a joke (except for the birthday part).

          - Made centering work with rotated sprites.

          - Fix centering with x and y flipped sprites.

          - Rotated sprites now get chopped off by the ceiling or floor in
               the same way normal sprites get chopped.  Sprites do not get
               chopped if there is a parallaxing sky / floor.

          - Made Shift + F12 is BUILD 2D mode inverse black and white.

          - If SETUP.DAT is not found then default options are loaded
                instead of quitting to DOS.

          - ATTENTION PROGRAMMERS!  Added 3 parameters to makepalookup that
                allow you to do FOG effects.  The first 2 parameters are the
                same as before.  The last 3 are the color that the palette
                fades to as you get further away.  Before, this color was
                always black (0,0,0).  White would be (63,63,63).

              makepalookup(long palnum, char *remapbuf,
                       char redvalue, char greenvalue, char bluevalue)

          - ATTENTION PROGRAMMERS!  Moved 2 things into BUILD.H.  Please
               make sure to update it:

             #define MAXPALOOKUPS 256
                and
             EXTERN char *palookup[MAXPALOOKUPS];

             The palookup array is an array of pointers that point to the
             first byte of each 8K palette lookup table.  All 256 pointers
             are initialized to NULL by initengine() except for palookup[0]
             which is the default 8K palette.  This will allow you to modify
             the palette lookup table directly for non-snowy fading effects,
             etc.  Each palette lookup table has 32 shades.  Each shade has
             256 bytes.  Shade 0 is closest (actual palette brightness) and
             shade 31 is farthest (dark usually).  (256*32 = 8192 or 8K)
──────────────────────────────────────────────────────────────────────────────
11/3/94   - Now show white lines in 'V' mode of EDITART at ART file tile
               boundaries.

          - Added Insert and Delete commands to EDITART!  These Insert and
               Delete keys WILL shift all tiles after the one being inserted
               or deleted just like a regular text editor.  To insert or
               delete tiles, simply go the 'V' screen in EDITART and bang
               away!  Don't worry these keys are fully multi-tile file
               compatible (unlike swapping right now).
                  You will notice that the white line boundaries that
               I just added will actually move if you press Insert or Delete.
               This changes the number of tiles per art file.  But that's
               ok.  If the art files ever get too unbalanced, you can run
               the RSIZEART.EXE utility to fix it.
                  Ken's lesson of the day:  For the final release of your
               games, you only need 1 art file.  The reason I spent my time
               programming multiple art files was because of EDITART.
               Since EDITART need to READ & WRITE to the art files, it must
               hold a whole art file in memory at a time.  Since Build and
               Game only READ the art files, a caching system can be made
               and only 1 art file is necessary even for lo-memory systems.
──────────────────────────────────────────────────────────────────────────────
11/4/94   - ATTENTION MAP DESIGNERS!  Added a long-awaited feature to BUILD
               which I call "loop joining".  Have you ever gotten this
               frustrating message in 2D EDIT MODE before?

                  "You can't split sector by connecting different loops."

                  Well, you're not going to see it any more because I fixed
               it!  Yup.  You can now split a sector along a line connecting
               different loops of the sector.

                  Try this - Convert the sector on the left to the sector
               on the right:

                                Split #1         Split #2
               ┌───────┐        ┌───┬───┐        ┌───┬───┐
               │ ┌───┐ │        │ ┌─┴─┐ │        │ ┌─┴─┐ │
               │ │   │ │        │ │   │ │        │ │   │ │
               │ └───┘ │        │ └───┘ │        │ └─┬─┘ │
               └───────┘        └───────┘        └───┴───┘
                (Given)        (Half done)        (Result)
               (1 sector)    (Still 1 sector)   (2 sectors)

                  Before the only was to do this was to delete all the
               sectors and then redraw them again.

                  I'm sure loop joining has its share of tricks, as most
               BUILD functions do, so you may want to spend some time just
               playing around with this new function.

          - Removed my own profiler stuff - Hline calculations, etc. code.
               Watcom's sampler and profiler is much better anyway.

          - Fixed neartag divide by zero bug with walls (I hope).
               Anyone calling neartag for every player per movethings?
               I would try not to - How much is that unnoticable extra 0.02
               frames per second worth to you anyway?
──────────────────────────────────────────────────────────────────────────────
11/9/94   - Added new array to BUILD.H called gotsector.  It works a lot
               like the gotpic array, but this array determines which sectors
               were considered during the drawrooms function.  Note that
               gotsector, unlike gotpic IS cleared to 0 during every call
               to drawrooms.

          - Fixed Editart 'U' mode mouse control bug.  I typed too fast this
               time.

          - Fixed Build split sector bug of accidently deleting sprites.  It
               should not delete any sprites when splitting sectors now.
──────────────────────────────────────────────────────────────────────────────
11/15/94  - ATTENTION PROGRAMMERS!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
               I moved ALL my timer code and arrow key code into GAME.C.
               Here are the exact instructions that you need to follow to
               upgrade your code to the new obj's:

               1.  From the end of my GAME.C, take the code from these 4
                   functions:

               ---> inittimer();
               ---> uninittimer();
               ---> void __interrupt __far timerhandler();
               ---> keytimerstuff();

               2.  After each initengine, call inittimer right AFTER:

                    initengine();
               ---> inittimer();

                   After each uninitengine, call uninittimer right BEFORE:

               ---> uninittimer();
                    uninitengine();

               3.  You may need to include this (if not already included):

               ---> #include <dos.h>

               4.  Add these 2 lines to declare the timerhandler:

               ---> void (__interrupt __far *oldtimerhandler)();
               ---> void __interrupt __far timerhandler(void);

               5.  Since BUILD.H NO LONGER has vel, svel, and angvel, you
                   must add the following line to your game:
                   (These variables are modified inside keytimerstuff())

               ---> static long vel, svel, angvel;

               6.  Let me list some variables that I recently removed from my
                   GAME.  This may or may not affect you:

                   oposx[], oposy[], oang[], etc.. - GONE!
                   lastpacket2clock - GONE!
                   lastsynctics - GONE!
                   drawscreen's smoothratio parameter & related code - GONE!
                   kenchaintimer - GONE! (Don't think anybody was using it
                      because it didn't solve sound compatibility problems)
──────────────────────────────────────────────────────────────────────────────
11/16/94  - Made swinging door clipping work MUCH better!  It is much more
               difficult to "sneak" through the door now.  With the swinging
               door clipping working much better, I made it possible to open
               doors even if you are in the same sector as the door itself.
               Now you won't have to stand back a certain distance to open
               the doors.  (It is much less annoying this way)  Since neartag
               does not normally scan cursectnum's sector tags, you will
               need to check cursectnum's tags yourself (if you so desire)

               Example: (extracted from my GAME.C)

               neartag(posx[snum],posy[snum],posz[snum],cursectnum[snum],
                       ang[snum],&neartagsector,&neartagwall,&neartagsprite,
                       &neartaghitdist,1024L);
               if (neartagsector == -1)    //If no neartagsector found...
               {
                  i = cursectnum[snum];    //Test current sector for tagging
                  if ((sector[i].lotag|sector[i].hitag) != 0)
                     neartagsector = i;    //Cursectnum is the neartagsector!
               }

          - Improved my gamma correction algorithm.  Since it now uses a new
               1K at the end of my TABLES.DAT, be sure to update all your
               TABLES.DAT files!  My new gamma correction supports 16 levels
               of brightness (0-15).  You need not change any code in your
               game if you already have gamma correction programmed.
──────────────────────────────────────────────────────────────────────────────
11/17/94  - Made swinging door clipping code work even better.  The bug was:
               it only tested the clipping if you were in the same sector as
               the door.  So I put this handy little function in GAME.C which
               simply tests if 2 sectors are neighbors or not:
                  testneighborsectors(short sect1, short sect2)
               Check it out!

          - Rewrote my clipinsidebox function.  You probably don't use this
               function, but if you copied my swinging door code, then you
               will need to update this.  Clipinsidebox is used for clipping
               to determine whether a player or sprite is too close to a
               wall.

               clipinsidebox(long x, long y, short wallnum, long walldist)
                  X and y are the position of the sprite or player.  Wallnum
               is the wall to test, and walldist is the fatness of the sprite
               or player (same as clipmove).  It returns a 1 if the sprite or
               player's clipping square intersects the wall or 0 if not.

               Example - You can test all 4 walls of a swinging door and make
                  sure the door doesn't run you over:

               short swingwall[4];   //4 wall indeces of a swinging door
               for (i=0;i<4;i++)
                  if (clipinsidebox(posx,posy,swingwall[i],128L) == 1)
                  {
                     //Swinging door swung into player, so move door back to
                     //its old position / inverse swinging direction.

                     break;
                  }
──────────────────────────────────────────────────────────────────────────────
11/22/94  - Made build posx, posy, posz, ang, cursectnum, and horiz variables
               nonstatic.

          - Made hi-res screen capture work.

          - Fixed some of those evil view-clipping bugs with rotated sprites
               on red sector lines.  I think it should work with rotated
               sprites on horizontal or vertical red lines.  I'm not sure
               about weird angled lines.
──────────────────────────────────────────────────────────────────────────────
11/23/94  - ATTENTION PROGRAMMERS:  Made parallaxing skies tileable in any
               equal size chunk where the chunk x size is a power of 2
               from 16-1024.  You can now make a 2048 wide parallaxing sky
               with 2 chunks of 1024 (gobble gobble).

               These lines were added to BUILD.H:

                  #define MAXPSKYTILES 256
                  EXTERN short pskyoff[MAXPSKYTILES], pskybits;

               pskyoff[MAXPSKYTILES] is an array of OFFSETS of each tile
                  from the picnum of the parallaxing sky.

               pskybits is NOT the actual number of tiles, but the
                  log (base 2) of the number of tiles.  Look at this table:

                    For 1 tile, pskybits = 0
                    For 2 tiles, pskybits = 1
                    For 4 tiles, pskybits = 2
                    For 8 tiles, pskybits = 3
                    For 16 tiles, pskybits = 4
                    etc.


               I know that most teams have a 1024 wide parallaxing sky that
                   wraps all the way around.  Don't worry - this is the
                   default now.  When initengine is called, the variables
                   default to this:

                  pskyoff[0] = 0;
                  pskybits = 0;

               You may have used a 512 wide parallaxing sky (like in my game)
                   that repeated every 180 degrees.  To make this work with
                   the new version, set these variables like this right after
                   initengine is called:

                   pskyoff[0] = 0;
                   pskyoff[1] = 0;
                   pskybits = 1;

                   Note that both pskyoff variables are 0 here.  This will
                   make the parallaxing sky repeat.

               With the new tiling, you can save memory by making small
                   chuck sizes, such as 64 or 128, and repeating certain
                   sections.
──────────────────────────────────────────────────────────────────────────────
11/25/94  - Fixed some really stupid keyboard problem in Build.
──────────────────────────────────────────────────────────────────────────────
11/29/94  - Added FLOOR SPRITES!!!  Here are some things that floor sprites
               can do for you:

             * Make bridges and balconies
             * Use them for ceiling and floor ornamentation
             * Use them in place of a rotating sector - modifying 1 angle
                  is much faster than rotating tons of points.
             * Weapon such as a spinning saw blade or, of course, a smiley
                   red jelly coconut. (?)
             * How about a "walking" hole like in Ken's Labyrinth
             * You could throw a footstep on the floor every time you make a
                  step.  The steps would go away after awhile.  Maybe if you
                  step in mud, the next 16 or so footsteps will be plotted.
             * You could even fake radial shading with a transluscent floor
                  sprite. (Transluscent floor sprites not yet programmed)

                Just imagine all the great stuff you can do with floor
             sprites combined with wall sprites!  I can't wait to see what
             you all come up with!

           ┌── To clear some confusion, and to shorten conversations, let
           │      me give official names to my 3 kinds of sprites:
           │
           │        Normal stupid sprites             - "FACE SPRITES"
           │        Rotated / masked wall sprites     - "WALL SPRITES"
           │        New ceiling & floor sprites       - "FLOOR SPRITES"
           │
           │   Also let me clear up the 2 kinds of clipping:
           │
           │        Clipping when moving something    - "MOVEMENT CLIPPING"
           └──      Clipping when drawing something   - "VIEW CLIPPING"

            To make a floor sprite in BUILD, simply press 'R' on any
               sprite until it becomes a floor sprite.  The xrepeat and
               yrepeat values should work perfectly with floor sprites.
               Floor sprites can be rotated at any of 2048 degrees, using
               the sprite[].ang.  Press < / > for course angle adjustment
               or < / > with shift for fine angle adjustment.  Also, you
               can press 'F' on a floor sprite to flip it over like a
               mirror.  I am using another bit in sprite[].cstat to determine
               the type of sprite.  See the documentation in BUILD.H.

            Now for the bad news:

               * Floor sprite textures have similar restrictions as normal
                  ceilings and floors - both dimensions must be a power of 2.
                  This will most likely not change.

            And some known problems which I will have to fix:
               * Transluscence doesn't work yet
               * Sorting with other sprites
               * View clipping with walls
               * Doesn't work too well in hi-res mode

            See NUKELAND.MAP for some examples of floor sprites.  I have both
               a bridge and a balcony on the level.  Please find them!
               Remember that floor sprites don't work in mode x or hi-res
               mode YET!
──────────────────────────────────────────────────────────────────────────────
11/30/94  - Made WALL SPRITE and FLOOR SPRITE correctly do movement clipping.
──────────────────────────────────────────────────────────────────────────────
12/1/94   - Made new map called BRIDGES.MAP.  It is excellent!  It has 3
               bridges all crossing each other.  Enjoy!

          - For Editart, I added re-centering after delete and / to reset
               centering while in centering mode.

          - Debugged more of the floor sprite stuff.
──────────────────────────────────────────────────────────────────────────────
12/2/94   - Made transluscence with floor sprites work.  Check out my fake
               radial shading under the lights of SUBWAY.MAP.

          - Optimized floor sprites in assembler.  Transluscent floor sprites
               are a bit slower than normal floor sprites.
──────────────────────────────────────────────────────────────────────────────
12/6/94   - Made a special 1-way mode for WALL and FLOOR sprites 
               (not FACE sprites).  In BUILD 3D mode, you can press '1' on
               a wall or floor sprite to make it only draw if you are on
               1 side of it.  This method of back-face culling will not only
               make non-masking objects with thickness draw twice as fast,
               but will also make fewer drawing bugs due to sprite drawing
               order.  Try out my BRIDGES.MAP.  There's a new section at the
               top and it's not another "ugly Ken's face n' slime" room.
──────────────────────────────────────────────────────────────────────────────
12/8/94   - Editart and Build now capture with capt#### rather than captur##
               allowing 10000 captured pictures.
──────────────────────────────────────────────────────────────────────────────
12/23/94  - Increased Maximum number of walls to 8192.
──────────────────────────────────────────────────────────────────────────────
12/28/94  - Made BUILD stub keys F5-F8 work in 3D mode also.  See top
               of BSTUB.C for full documentation of some new useful
               variables that can be used inside the stub:

                  extern long qsetmode;
                  extern short searchsector, searchwall, searchstat;

               In 3D mode, F5 and F6 do the exact same thing and F7 and F8
                  do the exact same thing (you don't need the extra key to
                  distinguish between sectors, walls and sprites, since the
                  mouse cursor can only be on 1 of the 3 objects in 3D mode.

               Note:  Since F5-F8 are called in 3D mode, you must be sure
                  NOT to use any 2D routines during those calls!  This means
                  you will have to put the 3D/2D case check in all six
                  subroutines:
                     ExtShowSectorData, ExtShowWallData, ExtShowSpriteData,
                     ExtEditSectorData, ExtEditWallData, ExtEditSpriteData

          - KEN'S PROPOSAL (NOT YET DONE)  I am thinking of making a new
               (and maybe final) map format.  But before I do it, I am going
               to ask all of you for any suggestions on new fields to add to
               my 3 big structures (sector, wall, sprite):

               Here are a few things already on my list to add:

                * char sprite[].clipdist - THIS IS FOR SURE
                      This will be a sprite's clipping distance.  My default
                   walldist is 128.  With this field, you will finally be
                   able to make a unique fatness for each sprite with no
                   clipping bugs.  I will probably shift this field up 2-4
                   bits to give it a range of more than just 0-255.

                * char wall[].pal, sprite[].pal - PRETTY SURE
                      It should have been this way the whole time.  Currently
                   the wall's palookup number is sector[].floorpal.  While
                   it may save some memory, It has too many limitations.
                   I can make my map converter automatically convert all
                   walls of a sector to equal the sector[].floorpal so
                   don't worry about that type of conversion.  If I do
                   this, I can get rid of the spritepal[] hack.

                * I have decided that the sector[].extra, wall[].extra, and
                    sprite[].extra will remain in the structures as little
                    gifts for your use only.  That's right!  ALL YOURS!
                    ENJOY!!!

                * char sprite[].xoffset, sprite[].yoffset - NOT SURE YET
                      Some have asked for monster animations using the same 
                   frame at 2 different times of an animation sequence having 
                   different centers.
                      These will be signed chars.  I'm not sure whether to 
                   make these offsets as offsets to the centering information 
                   from Editart or just make them the absolute offsets where
                   Editart's data is the default offset.  I wonder if there's
                   a better way to do this without having to waste 8K.
                
                * Do I have your permission to remove nextsector2 and
                     nextwall2?  Anybody using them?  If so, can you use the
                     extra variable instead?  This will save 16K since both
                     are shorts.  (MAXWALLS*short + MAXWALLS*short = 16384)
                  They were originally intended for 2 stories
                     but it would be totally ridiculous for me even to think
                     about programming that now.  Besides, you can use wall
                     and floor sprites to fake 2 story areas.  KEN PROMISE:
                     I will fix those darn view-clipping bugs eventually!


                Please send any comments/suggestions to my internet address
                   (kjs@lems.brown.edu)  I will consider each suggestion
                   carefully, because everything you suggest now won't have
                   to be an ugly hack like spritepal[] later.
──────────────────────────────────────────────────────────────────────────────
1/1/95    - █████ ██████ ██▄  ██ ██   ██ ██    ██ ██████ ██████ ██████ ██
            ██    ██  ██ ███▄ ██ ██   ██ ███▄▄███ ██  ██ ██  ██ ██     ██
            ██    ██  ██ ███████ ██▄ ▄██ ██▀██▀██ ██████ ██████ ██████ ██
            ██    ██  ██ ██ ▀███  █████  ██    ██ ██  ██ ██     ██  ██
            █████ ██████ ██  ▀██   ▀█▀   ██    ██ ██  ██ ██     ██████ ██

            CONVMAP6!  Here is what I changed in the structures:

               * Added wall[].pal, sprite[].pal
               * Added sprite[].clipdist
               * Expanded sprite[].cstat to a short
               * Added sprite[].xoffset, sprite[].yoffset
               * Removed wall[].nextsector2, wall[].nextwall2
               * Renamed wall[].nextsector1 to just wall[].nextsector
               * Renamed wall[].nextwall1 to just wall[].nextwall
               * Scrapped numextras and extratype structure - Don't confuse
                    this with sector[].extra, wall[].extra, sprite[].extra
                    which ARE in map version 6.

            Probably the only change above that will affect programmers is
               getting rid of the '1' in wall[].nextsector1&wall[].nextwall1.
               All the following changes were possible because of the new
               map format.

          - Got rid of the spritepal array in BUILD.H.  With my grea
               new map version 6, you can simply modify sprite[].pal!

          - Made all .pal fields editable in 3D EDIT MODE.  Press ALT-P
               and simply edit the number as you would in 2D mode.

          - Made sprite[].xoffset and sprite[].yoffset work as
               offsets to the offsets that are already in EDITART.
               Simple addition.  They should work for all 3 types
               of sprites.

          - Made BUILD Tab&Enter also copy tags&extra if copying similar
               structures.  Also fixed some other attributes when
               copying between structure types.

          - Made sprites highlighted with Rt. Shift duplicate and stamp
               when the insert key is pressed in 2D EDIT MODE.

          - Made sprite[].clipdist work as the FACE SPRITE'S clipping
               fatness.  NOTE:  Sprite[].clipdist is shifted up 2 to
               allow a range from 0-1020, so if the sprite[].clipdist
               is set to 32, then the clipping radius is actually 128.

          - Removed the walldist parameter from movesprite.  Movesprite
               now just uses the sprite[spritenum].clipdist field of
               whatever sprite is passed (shifted up 2).

            movesprite(short spritenum, long xchange, long ychange,
                       long zchange, long ceildist, long flordist,
                       char cliptype, long numtics)

               If you use clipmove or getzrange, you should check that you
                  are passing the correct walldist parameters.  I'm saying
                  you may want to change some of the 128s to
                  (sprite[spritenum].clipdist<<2).
──────────────────────────────────────────────────────────────────────────────
1/3/95    - Made startumost[], startdmost[] into shorts.

          - Optimized and fixed gamma correction in Stereo Red-Blue mode.

          - Made weapons or anything using overwritesprite in Stereo Red-Blue
               mode be at screen depth.
──────────────────────────────────────────────────────────────────────────────
1/4/95    - Not that anybody would care (except for some crazed mega-hackers)
               but I am just mentioning the fact that I did this:

               animateoffs(short tilenum, short fakevar);
                  where fakevar is sectnum+0
                                or wallnum+16384
                                or spritenum+32768
                                or 49152 (just ignore-it's for rotatesprite)

               Also:  I changed one: "mov al, 0" instruction into an
                                     "xor al, al", a net savings of
                                     ONE LOUSY BYTE!  Let's not get TOO
                                     excited! (By the way, just kidding)
──────────────────────────────────────────────────────────────────────────────
1/5/95    - Fixed the sprite centering with x-flipped FACE SPRITES.

          - Found a bug with my swinging door clipping code.  There are 2
               types of swinging doors, forwards (opens CCW) and
               backwards (opens CW).  The bug was that you could sneak
               through a backwards door from the back side.  Well, I fixed
               it, so now I challenge you to sneak through my swinging doors
               now!  If you are using my swinging door code, please make
               these changes which you should find in my new GAME.C:

             1.  Changed declaration at beginning of GAME.C:
                 static short swingwall[32][5];

             2.  Added new line in prepareboard:
                 swingwall[swingcnt][4] = lastwall(swingwall[swingcnt][3]);

             3.  Modified some stuff in tagcode:
                      //swingangopendir is -1 if forwards, 1 is backwards
                   l = (swingangopendir[i] > 0);
                   for(k=l+3;k>=l;k--)
                      if...

          - Here are some more functions that have been in the engine for
               a while, but I forgot to document...
                  precache, loadtile, lastwall, rotatepoint
               They are now documented at the top of this file.
──────────────────────────────────────────────────────────────────────────────
1/6/95    - Optimized loading/saving of maps by using fewer read/write calls.
──────────────────────────────────────────────────────────────────────────────
1/14/95   - Fixed evil crashing bug that I accidently introduced in the
               1/3/95 version of Build.  This crashing bug happened mostly
               in tall rooms with lots of FACE SPRITES.  It used to crash
               very infrequently when you were standing almost exactly, but
               NOT on the same x & y coordinates of a FACE SPRITE where the
               z-distance was high.
──────────────────────────────────────────────────────────────────────────────
1/15/95   - Fixed up network code so now you can miss 4 packets in row safely
               over the network rather than just 2.
──────────────────────────────────────────────────────────────────────────────
1/16/95   - Added strafe left / strafe right keys to my SETUP.DAT file and
               char keys[19] array.  If you actually use my keys array for
               custom keys, here's what to do:  I inserted the new strafing
               keys at keys[12] & keys[13], so just add 2 to any keys with
               an index >= 12.

          - Made the sprites in 2D EDIT MODE of Build highlight properly
               again.

          - Added another parameter to permanentwritesprite, palookup number:

              permanentwritesprite(long thex, long they, short tilenum,
                                   signed char shade, long cx1, long cy1,
                                   long cx2, long cy2, char dapalnum);
──────────────────────────────────────────────────────────────────────────────
1/17/95   - You can now select a bunch of sprites in 2D EDIT MODE with
               Rt. shift, then go to 3D mode and change the z's of all the
               highlighted sprites.
──────────────────────────────────────────────────────────────────────────────
1/20/95   - Made Tab&Enter in 3D EDIT MODE copy .pal also whenever .shade is
               normally copied.  Shift+Enter will now copy just
               .pal and .shade.
──────────────────────────────────────────────────────────────────────────────
1/21/95   - Made Splitsector work with overlapping better.

          - In Editart 'U' mode, it now goes automatically to the PCX and
               coordinates of a tile if it is already in capfil.txt.

          - In Editart, made capt????.PCX not get overwritten if they already
               exist.

          - Fixed Build tab on masked walls.

          - Made zmode and kensplayerheight variables public in BUILD/BSTUB
               so you can now compile BUILD to start out with your own
               preferred settings.  Kensplayerheight defaults to 32 and
               zmode defaults to 0.  You can over-ride these settings in
               the new ExtInit function.

          - Made Editart tiles much easier to center by showing all tiles
               in the center of the screen instead of at the top-left corner.
               (Editart will not allow you to center tiles larger than
                320*200 right now)

          - Made 'O' (optimize) key in Editart automatically preserve
               the centering information.

          - ATTENTION BSTUB PROGRAMMERS!  Added ExtInit, ExtUnInit, and
               ExtCheckKeys to BSTUB.C.  Please just copy mine into your
               current BSTUB.  ExtInit and ExtUnInit are called only once.
               ExtInit is called before loadpics() and after initengine().
               ExtCheckKeys() is called just before nextpage in both 2D
               and 3D modes.  In 3D mode, you must call editinput inside
               ExtCheckKeys just like my example in BSTUB.
──────────────────────────────────────────────────────────────────────────────
1/24/95   - Fixed vertical line chained mode bug with permanentwritesprites.
──────────────────────────────────────────────────────────────────────────────
1/31/95   - Fixed parallaxing sky tiling bug.
──────────────────────────────────────────────────────────────────────────────
2/1/95    - Fixed network crashing bug when call interrupt 0x1c.  You call
               this interrupt when you chain to the old timer handler.  Now

          - Rewrote multiplayer code in game.c a bit.  Moved both sync()
               and getpackets() into the main program.  The master code and
               single player game code are now the same.  Slaves need to
               drawscreen and send packets only when a packet is received
               from the master.  Got rid of fake sync[MAXPLAYERS] slot by
               using a local sync buffers.  Rewrote checkmasterslaveswitch().
               If you don't get what I did, it doesn't matter because you
               already have working network code!

          - Added new key in EDITART 'V' mode.  Press ALT-R to generate
               a tile frequency report.  It will scan all MAP files in the
               same directory as the ART files.  The frequency count will
               show up as text in the top-left corner of the boxes in
               'V' mode.  Press ALT-R again to turn off text.

          - Perfected NCOPY!  Copies about 150K / second which is 10 times
               faster than a 115200bps serial cable.  Since NCOPY already
               links with multi.obj, it should not be too difficult to make
               joining work in the middle of a network game.  Please wait
               until I make some sample code for you!
                  Receiver types:  NCOPY             (Ex: "NCOPY")
                  Sender types:    NCOPY [filespec]  (Ex: "NCOPY *.ART")

               Ken's formula:
                  NCOPY + Loading&Saving GAMES = joining in middle
                                                 of network game!

          - Made a DOOM to BUILD converter.  Right now it only converts
               ceilings, floors, and walls now, and some of the wall
               textures are screwed up.  Just because I converted some
               lousy stinkin' maps DOES NOT MEAN I AM GOING TO PROGRAM DOOM!
               Unfortunately, the converter is programmed in QuickBasic right
               now, it won't compile, and I didn't feel like putting up the
               7 MEG converted ART file up.  When I convert it to C, I'll
               upload it for all!
──────────────────────────────────────────────────────────────────────────────
2/7/95    - Fixed overwritesprite bugs with translucence in chained mode
               and above top of screen.

          - Made bit 3 of overwritesprite x-flip the picture if set.

          - Optimized various parts of engine.
──────────────────────────────────────────────────────────────────────────────
2/8/95    - Fixed shading of all sprite types so they match perfectly with
               their surroundings.  Floor sprites now shade in the exact same
               way as ceilings and floors.  (Before, the whole floor sprite
               had the same shade throughout)
──────────────────────────────────────────────────────────────────────────────
2/9/95    - Started on loading and saving game code so I could give some
               sample code for joining network games, but none of it works
               yet, so just ignore it for now!

          - Added frame rate in BSTUB.C.  It averages the last 16 frames.
──────────────────────────────────────────────────────────────────────────────
2/16/95   - Added another bit to sprite[].cstat for y-centering control.  If
               bit 7 is set then the sprite's center will be the actual
               center rather then at the default position which is at the
               bottom of the sprite.  If you use this centering bit,
               you finally get "WYSIWYG" centering contol in EDITART.
──────────────────────────────────────────────────────────────────────────────
2/24/95   - Made floor sprites now support any x size by a power of 2 y size.
               (That's better than before!)  These are the same restrictions
               on walls.
──────────────────────────────────────────────────────────────────────────────
2/25/95   - Got loading / saving code to work with my game.  Press Ctrl-L to
              load game and Ctrl-S to save game.  Saved games are called
              SAVE0000.GAME and are about 300K.  Don't worry about the large
              sizes of the saved game files since they can be easily
              compressed to less than 50K.
──────────────────────────────────────────────────────────────────────────────
2/26/95   - I Finally made my multiplayer code run with fast frame rates AND
               pefect controls on ALL computers of a multiplayer game.  I am
               now sending keystrokes of all computers at a constant rate of
               40 times per second.  All computers interpolate between frames
               to get screen frame rates of higher or lower than 40 fps
               (even in single player mode).

            Here are the exact steps you will need to follow if you want to
               update your code:

               1.  WHAT'S THE FAKETIMERHANDLER()?

                      To send packets exactly 40 times a second, it would
                   sure be nice to send them right from the timer interrupt
                   handler.  Too bad network packets just won't get sent
                   from the interrupt handler.  (It may work from the
                   interrupt handler in Serial/Modem mode)  So the solution
                   is to make a "fake" timer handler that must be called
                   at least 40 times a second even on the slowest computer.
                   Throughout my engine, I call faketimerhandler().  If you
                   have any slow parts in your game code, you may want to
                   called faketimerhandler() also.
                      Besides the very first few lines, the rest of the code
                   was taken directly from my old sync and getpackets
                   functions.

               2. BYE BYE SYNCTICS!

                      Now that all computers are calling movethings a
                   constant number of times a second, you don't need any
                   synctics variables anymore.  You can convert all the
                   synctics variables to a define such as:
                      "#define TICSPERFRAME 3"
                   Doing this will guarantee that a game runs the same on
                   all speed computers.

               3. FRAME INTERPOLATION (optional):

                   static long ototalclock = 0, gotlastpacketclock = 0;
                   static long oposx[MAXPLAYERS], cposx[MAXPLAYERS];
                   static long oposy[MAXPLAYERS], cposy[MAXPLAYERS];
                   static long oposz[MAXPLAYERS], cposz[MAXPLAYERS];
                   static long ohoriz[MAXPLAYERS], choriz[MAXPLAYERS];
                   static long ozoom[MAXPLAYERS], czoom[MAXPLAYERS];
                   static short oang[MAXPLAYERS], cang[MAXPLAYERS];

                   Add to prepareboard:
                      for(i=connecthead;i>=0;i=connectpoint2[i])
                      {
                         oposx[i] = posx[i];
                         oposy[i] = (etc.)
                      }
                      ototalclock = 0;
                      gotlastpacketclock = 0;

                   Even though you may be getting more than 40fps, you will
                only be seeing 40fps unless you interpolate between frames.
                The oposx[], etc. variables back up the last posx[], etc.
                variables so you can interpolate your actually drawing
                position as some fraction between the two.  This fraction
                is smoothratio.
                   See beginning of drawscreen code.  Here's where I actually
                calculate the interpolated position to draw the screen.

                  for(i=connecthead;i>=0;i=connectpoint2[i])
                  {
                     cposx[i] = oposx[i]+mulscale(posx[i]-oposx[i],smoothratio,16);
                     cposy[i] = oposy[i]+mulscale(posy[i]-oposy[i],smoothratio,16);
                     cposz[i] = oposz[i]+mulscale(posz[i]-oposz[i],smoothratio,16);
                     choriz[i] = ohoriz[i]+mulscale(horiz[i]-ohoriz[i],smoothratio,16);
                     czoom[i] = ozoom[i]+mulscale(zoom[i]-ozoom[i],smoothratio,16);
                     cang[i] = oang[i]+mulscale(((ang[i]+1024-oang[i])&2047)-1024,smoothratio,16);
                  }

                  Draw the screen using cposx[], etc. instead of posx[], etc.

                  #pragma aux mulscale =\
                      "imul ebx",\
                      "shrd eax, edx, cl",\
                      parm [eax][ebx][ecx]\
                      modify [edx]\

                      It reads: eax = (eax*ebx)>>cl.  Unlike C, this will
                  not overflow even if eax*ebx > 2^31, making full use of
                  the 64-bit result of the imul instruction.

               4. MOVETHINGS FIFO:

                   static long movefifoplc, movefifoend;
                   static signed char baksyncvel[64][MAXPLAYERS];
                   static signed char baksyncsvel[64][MAXPLAYERS];
                   static signed char baksyncangvel[64][MAXPLAYERS];
                   static short baksyncbits[64][MAXPLAYERS];

                   Add to prepareboard: movefifoplc = 0; movefifoend = 0;

                   It is bad to call movethings inside faketimerhandler
                because you don't want things to move while you're drawing
                the screen.  To solve this, I made movethings just save
                away the parameters it was called with using a circular
                buffer, and when I'm actually ready to DO the movement code,
                I call domovethings.
                   Rename movethings to domovethings and see my new
                movethings.  This code is all for the fifo.

                  Put this line in movethings:
                     gotlastpacketclock = totalclock;

                  At the top of domovethings, copy my code for loading off
                     of the fifo.  Also set oposx[] = posx[], etc. here.

               5. You may want to add a global variable that controls whether
                    you are in continuous packet sending mode or not.  Only
                    in the main loop should ready2send be != 0.
                   static long ready2send = 0;

               6. The new main loop can be as short as this, with no case
                     checking for masters and slaves.

                  ready2send = 1;
                  while (keystatus[1] == 0)       //Main loop starts here
                  {
                        //Actaully move everything here.
                     while (movefifoplc != movefifoend) domovethings();

                        //Second parameter is for frame interpolation,
                        //A fraction that ranges from 0-65536.
                     drawscreen(screenpeek,(totalclock-gotlastpacketclock)*(65536/TICSPERFRAME));
                  }
                  ready2send = 0;
──────────────────────────────────────────────────────────────────────────────
3/6/95    - New key in BUILD.  Now when you use relative alignment mode on
               ceiling and floor textures, you can press Alt-F on the ceiling
               or floor to choose a new wall to align to.  It actually
               rotates the walls of a sector by 1.

          - Fixed screen capture PCX saving bug in both EDITART and BUILD.

          - Added a parameter to screencapture, a filename.
               screencapture(char *filename)
               Ex: screencapture("captxxxx.pcx");
               Please specify the full filename.  Screencapture will modify
               the 4 x's of the string to be a number starting at 0000.
──────────────────────────────────────────────────────────────────────────────
3/8/95    - Made my rotatesprite function use Editart centering information.
               The center is the pivot point of rotation.

          - Added y-flipping to overwritesprite.  See above documentation.

          - Added 2 new parameters to rotatesprite, shade and pal

             rotatesprite (long sx, long sy, long z, short a,
                     short picnum, signed char shade, char pal);

──────────────────────────────────────────────────────────────────────────────
3/10/95   - Fixed sprite showing through closed sectors bug.

          - Made it possible to draw the overhead map in 3D mode by adding
              a line drawing function in 3D mode called drawline256.  It
              draws a line clipped to the viewing rectangle last set in
              setview().  Here are the parameters:

              drawline256(long x1, long y1, long x2, long y2, char col);

              Note:  The coordinates are all shifted up 12.
                 Example: drawline256(0L,0L,319L<<12,199L<<12,31);
──────────────────────────────────────────────────────────────────────────────
3/11/95   - Made drawline256 draw in a cleaner way, making use of the full
               12 bits of precision.
──────────────────────────────────────────────────────────────────────────────
3/14/95   - Optimized / cleaned up parts of hitscan, neartag, cansee.
               Gee, I hope they all still work!
──────────────────────────────────────────────────────────────────────────────
3/18/95   - I'm back in RI!

          - Fixed recently added movesprite z parameter bug that may have
               done strange things with monsters, such as stuck in sectors
               I thought the following expression:  !(cstat&128)
               would be true if bit 7 was a 0, but I WAS WRONG!
               Get this straight:
                  ! - logical NOT, returns 0 if != 0 else 1 (!2457=0, !0=1)
                  ~ - bitwise NOT, xor's it with 0xffffffff (~0x5f=0xa0)
──────────────────────────────────────────────────────────────────────────────
3/21/95   - Gave access to some more variables in BSTUB.

          - Made clipmove return a valid sector even if you're not between
               its ceiling and floor.  If you use overlapping sectors,
               clipmove will find the sector closest to your z.
──────────────────────────────────────────────────────────────────────────────
3/28/95   - Optimized transluscence for masked walls and wall sprites.

          - Today is the day I declare my serial/modem error correction code
               perfect!  Sure, I may have bragged over and over again about
               how perfect my correction method is each time, but this time
               I mean it.  This is ship-it quality error correction.  The old
               method had a few dark, evil, and ugly bugs that sometimes
               totally screwed up bigtime.
──────────────────────────────────────────────────────────────────────────────
4/5/95    - Got Master/Slave switching to stay in sync again with new
               multiplayer code.  Right after the master sends a packet
               where a switch is made, I disable the master from sending
               any more packets by setting ready2send to 0.  Ready2send
               will be set back to 1 only after the actual switch in
               checkmasterslaveswitch.  Here's what I added to movethings:

                    //Do this for Master/Slave switching
                 for(i=connectpoint2[connecthead];i>=0;i=connectpoint2[i])
                    if (syncbits[i]&512) ready2send = 0;
──────────────────────────────────────────────────────────────────────────────
4/10/95   - Optimized continuous setview calls between 2 different window
            sizes.

          - ATTENTION PROGRAMMERS:  Moved movesprite code into game.c.  It
              no longer exists in the engine.  It is a "cover-up" function
              and it should be yours.

          - Added a circular keyboard buffer to my keyboard handler.  It is
              fully compatible with the keystatus arrays.  Use this buffer
              for typing in messages or cheat codes.  If you do not use this
              buffer (or you wrote your own interrupt handler), you will miss
              keys and it is very annoying.

              I added these variables to build.h:

              #define KEYFIFOSIZ 64
              EXTERN volatile char keyfifo[KEYFIFOSIZ],
                                   keyfifoplc, keyfifoend;

              Every time a key is pressed and released, I add 2 bytes to the
              circular buffer.  The first byte is the scan code.  The second
              byte is a 1 for key pressed or 0 for key released.  You can
              ignore the key releases if you wish.  You must read 2 bytes at
              a time from this buffer.
                 (scancode,keystat),(scancode,keystat),...

              Here's how you read the keyboard fifo:
                  while (keyfifoplc != keyfifoend) //More characters to read
                  {
                     ch = keyfifo[keyfifoplc];
                     keystate = keyfifo[(keyfifoplc+1)&(KEYFIFOSIZ-1)];
                        //Increment buffer pointer
                     keyfifoplc = ((keyfifoplc+2)&(KEYFIFOSIZ-1));

                     printf("Scancode: %d, status: %d\n",ch,keystate);
                  }

              The interrupt handler does the same as above but writes and
                 increments using keyfifoend as the index.
              You can easily clear the buffer this way:
                 keyfifoplc = keyfifoend

          - Made clipmove/getzrange not clip on the back side of a 1-sided
               wall/floor sprite.  It makes the clipping a little faster
               and seems to fix a few minor clipping bugs.  Don't get too
               excited.

          - Fixed getzrange's ceilz/florz mismatch with floor sprites against
               normal ceilings and floors.

          - Made Build default to y-centered centering mode when sprites
               are inserted (bit 7 of sprite[].cstat)

          - Fixed clipmove bugs with y-centered centering mode.

          - Made it so you don't get stuck sliding along multiple properly
               aligned wall/floor sprites.

          - Please update to my new tables.dat.
──────────────────────────────────────────────────────────────────────────────
4/11/95   - Optimized parts of clipmove / getzrange.  Is it faster?  Did I
               make more bugs?

          - Made the movesprite code in GAME.C now use getzrange.  This
               allows sprites to walk on floor sprites and bombs roll across
               bridges.  I will include the original movesprite code that
               came from the engine in case you want to start with things
               the way they used to be.

          - Made ornamentation in BUILD 3D mode much easier.  Now you can
                press 'S' to insert a sprite on walls (not just ceilings
                and floors).  When you insert a sprite on a wall, it will
                automatically be set to a wall sprite at the wall's angle.
                Also it will be 1-sided with blocking off (The optimal
                options for a decorative sprite on a wall).

          - Added ALT-D to BUILD 3D mode.  It lets you type in clipdist
                for sprites.  It works in the same way as ALT-P for palookup
                changing.

──────────────────────────────────────────────────────────────────────────────
4/15/95   - Fixed return value bug in setanimation and cleaned up these 3
            functions:  doanimations, getanimationgoal, setanimation.
            Since they are now in game.c, you will need to copy them to
            update.
──────────────────────────────────────────────────────────────────────────────
4/16/95   - Fixed hitscan not passing through loops bug.

          - Fixed hitscan so it hits wall and floor sprites in the right
            places.

          - ATTENTION PROGRAMMERS!!!  Made bit 8 of sprite[].cstat the
            hitscan clipping bit.  It works like the hitscan bit for walls.
            Note that both the wall and sprite cstat variables now have
            2 separate bits for clipping, where:
                1 is for use with the clipmove/getzrange function and
                1 is for use with the hitscan function only.
            Before, hitscan used to use 1 bit for all sprite clipping.
            In your maps, the sprite hitscan bits are all zeros.  That's bad!
            To make things work like they used to, the sprite hitscan bit
            must be set equal to the old sprite blocking bit.  So I was nice
            enough to make a program, FIXSPRBT.EXE, which will do just that.

            PLEASE RUN FIXSPRBT.EXE ON ALL YOUR MAPS.  You don't HAVE to
            run it on all your maps since the map format hasn't changed, but
            it will save you annoying attribute setting time if you do.

          - Did a few things to make these blocking bits easier to edit:
            Since H and ALT-H were already used, I made CTRL-H toggle the
            hitscan bit for both walls and sprites in 2D mode.  B toggles
            the blocking bit.  B also now sets the hitscan bit to its
            default value when you press it.  For sprites, the hitscan bit
            default is equal to the clipmove bit.  For walls, the hitscan
            bit default is always 0.

          - Added new map mode!  Check it out in my game.  I now have 3 map
            modes.  Not that this really matters to you game programmers, but
            this is how my game works:
                  dimensionmode[snum] == 1   3D MODE + junky line map
                  dimensionmode[snum] == 2   SUPER MAP!
                  dimensionmode[snum] == 3   3D MODE

               I added show2dsector to BUILD.H which controls which works
               like the other show2d... bit arrays.  It tells which sectors
               to show.
                  EXTERN char show2dsector[MAXSECTORS>>3];

               I rewrote parts of drawoverheadmap to accommodate this new
               mode - so it would be nice if you updated to my new code.

               Added a new function that clears full screen to a specified
               color:
                  clearview(0L);

               Oh and did I forget to mention the big function!  Parameters
               are the exact same my drawoverheadmap function:
                  drawmapview(cposx,cposy,czoom,cang);

               Note:  The new map mode right now is slowed down bigtime by
                  the face sprites using the rotatesprite function.  The
                  rotatesprite right now is using a worse than awful
                  algorithm.  When I optimize rotatesprite, the frame rate
                  of the new map mode will fly!
──────────────────────────────────────────────────────────────────────────────
4/18/95   - Added default sprite cstat variable when you insert new sprites.
               Set it to 0 if you hate the new centering mode in ExtInit
                    or 128 if you like the new centering mode.
               Add this line to your Bstub if you wish:
                  extern short defaultspritecstat;

          - If a wall & sprite are same distance away using hitscan, hitscan
               now chooses the sprite.

          - Optimized rotatesprite.

          - ATTENTION PROGRAMMERS:  Added new paramater at end of
              rotatesprite:  A char where the first bit tells it to use
              transluscence mode or not.

              rotatesprite(long sx, long sy, long z, short a, short picnum,
                            signed char dashade, char dapalnum, char dastat)

                 if ((dastat&1) == 0) - no transluscence
                 if ((dastat&1) != 0) - transluscence

          - Cleaned up drawmapview further by making ALL floor sprite draw
               with the texture in the right place and made the polygon
               filling algorithm use higher screen coordinate precision.
──────────────────────────────────────────────────────────────────────────────
4/19/95   - Added parameter to makepalookup:

            makepalookup(long palnum, char *remapbuf,
                         signed char r, signed char g, signed char b,
                         char dastat)

            if ((dastat&1) == 0) then makepalookup will allocate & deallocate
                the memory block for use but will not waste the time creating
                a palookup table (assuming you will create one yourself)
            if ((dastat&1) != 0) then makepalookup will allocate & deallocate
                the memory block AND create a palookup table using the rgb
                values you pass.

          - Made my ceiling&floor update the self-modified palookup pointers
               when palookup[sector[].?pal] changes, not just when
               sector[].?pal] changes.  Watching for changing pointers rather
               than changing indeces should solve the problem with screwy
               palookup selection for ceilings&floors.  Ignore what I said
               before.  You should now be able to change palookup pointers
               freely.

          - Optimized relative alignment.  It should be the same speed as
               all other ceilings & floors now.

          - Warning:  Since I added the new bit in sprite[].cstat, there are
               now 9 bits in use.  Make sure to treat it as a short.  In my
               code, I had some bugs where I did this:
               sprite[].cstat &= (255-4);  BAD!  Cstat's a short!  Please
               check your code and make sure you clear the bits this way:
               sprite[].cstat &= ~4;
──────────────────────────────────────────────────────────────────────────────
4/21/95   - Fixed bug with flipping textures on ceilings & floors.  In
               BUIL0419.ZIP I had a bug whenever a ceiling or floor texture
               had bit 4 set.  It drew the texture backwards.  Well, I
               fixed it.  Hope you didn't "Build" on this bug!
──────────────────────────────────────────────────────────────────────────────
4/22/95   - Fixed really really stupid hitscan returning sector < 0 bug.  If
               you were calling hitscan from the top-left triangular region
               of the board it stupidly returned a -1 for the sector.  Well,
               I fixed it.

          - ATTENTION EVERYBODY!!!  Moved setup.dat loading from the engine
               into GAME.C and BSTUB.C.  If you copy this code from game.c
               into your game / bstub, they will work like it used to:

            ----- NEW GLOBAL VARIABLES: -----

            #define NUMOPTIONS 8
            #define NUMKEYS 19
            static long chainxres[4] = {256,320,360,400};
            static long chainyres[11] = {200,240,256,270,300,350,
                                         360,400,480,512,540};
            static long vesares[7][2] = {320,200,640,400,640,480,
                                         800,600,1024,768,
                                         1280,1024,1600,1200};
            static char option[NUMOPTIONS] = {0,0,0,0,0,0,1,0};
            static char keys[NUMKEYS] =
            {
               0xc8,0xd0,0xcb,0xcd,0x2a,0x9d,0x1d,0x39,
               0x1e,0x2c,0xd1,0xc9,0x47,0x49,
               0x9c,0x1c,0xd,0xc,0xf,
            };

            ----- Put this where you call initengine -----

            long fil;

            if ((fil = open("setup.dat",O_BINARY|O_RDWR,S_IREAD)) != -1)
            {
               read(fil,&option[0],NUMOPTIONS);
               read(fil,&keys[0],NUMKEYS);
               close(fil);
            }
            if (option[3] != 0) moustat = initmouse();

            switch(option[0])
            {
               case 0: initengine(0,chainxres[option[6]&15],chainyres[option[6]>>4]); break;
               case 1: initengine(1,vesares[option[6]&15][0],vesares[option[6]&15][1]); break;
               case 2: initengine(2,320L,200L); break;
               case 3: initengine(3,320L,200L); break;
               case 4: initengine(4,320L,200L); break;
               case 5: initengine(5,320L,200L); break;
               case 6: initengine(6,320L,200L); break;
            }

            ----- That's it! -----

            Initengine now has 3 parameters:
               initengine(char davidoption, long daxdim, long daydim)
            See revised initengine description at the top of this file.

            Now that engine.c doesn't use setup.dat, you can use your
               own setup program.

            Don't forget to update BSTUB!

            NOTE!!!  While MY setup program allows you select many different
               video modes, no new modes are supported yet in BUILD!
               (It's on my list.)


          - Fixed keyboard repeating.
──────────────────────────────────────────────────────────────────────────────
4/26/95   - Hi-res now works in all modes!  The code I gave in the 4/22/95
               description is still perfectly valid, so please copy it if
               you haven't already looked at it.  Here are some important
               new variables I put in BUILD.H:

                  EXTERN char vidoption;
                  EXTERN long xdim, ydim, ylookup[MAXYDIM+1];

               Vidoption is simply the first parameter you pass to
               initengine.  Xdim and Ydim are the screen sizes you pass to
               initengine, such as 320*200 or 640*480.
               Ylookup is a lookup table that works like this:
                  If (vidoption == 0) ylookup[i] = ((i*xdim)>>2);
                  if (vidoption != 0) ylookup[i] = i*xdim;
               There is 1 exception:  If you are using a chained mode which
               can only fit only 1 viewing page, then the engine actually
               does a screen-buffer mode and blits to the chained screen
               so for this case, ylookup[i] = i*xdim.

          - Added bit to the dastat parameter of rotatesprite.
              if ((dastat&2) != 0) - align to screen size so the gun or
              whatever is always over the same relative spot of the screen.
              Works like bit 2 of the flags parameter in overwritesprite.

          - Added pixel writing and reading that will work in all BUILD
              graphics modes.  They work just like you think they should.

                 plotpixel(long x, long y, char col);
                 char getpixel(long x, long y);

              Please do not overuse these functions!  They are NOT intended
              for fast drawing!

          - Changed tables.dat so parallaxing skies at high resolutions
              work accurately.

          - I made bit 15 of sprite[].cstat the invisible bit.  If it is
            set, then the sprite won't even be considered for sorting and
            there is absolutely no speed loss due to its existence.  It is
            faster to use this bit then manually set thesprite[] to -1.

            Since drawrooms collects the list of sprites drawmasks is about
            to sort, make sure the bit is set before drawrooms.  If you use
            frame interpolation, you usually want to tell drawmasks somehow
            to not draw yourself.  Before you used to use the
            thesprite[] = -1 trick.  Now you do it like this:

               sprite[i].cstat |= 0x8000;        //Set invisible bit
               drawrooms(posx,posy,etc.);
               sprite[i].cstat &= ~0x8000;       //Restore invisible bit

          - Fixed distance overflow bug (the one that showed garbage
               textures if a wall was really far away).
──────────────────────────────────────────────────────────────────────────────
4/27/95   - Made an example board, MONWALK.MAP, where monsters walk along
               a bridge with no railing, onto a sector, and even up and down
               a stairway without falling off it.  If you shoot a brown so
               it's almost dead, it will turn transluscent.  If it's
               transluscent, it won't shoot bullets.  It is much easier to
               test the monsters' walking when they don't shoot!

          - Try my new and improved fake network player mode!  Each fake
               network player now gets their own window.  Use insert and
               delete to add or remove players.  Use Scroll lock to switch
               which window you control.  If you have a Pentium, I would
               recommend trying this out in 640*480 mode.

          - Noticed a bug with my movesprite code:
               For clipmove, I was getting the z-coordinate right, but for
               getzrange I forgot to subtract half the sprite's height if
               the sprite was using the real centered centering mode.

               These lines set daz to the actual center of sprite i no matter
                  what centering mode is used:

               daz = sprite[i].z;
               if ((sprite[i].cstat&128) == 0)
                  daz -= ((tilesizy[sprite[i].picnum]*sprite[i].yrepeat)<<1);
──────────────────────────────────────────────────────────────────────────────
4/28/95   - ATTENTION PROGRAMMERS!  When a tile is not in the cache:
              Before it used to be this:

                 if (waloff[tilenum] == -1) loadtile(tilenum);
                    or
                 if (waloff[tilenum] < 0) loadtile(tilenum);

              Now it's this:

                 if (waloff[tilenum] == 0) loadtile(tilenum);

              PLEASE MODIFY YOUR CODE IF YOU USE WALOFF!

              Windows can allocate memory with addresses so high, they
                 are negative.  That makes the (waloff[tilenum] < 0)
                 method totally stupid and attempt to reload from disk
                 the tile constantly!  I can think of 3 reasons why I
                 originally designed my system in this fashion:
                 mestupid, mestinx, and merottts.

          - Fixed visibility for different screen sizes.

          - ATTENTION PROGRAMMERS!  Now multiply by visibility rather than
               shift right by visiblity so you get a broader range of
               visibility.  If you want the visiblity to work like before,
               use this conversion table:

                Old visibility  New visibility
                         8   ->  16384
                         9   ->   8192
                        10   ->   4096
                        11   ->   2048
                        12   ->   1024
                        13   ->    512
                        14   ->    256
                        15   ->    128

          - Made Alt-F make the selected wall the first wall of a sector.
               This is useful for quick relative alignment adjustments.
               Alt-F for 2D and 3D modes may not work for sectors with loops
               inside of them.
──────────────────────────────────────────────────────────────────────────────
5/1/95   - Optimized horizontal line setup code from 9 multiplies to
              7 multiplies.

         - Fixed dark vertical lines in parallaxing skies bug I recently
              introduced.

         - Optimized horizontal line assembly code so it doesn't use the
              awful SHLD instruction any more.  This is a good speed
              improvement for Pentiums only.
──────────────────────────────────────────────────────────────────────────────
5/2/95   - Added some code to my GAME.C which detects when a computer gets
              out of sync in a multiplayer game.  It's not as easy as you
              may think with all the faketimerhandler and fifo crap.  If
              you want to put this code in your game, search all areas in
              my code with the keyword "syncval".

         - Fixed palookup crashing bug.
──────────────────────────────────────────────────────────────────────────────
5/6/95   - Moved windowx1, windowy1, windowx2, windowy2 variables into
              BUILD.H.  They are the exact parameters you passed to the
              last setview call.  Please DO NOT modify them directly.
              Use setview to modify them.
──────────────────────────────────────────────────────────────────────────────
5/10/95  - Fixed makepalookup bug when shading to non 0.

         - Fixed palookup pointer setting bug.

         - ATTENTION PROGRAMMERS!  I made a new cacheing system which allows
              any type of object to be allocated in my cache, such as
              artwork tiles and sounds or whatever else you may want to
              put on the cache.  This may or may not affect you.  If you
              haven't been hacking into my code, then you shouldn't have to
              change your code.  The cacheing routines have been moved into
              a separate module, cache1d.obj.  If you link engine.obj, then
              you must also link cache1d.obj.  Please update your makefiles.

              For anybody who may want to re-write my cacheing system, here's
                 how it now works:  (I have only 3 functions in cache1d.obj
                 right now, initcache, uninitcache, and allocache)

                 Allocate a nice BIG buffer, like from 1MB-4MB and
              call initcache(long cachestart, long cachesize) where

                   cachestart = (long)(pointer to start of BIG buffer)
                   cachesize = length of BIG buffer

                   Ex: initcache(FP_OFF(pic),cachesize);

                 Loadpics calls this function for you so you don't normally
                   need to call it.

              call allocache(long ptr, long siz) whenever you need to
                 allocate a temporary buffer, where

                 ptr = (long)(pointer to (4-byte pointer to thing))\
                 siz = number of bytes

                 Ex:  if (waloff[tilenume] == 0)
                         allocache((long)&waloff[tilenume],walsiz[tilenume]);

                 Allocache is totally tile independent.  To allocate a sound
                    on the cache, you can call allocache.

              There are 3 functions in the engine which help manage the
                 cache for you: loadpics - calls initcache
                                loadtile - calls allocache
                                allocatepermanenttile - special function
                                          that allocates permanent memory
                                          from the cache.

         - Fixed clipmove crashing bug when passing a sectnum < 0.  Whenever
              you moved in BUILD and you weren't in a valid sector, memory
              was getting trashed.
──────────────────────────────────────────────────────────────────────────────
5/12/95  - Added ExtPreCheckKeys(void) to BSTUB.C.  It is called before
              drawrooms / drawmasks in 3D mode, whereas ExtCheckKeys(void)
              is called after drawrooms / drawmasks (and before nextpage).

         - Added bit to flags of rotatesprite to allow x-flipping.  See
              updated documentation.
──────────────────────────────────────────────────────────────────────────────
5/17/95  - Added aspect ratio for those weird modes like 320*400, etc.
              You could call setaspect to properly adjust a mode that is
                 not exactly correct, or for special effect that stretch
                 the screen.

              In ENGINE.OBJ, I added a function, setaspect(long daaspect),
                 where you pass the Y/X aspect ratio scaled up 16 bits, so
                 65536 would be normal.  You don't need to call this if you
                 don't want to.  By default, in setview, I call setaspect
                 with these parameters:

                    setaspect(divscale16(ydim*320,xdim*200));
                       (also written as:)
                    setaspect(((ydim*320)<<16)/(xdim*200));

                 Note that in 320*200 mode the value passed would be 65536
                    which is a 1:1 aspect ratio.

              In BUILD.H, I added yxaspect and xyaspect.

                 When you call setaspect(daaspect),

                   yxaspect = daaspect;
                   xyaspect = (1<<32) / yxaspect;   //reciprocal
                   and other internal variables, so DON'T MODIFY YXASPECT
                      AND XYASPECT DIRECTLY!

              Since drawmapview is also affect by the aspect ratio, you
                 will need to make sure drawoverheadmap is affected so
                 the map modes match up.  Please look at and copy my updated
                 drawoverheadmap function into your GAME.C if you use it.
──────────────────────────────────────────────────────────────────────────────
5/18/95  - Revised caching system so it supports locking.
              Here are the new parameters to allocache:

              allocache(long *bufptr, long bufsiz, char *lockptr)
                 *bufptr = pointer to 4-byte pointer to buffer
                 bufsiz = number of bytes to allocate
                 *lockptr = pointer to 1-byte locking char.  1=locked, 0=not

              And uninitcache works a little differently too:
                 Call uninitcache(0) to remove all UNLOCKED items or
                 Call uninitcache(1) to remove ALL items.
                    After calling uninitcache, you do not need to call
                 initcache to use the cache again.
──────────────────────────────────────────────────────────────────────────────
5/21/95  - Made changespritesect and changespritestat return 0 instead of -1
              when changing to same value.
──────────────────────────────────────────────────────────────────────────────
5/25/95  - Added relative visibility for sectors.  In 3D EDIT MODE, use
             ALT+(keyp.)+/- to change an individual sector's visibility.
             Press Shift in addition for fine visibility changine.  By
             default you change it by 16.  Press CTRL+ALT+(keyp.)+/- to
             change the global visibility. The global visibility is not
             saved in the map.

         - Can now delete many sectors at a time.  Select sectors with the
             rt. Alt.  Then press Ctrl-delete on any highlighted sector to
             delete all the highlighted sectors.

         - Fixed build sometimes not saving when quitting from 2D mode bug.
──────────────────────────────────────────────────────────────────────────────
5/27/95  - Fixed bugs in network work that should make it miss packets
              much less often than before.

         - Fixed bug in ncopy so it shouldn't halt or crash anymore.

         - Added spritecstat[] the thesprite arrays.  Please use my
              thesprite arrays rather than modifying the sprite directly
              since hitscan and clipmove use bits 4 and 8 of sprite[].cstat.

         - ATTENTION PROGRAMMERS:  Added 1 parameter to neartag that will
              allow you to search only lotags or only hitags.  See updated
              documentation above.

           neartag (long xs, long ys, long zs, short sectnum, short ange,
              short *neartagsector, short *neartagwall, short *neartagsprite,
              long *neartaghitdist, long neartagrange, char tagsearch)

              If tagsearch = 1, neartag searches lotag only
              If tagsearch = 2, neartag searches hitag only
              If tagsearch = 3, neartag searches lotag&hitag

              Neartag used to always search both lotag&hitag.
──────────────────────────────────────────────────────────────────────────────
5/30/95  - Added ExtAnalyzeSprites(void) to BSTUB and a spriteshade array
              to BUILD.H.

         - Made circle drawing in 2D EDIT MODE work better at large sizes.
──────────────────────────────────────────────────────────────────────────────
6/5/95   - Added shareware / registered artwork version control to EDITART.
              In 'V' mode, when you press ALT-R to get a report of tile
              frequencies of tiles on all the maps in the current directory,
              you can toggle the shareware/registered bit with the space bar.
              This bit is saved in the top bit of the picanm bits so it is
              stored permanently in the art file.  Press ALT-D in this mode
              to automatically delete all registered tiles.  Please before
              you do the deleting phase, copy all artwork to a temporary
              directory!!!  It is safe, however, the toggle the new bit in
              the full version.
──────────────────────────────────────────────────────────────────────────────
6/7/95   - Made 3 main structures defined with typedef so struct prefix
              is no longer needed with sectortype, walltype, and spritetype.

         - Added new function to engine:

             short sectorofwall(short dawall);

             It returns the sector of a given wall.  It is well optimized
                including sector[sector[].nextwall].nextsector if a red wall
                and a binary search on the sector[].wallptr's if it is a
                white wall.  On average, sectorofwall should have to scan
                through only 10 different sector indeces to find the right
                sector (not 1024!).

         - ATTENTION PROGRAMMERS!  Made all the separate thesprite arrays
              into a single array of sprite structures call tsprite.  See
              BUILD.H!!!  This means:

                 spritepicnum[i] is now tsprite[i].picnum
                 spritex[i] is now tsprite[i].x
                 spritey[i] is now tsprite[i].y
                 spritez[i] is now tsprite[i].z
                 spriteshade[i] is now tsprite[i].shade
                 spritecstat[i] is now tsprite[i].cstat
                 spritepal[i] is now tsprite[i].pal
                 thesprite[i] is now tsprite[i].owner     <<<============

                 Everything above is straight forward, except for thesprite,
                    which has been renamed to owner.

                 All other tsprite parameters, such as .xrepeat, .yoffset,
                     etc.  can also be modified without changing the real
                     sprite structure so the game won't out of sync.

         - Made tsprite[].statnum be a priority variable for sorting sprites
              that are the exact same distance from you.  I think higher
              means more in front.

         - Made clipmove allow you to cross any red sector line when the z's
              of the next sector are farther apart.  This may solve clipping
              bugs such as when you're in water.  You may be able to remove
              some work-arounds you may have programmed previously.

         - NEW FILE GROUPING SYSTEM!!!
                 My system will first search for the stand-alone file in the
              directory.  If it doesn't find it, then it will search for it
              in the group file.  If it still doesn't find it then -1 city.
                 Use KGROUP.EXE to create a grouped file from a lot of other
              files.  Type KGROUP [grouped filename][filespec][filespec][...]
              For example this line will create the new grouped file,
              stuff.dat, including all the following files specified:
                 kgroup stuff.dat *.art *.map tables.dat palette.dat
              Feel free to make your own batch files.  If there is demand,
              I can make kgroup support appending, replacing, and extraction.

              Here is the file format of a grouped file:
         ┌─────────────────────────────────────────────────────────────────┐
         │   12 bytes - File grouping ID                                   │
         │    4 bytes - Number of files                                    │
         ├─────────────────────────────────────────────────────────────────┤
         │For each file: (16 bytes per file)                               │
         │   12 bytes - Filename with extension (13th byte would be a 0)   │
         │    4 bytes - Length of file                                     │
         ├─────────────────────────────────────────────────────────────────┤
         │ Pure, raw file data, ordered just like you think it would be    │
         └─────────────────────────────────────────────────────────────────┘
              There just couldn't be a simpler format.  This format is so
                 hacker happy that when you view it in a hex editor, all the
                 filenames line up perfectly since they're multiples of 16.
                 Anybody who can't figure this out, of course, not only rots,
                 but is probably one of those really stupid people who would
                 actually "pay" to get the full version of a game.

              The engine currently supports grouping for:
                  *.ART, *.MAP, TABLES.DAT and PALETTE.DAT.  If you want
              to group your own files, you will have to use my loading
              routines rather than the standard ones for those files.  My
              file routines are basically an extra layer around the standard
              lo-level functions.  Look at these 5 routines I currently
              support:

                         Welcome to the K-routines!
                   open -> kopen4load(char *filename)
                   read -> kread(long handle, void *buffer, long leng)
                  lseek -> klseek(long handle, long offset, long whence)
             filelength -> kfilelength(long handle)
                  close -> kclose(long handle)

              Note that you only pass the filename to my kopen4load function.

              Here are 2 other routines that you MUST use, whether you like
                 my file grouping system or not:

                 initgroupfile(char *groupfilename)
                    Call this with the name of your grouped file before
                 any possible file loading will the k-routines.  Note that
                 tables.dat uses the k-routines and it is in initengine().
                 Please don't give your grouped filename an extension that
                 starts with W, ends with D, and has a vowel in between.
                 And don't even bother to write your own file grouping
                 system because even if you do, people still have to write
                 their own new utilities to read the ART and MAP files.

                 uninitgroupfile()
                    Call before quitting to DOS.
      ------------------------------------------------------
         - ATTENTION PROGRAMMERS!  Changed kopen4load from:

            kopen4load(char *filename)
               to:
            kopen4load(char *filename, char searchfirst)

               where:
            if searchfirst = 0 then search stand alone first then group file
            if searchfirst = 1 then search group file only
──────────────────────────────────────────────────────────────────────────────
6/12/95   - Tracked down an OUT OF SYNC bug with my multiplayer code!
               In my GAME.C, the way I had my sync arrays organized were
                  wrong.  The problem was that if faketimerhandler was
                  called during domovethings, the sync arrays may have been
                  changed, getting the game out of sync.  The solution is
                  to keep the sync arrays in domovethings totally separate
                  from the sync arrays in faketimerhandler.  I split my sync
                  arrays into 2 separate arrays, sync and fsync, where fsync
                  and osync are for use BEFORE the FIFO only (such as
                  faketimerhandler and getpackets), and sync is used AFTER
                  the FIFO only (such as domovethings).  PLEASE SPLIT YOUR
                  SYNC ARRAYS AS I DESCRIBED!

          - The OUT OF SYNC message doesn't flicker any more.  See the
               section of code in GAME.C where I set syncstat.

          - Fixed up a waitforeverybody function for network games which
               waits for everybody to be at a certain part of the game.
               Waitforeverybody also uses getpackets message number 5.

          - Added new clipping function that will push players away from
               walls that are too close.  It solves A LOT of movement
               clipping problems.  It can be pretty darn slow if it detects
               that you're too close to a wall, so I'd recommend using it
               only for players.

               pushmove (long *x, long *y, long *z, short *sectnum,
                 long walldist, long ceildist, long flordist, char cliptype)

               The parameters are exactly the same as clipmove but with no
                  xvect or yvect.  Pushmove returns either a 0 or -1.  If
                  it returns a -1, then that means that it could not push
                  the player away from the offending wall after 256 tries.
                  When this happens, then you should kill the player
                  instantly, because this only happens when the player is
                  getting smooshed.
──────────────────────────────────────────────────────────────────────────────
6/13/95   - Made clipinsidebox and clipinsideboxline return 0 if line doesn't
               intersect box, 1 if line intersects box and center of box
               is in front of line, or 2 if line intersects box and center
               of box is behind line.

          - Cansee should now work properly with overlapping.
──────────────────────────────────────────────────────────────────────────────
6/19/95   - ATTENTION!  Remove work-arounds!!!  Found nasty getzrange bug.
               Getzrange used to let you fall through red sector line cracks
               when you were near more multiple red sector line sharing the
               same 2 sectors.  Sound familiar anybody?

          - Made pushmove return the sector of whatever x and y end up in.
──────────────────────────────────────────────────────────────────────────────
6/22/95   - I included cache1d.c renamed to cache1d.txt in this upload.
──────────────────────────────────────────────────────────────────────────────
6/26/95   - Made face and wall sprites of clipmove, getzrange, hitscan, and
               neartag sensitive to the y-offset that you set in Editart.

          - Made one sprite sorting case work perfectly:  where multiple
               sprites have the same x and y location (and priority).  The
               sorting still doesn't work perfectly, but this one case can
               solve many of the current sprite sorting bugs, such as
               inserting a floor sprite of blood below a dead body.

          - Fixed some sprite drawing clipping bugs for wall and floor
               sprites.  Now, wall sprites and floor sprites should not
               show through walls, ceilings, or floors when they're not
               supposed to.  Also, I fixed a case where wall sprites
               disappeared when they happened to be between 2 white walls
               that clipped it.
──────────────────────────────────────────────────────────────────────────────
6/27/95   - Fixed even more sprite drawing clipping bugs for wall sprites.

          - BONUS!!!  Wall sprites now clip to white walls! (not red walls)
               If you ornament a wall, make sure that neither endpoint of the
               wall sprite is behind the wall or else it will get clipped.
               Please run lookatme.map from my game.exe which demonstrates
               some of the things you can do in build now without bugs.
──────────────────────────────────────────────────────────────────────────────
6/30/95   - Made drawline256 clip with startumost/startdmost.

          - Moved keyboard handler into GAME.C.  Here's what you need to do
               to use my keyboard handler (see my GAME.C):

               1.  Some global variables:

                      #define KEYFIFOSIZ 64
                      void (__interrupt __far *oldkeyhandler)();
                      void __interrupt __far keyhandler(void);
                      volatile char keystatus[256], keyfifo[KEYFIFOSIZ];
                      volatile char keyfifoplc, keyfifoend;
                      volatile char readch, oldreadch, extended, keytemp;

               2.  initkeys()

               3.  uninitkeys()

               4.  keyhandler()

               5.  Make sure to call initkeys and uninitkeys.  A good place
                     would be next to where you call inittimer / uninittimer.

               I didn't put my keyboard handler into BSTUB since I am using
            keystatus throughout build.obj.  Besides I didn't want to anybody
            too bad of a headache for one upload.

          - Did you know that screencapture inverses black and white if you
               hold either shift key down?  This option is useful for
               printing out board maps.  Since I don't own the keyboard
               any more, I had to make screencapture take inverseit as the
               second parameter.

               screencapture(char *filename, char inverseit);

           Ex: screencapture("captxxxx.pcx",keystatus[0x2a]|keystatus[0x36]);

               If (inverseit == 0) then nuttin' special
               If (inverseit == 1) then super-reverso blacko-whiteo mode!

               I also had to move the stereo adjustment keys into game.c
               Just search for the "stereo" keyword in GAME.C.  It'll take
               through all the necessary places.
──────────────────────────────────────────────────────────────────────────────
7/5/95    - Moved my keytimerstuff from my timerhandler into getinput.
               (which is where it should be)
──────────────────────────────────────────────────────────────────────────────
7/10/95   - Added a loadtilelockmode variable to BUILD.H.  It's a global
               variable that makes future loadtile calls allocate their
               buffers on the cache as unlocked (0-default) or locked (1).
               I decided to make this a global variable rather than a
               parameter to save some people from rewriting code.

               EXTERN char loadtilelockmode;

          - Made my 2D EDIT MODE space bar code clear out new sectors and
               wall structures to 0. (except for the extras which are -1)
──────────────────────────────────────────────────────────────────────────────
7/20/95   - I forgot to documenent rotatesprite bit 3, which controls
              whether or not the sprite will be clipped to the startumost
              /startdmost arrays.  If the bit is a 1, then the sprite will
              not be clipped.  This mode is useful for doing status bars
              at variable resolutions.  What you do is instead of calling
              permanentwritesprite, you call rotatesprite with the scaling
              bit and startumost/startdmost clipping bit off.  For non
              screen-buffer modes, be sure to call rotatesprite for each
              page of the mode.  Use the numpages variable in BUILD.H to
              get the number of video pages used.

          - Added clipping box parameters to rotatesprite:  Here is the
              prototype now:

              rotatesprite (long sx, long sy, long z, short a, short picnum,
                            signed char dashade, char dapalnum, char dastat,
                            long cx1, long cy1, long cx2, long cy2)

              For your information, rotatesprite used to choose default
                 clipping box sizes using this code:

              if ((dastat&8) == 0)
              {                                    //0,0,xdim-1,ydim-1
                 cx1 = windowx1; cy1 = windowy1;
                 cx2 = windowx2; cy2 = windowy2;
              }
              else
              {
                 cx1 = 0; cy1 = 0;
                 cx2 = xdim-1; cy2 = ydim-1;
              }

              Now both permanentwritesprite and overwrite can both be
                 replaced with rotatesprite which now does everything.
                 To replace permanentwritesprite, you must make sure to
                 draw to all the pages in numpages.
──────────────────────────────────────────────────────────────────────────────
7/27/95   - Fixed recently introduced build 3D mode bug when you press the
               L.ENTER key on a blank map.  Also made sectorofwall return -1
               if an invalid wall is passed to it.
──────────────────────────────────────────────────────────────────────────────
7/28/95   - Added a function to my game that shows only the local area of
               the map by scanning through the nextsectors of walls and
               setting the show2dsector, show2dwall, and show2dsprite
               arrays properly.  You should call this function any time
               (show2dsector[cursectnum>>3] & (1<<(cursectnum&7))) == 0)
               That means it's not showing the sector you're currently
               in.  You should also manipulate show2dsprite any time you
               insert or warp a sprite.

          - You know the one thing in my engine that doesn't work in chained
               mode?  Well I fixed it!

          - Fixed a bug which made it print out of sync when it really
               wasn't  (This bug probably only affecting my game.)  In
               getsyncstat, I was calling updatecrc16() with things other
               than chars, making it index invalid parts of the crctable
               array.
──────────────────────────────────────────────────────────────────────────────
7/30/95   - Reduced some overhead memory:
             * Removed walsiz[] array.  (16K saved).  If you ACTUALLY used
                  this array, you can multiply tilesizx[]*tilesizy[] instead.
             * Made sqr table from longs to shorts with same precision.
             * Removed tantable. (4K saved)
             * Reduced internal constant, MAXWALLSB, from 4096 to 2048
               (96K saved).
──────────────────────────────────────────────────────────────────────────────
8/1/95    - Added another bit to rotatesprite for top-left corner mode and
               made documentation of the stat bits more clear.
──────────────────────────────────────────────────────────────────────────────
8/3/95    - Fixed a bug with rotatesprite so it now scales perfectly to the
               full screen view when enabling bits 2 and 8 of the stat field.
               This means that with rotatesprite, you can easily get your
               status bars, etc., to work at any resolution.
──────────────────────────────────────────────────────────────────────────────
8/4/95    - New Doom->Build update!  You can now convert doors and lots of
               other weird things.  Fixed more conversion bugs.  Sprites now
               convert properly in doom-artwork mode.  You can now convert
               PWADS.

          - Added some wacky aspect ratio keys to 3D EDIT MODE of BUILD.
               Use  Rt.Ctrl Rt.Alt Lt.-  or  Rt.Ctrl Rt.Alt Lt.=  to select.
──────────────────────────────────────────────────────────────────────────────
8/12/95   - Added parameter to initmultiplayers to allow who becomes
               master at the start of a multiplayer game.

          - Added parallaxyscale variable to BUILD.H which control the ratio
               at which the parallaxing skies scroll in relation to the
               horizon.  Default is 65536.  With lower values, you don't
               need as much artwork and can look higher, but I like 65536
               because it is the correct projection.
──────────────────────────────────────────────────────────────────────────────
8/13/95   - Had MAJOR bug with my searchmap function.  If you use it, please
               re-copy from my game.c
──────────────────────────────────────────────────────────────────────────────
8/14/95   - Fixed some EVIL bugs with Rt.ALT sector copying.  It shouldn't
               screw up your maps anymore!  And if you have maps that were
               screwed up with it, you can try my new map correcting key,
               L.Ctrl+L.Shift+L.Enter.  This key will not affect an already
               perfect map.  However it can SOMETIMES clean up a map that
               was screwed up.  I take no responsibility if it screws up
               your map even more, so please check them before saving!
