code:
// "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 (7/24/96):

BUILD programmed by Ken Silverman

BUILD.TXT CONTINUED...  (BUILD.TXT WAS GETTING TOO BIG!!!)
������������������������������������������������������������������������������
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!
������������������������������������������������������������������������������
8/16/95   - Added error correction to mmulti.obj.  Mmulti.obj is like
					multi.obj but it is made to work with Mark's real mode
					multiplayer driver that he calls COMMIT.

				MMULTI.OBJ vs. MULTI.OBJ:
				 * initmultiplayers is the same, but the parameters are ignored
				 * uninitmultiplayers can be called but does nothing.
				 * sendlogon can be called but does nothing.
				 * sendlogoff sends packet 255 to everybody else.  It does not
					  need to be called any more.
				 * sendpacket and getpacket are the same.
				 * connecthead, connectpoint2[], numplayers, myconnectindex
						are the same.  They can be externed just like before.
				 * getoutputcirclesize always returns 0.  It is not needed.
				 * setsocket does nothing.  The socket is now in commit.dat.
				 * crctable can still be externed.
				 * syncstate can still be externed but is not used.
				 * Do not use the option[4] variable.  Initmultiplayers will
						always return a valid number in numplayers from 1-16.

					You can link mmulti.obj in place of multi.obj and use
				 commit.dat as the setup file for multiplayer options.

					There are 2 ways you can run your game through commit:
						1.  Set the launch name (usually game.exe) in commit.dat
							 and type something like "commit map01 /asdf"
						2.  Type "commit launch game map01 /asdf".  This method
							 is easier if you want to use the debugger with commit
							 since you won't have to change commit.dat constantly.
							 Ex: "commit launch wd /tr=rsi game map01 /asdf"

				 I have not tested mmulti.obj with my BUILD game yet because
				 I have been using 2DRAW as my test program.  I may put up a
				 new version of mmulti.obj with better error correction for
				 extremely error-prone lines soon.

			 - Kgroup can now accept a parameter as a filename with a list of
					files to be put into the group file.
					Ex:  "kgroup @filelist.txt"
������������������������������������������������������������������������������
8/18/95   - Found a way to greatly reduce slave "hitching" on multiplayer
					games for faketimerhandler style communications.  It's pretty
					simple.

					Step 1:  In the beginning of faketimerhandler, change

						ototalclock = totalclock;
							TO:
						ototalclock += TICSPERFRAME;

							This makes the timing of the game more constant and to
						never miss ticks.  I should have done this in the first
						place all along.

					Step 2:  In getpackets, (for slaves only) count the number of
						times movethings is called.  Normally, movethings should
						be called exactly once for every time getpackets is called
						inside faketimerhandler.  The hitching is because
						movethings is called in a 0-2-0-2-0-2 cycle instead of the
						standard 1-1-1-1-1-1 cycle.  This happens because the
						timers of the 2 computers are aligning in such a way that
						the slave is receiving the master's packets at nearly the
						same time as it calls getpackets.  To correct the problem,
						if movethings is called an even number of times, I randomly
						add or subtract (TICSPERFRAME>>1) to ototalclock.  Throw
						this code at the end of getpackets:

					Beginning of getpackets:
						movecnt = 0;

					Where slave receives buffer in getpackets, next to movethings:
						movecnt++;

					End of getpackets:
						if ((myconnectindex != connecthead) && ((movecnt&1) == 0))
						{
							if (rand()&1) ototalclock += (TICSPERFRAME>>1);
										else ototalclock -= (TICSPERFRAME>>1);
						}

			 - Found a way to interpolate anything using the doanimations
					code for faketimerhandler style multiplayer games.  It's not
					as hard as I thought it would be (and it doesn't waste too
					much memory!)  To smooth out doanimations, since there's no
					temporary variables that can be thrown away like with tsprite,
					the current doanimations positions must be backed up at the
					beginning of drawscreen and restored at the end of drawscreen.
					If you want smooth up&down doors, do this:

					Global declaration:
						static long oanimateval[MAXANIMATES];

					Beginning of drawscreen:
						for(i=animatecnt-1;i>=0;i--)
						{
							 oanimateval[i] = *animateptr[i];  //Backup doanimations interpolation

							 j = *animateptr[i];
							 if (j < animategoal[i])
								 j = min(j+animatevel[i]*(totalclock-gotlastpacketclock),animategoal[i]);
							 else
								 j = max(j-animatevel[i]*(totalclock-gotlastpacketclock),animategoal[i]);

							 *animateptr[i] = j;
						 }

					End of drawscreen:
							//Restore doanimations interpolation
						for(i=animatecnt-1;i>=0;i--) *animateptr[i] = oanimateval[i];
������������������������������������������������������������������������������
8/19/95   - Added global invisibility variable.  Initengine sets it to 0
					by default.  If you set global invisibility to 1, then
					all sprites with the invisible bit set (sprite[].cstat&0x8000)
					will be shown.  This is useful for editing invisiblie sprites.

			 - Made the hitscan blocking bit for sprites be the bit checked
					when using cliptype 1 instead of the blocking bit.  Before
					I was accidently using the clipmove blocking bit on sprites
					with cliptype 1.  I should have done it this way in the first
					place.  I hope you haven't "built" around this bug too much!
					Here's how everything works now:

						Clipmove blocking bits:
							Which bits:
								wall[].cstat & 1
								sprite[].cstat & 1
							Used in these cases:
								clipmove when cliptype = 0
								getzrange when cliptype = 0

						Hitscan blocking bits:
							Which bits:
								wall[].cstat & 64
								sprite[].cstat & 256
							Used in these cases:
								clipmove when cliptype = 1
								getzrange when cliptype = 1
								hitscan always
������������������������������������������������������������������������������
8/20/95   - Reduced some overhead memory by combining 2 large internal
					arrays which were used in different parts of the engine.
					This is a total savings of about 90K.
������������������������������������������������������������������������������
8/24/95   - Changed the way 'E' mode works on ceiling and floor textures.
					64*64 and 128*128 textures are the same as before so I think
					this won't screw up your existing maps. (But I can never be
					sure)  Now, 'E' mode always smooshes the texture size by 2 for
					any size texture.
������������������������������������������������������������������������������
8/29/95   - �������� ��       �������� �������� �������� �������� �� �� ��
				��       ��       ���  ��� ��    �� ��       ��       �� �� ��
				�������� ��       ��    �� �������� ������   �������� �� �� ��
						�� ��       ���  ��� ��       ��             �� �� �� ��
				�������� �������� �������� ��       �������� �������� �� �� ��

				New 3D EDIT MODE BUILD keys:
					Press [ or ] on a ceiling or floor to change the slope.
					Shift + [ or ] on a ceiling or floor to adjust finely.
					Press / to reset a ceiling or floor to slope 0.
					Use Alt-F in either 2D or 3D mode to change the slope's hinge.

				Sector fields used for slopes:
					The useless groudraw fields, ceilingheinum and floorheinum,
					are now being used as the slope value.  They are treated
					as signed shorts, where 0 is slope 0, of course.  To enable
					slope mode, you must also set bit 1 of the ceilingstat or
					floorstat bits.

				Known bugs:
					There are still a few bugs that I haven't gotten around to
					fixing yet.  They're not necessarily hard to fix - it's just
					that there are a lot of little things that need to be updated
					(on my part).

					- Hitscan does not hit slopes correctly.
					- Rare divide overflow bug in my wallmost function only when
					  you're very close to a red sector line of a slope.
					- Relative alignment for slopes not yet programmed.  Relative
					  alignment for slopes will allow square aspect ratio for all
					  slopes of slopes.
					- ALT-F code with passing loops would be nice.
					- Visibility not implemented for slopes yet.
					- Sometimes an annoying tiny wall is drawn at sector lines.

					Don't call me to tell me about these bugs.  I know about them.
					And if I didn't list your favorite bug of the day, it will
					probably be brought up by the 2 internal teams now working
					with BUILD.
������������������������������������������������������������������������������
9/4/95    - Made ALT-F select any wall of a sector, even crossing loops.
					In 3D mode, ALT-F now sets the selected wall directly to the
					first wall.

			 - Fixes related to slopes:
				 * Relative alignment now works.  Use relative alignment on high
						slopes if you don't like the way the texture is stretched
						out.
				 * Flipping now works
				 * Panning now works
				 * Fixed seams for SOME (not all) slope borders

			 - My wonderful divide overflow bugs in wallmost aren't quite as
					rare as they used to be.  Wait a minute - I thought I was
					supposed to document bug fixes here!  This is what you get
					for wanting BUILD tonight.  As soon as I fix these darn
					divide bugs, I'll put up a new version.
������������������������������������������������������������������������������
9/5/95    - Fixed all known divide overflow bugs related to wallmost.  I
					haven't gotten a divide overflow yet in the last few hours,
					but that doesn't guarantee that you'll never get one.  Take a
					look at GROULAND.  It has a cool new cylindrical tunnel.  On
					the other side of the level is my house in Rhode Island.

				Known bugs with slopes now:
					- Hitscan does not hit slopes correctly.
					- Visibility not implemented for slopes yet.
					- Clipmove will not allow you to walk off high sloped cliffs.

					- Also, I noticed a rare sprite drawing clipping bug.  I
						 don't know if this is new or not.
������������������������������������������������������������������������������
9/7/95    - Fixed an exception 0xe bug by using tsprite[MAXSPRITESONSCREEN-2]
					instead of tsprite[MAXSPRITESONSCREEN-1].  I don't understand
					why this fixed it so I expect that it will come back to haunt
					me.  Oh happy day. :(

			 - I forgot to document 2 new and useful functions in the engine:

				  long getceilzofslope(short sectnum, long x, long y); and
				  long getflorzofslope(short sectnum, long x, long y);

					  Returns the z coordinate of the ceiling/floor at that x, y
					  location.  If the sector doesn't have a ceiling/floor slope
					  then it immediately returns the sector[].floorz or
					  sector[].ceilingz so it's not that slow.  You may want to
					  check for slopes yourself ceilingstat&2/floorstat&2 if you
					  think the overhead of calling these functions are too slow.

			 - Made clipmove use getceilzofslope and getflorzofslope when
					checking for overlapping.

			 - Cleaned up mirror code for non-chained modes a little bit.  The
					mirrors should no longer have a stay line on the right anymore
					for these modes.

			 - Added Chain-Buffer mode.  See my new SETUP.EXE.  Chain-Buffer
					is a combination of the chain and screen buffer modes - a
					buffered chain mode.  This mode is faster than standard chain
					mode when a lot of screen memory is being read, for example
					when you use transluscence or mirrors.  Also, Chain-Buffer
					mode is cleaner than screen-buffer mode since you don't see
					memory being copied to the screen.  Unfortunately, in most
					cases, Chain-Buffer is the slowest of all modes.  Actually,
					Chain-Buffer mode sucks.  There is a use for this mode,
					however!  In the future, I may make some kind of chain mode
					automatically switch into chain-buffer mode for extra speed
					when there is a large area of transluscence, etc.

				ATTENTION PROGRAMMERS:  Here is the new order of modes that
					initengine accepts:

					0 = Chain mode
					1 = Chain-Buffer mode
					2 = Screen-Buffer/VESA mode
					3 = TSENG mode
					4 = Paradise mode
					5 = S3
					6 = Crystal Eyes mode
					7 = Red-Blue mode
������������������������������������������������������������������������������
9/9/95    - Fixed visibility for face sprites in high resolution modes.
������������������������������������������������������������������������������
9/14/95   - Added a new bunch of graphics modes using the new VESA 2.0 linear
					buffer.  (I replaced the useless chain-buffer mode with VESA
					2.0 modes)  See my new SETUP.  Since most cards only support
					VESA 1.2 right now, you will probably need a VESA 2.0 driver.
					I have been using the UniVBE(tm) 5.1 from SciTech Software
					(ftp: ftp.scitechsoft.com, www: http://www.scitechsoft.com)
					Note that since I inserted the new modes between chained mode
					and screen buffer mode, you will need to update your setup
					program and fix your initengine calls.

			 - Programmed a LRU/MRU-style cacheing system.  It should be fully
					compatible with the old cache1d except for the locking byte.
					The locking byte would be better described as a priority byte.
							Priority byte:

								 0 - non-cached memory, you NEVER set the byte to 0!
							1-199 - cached unlocked memory
						 200-255 - cached locked memory

					When the cacheing system needs to remove a memory block, it
					will try to remove the FEWEST number of SMALLEST blocks with
					the LOWEST priority.

					Note: Never set the priority byte to a 0!  Let the cacheing
						system do that for you.
������������������������������������������������������������������������������
9/15/95   - Optimized hitscan a little and made it work with slopes.

			 - Made some internal things in the build editor work better with
					sprites on slopes.
������������������������������������������������������������������������������
9/16/95   - Note:  Initengine now only does memory allocation for the
						 graphics modes.  The ylookup table is calculated in
						 setgamemode since with VESA, I don't know certain
						 variables such as bytesperline until the mode is
						 actually set.

			 - Cansee should now work with slopes properly.

			 - Added a new function which is a combination of the
					getceilzofslope and getflorzofslope functions.  Whenever you
					need both the ceiling and floor, it is more optimal to call
					this function instead.  The parameters are just like the other
					functions except the ceiling and floor are returned through
					pointers instead of a return value.

						getzsofslope(short sectnum, long x, long y,
										 long *ceilz, long *florz);

			 - Fixed recently introduced hitscan divide overflow bug.

			 - Fixed a sprite drawing clipping bug.
������������������������������������������������������������������������������
9/17/95   - ATTENTION PROGRAMMERS:  I moved the sprite shading code from
					the engine into GAME/BSTUB.  If you want sprites to shade
					like they used to, be sure to copy the code I recently added
					to the end of analyzesprites in GAME.C or ExtAnalyzeSprites
					in BSTUB.C.

			 - Added 2 new interesting functions to the engine to make moving
					slope programming easier.  These functions will align a slope
					to a given (x, y, z) point.  It will make the slope pass
					through the point.  The function will do nothing if the point
					is collinear to the first wall of the sector.

					alignceilslope(short sectnum, long x, long y, long z);
						and
					alignflorslope(short sectnum, long x, long y, long z);
������������������������������������������������������������������������������
9/18/95   - Kgroup will now work with subdirectories.

			 - ATTENTION PROGRAMMERS:  I have not done this yet, but I am
					planning on doing a new map version!  Before I do it, I
					want to give you a chance to give me any suggestions you may
					have.  The only changes I am planning on, besides re-arranging
					the structures for better alignment is to give the slopes some
					more bits precision.  This may limit slopes to a maximum slope
					of 4 (almost 76�) which is still extremely high.  Here are the
					new structures that I am proposing:

						//40 bytes
					typedef struct
					{
						long ceilingz, floorz;
						unsigned short wallptr, wallnum;
						short ceilingstat, floorstat;
						short ceilingpicnum, ceilingheinum;
						signed char ceilingshade;
						char ceilingpal, ceilingxpanning, ceilingypanning;
						short floorpicnum, floorheinum;
						signed char floorshade;
						char floorpal, floorxpanning, floorypanning;
						char visibility, filler;
						short lotag, hitag, extra;
					} sectortype;

						//32 bytes
					typedef struct
					{
						long x, y;
						short point2, nextwall, nextsector, cstat;
						short picnum, overpicnum;
						signed char shade;
						char pal, xrepeat, yrepeat, xpanning, ypanning;
						short lotag, hitag, extra;
					} walltype;

						//44 bytes
					typedef struct
					{
						long x, y, z;
						short cstat, picnum;
						signed char shade;
						char pal, clipdist, filler;
						unsigned char xrepeat, yrepeat;
						signed char xoffset, yoffset;
						short sectnum, statnum;
						short ang, owner, xvel, yvel, zvel;
						short lotag, hitag, extra;
					} spritetype;

					Note that I am adding 1 byte of filler to the sprite structure
					(4K more) and 3 bytes of filler to the sector structure
					(3K more).  (If I'm a nice guy, one of those bytes may even
					be use for fog. (DOH!))

			 - Fixed another wonderful hitscan divide bug.
������������������������������������������������������������������������������
9/22/95   - Optimized slopes.  On a 486-66 w/ local bus in VESA 2.0 320*200
					mode, a full screen slope was optimized from 24fps to 35fps!
������������������������������������������������������������������������������
9/25/95   - Re-optimized some more assembly.

			 - ATTENTION EVERYONE!  CONVMAP7!  You know what this means.  Just
				  type "convmap7 *.map" and you'll be back in business.  P.S.
				  enjoy the newly aligned structures!

			 - ATTENTION PROGRAMMERS!  New TRANSPAL!  You can now use 2 levels
					of transluscence by using just a single 64K transluscent
					buffer, such as 33/67 and 67/33 transluscence.  I changed the
					format of palette.dat, so please run TRANSPAL before you try
					running the game with the new OBJS! If you don't, the palette
					tables will be screwed up.

					 Old PALETTE.DAT:
						 768 bytes - VGA palette
						 numshades*256 bytes - palookup[0] table
						 32640 bytes- transluscent palette (smaller from symmetry)

						 The way I used to get numshades (fun):
							 numshades = ((filelength-32640-768)>>8)

					 New PALETTE.DAT:
						 768 - VGA palette
						 2 - numshades
						 numshades*256 - palookup[0] table
						 65536 - transluscent table

					 To keep things less confusing, could you all use a
					 transluscent constant >= 128.  For example, I used 170 on
					 my PALETTE.DAT.

					READ THIS!  New bits added to things:
						Bit 5 of rotatesprite, bit 9 of both sprite[].cstat
						and bit 9 of wall[].cstat are now used for reverse
						transluscence.

			 - Added super-cool new feature to 2D EDIT MODE (that should have
					been there from the start).  Try inserting some points!

			 - ATTENTION PROGRAMMERS!  Removed overwritesprite.  Use
					rotatesprite instead.  I don't want to support a function that
					can totally be done with another better function.  I will
					eventually optimize rotatesprite for 90� cases so it's even
					faster than the current overwritesprite.  If you're too lazy
					to convert right now, then you can use this overwritesprite
					stub function:

overwritesprite (long thex, long they, short tilenum,
					  signed char shade, char stat, char dapalnum)
{
	rotatesprite(thex<<16,they<<16,65536L,(stat&8)<<7,tilenum,shade,dapalnum,
		((stat&1^1)<<4)+(stat&2)+((stat&4)>>2)+((stat&16)>>2)^((stat&8)>>1),
		windowx1,windowy1,windowx2,windowy2);
}

			 - Fixed the rotatesprite centerting bug in y-flipping mode.  Oh
				  by the way, you know how I originally said the flipping bit
				  was x?  Well, I screwed it up.  It was actually y-flipping all
				  along.

			 - This one's for hackers (N&P/Qs)  In the engine, I call
					getpalookup every time I need a pointer to a 256-byte
					palookup table.  One big limitation right now is that each
					bunch of 256-byte tables has a limit of 64K because a shade
					is being returned, not a 4-byte pointer.  This means you
					could have a maximum of 8 shades, with 8 fogs per palookup.
					It's not as expandable as I originally intended since this is
					a speed-critical function.  It is called EVERY single time a
					line is drawn - probably called 50 times more often than
					faketimerhandler.

					#pragma aux bound32 =\
						"test eax, 0ffffffe0h",\
						"jz endit",\
						"cmp eax, 80000000h",\
						"sbb eax, eax",\
						"and eax, 31",\
						"endit:",\
						parm [eax]\

					getpalookup(long davis, long dashade)
					{
						return(bound32(dashade+(davis>>8)));
					}

					This is just a start.  Eventually I hope to use some kind of
					lookup table, such as:  palookup = palptr[davis][dashade],
					but I couldn't think of a good way to make the array small
					enough for what people need.
������������������������������������������������������������������������������
9/26/95   - Fixed drawmapview so it actually clips to the rectangular window
					properly.  It does not clip to startumost/startdmost so you
					should call a rotatesprite to draw the status bar every frame
					if it is not rectangular.
������������������������������������������������������������������������������
9/30/95   - Fixed recently introduced mirror bug.

			 - Fixed rotatesprite x-positioning bug in scale mode for weird
					  resolutions.

			 - Fixed tilting for pure chained modes.
������������������������������������������������������������������������������
10/2/95   - Changed setbrightness call:

					setbrightness(char dabrightness, char *dapal)
						dabrightness is gamma level(0-15)
						dapal is pointer to standard VGA 768 byte palette.
������������������������������������������������������������������������������
10/3/95   - Fixed flicker in UNIVBE modes.
������������������������������������������������������������������������������
10/6/95   - Added a global visibility variable to BUILD.H specific for
					p-skies called parallaxvisibility.  Now you can do lightning
					effects without modifying the visibility for everything.

			 - Really fixed the drawmapview window clipping this time.

			 - Made my clearview function clip to the viewing window since it
					is usually used just before the drawmapview function.

			 - You can now use bit 6 (64) to tell rotatesprite whether or not
					the tile has transparent regions or not.  If there are no
					transparent regions, then it would be faster to set this bit
					because it doesn't have to check for color 255 at every pixel.
������������������������������������������������������������������������������
10/11/95  - Back in RI!
������������������������������������������������������������������������������
10/13/95  - Cleaned up setup program by having only 1 VESA menu for all
					possible VESA modes.  The engine will now auto-detect whether
					or not your computer supports VESA 2.0.  The screen buffer
					menu now only supports 320*200.  (I noticed that Mark's setup
					program doesn't list 320*400 mode even though it is supported
					by my game and UNIVBE. 320*400 is considered by some people
					the next best mode after 320*200 and 640*480.  You have my
					permission to annoy him.)

			 - I bought UNIVBE by credit card for $28!  When you buy UNIVBE,
					you don't have to struggle with beeps or date changing - and
					it loads instantly.  I try not to use TSR's whenever possible,
					but I found that UNIVBE is worth wasting 8K.  I don't think my
					computer has ever crashed due to something related to UNIVBE.
������������������������������������������������������������������������������
10/17/95  - Found an easy way to interpolate everything for faketimerhandler
					in my game, including swinging doors, revolving doors, and
					other moving sectors.  My doanimations interpolation now uses
					these functions instead.  Check them out in my GAME:

						setinterpolation(long *posptr);  //Call when door starts
						stopinterpolation(long *posptr); //Call when door stops
						updateinterpolations();  //Call at start of domovethings
						dointerpolations()       //Call at start of drawscreen
						restoreinterpolations()  //Call at end of drawscreen

					Call setinterpolation with a pointer to the long variable
					being interpolated.  If an object stops moving, you can
					speed things up by calling stopinterpolation.  For things
					that always move, you can call setinterpolation in pre-map
					and don't stopinterpolation for those things.  Don't forget
					to set numinterpolations to 0 in premap whenever changing
					levels in the game.

			 - Optimized lava.  You can re-copy my code if you want.

			 - Added auto screen-buffering mode for linear VESA modes.  (In
					case you didn't know, reading video memory can be 10 times
					slower than reading non-video memory.)  Auto-buffering
					means that if the engine detects that more than 1/8th of the
					screen has transluscence or mirrors, then it will switch into
					a screen-buffering mode.  Screen-buffer mode in linear VESA
					not only will still have smooth page flipping, but will be
					faster than standard screen buffer mode on some video cards
					just because it's linear memory.  (That's a good thing)

			 - Optimized screen buffer modes so they copy only the viewing
					window instead of the whole screen from non-video memory to
					video memory.  If a permanent sprite is written, as a special
					case, it will copy the whole screen - but this doesn't affect
					you.  Me and my engine babble.

			 - Moved the printext function out of the engine and into my game.
					Use printext16/printext256 instead.

			 - I tried removing chain mode twice and it didn't improve the frame
					rates of the other modes at all.  So it stays!  If you don't
					like it, then don't include it in your setup program.

			 - Made allocatepermanenttile return 0 instead of -1 if something
					bad happens.  My GAME.C was crashing in windows with a memory
					error because I was assuming all memory pointers were
					positive.  Please make sure your code will work if an
					allocation function returns a negative pointer (Windows often
					returns negative pointers).
������������������������������������������������������������������������������
10/19/95  - Tried to make pushmove work with face sprites, but it kept making
					the player die too much.  Also it presented some interesting
					new problems, such as:  You get pushed by your own bullets
					that you just shot - and get this - if you don't temporarily
					set yourself to non-blocking you kill yourself because you're
					inside you're own sprite!  So I have decided to comment out
					this code for now.

			 - Fixed hitscan bug with ceiling slopes.

			 - ATTENTION PROGRAMMERS!!! PERMANENTWRITESPRITE was removed from
					the engine and be put into my game as an emulated function.
					If you still use permanentwritesprite, get the emulated
					function out of my GAME.C.

				Drawing permanent areas is now easier than ever!!!  Simply
					call rotatesprite with bit 3 (&8) set or the
					permanentwritesprite stub call, and the engine now
					automatically takes care of copying the permanent region
					to all necessary pages in the future.  It even keeps track
					of whether the call was before or after drawrooms!
				I am using an internal fifo to back up the parameters for each
					rotatesprite call.  It can support up to 512 calls per
					page right now.  This number can get reached quicker than
					you think, especially if you use fonts with shadows behind
					each character.  Permanentwritespritetile could also go
					over the limit in hi-res modes since each tile is considered
					a separate call.
				I optimized out the case where an older rotatesprite region gets
					completely covered by a newer rotatesprite region with bit 6
					(&64 for non-masking) set.  Currently this has a few
					limitations - such as the x, y, zoom, and ang must be the same
					right now to knock out an older rotatesprite.

			 - I corrected an off-by 1 error in my GAME.C for window size
					calculations.  I simply used more precision. Ex:
					* changing (xdim>>1)-(screensize>>1) to ((xdim-screensize)>>1)
					* using the scale function instead of a separate mul & div.
				By the way, if you haven't already, please stick your screen
					re-sizing code BEFORE drawrooms.  It will make the screen
					re-size update more responsively and you're less likely to get
					drawing bugs.

			 - Future plans for possible rotatesprite optimizations:
				  1. Angle is 0, 512, 1024, 1536
				  2. Bit 6 (&64) is set for non-masking mode
				  3. tilesizy[tilenum] is a power of 2
				  4. If coincidentally, x=34562, y=34682, and zoom=67275
������������������������������������������������������������������������������
10/20/95  - Fixed vertical line shading bugs when looking at walls at harsh
					angles.

			 - Made masked walls clip to slopes properly.  TRY IT!
					(hope you didn't "BUILD" on this bug!)

			 - Fixed overflow and precision bugs with extremely long walls.
					I even got the chance to optimize out a multiply in the
					wallmost clipping code!
������������������������������������������������������������������������������
10/25/95  - Now absolutely everything in my game is being interpolated
					for smooth play - including subways!  No interpolation
					disable variable exists any more.  The setinterpolation
					function is really easy to use.  Try it!

			 - Programmed VISIBILITY for slopes!!!!!  On my P-100, the slopes
					are almost as fast as they used to be.  You can now start
					adjusting the shades of slopes.

			 - The shading lines on ceilings and floors now match to the shading
					lines on walls.  Before they didn't match. (oops).

			 - For those of you who want to program their own palette functions,
					using my gamma correction lookup table, here's the code:

					extern char britable[16][64];

					koutp(0x3c8,0);
					for(i=0;i<768;i++)
						koutp(0x3c9,britable[curbrightness][palette[i]]);

					If you use this code, don't bother calling setbrightness any
					more.  The only bad thing about programming this yourself
					is that you lose support for red-blue glasses mode.
������������������������������������������������������������������������������
10/26/95  - Added support for VESA 2.0 protected mode extensions.  With the
					protected mode extensions, the page-flipping is less likely to
					flicker, if at all.  This is because the retrace timing isn't
					screwed up as badly as if it had to switch to real mode.

			 - Fixed a weird buffer bug in my GAME.C.  I was using tempbuf in
					faketimerhandler/getpackets for sending packets.  I was also
					using tempbuf in many other places, such as my printext
					function.  Unfortunately, since rotatesprite is now called
					for each character, faketimerhandler would sometimes get
					called and the rest of the word got screwed up.  Please make
					sure you are not sharing any buffers in faketimerhandler /
					getpackets with the rest of your code.

			 - Fixed a bug for linear VESA modes.  Now, the screen will update
					properly when calling rotatesprite and nextpage for the first
					time before any drawrooms calls.  This used to not work right
					at my "waiting for other players" code in this mode.
������������������������������������������������������������������������������
10/27/95  - Did you notice I fixed some of the weird cases with slope drawing
					in the last upload?   I fixed things like when a ceiling of
					one sector is partially below the floor of another, etc.

			 - Fixed precision of shading lines on slopes.

			 - Fixed visibility overflow when slopes are near horizons.  This
					means you won't see ugly bright pixels any more when you're
					looking at a slope at an extremely sharp angle.

			 - Fixed a bug in BUILD 3D EDIT MODE with slopes.  Now when you
					press '/' on a ceiling or floor slope, it stays straight
					until you press [ or ] on it again.  Before, sometimes the
					ceiling would stupidly get sloped again if you pressed [ or ]
					on the floor and vice versa.

			 - Removed some unnecessary sounds from my game.  This doesn't
					really affect you people out there.
������������������������������������������������������������������������������
10/30/95  - Fixed recently screwed up p-sky tiling.

			 - Made Editart work with map version 7 for tile sorting and the
					Alt-R command.  Alt-R does not scan the heinum's of sectors
					any more, and only adds in the overpicnum if a masked or
					1-way wall is actually defined on the map.
������������������������������������������������������������������������������
10/31/95  - Fixed a bug with my slopalookup array.  It was sometimes writing
					up to 3 bytes after the end of the array.

			 - Fixed recently screwed up p-sky tiling for real this time.

			 - Permanentwritesprite is out of my game.  Here it is just in case
					I will need it again for future reference.

					 permanentwritesprite (long thex, long they, short tilenum,
						 signed char shade, long cx1, long cy1,
						  long cx2, long cy2, char dapalnum)
					 {
						 rotatesprite(thex<<16,they<<16,65536L,0,tilenum,shade,
								dapalnum,8+16,cx1,cy1,cx2,cy2);
					 }
������������������������������������������������������������������������������
11/1/95   - Guess what?  I turned 20 today.  This means I'm no longer a
					teenage programming genius.  Doesn't that just suck.  If you
					guys don't get a game out by the next time my age plus
					plusses, I'm going to get really p-skied off and kill some
					people.  That would be bad.  (When I say killing, I mean
					virtual killing in the entertaining game of Ken-Build
					in which a fun time is always had by all.)  So put on your
					happy fun smiles and work out all those rotatesprites,
					fsyncsyncvel.bits, and interpolation fifos before it's
					ototalclock!

			 - Optimized the NON-MASKING cases of rotatesprite for
					NON-(un)CHAINED modes.  This means that low detail modes,
					status bars, and permanent background tiles are now drawn
					about as fast as they're ever going to be drawn!  Try the
					F5 key in my game to compare frame rates if you want.  I have
					not optimized guns and stuff yet.  That's next on my list.

			 - Made the engine call new internal functions, kmalloc and kfree,
					instead of malloc or free.  Unless you're a hacker and wrote
					your own memory manager (like N&P/Qs) you can just ignore
					this message.
						void *kmalloc(size_t size) { return(malloc(size)); }
						void kfree(void *buffer) { free(buffer); }

			 - Supposedly fixed the new rotatesprite and linear vesa crashing
					bugs.  Since I'm not totally sure that I fixed the problems,
					all I can do is hope that I don't get too many phone calls!
������������������������������������������������������������������������������
11/4/95   - Did some neat optimizations that will make the drawing code
					faster, especially in hi-res modes, but I'm sure nobody cares.
					Instead I'm probably just going to get 10 totally different
					bug report stories.  So I'll be talking to you soon!

			 - Fixed stupid bugs in cansee.  If the 2 objects were in the same
					place, sometimes it returned 1 even if it couldn't see you.
					I made 2 fixes:
						This one to fix overlapping:

							if ((xs == xe) && (ys == ye)) return(sects == secte);

						And I removed the early out return(1) optimization - I
							put this after the big loop instead:

						for(i=danum-1;i>=0;i--)
							if (clipsectorlist[i] == secte) return(1);
������������������������������������������������������������������������������
11/7/95   - Optimized the awful shld's out of my divscale's in pragmas.h.
					You can update to the new pragmas.h if you actually use it.

			 - Remember the lovely horizontal lines on top of face sprites.
					Well you won't be seeing them any more.  At least the case
					I trapped works better than before.
������������������������������������������������������������������������������
11/8/95   - Made krand generate better random seeds.
������������������������������������������������������������������������������
11/9/95   - ATTENTION PROGRAMMERS!  Moved part of stat bit 3 of rotatesprite
					to bit 7.  Have you noticed that your menu code was kind of
					screwy in modes where numpages > 1?  This is because I made
					rotatesprite automatically copy to all pages whenever the
					"don't clip to startmost's bit" (bit 3 or +8) was set in
					the stat bit of rotatesprite.  To give you more control, I
					moved the auto copy to all pages bit to bit 7 and left bit 3
					as the don't clip to startmosts bit.  If you want your code
					to work the same as the last update of BUILD, simply go
					through all your rotatesprite calls and add 128 in addition
					to the stat bit whenever you were adding 8 before.  See the
					revised rotatesprite documentation:

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.  In auto-scale mode, be sure that (sx, sy) is using
		a 320*200 size screen even though the real resolution may be different.
	(z) is the zoom.  Normal zoom is 65536.  > is zoomed in, < is zoomed out.
	(a) is the angle (0 is straight up)
	(picnum) is the tile number
	(dashade) is shade number
	(dapalnum) is the palookup number

	if ((dastat&1) != 0) - transluscence
	if ((dastat&2) != 0) - auto-scale mode
			 Auto-scale mode will automatically scale from 320*200 resolution
		coordinates to the clipping window passed (cx1, cy1, cx2, cy2).  In
		auto-scale mode, don't pre-scale the (sx, sy) coordinates.  Simply pass
		(sx, sy) as if the resolution was 320*200 even though it may be
		different.  This means that you shouldn't use xdim or ydim to get
		(sx, sy).
	if ((dastat&4) != 0) - y-flip image
	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) - 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)
	if ((dastat&128) != 0) - automatically draws to all pages as they come

	(cx1, cy1, cx2, cy2) - The clipping window.  These coordinates are never
		 scaled, not even in auto-scale mode.  Usually you should pass them as
		 (windowx1,windowy1,windowx2,windowy2) for things scaled to the viewing
		 window or (0L,0L,xdim-1L,ydim-1L) for things scaled to full screen.
		 Probably the only time you wouldn't follow this rule is if you program
		 a non-scaled tiled background function.
������������������������������������������������������������������������������
11/21/95  - Optimized cansee - before it was checking each red wall 2 times
					as much as it needed to.  Now it only checks the side facing
					the first object passed to cansee.  Since this optimization
					is only 1 line, I don't think I messed anything up this time.

			 - Made cansee not pass through 1-way walls.  This means that the
					order of points passed to cansee could give you different
					results, but only for 1-way walls.

			 - FIXED CRASHING AND PRECISION ON HIGH SLOPES!!!  Try it for the
					first time again!  The bug was:  In wallmost, if a slope's
					line was getting clipped by both the top and bottom of the
					screen, the x-intercept of the bottom was calculated wrong.
					And if you were real lucky, if the x-intercept could have been
					so wrong that a loop would happily overwrite random chunks of
					memory.
������������������������������������������������������������������������������
12/1/95   - In my GAME.C, made tilting zoom in smoothly as it rotates to
					hide the ugly corners.
������������������������������������������������������������������������������
12/4/95   - Made some more optimizations to rotatesprite - added more cases
					for removing things from the permanent list early, such as:
					any full screen rotatesprite with 64 knocks everything else
					off the list, and any rotatesprites with zoom=65536,ang=0,
					stat&64 will knock out anything totally under its rectangle.
					These optimizations seemed to actually fix some bugs.
������������������������������������������������������������������������������
12/10/95  - Well, well, well.  It has now been 2 years since you know what
					was released and Apogee still hasn't put out a (real) Build
					game.  Trivia of the year:  Did you know that the first Build
					game was originally scheduled to be released a month before
					Doom was to be released?  Maybe I should rename the "Build"
					engine to the "ROTTT" engine because it's probably just going
					to rot on my hard drive for another year until all the games
					just get cancelled.  PROVE ME WRONG!

			 - Added selectable viewing angles - not locked at 90� anymore!
					Try playing around with the ( ) - = keys on the main keyboard
					in my new BSTUB.C.  See the example code in BSTUB for setaspect.
					Use the setaspect function to control both the viewing range
					angle and the y/x aspect ratio.

					setaspect(long daxrange, long daaspect)

					For a standard 90� 320*200 screen, daxrange and daaspect are
						65536.  For square aspect ratio at 320*400, set daaspect
						to 131072.  Since daxrange is actually zoom, you must
						modify the aspect ratio inversely if you only want to
						change the viewing angle.

				ATTENTION EVERYONE!  To make parallaxing skies work with larger
					than 90� viewing ranges, I had to change some things around
					in TABLES.DAT.  Don't forget to update to the new one!  The
					new one is smaller because I removed a useless array.

			 - Improved red-blue glasses mode by fixing some palette problems
					and removing the overlap problem on the right side of the
					screen.  It still has some things wrong with it though.

			 - Perfected screen tilting by using the new setaspect function.  It
					now tilts all 360� smoothly with no zoom change and with no
					ugly corners.  This is possible only because you can now set
					the viewing angle higher than 90�.

			 - Fixed a flicker bug with permanent rotatesprites in multi-page
					modes.  When rotatesprite was called because drawrooms in a
					multipage mode, the rotatesprites were being called in the
					wrong order from the fifo for the first page.  Note that
					before, the auto-knock out feature sometimes hid the problem.
������������������������������������������������������������������������������
12/11/95  - Made EDITART work with the new TABLES.DAT.

			 - Upgraded to Watcom 10.5.  The engine is now about 1K larger,
					and 0.0000001% faster.

			 - Added some interesting compression routines to cache1d.obj,
					dfread and dfwrite.  These functions take the same parameters
					as fread and fwrite respectively.  These functions are useful
					for loading/saving games or for recording demos.

				Note: Since these functions need to allocate 118K off of my LRU
					cacheing system, you must not call them before
					initcache(loadpics) is called.
������������������������������������������������������������������������������
12/18/95  - Made rotatesprite use getpalookup to get the palette shade.
������������������������������������������������������������������������������
12/31/95  - Cleaned up KGROUP.EXE and wrote a KEXTRACT.EXE utility.  With
					KEXTRACT, you can extract any files from a group file.
					Note that in KGROUP, DOS handles the ? and * wildcards whereas
					in KEXTRACT, I had to program the wildcards myself for finding
					files inside the group file.  The following combinations
					should work:  *.*, *.map, game.*, tiles???.art, *.k??

			 - NEW NETWORK METHODS!  TRY MY GAME IN MULTIPLAYER MODE!  MODEM!

					SUMMARY:  Lag Time: ZERO!  That's right Z.E.R.O. (0)
								 Smoothness: Perfect on ALL computers!

						My new network code builds upon everything you've already
					programed with faketimerhandler, getpackets, movethings, and
					domovethings, so you don't need to throw away any of that
					great stuff.  There are actually 2 totally separate things I
					did to improve the multiplayer performance.  First I added a
					new network mode which sends packets in a different way (this
					is way Doom actually does it)  Then I found a way to reduce
					lag-time to 0 with perfect smoothness.  (Doom does NOT do
					this, but G&S like it anyway for some reason.)  I will first
					describe the new network mode:

				 -------------------------------------------------------------
				 1.   First, let me describe the new network mode.  Instead of
					a master/slave system, every computer sends its own controls
					to every other.  You are currently using the master/slave
					system which sends 2(n-1) packets per frame where n is the
					number of players.  My new network mode sends n(n-1) packets
					per frame and treats every player evenly.  See the chart
					below of packets per frame:

									2(n-1) method:   n(n-1) method:
					2 players        2             2
					3 players        4             6
					4 players        6            12
					5 players        8            20 (OUCH!)
					6 players       10            30 (OUCH!)
					7 players       12            42 (OUCH!)
					8 players       14            56 (OUCH!)

						You may be asking why I am bothering you with this new
					network method if it sends more packets then the old one?
					I'll explain:  With the old network method, slaves had to
					wait for their packets to take 2 trips before they could move,
					whereas with the new method the packets need to take only 1
					trip.  Also with the new method the players are treated
					evenly.  For 2 players, the new network method is definitly
					the mode of choice since it sends the same number of packets
					AND the packets can be smaller since each computer only needs
					to send its own controls to the other computer, not everyone's
					controls (good for modem play).  It's up to you what your
					break even point is before you switch into the old network
					method.  I recommend: 1-4 New, 5+ Old.
						Now let me explain how the new method REALLY works.  Since
					every computer must call the movement code in the same order
					to stay in sync, all computers must wait until every packet
					is received for that frame before it can actually go into
					the movement code.  You could say that all the computers are
					half-slaves.  Your computer should always be ahead of the
					other computers.  If you are player 0, the packets you
					currently have might look like this:

					Chart for Player 0:

								  Player 0   Player 1   Player 2
					 Tic 0:    GOTMINE------GOT--------GOT------>     MOVE!
					 Tic 1:    GOTMINE------GOT--------GOT------>     MOVE!
					 Tic 2:    GOTMINE--X WAITING...   GOT         CAN'T MOVE!
					 Tic 3:    GOTMINE    WAITING... WAITING...    CAN'T MOVE!
					 Tic 4:    GOTMINE    WAITING... WAITING...    CAN'T MOVE!

						As soon as player 0 receives player 1's next packet,
					player 0 can call domovethings for tic 2.
						One interesting complication of the new network method is
					the timing.  If for some reason player 0 sends packets faster
					than player 1, then player 1 will have no lag and player 0
					will start with twice the normal lag which will increase
					until bad things happen.  See this chart:

							Player 0's side:         |   Player 1's side:
						 Player 0:  Player 1:       | Player 0:  Player 1:
			 Tic 5:   GOTMINE      GOT    (MOVE) |   GOT      GOTMINE  (MOVE)
			 Tic 6:   GOTMINE    WAITING         |   GOT      GOTMINE  (MOVE)
			 Tic 7:   GOTMINE    WAITING         |   GOT      GOTMINE  (MOVE)
			 Tic 8:   GOTMINE    WAITING         |   GOT      GOTMINE  (MOVE)
			 Tic 9:   GOTMINE    WAITING         |   GOT      GOTMINE  (MOVE)
															 |   GOT
															 |   GOT
															 |   GOT

						This can be corrected by sending a byte which tells the
					other computer how many packets behind it is.  You want the
					other computer to be a little behind.   Player 0's packet
					delta in this case would be 4.  Player 1's would be -3.

						Another interesting thing about this network method is
					that a slave's packet cannot be skipped like in the
					master/slave system.

						The actual code for everything described above is already
					in my GAME.C and working perfectly.  Go through the file
					searching for the keyword, "networkmode".  If it is non-zero,
					that's the new mode.  Look mainly at the section inside
					faketimerhandler and case 17 of getpackets.

					(I've talked about "2(n-1)" and "n(n-1)" networking modes.
					 Believe it or not, it's possible to do a network mode as
					 low as an "n" mode.  I haven't done it due to some
					 inherent problems.  I'd be impressed if you can figure out
					 how this one works)
				 -------------------------------------------------------------
				 2.   Now I'll type about the "KLAG-0 Technology" I promised you.

					The secret to life is, "
					NO CARRIER

						Why do you have to wait for another computer to tell you
					when to move when you already know where you want to go?
					So I added a fake set of variables that simulate
					where the player's x, y, z, and ang variables will be after
					it does sync crap with other computers.  I added a function
					called fakedomovethings which is called in the same fashion
					that domovethings would be called in a 1-player game.
					Fakedomovethings modifies ONLY the fake x, y, z, and ang
					variables.  It is a shadow of the processinput function.  It
					only needs the parts of processinput that modify the position
					of player[myconnectindex].  If it modifies real variables,
					the game will get out of sync.
						Sometimes the fake variables can get out of sync with the
					real variables.  This can happen if you walk into another
					player or anything else that moves.  This doesn't happen too
					often though and when it does, there are things that you can
					do to smooth out the jump to the right position.  This brings
					me to my other new function: fakedomovethingscorrect.
						The easy way to correct the player's position in a smooth
					way is to say something like: fakex += ((realx-fakex)>>3);
					This would effectively move the player 1/8th of the way closer
					to the real position.  The problem with this method is that
					the realx and fakex are not sampled at the same time,
					therefore creating more error than isn't any.
						So instead of comparing your fake position with where the
					real position was a few tics ago, I chose to compare both
					of them a few tics ago.  FIFO time!  To do this, I am keeping
					a fifo of the new fake positions and comparing the positions
					as the real positions come.  Now that they're being compared
					at the same tic, correction will only occur when you've truly
					run into a moving object.
				 -------------------------------------------------------------
				 I hope all this was as much fun for you to read as it was for
				 me to type.  If you don't understand it, then I was probably
				 just trying to confuse you with all the possible word
				 combinations I could come up with that contain the letters in:
				 "fake", "sync", and "fifo".
������������������������������������������������������������������������������
1/6/96    - Made RT.SHIFT highlighting in build.obj not pick up non-existent
					sprites in the selection rectangle.
������������������������������������������������������������������������������
1/9/96    - Fixed a rare hitscan overflow bug for sectors that had a
					ceiling&floor z-difference of more than 400000 z-units.
					(About 20 stories tall)
������������������������������������������������������������������������������
1/15/96   - Changed something in rotatesprite so that status bars could be
					clipped easily by using the clipping parameters.  (I never
					knew this didn't work until now)  This change will only affect
					your code if you were using bits 2+8 and the clipping window
					was something other than the standard (0,0,xdim-1,ydim-1).

			 - Improved fakedomovethingscorrect.  Before when there was an
					error, I was adding the difference into the current
					position.  This method conflicted with pushmove - where
					sometimes it never fully corrected the position leaving you
					stuck behind a wall.  Now I make your position the position
					it would have been if it had predicted the right position
					several ticks ago.  In other words, I set the my... variables
					to the real, older position then call fakedomovethings for
					every tic from the time of the real, older position up to
					the current time.
������������������������������������������������������������������������������
1/19/96   - Added snow reduction code to setpalette.  It takes about 4 ms
					to set a full palette.  I write during the horizontal retrace
					period to save time.
������������������������������������������������������������������������������
1/23/96   - Added hack to loadboard to make it load a board only from the
					group file if the last character of the filename is a 255.
������������������������������������������������������������������������������
1/25/96   - Fixed a stupid bug in UNIVBE segmented modes so it doesn't
					flicker anymore.  Why didn't you guys tell me about this
					sooner!
������������������������������������������������������������������������������
2/1/96    - Optimized cansee according to Peter's suggestions.
������������������������������������������������������������������������������
2/2/96    - Made setview calls not mess up the stereo vision variables.
������������������������������������������������������������������������������
2/5/96    - ATTENTIONS ALL PROGRAMMERS!!!  If you use use any VGA registers
					(such as 0x3da, or 0x3c6 - 0x3c9), you will need to re-code
					those sections of code.  Please make your BSTUB's work with
					this too!

					1.  If you use register 0x3da, limitrate, or qlimitrate, you
						 will need to do a special check to see whether or not the
						 video mode is VGA register compatible.  If you do not
						 check this, then the computer may lock up because register
						 0x3da simply doesn't change on certain video cards.
						 Unless you know the code won't be called in VESA mode, you
						 need to perform this check.  Here's an example of how you
						 can do this check:

					 Instead of calling limitrate (or your own 0x3da function):
						 limitrate();
					 Do this:
							 //extern char vgacompatible;
						 if ((vidoption != 1) || (vgacompatible == 1)) limitrate();

					2.  You must now use my function to set or get any palette
						 registers.  This means that the keywords "3c7", "3c8",
						 and "3c9" should not even exist in your code.  I really
						 didn't want to force you to use my palette functions, but
						 since VESA 2.0 supports non VGA compatible cards, you must
						 do it this way.  If you use setbrightness for all of your
						 palette setting, then you can ignore this.  Note that the
						 palette format here is VESA's palette format, which is
						 different than my other palette control functions.  It's
						 4 bytes and RGB are backwards.  Here are the function
						 prototypes:

					 VBE_setPalette(long palstart, long palnum, char *dapal);
					 VBE_getPalette(long palstart, long palnum, char *dapal);
						 palstart is the offset of the first palette to set
						 palnum is the number of the palette entries to set
						 dapal is a pointer to the palette buffer.  The palette
							 buffer must be in this format:
								 char Blue, Green, Red, reserved;
							 I think this format stinks, but since VESA 2.0 uses
							 it, the code will run fastest if the buffer is not
							 copied.  You can make your own cover up function if
							 you don't like this format.

						 This example sets up a wasteful gray scale palette:

						 char mypalette[1024];
						 for(i=0;i<256;i++)
						 {
							 mypalette[i*4+0] = (i>>2);   //Blue
							 mypalette[i*4+1] = (i>>2);   //Green
							 mypalette[i*4+2] = (i>>2);   //Red
							 mypalette[i*4+3] = 0;        //reserved
						 }
						 VBE_setPalette(0,256,mypalette);
������������������������������������������������������������������������������
2/7/96    - Did some minor optimizations to functions that use cliptype.
������������������������������������������������������������������������������
2/9/96    - ATTENTION PROGRAMMERS!!!  There is no such thing as a cliptype
				anymore!  Instead you use clipmasks.  Clipmasks are much better
				because it gives you full control of which bits are used to
				test whether or not a sprite or wall will block things.

				Let me refresh your memory on how cliptypes used to work:
					For CLIPMOVE, PUSHMOVE, and GETZRANGE:
						If (cliptype == 0) these bits were the blocking bits:
							(wall[].cstat&1), (sprite[].cstat&1)
						If (cliptype == 1) these bits were the blocking bits:
							(wall[].cstat&64), (sprite[].cstat&256)
					For HITSCAN, cliptype was not passed and assumed to be 1:
							(wall[].cstat&64), (sprite[].cstat&256)

				Note that I added these 2 defines in BUILD.H:
					#define CLIPMASK0 (((1L)<<16)+1L)
					#define CLIPMASK1 (((256L)<<16)+64L)

				In order to make clipmasks work, I had to change the parameters
				to these 4 important functions.  CLIPMOVE, PUSHMOVE, GETZRANGE,
				and HITSCAN.  For CLIPMOVE, PUSHMOVE, and GETZRANGE, I simply
				made the cliptype from a char to a long.  For HITSCAN, I had to
				add the clipmask parameter at the end.  Here are the new function
				prototypes:

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

pushmove (long *x, long *y, long *z, short *sectnum,
			 long walldist, long ceildist, long flordist, unsigned long clipmask)

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

hitscan (long xs, long ys, long zs, short sectnum, long vx, long vy, long vz,
			short *hitsect, short *hitwall, short *hitsprite,
			long *hitx, long *hity, long *hitz, unsigned long clipmask)

				You should convert your source code using these rules:
					* For CLIPMOVE, PUSHMOVE, and GETZRANGE, look at the last
					  parameter.  If it is a 0, then change it to say CLIPMASK0.
					  If it is a 1, then change it to say CLIPMASK1.

					* For HITSCAN, add a new parameter that says CLIPMASK1.

					* If you have your own movesprite function, then you will
					  need to make the cliptype parameter into a long variable.

			 - If you want to set a range for hitscan, or optimize its
					performance, you can now extern these 2 variables from
					the engine.  You can set them just before you call hitscan.
					If you DO choose to screw around with these variables, then
					you must set them before EVERY hitscan in your code.
					I have them defaulting to a really high number, totally out
					of range of the map, but not high enough for a subtraction
					to overflow it.  These variables are used in hitscan only
					to compare whether the new hit is closer then the last one.

					long hitscangoalx = (1<<29)-1, hitscangoaly = (1<<29)-1;
������������������������������������������������������������������������������
2/13/96    - Fixed a bug in clipmove that showed up in the latest upload
					 related to sector searching.
������������������������������������������������������������������������������
2/18/96    - Re-wrote waitforeverybody / case 250 of getpackets, so the slave
					 sends acknowledge packets in a better way.  Before there was
					 a possibility of a slave sending an invalid acknowledge
					 packet if the last packet received was not 250.

			  - ATTENTION PROGRAMMERS!  Setgamemode now returns -1 if the mode
					 is invalid or 0 if it is valid.  Since the engine no longer
					 quits to DOS from an invalid video mode, you must program the
					 -1 case or else the computer will probably lock up!
������������������������������������������������������������������������������
3/5/96     - Solved tile index corruption bug in EDITART.  After looking at
					 the latest art files I have from you, It looks like SW has
					 this problem, but DUKE and BLOOD seem ok.  Let me explain:
						 In Editart 'V' mode (with no tile report), when you use
					 INSERT or DELETE, it very quickly moves all tiles after the
					 cursor forward or back 1.  With multiple art files, this
					 operation would be very slow, since I'd have to load and
					 save every art file after the current one.  But I figured
					 out a way to still keep it fast - instead of reading and
					 writing all the later art files, I instead changed just the
					 headers that tell which tile indeces the art files start and
					 end with.  This works nice when you have 1 big art file, or
					 only 1 person using EDITART to change stuff.
						 Unfortunately, some of you have been passing around
					 individual art files and this is where the problem comes in.
					 If either person used INSERT of DELETE in 'V' mode and passed
					 an individual art file to another person, the headers could
					 possibly conflict and cause strange lockups or crashes in
					 EDITART, BUILD, or GAME.
						 So I fixed the problem by making EDITART only change the
					 CURRENT art file.  Insert mode will now delete the last
					 tile in the CURRENT art file, so be careful that the last
					 tile in the CURRENT art file is blank.  Delete will now
					 insert a blank tile at the end of the CURRENT art file.
					 If your ART files are already corrupt, you can run
					 RSIZEART.EXE and agree on a number of tiles per file.
������������������������������������������������������������������������������
4/27/96    - Fixed a bug where rotatesprite seemed to clip the tile
					 to the viewing window even though stat&8 (don't clip
					 to startumosts/startdmosts) was set.  The problem was
					 actually related to an optimization in the VESA blitting
					 code that copies only the screen if it thinks nothing
					 was drawn outside of the viewing window.
������������������������������������������������������������������������������
6/15/96    - I must have added a whole bunch of unnoticable optimizations
					 since the last time I typed stuff into this file, but since
					 I didn't keeping track of it, I couldn't tell you what they
					 were off hand.

			  - I added support for the 'new' Crystal Eyes in such a way where
					 you don't have to mess with the timer.  I use the real-time
					 clock (IRQ8) in order to avoid conflicts.  Crystal Eyes mode
					 works only in VESA 2.0 modes with at least 4 pages.  The nice
					 thing about this mode is that you can switch in and out of
					 it easily during the game.  There are 2 very simple functions
					 and 3 variables you can extern.  To use Crystal Eyes mode,
					 simply call initstereo().  Initstereo should be called after
					 setgamemode and the palette is loaded into the VGA.  To
					 return back to normal mode, simply call uninitstereo().
					 Here's what the code would look like:

					 extern long stereomode, stereowidth, stereopixelwidth;

					 if (KEYTOTOGGLESTEREOMODE)
					 {
						 KEYTOTOGGLESTEREOMODE = 0;
						 if (stereomode == 0)
							 initstereo();
						 else
							 uninitstereo();
					 }

				Notes:
				  * The following 2 variables can be changed any time, and
					 should always be >= 0.

							stereowidth: distance between left and right eyes
					 stereopixelwidth: parallax - pan offset in pixels between
											 left & right screens

				  * initstereo automatically sets stereomode to nonzero and
					 uninitstereo automatically sets stereomode to 0.
������������������������������������������������������������������������������
7/5/96     - Made cache1d.obj support up to 4 group files at the same time.
					 To use multiple group files, simply call initgroupfile again.
					 You need to uninitgroupfile only once.  This is useful if
					 users want to add their own .ART, .VOC, or other files and
					 distribute it all in one file without telling people they
					 need to back up stuff.  For example:

					 Beginning of program:
						 initgroupfile("duke3d.grp");
						 if (usergroupfile)
							 initgroupfile(usergroufile);

					 End of program:
						 uninitgroupfile();

					 Here's the order cache1d will search for a file when
						 kopen4load is called.  Remember that the second parameter
						 to kopen4load is the searchfirst parameter.  If you set
						 this variable to non-zero, you can tell kopen4load to search
						 the main group file only.  This is useful if invalid user
						 files are detected.

						 if (searchfirst == 0)
						 {
							 1. Look for it as a stand-alone file
							 2. Look for it in the 4th group file (user group file)
							 3. Look for it in the 3rd group file (user group file)
							 4. Look for it in the 2nd group file (user group file)
						 }
						 5. Look for it in the 1st groupfile ("duke3d.grp")
						 6. If file still not found, return -1

			  - Fixed a stupid bug with choosing the side of a red line in BUILD
					 2D edit mode.
������������������������������������������������������������������������������
7/24/96    - Increased MAXTILES to 6144.  Fixed some scrolling errors related
					 to MAXTILES in 'V' mode of EDITART and BUILD.
������������������������������������������������������������������������������
8/20/96    - Made it possible to safely default to NORMAL mode 320*200 when
					 VESA is not supported or found.  Check out the beginning of
					 my setup3dscreen function.  It shows how you can offer the
					 player a choice of quitting to DOS or continuing in NORMAL
					 mode.  It is very annoying when setting up multiplayer games
					 when the game always quits to DOS because somebody forgot
					 to load their VESA driver.
������������������������������������������������������������������������������
9/6/96     - Moved internal function, setfirstwall, in BUILD.OBJ into
					 ENGINE.OBJ because it can be useful to game programmers.
					 This function sets the first wall of a sector to be the
					 wall index passed.  This function is useful for setting
					 the hinge wall for slopes or relative alignment.  (ALT-F
					 uses this function)  Here's the function:

					 setfirstwall(short sectnum, short newfirstwall)

			  - Added a variable, clipmoveboxtracenum, to ENGINE.OBJ which can
					 be externed.  As a special case, if you set it to 1 just
					 before calling clipmove, then clipmove will return when it
					 first hits a wall - in other words, the (x,y,z) that clipmove
					 returns will be before any sliding calculations occur.  Be
					 sure to set clipmoveboxtracenum back to 3 after calling
					 clipmove.

						 This is how it is defined in ENGINE.OBJ:
					 long clipmoveboxtracenum = 3;

						 Example of calling clipmove with no sliding:
					 clipmoveboxtracenum = 1;
					 i = clipmove(...);
					 clipmoveboxtracenum = 3;
������������������������������������������������������������������������������
9/25/96    - Removed support for specially optimized TSENG, Paradise, and S3
					 modes.  If you want the engine to run as fast on these cards,
					 get Scitech Software's latest Vesa 2.0 driver.

			  - ATTENTION PROGRAMMERS!  Added video mode changing during the game.
					 I moved the option, xdim, and ydim parameters from initengine
					 into the setgamemode function.

				 Here are the updated prototypes for the 2 functions:
					 initengine();
					 setgamemode(char newvidoption, long newxdim, long newydim);

				 You are free to call setgamemode as often as you like.  If you
					 have very low memory, it's possible that the call will fail
					 and quit to DOS with one of those awful CACHE SPACE ALL LOCKED
					 UP messages.

				 Note: When updating your code, be careful to not rely on
					 vidoption, xdim, of ydim being valid until your first
					 setgamemode call.  If you're not careful with this, you may
					 get a divide by zero or something.  I had a bug in my GAME.C
					 where I was calling setview between initengine and
					 setgamemode.  I had to move setview after setgamemode.

			  - Added function to the engine that returns all valid VESA modes.

					getvalidvesamodes();

				 This function prepares the list of valid VESA modes in these
				 new variables which I put in BUILD.H.  This function needs to
				 only be called once, but it doesn't hurt to call it multiple
				 times since I have a flag that checks if it has already been
				 called.

					EXTERN long validmodecnt;
					EXTERN short validmode[256];
					EXTERN long validmodexdim[256], validmodeydim[256];

					validmodecnt - number of available 256 color VESA modes.
					validmode[] - array of vesa mode numbers (640*480 is 0x101, etc.)
					validmodexdim[] - array of x dimensions for each mode
					validmodeydim[] - array of y dimensions for each mode

				 In my GAME.C, I have code that cycles through all VESA modes
					 and screen buffer mode when you press F4.  Search for the
					 keyword "//F4" to find it.

				 Note: Be careful when you call setgamemode!  Be sure that it
					is not called inside any function of sub-function of
					faketimerhandler because this would be in the middle of the
					drawing code.  Actually, the safest place to call setgamemode is
					right after nextpage in your main drawing loop.
������������������������������������������������������������������������������
12/13/96   - Fixed the nasty caching bug first found in the Plutonium version
					of Duke3D.  I was using copybuf when I should have been using
					copybufbyte.

			  - Made the '>' key in 3D EDIT MODE not lock up any more for
					textures that have non power of 2 tilesizy's.
������������������������������������������������������������������������������
2/12/97    - Optimized mirror code so it x-flips only the bounding rectangle
					 (x AND y).  Actually calculates left & right boundaries instead
					 of using the horizontal line checking stuff which didn't really
					 work all the time anyway.
������������������������������������������������������������������������������
5/22/97    - Frank noticed a divide overflow in clippoly when drawmapview was
					 in hi-res mode.  -fixed by lowered precision from 16 to 12
					 bits.
������������������������������������������������������������������������������
6/2/97     - Added support for the Nuvision 3D-Spex stereo glasses.  I renamed
					 initstereo to setstereo and got rid of uninitstereo.  Here's
					 complete documentation for all of the new stereo glasses stuff:

					setstereo(0);   //Set to default normal non-stereo mode
					setstereo(1);   //Use Stereographics Simuleyes (white line code)
					setstereo(2);   //Use Nuvision 3-D Spex stereo (uses LPT1)

						//You can extern these 2 variables from the engine to add
						//your own stereo adjustment code
					extern long stereowidth = 23040;
					extern long stereopixelwidth = 28;

						It would be nice to allow the adjustment of these variables
					inside the game, but if you're too lazy, it would be nice to
					have some way of modifying it.
						 Please do not extern stereomode any more since I use it now
					to uninit the old mode automatically.  Use your own current
					stereo mode variable.
������������������������������������������������������������������������������
10/4/97    - I have upgraded to Watcom 11.0.  The only changes I had to make
					in the engine were some "#pragma push" and "#pragma pop"
					calls in BUILD.H since the default structure packing alignment
					was moved from 1 to 8.  The sector, wall, and sprite structures
					are already aligned well and must remain the same for .MAP
					files to remain compatible.

			  - Increase MAXTILES for all utilities (EDITART,BUILD,GAME,etc.) to
					9216 for Xatrix.

				 You may have some initial bugs with EDITART when you add new
					tiles for the first time.  I haven't touched the code in years
					and I'm afraid if I did, I'd mess it up more.  One bug I know
					of is this:  Let's say you have 256 tiles per art file, and
					then you add 1 silly tile way up at picnum=8500.  When you
					save and quit, your directory may look like this:

						TILES000.ART
						TILES001.ART
						TILES002.ART
						TILES029.ART

					When you load EDITART again, that 1 tile will not appear unless
					you kind of mess with it and scroll around for a while.  This
					is because EDITART loads TILES###.ART files until 1 is not
					found, meaning that it will stop when it doesn't find
					TILES003.ART.  There are probably some other nasty bugs like
					this.  To get around it, you could add 1 tile to the next file,
					save and quit.  Or you could use RSIZEART.EXE to make 1 huge
					ART file, add 1 tile way up high (if you have enough memory),
					and then run RSIZEART.EXE again to 256 tiles per file or
					whatever you like.  Remember that the ART files need to be split
					in such a way that each individual .ART file must fit in
					memory, or else EDITART will crash.

			  - It's time I document how you can add voxels to the Build engine.
					The code is not the cleanest, but at least it works.  First
					you must load each voxel into memory using the qloadkvx
					function.  You specify the filename of the .KVX file and an
					index that you want to use to reference the voxel.  You can
					pack .KVX files in a .GRP file if you want.  The index must
					be in this range: 0 <= index < MAXVOXELS where MAXVOXELS is
					currently 512.  This index works sort of like a picnum, but I
					have a totally separate array for these indeces so you don't
					need to mess with your art file at all.  Since qloadkvx
					allocates memory off of my cacheing system, you must call it
					after loadpics which allocates all memory.

					Function parameters:
						void qloadkvx(long voxindex, char *filename)

					loadpics("tiles000.art");
					qloadkvx(0L,"voxel000.kvx");
					qloadkvx(1L,"voxel001.kvx");

					Now to actually display the voxel, you need to set the
					(sprite[?].cstat&48) to equal 48 like this:
						sprite[?].cstat |= 48;
					I have no special collision code for voxels.  They are simply
					treated as face sprites.

					If ((sprite[?].cstat&48) == 48)
						You should set the sprite[?].picnum to equal the VOXEL
					index of the voxel that you passed to qloadkvx.  If you don't
					do this you will see nothing.  To handle this index remapping
					it is a good idea to make some array like this:
						short picnumtovox[MAXTILES];
					and save the array in some file (or in the .GRP file)

					Many other fields of the sprite structure also affect voxels,
					such as:  ang, shade, pal, xrepeat, yrepeat.

					Note: To view voxels in the Build editor, you will need to do
					the same qloadkvx calls in your BSTUB.C.

					And now a warning:  Voxels tend to draw the fastest when
						they are tall and thin.  For example, a telephone poll
						would work just great.  They slow down very quickly as
						you add detail.  This is why you don't see any large
						voxels in SW and Blood.

			  - Something you should know about this version of the engine:
					 Over the summer, I got a lot of pressure from GT to add
					 MMX support to the Build engine, so I bought a Pentium II.
				 I messed around with the code quite a bit and discovered
					 that MMX really didn't help at all.  In fact, in some ways
					 it made the code slower.  The problem is that the inner
					 loops of Build are already optimized really well.  I have
					 found that MMX is useful only because it gives you extra
					 registers.  Unfortunately, the Build loops don't need extra
					 registers since they aren't very complex.
				 Now there are 2 major differences between an old Pentium and
					 a Pentium II.  A Pentium II is like a Pentium PRO with MMX.
					 So I tried to optimize Build for Pentium PRO's.  I was
					 actually able to get about a 50% speed increase mostly due
					 to avoiding those awful partial stalls.  But there are
					 SEVERAL catches:

					 1.  First of all, you need to enable WRITE-COMBINING for
					 video memory to get all this extra speed.  One popular
					 program which does this is FASTVID.  You should be able
					 to find it easily on the net.  If you do not enable
					 WRITE-COMBINING, the engine actually runs SLOWER than
					 the original version!  Unfortunately, neither DOS nor
					 WINDOWS 95 enable WRITE-COMBINING by default, so you need to
					 load a driver.  Even worse, if you're in WINDOWS 95, the
					 program which enables WRITE-COMBINING will crash because
					 you can only set the PPRO registers in Priviledge Level 0.
					 You can still enable WRITE-COMBINING in WINDOWS 95, but you
					 have to run the program in your AUTOEXEC.BAT file.  I wish
					 you all good luck on getting the average user to be able
					 to accomplish this feat.  Quake has the same problem.
					 You'll find the same thing in their documentation.

					 2.  The second catch is that my code tries to auto-detect
					 whether you have a Pentium, Pentium MMX, Pentium PRO, of
					 Pentium II.  Of course this means, that if you don't have
					 an Intel processor, it's possible that the auto-detect code
					 may crash.  I haven't had the opportunity to test this
					 myself.  And since I can never guarantee it will always work
					 I made a way for you to disable the new code altogether
					 should this happen.  Here's how you disable the new code
					 in the Build engine:

					 extern long dommxoverlay;   //(from engine.obj)

					 Before you first call initengine, set dommxoverlay = 0;

					 The default is dommxoverlay = 1 and it will run the code.

repo_name: orochi/nblood

license: mit