                           Ultra VGA Engine 32
                              Version 1.40
                                    
                Copyright (c)1996-97 Constantine Prouskas
                          InterAction software
                                    
Prologue                                                3
Quickstart                                              3
Requirements                                            3
What's UVE32?                                           3
License Agreement                                       4
Technical Information                                   5
UVE Theory                                              6
 Frames                                                6
 Pages                                                 7
  The VGA page                                         7
  The Hidden page                                      7
  The Background page                                  7
  The Tiles page                                       7
 Sprites                                               8
  Loading sprite images                                8
  Displaying a sprite                                  9
  Sprite Chaining                                     10
  Moving sprites                                      10
  Sprite collision                                    11
  Deleting sprites & images                           11
  Other sprite attributes                             12
 Palettes                                             12
 Palette maps                                         13
 Tiles                                                13
 PCXs                                                 14
 Fonts                                                14
 FLIs                                                 15
 WAD files                                            15
UVE Interface                                          16
 Unit interface section                               16
 Other variables                                      17
 Functions & Procedures                               17
  General Purpose Commands                            17
  Beginner Commands                                   18
  Intermediate Commands                               21
  Advanced Commands                                   25
Supporting Utilities                                   30
 UVSINFO                                              30
 PCX2UVS                                              30
 PCX2UVT                                              30
 LIBMAKER                                             30
Known bugs... err.. features                           31
UVE32 Frequently Asked Questions                       31
Where can I get UVE32?                                 31
Contacting InterAction software                        34
UVE mailing list                                       31
Registering                                            34
 Credit card orders                                   35
 Other (non-credit card) orders                       36
Epilogue                                               36


Prologue

Right off the bat, I'd like to clear up some things:

    Version 1.40 is the second public release of UVE32 and, as such, is
 still prone to various bugs and errors.  I've been using UVE32 for more
 than two years now and everything is pretty much ironed out, but I wrote
 it, and I'm sure someone out there will try something I had never
 thought of and crash it.  So, if you encounter any bugs, please report
 them to me and I will correct them in the next release.  Read the
 section on contacting InterAction software for further info.

    UVE32 is shareware.  Try before you buy.  And we all know what that
 means.  If you like UVE32 and continue to use it, you must register it.
 Registering not only clears your conscience, but also entitles you to
 free updates, loads of utilities and support.  Read the section on
 registering for more information.




Quickstart

For those of you who are really, really in a hurry, take these steps to
get you started:

    Unzip UVE32.
    Run BENCH.EXE to see if your VGA is fast enough for UVE32
animation.
    Run UVEDEMO.EXE for an example of UVE32's capabilities.
    Load up Pascal and run the examples provided.
    Off you go!



Requirements

What you need to run UVE32:
    A computer.  I guess you have one of those already.  Must be at
 least a 386, though, because of the 32-bit code.  Not much of a
 limitation, if you ask me.
    A VGA card.  The faster the better.
    Turbo Pascal v6.0 or Borland/Turbo Pascal v7.0.

That's it.




What's UVE32?

Let's start with the easy stuff.  UVE32 is a sprite-based graphics &
animation unit for Turbo Pascal 6.0 & Borland/Turbo Pascal 7.0.  Drop to
DOS and run BENCH.EXE and UVEDEMO.EXE to see an example of  what this
baby can do.

UVE32 stands for Ultra VGA Engine 32-bit.  Ultra is there to distinguish
UVE32 from the VGA Engine series, which is a set of three older
libraries (VENGINE, VENGINE2 and VENGINE3).  UVE32 has been rewritten
from scratch, using a different animation algorithm.  VGA because UVE32
supports only the VGA (CGA support is on the way ;-).  Engine because it
provides the horsepower for an animation-intensive application, i.e.
(who am I trying to fool) GAMES. 32 because it contains 32-bit code and
is capable of running in protected mode. For the time being, 32 bit code
is more or less useless because of Borland's 16-bit compiler, but is
included for when a decent 32-bit compiler is released.

Here's a quick run-down of UVE32's features before we get started.

    runs in VGA 320x200 256 colors
    500 simultaneously active sprites
    32K maximum sprite dimension (width/height)
    64K maximum single sprite size
    sprite chaining
    sprite collision detection
    automatic memory management
    virtual coordinates in the -32K...+32K range
    animation window
    14 loadable fonts
    static or scrolling background
    transparency
    translucency
    palette maps
    palette cross-fades
    PCX support
    FLI support
    WAD file support

In my experience, sprite libraries can be divided into the following
categories:

    Easy to use (i.e., slow)
    Powerful (i.e., extremely user-unfriendly)
    Fast and user friendly (and written in C)

UVE32 offers an easy to use interface as well as low-level routines for
squeezing out that last bit of potential.  If you are a beginner, you
will find the interface very simple to use.  If you are more advanced,
you will not be held back by the inherent limitations that inevitably
come with easy user interfaces.

UVE32 might not run well on your system, depending on your VGA card.  If
you are one of those people that read the documentation before running
every executable in sight (fat chance), I suggest you drop to DOS and
run the demonstration and the benchmarking program to discover if
everything runs as expected.  Unless your VGA is a painfully slow one,
however, I don't expect you will run into any difficulties.

Before we get into the intricacies of UVE32, take a moment to read the
License Agreement, which contains some important information concerning
the use of  UVE32.




License Agreement

InterAction software grants you the right to use Ultra VGA Engine 32
("UVE32").  For purposes of this section, "use" means loading UVE32 into
RAM, as well as installation on a hard disk or other storage  device.

You may use the shareware version of UVE32 for a period of 21 days,
after which you must either delete it or obtain the registered version.

You may freely distribute, transmit and store the shareware version of
UVE32 on any electronic media, including but not limited to floppy
diskette, hard disk, CD-ROM or other storage device, provided that you
do not remove, edit, modify, or otherwise alter the original files of
UVE32.  You may not distribute the registered version of UVE32.

You may not modify, translate, disassemble, decompile, reverse engineer
or create derivative works (excluding software that uses UVE32 directly)
based upon UVE32.

You may use UVE32 in your own software, provided that you have complied
with the registration requirements set forth by InterAction software.
You may not use the shareware version of UVE32 in any piece of software
that will become publicly available, including but not limited to public
domain, freeware, shareware and commercial/retail software.

UVE32 is copyrighted by InterAction software and is protected by
international copyright laws and international treaty provisions.  You
must treat UVE32 like any other copyrighted material.  You
agree to use your best efforts to see that any user of UVE32 licensed
hereunder complies with this agreement.

This software and accompanying written materials (including instructions
for use) are provides "as is" without warranty of any kind. Further,
InterAction software does not warrant or make any guarantees
representations regarding the use, or the results of use, of the
software or written materials in terms of correctness, accuracy,
reliability, currentness, or otherwise. The entire risk as to the
results and performance of the software is assumed by you.

The above is the only warranty of any kind, either expressed or implied,
including but not limited to, implied warranties of merchantability or
fitness for a particular purpose.  In any case, InterAction software
shall not be liable for any direct, indirect, consequential or
incidental damages (including damages for loss of business profits,
business interruption, loss of business information and loss of data)
arising out of the use or inability to use UVE32, even if InterAction
software has been advised of the possibility of such damages.

You acknowledge that you have read this agreement, you understand this
agreement, and understand that by continuing the installation of UVE32,
by loading or running UVE32, or by placing or copying UVE32 onto your
computer hard drive, you agree to be bound by this agreement's terms and
conditions.




Technical Information

NO!  NO!  NO!  I said READ the License Agreement, not SKIP OVER it!  As
boring as it sounds, you must read it before using UVE32.  You have?
That's OK, then.  Let's get moving.

Note:  This section contains, as the heading would suggest,  technical
information.  You do not need to know anything contained in this section
to use UVE32.  Instead, it's meant to provide some insight at how UVE32
works.  If you haven't got any experience with the VGA and the VGA
hardware, it's best to skip this section altogether.

UVE32 runs in the standard VGA mode 0x13 (19 to you ten-fingered
people).  That's your average 320x200 256-color mode supported by the
BIOS in every VGA since, well, the EGA.  Okay, so it's only got one page
and an odd aspect-ratio, but when it comes to rock-solid animation and
simple code (the latter bearing the most weight for me personally), the
fact is, you can't beat mode 0x13's non-banked linear bitmap
organization.

UVE32 is based on a, for lack of a better term, page-copy algorithm.  To
those of you familiar with dirty-rectangle animation, you can think of
the whole screen as being a single dirty rectangle, updated each frame.
What does this mean?  It means that all preparation for a frame is done
in non-displayable system memory area the same size as the screen (a
"page").  Once the whole frame is complete, the "hidden" page is simply
copied onto the visible screen.  You might think copying the whole
screen every frame is overkill; and you'd be right (didn't expect me to
say that, did you?), but only in light load conditions.  In heavy load
conditions and in cases where the whole screen changes from frame to
frame (such as in panning around a virtual background), this is much
faster than trying to maintain a list of updated areas and optimizing it
so that each pixel is drawn once and only once.

I'd like to take a moment to point out that when I came up with this
sort of algorithm, I didn't have a clue about page-flipping (or any
other animation technique, for that matter), and it seemed like the
intuitive thing to do.  I have to admit, though, that, even now, I would
be hard pressed to find another algorithm that can deliver flawless
frames given an adequately fast VGA.  Also, a little known fact is that
page-flipped modes are built around the VGA's vertical sync/retrace
signal, which cannot be relied upon, because there is no specification
about exactly when it should occur.  Some of the other page-flipped
libraries around flickered hideously when I ran them on my computer.

All drawing is done in system memory, which is much faster that video
memory.  Since the image has to be copied to video memory in the end,
however, video memory speed becomes the ultimate bottleneck, and,
believe me, a narrow bottleneck it is.  On my slow ISA VGA, video access
takes 50% of a 50-ms frame.  It can get much worse than this, though.
Some VGAs in my university lab (no names, please) take more than 500ms
to copy the page onto display memory.  FIVE HUNDRED milliseconds for a
maximum of under 2 frames per second!  You need not worry if you have a
local bus VGA, however, and I've found that all Trident-based cards I've
tested peak at about 20fps, which btw is fine for animation.  On a
typical 486-33 with a local bus VGA, a sustained rate of 35-40 frames
per second is not extraordinary.

Apart from the VGA access time bottleneck, another potential problem is
shearing.  Shearing occurs when the VGA is scanning displayable data
while the CPU is copying from the hidden page to the visible page.
Let's just say for a second that the CPU cannot copy data as fast as the
electron beam sweeps the screen (usually 70Hz).  Let's also assume that
the electron beam starts scanning when the CPU is halfway through
copying data, and that it catches up with the CPU at some point within
the displayable data area.  Everything displayed up to that point will
belong to the new frame, and everything below will belong to the old
frame, since the CPU hasn't had a chance to update it yet. The result
will be a frame composed of parts of two different frames.

Shearing is bad, but not as bad as you might think.  It is only
noticeable on a slow VGA, with a continuously scrolling background of
specific pattern types and even then, it registers as some sort of
flicker (not the type you're used to, just some sort of color dimming at
specific scanlines of the screen).  Shearing is inherent to beam
scanning anyway, since the screen is not updated all at the same
instant.  If your VGA is capable of copying the whole page in the 70Hz
time window, then shearing should not pose a problem.  In my experience,
you can only see it when a disk caching program kicks in and writes to
disk during the memory move (btw, in UVE32, interrupts are not disabled
during a memory move).




UVE Theory


Frames

Note:  This section, despite being the basis of everything else,
contains a lot of forward references.  If you don't understand what's
going on, simply go on and come back once you've read the rest of the
UVE32 theory.

Frames are the building blocks of animation.  They are the same type of
frames used in traditional cinema and television animation.  A typical
UVE32 frame cycle goes as follows:
    First, the background is copied onto the workarea (the hidden
  page), erasing everything from the previous frame.
    The scrolling background, if enabled, is constructed and placed on
  top of the background.
    The sprites are copied, with sprite 500 going first and sprite 1
  last.  This means that, as viewed on the screen, sprite 1 is on top and
  sprite 500 is at the very bottom.
    The frame is now complete and is made visible on the screen.
    If there is still time, UVE32 waits until the extra time expires
before starting on the next frame.

I didn't know when I first built UVE32, but the same frame technique is
used by Mortal Kombat and Quake (in videomode 0).  It's the most
efficient way to do animation when the whole screen changes from frame
to frame.  If you know of any more games that use this technique, I'd
like to hear from you.

Related functions:
procedure ia_waitretrace;
procedure ia_doframepart1;
procedure ia_doframepart2;
procedure ia_doframepart3;
procedure ia_doframe;
procedure ia_setcycletime (i:integer);



Pages

A page is simply a region of memory that can hold a 320x200 image
(actually it's a bit more that that, but who's counting).  UVE32 has 4
predefined pages, on top of which you can declare your own, should that
be necessary.  These are:


The VGA page

The vga page is the only page of the four that is visible to the user.
This page holds the contents of the visible screen, and any change to
that page will be immediately visible.


The Hidden page

The hidden page is where virtually all work takes place.  To avoid all
complications relating to updating the vga page in real time, the frame
is first prepared in the hidden page and, once it is complete, is copied
onto the vga page, thus becoming visible.


The Background page

The background page holds, rather unsurprisingly, the background image.
At the beginning of each frame the background page is copied onto the
hidden page to erase everything that was previously there and start
fresh for the new frame.


The Tiles page

The tiles page is not used for drawing, but is rather an area of memory
that tiles are stored in.  It can hold up to 255 16x16 tiles.  Note that
this page is not automatically allocated, as it is not needed if there
is no scrolling background.  UVE32 will automatically allocate this page
once ia_setscrolling (which enables a scrolling background) is called.

Related functions:
procedure ia_copypage (source,dest:word);
procedure ia_putpixel (where,x,y:word;c:byte);
function ia_getpixel (where,x,y:word):byte;
procedure ia_hline (where:word;x,y,len:integer;c:byte);
procedure ia_vline (where:word;x,y,len:integer;c:byte);
procedure ia_line(where:word; x1,y1,x2,y2:integer;color:byte);
procedure ia_box (where:word;x1,y1,x2,y2:integer;c:byte;filled:boolean);
procedure ia_box_clip
(where:word;x1,y1,x2,y2:integer;c:byte;filled:boolean);
procedure ia_cls (where:word;col:byte);
procedure ia_fadein (where,time:word);
procedure ia_newpage (var p:pagepointer; var where:word);



Sprites

A sprite is, simply put, an animated graphic object on the screen.  An
example to illustrate: in PAC-MAN, both the thing the player controls
(moves) around the maze (that yellow circle with the big mouth; I
suppose it's called the "pac-man") and the ghosts are sprites, in the
sense that they are movable entities separate from something static like
the background.

Now, I must confess that if this is the first time you hear about
sprites, you are probably totally confused by my explanation; and anyone
short of a nuclear scientist would be.  The best solution is to run some
of the examples included with UVE32 to get an intuitive idea about what
sprites are.  For the rest of this discussion, I will assume that you
understand the word sprite has nothing to do with fairies or pixies in
this context.  (Although, just to confuse you more, it is possible to
have a sprite that looks like a fairy.  On second thought, forget I just
said that.)

A sprite is an object that has coordinates (so it can move around) along
with some other attributes, the most important of which is what it looks
like; that is, what it's physical representation on the screen is.  A
sword, a dragon, a monster, all these are different sprites in the sense
that what you see on the screen can be uniquely identified by its
appearance.  Often the term sprite is used to refer to the picture that
is visible on the screen, but that's not what I am going to follow,
because beginners will get completely lost.

To recap:  A sprite is an object that has coordinates along with other
attributes, and which is linked to its image, its likeness, its
semblance, which is a picture that is used to represent that sprite on
the screen.  That picture is not necessarily unique.  For example, ten
swords displayed on the screen are ten different sprites, in the sense
that each one is individually controllable.  However, they are all
linked to the same image.  They are ten different objects with a common
appearance on the screen.

I will use the term "sprite" when referring to an object and the term
"image" when I am referring to the picture that represents the sprite.

The reason the sprite is different from its image is memory constraints.
Imagine 100 identical swords displayed on the screen.  Clearly, to store
the same image 100 times along with each sprite would be clearly a waste
of memory space.  We therefore separate the image from the sprite, and
from within the sprite structure, we select one of the available images
to represent it.


Loading sprite images

Okay.  Before an image can be used, it must first be loaded into memory.
In UVE32, there are 500 slots (positions) for images to be placed in,
from 1 to 500 (0 has a special meaning, as we will soon see).  Each slot
can be thought of as a pigeon-hole that can hold a single image.  Let's
say there's a file called "pacman.uvs" that contains an image  and you
want to load it into slot 7.  You don't need to worry about memory
allocation and the like, you just say

     ia_loadspr ('pacman.uvs',7);

Note that, although the function name is ia_loadspr, it doesn't load a
sprite (sprites cannot be "loaded" in the normal sense); it loads an
image.  At this point it might be a good idea to refer to the examples
to see how images are loaded.

A slight digression:  .UVS files hold a single sprite image, whereas
.UVL files hold multiple images that are loaded into consecutive
locations.  UVE32 automatically detects whether a given file holds a
single or multiple images, so you don't need to stick to the extensions
(but it's better if you do, because some UVE32 utilities assume default
extensions).

It is important to realize that loading an image into memory does not
display it on the screen in any way. This is so important it bears
repeating.  Loading an image does not cause anything to be displayed on
the screen.  You are simply loading an image and putting it into one of
the 500 available slots.  The number of the image's slot is the image's
id; the way in which it will be referred to when you want to use it.
Continuing the example above, every time you want to use the image
'pacman.uvs', you will refer to it by using the number 7, the number of
it's slot.

As a sidenote, UVE32 sprites are use a proprietary RLE & zero-byte
variant compression method for fast rendering.  This offers great
performance but screen-space clipping is hell.  I definitely wouldn't
recommend it to the faint-hearted :-)

Related functions:
procedure ia_loadspr (filename:string; n:integer);


Displaying a sprite

Now that the image has been loaded, we need to define a sprite and link
it to that image.  Up to 500 sprites can be simultaneously defined
(ranging from 1 to 500).  As mentioned earlier, defining a sprite is as
simple as linking it to an image, so it possesses an external, physical
representation and therefore is able to be displayed.  This is easily
done, if we remember that each image has it's own unique slot number.

A sprite is defined by linking it to an image.  As soon as a sprite is
linked to an image, it is displayed (not instantly, though; you have to
wait until a frame occurs).  Sprites are held in an array of 500
elements, with each item being a single sprite.  This array is called
spr.  So, if we want to access the 10th sprite we would do something
along the lines of

     with spr[10] do
     begin
          ...
     end;

So, how do we link the image with the sprite?  Couldn't be easier.  No,
really.  Simply set the n field of the sprite in question to the number
of the image slot.  So, from above, if the pacman is loaded into slot 7:

     with spr[10] do
     begin
          n:=7;
     end;

or simply:

     spr[10].n:=7;

Done!  Now, in the next frame, the sprite will appear at (0,0) screen
coordinates.  Of course, in a real application, some of the other fields
should be set accordingly.  A sprite is disabled (not displayed) by
setting the n field to zero.  In fact, this is the default value for all
sprites after calling ia_inituve.

A word of caution, however.  If you link a sprite to an image that has
not been loaded, the results will be unpredictable (okay, okay, it will
most likely crash the program).  So always, always make sure that the
image the sprite is linked to is loaded.  This might seem an easy thing
to do, but it gets a bit more complicated when you are dealing with more
sophisticated features such as chained sprites.  And with that, on to
chained sprites.

Related functions:
procedure ia_displaysprite (n:integer;destofs,destseg:word);
procedure ia_displaysprite_mapped
(n:integer;destofs,destseg:word;m:byte);
procedure ia_displaysprite_transparent
(n:integer;destofs,destseg:word;m:byte);


Sprite Chaining

In a real application, sprites rarely have a constant image throughout
the program.  Most likely, you will want the hero you are controlling to
move his legs when he is walking instead of sliding on the ground.  What
this means is that you will need to change the image that is linked to
the sprite.  This is easily done by setting n to a new number.  When
dealing with many sprites, however, this can become extremely difficult
to keep track of.  On top of that, some sprites loop through some
predefined frames of animation.  A fire, for example, can loop through
five different images to give the appearance of burning.  Can UVE32 help
when it comes to this kind of bookkeeping?  You bet.

Images are kept in an array called ln (the full description of the array
field is given at the end).  When you are loading a sprite in a slot,
you are simply giving the array index of that slot.  Each array item
holds pointers to the image data and collision detection information
(stuff you don't need to know about) along with the image's width,
height (which are handy to know about) and next field (which you do need
to know about).  The next field is the number of another image slot.
Each frame, every sprite that is linked to an image slot will be linked
to the image slot specified in the next field.  Take a moment to re-read
the above sentence.  An example will make things clear (hopefully):

Assume that image slot number 7 has a next field with a value of 220.
This means that any sprite which is linked to image slot 7 (i.e.
spr[...].n:=7), after a frame occurs, will be linked to image slot 220
(i.e. spr[...].n:=220).  After yet another frame, sprites linked to
image slot 220 will be linked to the number of that slot's next field.
By default, a slot's next field is initialized to the same slot number
(i.e. ln[x].next:=x).  This means that the sprite doesn't change it's
image over time.  You can change that, however.

The "chain" can be completely arbitrary, but what's usually chained are
images with consecutive slot numbers.  This is so because the
ia_loadsprite function is capable of loading multi-image files into
consecutive slots.  Anyway, even if images are loaded one by one in
consecutive locations, they can easily be chained using:

     ia_chainsprite (start slot, chain length, end slot);
     
The start slot is the first slot in the chain.  The chain length is the
number of slots in the chain.  The end slot is what the next field of
the final chain slot will be set to.  To create an infinite loop, set
end slot=start slot.  To create a disappearing sprite, set end slot=0.
In my games, I have frequently used missile smoke sprites.  These
consist of a few chained frames of a puff of smoke (decreasing in size),
with the final slot pointing to 0.  So, I can simply define a smoke
sprite and let it take its course, without worrying about when I should
delete it.  It will simply be deleted by itself when the time comes.
(Another, more general, way is to set the life field of the spr array,
as will be explained further down the road.)

If you have created a chain, you can pick it up at any point.  For
example, if you have ten fires that you don't want to burn in sync with
one another, you can set their starting slots at different values within
the chain.  Check the examples for some, well, examples of this.

Related functions:
procedure ia_chainsprite (nr,len,endloop:integer);
procedure ia_unchainsprite (nr,len:integer);


Moving sprites

Sprites have coordinates, as specified by the x and y fields of spr.
Sprites move by changing these coordinates.  The coordinates are virtual
coordinates, in the integer range -32K to 32K in pixels.  If a sprite
lies outside the displayable area, it will not be displayed.  If it is
partially visible, it will be clipped.  Note that sprite visibility is
not a prerequisite for sprite collision.  Sprites can collide even
outside the visible area.

You can pan around the virtual world (i.e. change the visible area) by
setting vpx (viewport x) and vpy (viewport y) to appropriate values.  If
you have selected a scrolling background, it will scroll appropriately.
The viewport always spans (vpx,vpy)-(vpx+319,vpy+199).  (vpx,vpy) always
maps to the top left of the screen, even if an animation window has been
defined.

Related functions:
function ia_visiblex(i:integer):boolean;
function ia_visibley(i:integer):boolean;
function ia_visible(i:integer):boolean;
procedure ia_center(i,x,y:integer;pos:byte);


Sprite collision

Perhaps one of the most important aspects of sprites is the ability to
detect if two of them overlap ("collide").  After all, you wouldn't want
your missiles to whiz right through the enemy ships, would you?  In
UVE32, sprite collision is pixel-precise (with a few exceptions).
Bounding boxes are not used, as they are very infuriating when dealing
with 2D graphics ("But that missile didn't touch me!  I'm telling you it
didn't touch me!").  You are free to use the width and height variables
to implement such an algorithm, however.

To speed up collision detection, sprites are assumed to be vertically
and horizontally monotone, i.e. a vertical or horizontal line through
the sprite intersects its edges only twice.  Think of it as the sprite
being convex, only it includes and some cases of non-convex sprites.
Holes are out of the question.  Another way of putting it is assuming
that every pixel between the leftmost and rightmost non-black pixels in
each line (and similarly topmost and bottommost for vertical lines) is
considered to be part of the sprite, and therefore can collide with
another sprite.  This requires extra collision data to be stored along
with the image data.  UVE32 takes care of that for you automatically.
All you need to remember is to tick the "save collision data" box when
saving sprites through the utilities provided.

Sprite collision between two sprites can be detected by calling
ia_collide, which will return true if the two sprite indices provided as
parameters overlap.  Caution!  You must make sure that both sprites have
collision data embedded in them.  UVE32 does not check for that, and
will use garbage data for collision, if none is available.

A couple of notes on sprite collision.  Sprites can collide in the
64Kx64K virtual world, even outside the screen area.  A sprite cannot
collide with itself.  Inactive sprites cannot collide (sprites for which
n:=0).  Another thing is that when there is obvious biasing towards
sprite size in collision pairs (as in the case of missile-spaceship,
where the missile is usually smaller than the ship), you will speed up
collision check if you pass the smallest sprite as the first parameter.

Related functions:
function ia_collide(a,b:integer):boolean;


Deleting sprites & images

Images can be deleted but, in my experience, it's rarely necessary.
Should the need arise, you can call ia_delspr to delete an image.  If
you load an image on top of a filled slot, the old image will be deleted
and memory will be freed before the new one is loaded.

Sprites are a different matter altogether.  They cannot be deleted in
the normal sense.  They are "deleted" by setting the n field to zero.
Another way is to set the next field of some image slot to zero, as
described in the section about sprite chaining.  Yet another way is to
use the life field, which simply holds the number of frames left for
which the sprite will be visible.  Once this counter counts down to
zero, the sprite is deleted (i.e., not displayed).  When a sprite is not
displayed, other sprite fields remain unchanged, so if you want a sprite
to blink, you can toggle the n field between zero and some non-zero
value.

Related functions:
procedure ia_delspr (n:integer);


Other sprite attributes

For completeness, here is the whole sprite structure:

type  scb      =record
       x,y,                        sprite coords
       n,                     sprite loadnumber
       life              :integer; frames left, if 0  n:=0
       transparent  :boolean;      use sprite/background data
       map               :byte;         which palette mapping to use
       end;

Transparent specifies whether to use actual sprite data or the
underlying background data.  Why is that useful?  If map is zero,
specifying that no palette map will be used, then displaying the sprite
will use the background colors, in effect making it invisible.  That's
of limited use (although I've used it in a game).  If map is non-zero,
however, background colors will be mapped.  If the palette mapping used
is, say, one where each color maps to another of 70% brightness, then
the net effect will be a sprite that looks like a shadow.  Check the
examples and the palette maps section for more info.

And similarly, for the sprite images:

     loaded         =record
       data              :pointer;      pointer to sprite data
       collength,                  length of collision data
       length,                size in memory
       width,height :word;       size of sprite
       next              :word;         sprite sequence
       end;
     loadedarray         =array[0..nspr] of loaded;

Related Functions:
procedure ia_setspritesused (i:integer);
procedure ia_setclipdepth (i:integer);
procedure ia_setwindow (x1,y1,x2,y2:integer);



Palettes

A palette is simply a set of 256 colors, each of which is comprised of
red, green, and blue values in the range 0-63.

     palcolor       =record
       red,green,blue:byte
       end;
     palette        =array[0..255] of palcolor;

You can define your own palettes, but UVE32 has two predefined palettes
called blackpal and pcxpal.  Blackpal is simply a palette where all the
colors have RGB triplets of (0,0,0) (i.e. they are all black).  Very
handy for those fade-ins/fade-outs.  Pcxpal is where the last loaded
.PCX's palette is stored.  Loading a .PCX file does not automatically
set the palette.

Finally, a note on setting the palette.  Continuously setting the
palette DAC (as happens in fade-ins/outs) can cause "snow" to appear on
the screen.  The effect can range from minimal to a complete snowstorm.
There is no way around this, except inserting a delay between palette
sets and/or synchronizing to the VGA vertical retrace signal.  UVE32
protects against these effects by synchronizing with the vertical
retrace signal.  As all clones are not created equal, however, this does
not guarantee that it will work on your system.  If you experience snow,
stay in until the sun comes out :-)

Related functions:
procedure ia_setpalette (pal:palette);
procedure ia_getpalette(var pal:palette);
procedure ia_loadpalette (filename:string; var p:palette);
procedure ia_fadeto (s,t:palette; steps:word);
procedure ia_weirdfadeto (s,t:palette;d:integer);



Palette maps

Palette maps are color look-up tables (CLUTs) that map each of the 256
colors to another color index.  They do not define new color values.
UVE32 defines maparray which holds 5 palette maps.

     maparray       :array[1..5] of maptab;

These maps can be applied to sprites by setting the map field of the spr
structure to the color map index (1-5) of the palette map to use.  In
such a case, the sprite is not displayed in its normal colors, but goes
through a look-up stage.  Example time:  imagine that maparray[1] has
all its entries set to 255:

     for i:=0 to 255 do maparray[1][i]:=255;

Then, if the map field of a sprite is set to 1, then every pixel of the
sprite will be displayed as color 255.  Again, the best way to
understand how these things work is to take a look at the examples.

Apart from defining a maparray entry manually, you can use
ia_makeshadowmap and ia_makeglenzmap, which set a map automatically.

Related functions:
function ia_findclosestcolor (var p:palette; r,g,b:integer):byte;
procedure ia_makeshadowmap (var p:palette; percent:integer;var
mapped:maptab);
procedure ia_makeglenzmap (var p:palette; xr,xg,xb:integer;var
mapped:maptab);



Tiles

The background page holds a static image.  You can have a larger image
that you can pan around, however.  The memory required to store such an
image is prohibitive, so the image is composed of smaller 16x16 pixel
images called tiles. You can have as many as 255 tiles that make up an
image up to 5020 (stupid number, but don't ask) tiles in size (width x
height). Think of tiled floors or sidewalks and how a single tile can be
repeated to fill a larger area, only here you have a choice of 255 tiles
to place at each position.

In order to set up the scrolling background, you must first call
ia_setscrolling.  This allocates memory for the tiles.  Your tile area
can have any dimension, as long as the total tiles do not exceed 5020.
You set your desired tile area with ia_setscrollarea.  Note that the
width and height parameters are given in tiles, not pixels.

You can scroll around the tiled imaged by changing the vpx and vpy
variables, which define the coordinates of the top-left of the screen
into your virtual world.

Tiles cannot have transparent areas.  If part of a tile is drawn in
black, then a black pixel will be placed on the screen.  The static
background will not show through.

If you pan out of the defined tile area, the screen is filled with the
tile at index 0.  In other words, the area outside of the tile area is
assumed to be filled with tile 0.

If your animation window is smaller than the screen, then the outside
will show the static background image.  If you intend to use the whole
screen, however, you can speed things up by not using the static
background, since no part of the background will be visible.  You can
disable the static background with the usebackground variable.

If you intend to use tiled backgrounds along with windowed animation,
read the bugs section for an important limitation.

Related functions:
procedure ia_loadtil (filename:string; n:integer);
procedure ia_displaytile (n:byte; lineofs,destseg:word);
procedure ia_setscrolling;
procedure ia_setscrollarea(x1,y1,w,h:integer);
procedure ia_puttile(x,y:integer;n:byte);
function ia_gettile(x,y:integer):byte;
procedure ia_setstatic;



PCXs

UVE32 is capable of loading Zsoft's popular PCX file format pictures.
The header is ignored, however, so PCX files must be exactly 320x200 in
size, with 256 colors.  Loading a PCX does not set its palette.  The
palette is automatically saved in the pcxpal variable.

Related functions:
procedure ia_loadpcx (where:word;filename:string;seethru:byte);




Fonts

UVE32 uses BIOS mode 0x13, so there should be no problem displaying text
by calling standard procedures such as writeln.  Personally, I've never,
ever used the BIOS to print text.  Printing text in this way uses the
built-in (i.e., ugly) font.  On top of that, the background is
destroyed. Have no fear, though, because UVE32 is here to get you out of
another mess.  With its 15 fonts, horizontal and vertical printing,
background preservation, shadows, textures, proportional and fixed
spacing, it's everything the font-conscious user might want (okay, maybe
not).

First thing's first, though.  In order to use fonts, some steps must be
taken.
    The file UVE.UVW must be in the current directory.
    You must load the font into memory by ia_loadfont.  All of the 15
  fonts can be present in memory at the same time.
    You must call ia_setfontattr to set the desired font attributes.
    Finally, you can print text by calling ia_printstr.

The 15 built-in fonts must be referred to by index number.  Okay, so an
enumeration would be a better idea, but no-one is stopping you from
creating one!  The fonts are:

Font index   Font name    Font origin
0            Default 5x5  InterAction
1            Default 5x5  InterAction
             square
2            3x5          InterAction
3            3x5 square   InterAction
4            Future       Unknown
5            Antique      Unknown
6            Big          Unknown
7            Computer     Unknown
8            Deco         Unknown
9            Dott         Unknown
10           Medieval     Unknown
11           Modern       Unknown
12           Scrawl       Unknown
13           WC2          InterAction
14           5x5 Thick    InterAction

Take note of a few things when dealing with fonts.  First, make sure you
load the font before using it, otherwise you get a cryptic error message
about dereferencing a null pointer.  Second,  in the interest of speed,
no clipping is performed.  Third, the fire texture makes use of the
palette colors 224-245, so make sure you place your desired colors there
(for example, a red-yellow-white gradient). Fourth, whatever you do,
never, EVER try to .... oops, slipped my mind :-).

Related Functions:
procedure ia_setfontattr (a:byte; b:fonttextures; c:fontcpi;
d:fontorient; e:byte; f:boolean; g:byte);
function ia_strlength (s:string):word;
procedure ia_printstr (where:word;xx,yy:integer; s:string; clr:byte);
procedure ia_loadfont (i:byte);



FLIs

UVE32 is capable of loading and playing Autodesk .FLI files.  Before you
play any .FLI file you must make a call to ia_setfliparameters to set
some parameters such as looping and play rate.   Apart from that,
playing .FLI files is as easy as calling ia_playfli.  You can have more
control over the way a .FLI file is played by reverting to a lower-level
interface.  Check the functions & procedures section for more info.

Related functions:
procedure ia_openfli(filename:string);
procedure ia_playframe;
procedure ia_wait4nextframe;
procedure ia_closefli;
procedure ia_setfliparameters(a:integer;b:word;c:boolean);
procedure ia_playfli(s:string);



WAD files

It is possible to combine all the read-only files (i.e., not files such
as config and highscore files, which are written to) that your
application needs in a single master wad file.  UVEDEMO uses such a file
(UVEDEMO.UVW).  UVW files are not fully supported in the shareware
version.  All the relevant code is there, but you need to register to be
able to create them.

Related functions:
procedure ia_setwadfile (s:string);
function ia_searchinwad (var fp:file;lookingfor:string):longint;




UVE Interface


Unit interface section

const     nspr      =500;               maximum number of sprites
     maxtiles  =5020;              maximum number of scrolling area in
tiles

type   scb          =record
       x,y,                        sprite coords
       n,                     sprite loadnumber
       life              :integer; frames left, if 0  n:=0
       transparent  :boolean;      use sprite/background data
       map               :byte;         which palette mapping to use
       end;
     scbarray  =array[0..nspr] of scb;
     loaded         =record
       data              :pointer; pointer to sprite data
       collength,                  length of collision data
       length,                size in memory
       width,height :word;       size of sprite
       next              :word;         sprite sequence
       end;
     loadedarray         =array[0..nspr] of loaded;
     maptab              =array[0..255] of byte; used for colormapping
     fontorient          =(horizontal,vertical); font attribute types
     fontcpi        =(fixed,proportional);
     fonttextures        =(plain,fire);
     pagetype       =array[0..$FFFE] of byte;
     pagepointer         =^pagetype;
     palcolor       =record
       red,green,blue:byte
       end;
     palette        =array[0..255] of palcolor;

var  vpx,vpy        :integer;      viewport topleft corner coords
     hidden              :word;              segment of the hidden page
     hiddenpoint         :pagepointer;  pointer to hidden page
     background          :word;         segment of background
     backgroundpoint     :pagepointer;       pointer to background
     tiles               :word;              segment of tiles
     tilespoint          :pagepointer;  pointer to tiles
     pcxpal              :palette;      last PCXs palette
     blackpal       :palette;      all blacks (0,0,0)
     spritesloaded  :integer;      set by last "loadSPR" call
     retraceenable,                wait 4 vertical retrace?
     usebackground  :boolean;      copy bg to hidden page?
     developermode  :boolean;      show developer stats
     maparray       :array[1..5] of maptab;
                                   these tables are uses to map
                                   colors when display method
                                   is eg shadow, glenz, ...
     spr            :scbarray;
     ln             :loadedarray;
     vga            :word;

     lastframe      :boolean;
     textenable          :boolean;      enables uve_text
     fliframenumber :word;              number of current FLI frame
     frame               :integer;      current uve frame number



Other variables

    spritesloaded:  Sprites loaded by last ia_loadspr call

    retraceenable (false):  If true, UVE waits for a vertical retrace
 signal before copying the hidden page to the vga page.  This is not
 needed, but included for the sake of completeness.  In the unlikely case
 you are experiencing screen flicker, turn this option on.  Minimizes
 flickering, but slows down frame rate.

    usebackground (true):   If true, UVE will copy the background page
 to the hidden page before anything else to clear the last frame.   If
 you've got a full-screen scrolling background, however, this is not
 necessary.  Turning this option off will improve frame rate.

    developermode (false):  When true, displays a bar on the top-left
 of the screen with some statistics.  Each pixel in the graph corresponds
 to 1 millisecond:
         White portion represents time spend by UVE rendering the screen.
         Blue portion represents time taken by your program apart from
      rendering time.  This includes game logic time, collision checks,
      keyboard input etc.
    Red portion represents time taken to copy hidden to vga page.  The
reason this is separate is that, depending on your VGA, it can take up
to 90% of the total frame time.
         White arrow under bar represents desired (set) frame rate.
 Note that colors mentioned correspond to default palette.

    lastframe (false):  True if current FLI frame is the last frame.
 Used for manual FLI play loops.
    textenable (true):  Set to false if you don't want any text output
 from UVE (for example when loading sprites).  Output is always disabled
 when in graphics mode.
    fliframenumber (0):  Current FLI frame number
    frame (0):  Current UVE frame number



Functions & Procedures

The functions and procedures of UVE are divided into four categories:
general-purpose, beginner, intermediate and advanced.  General purpose
procedures are simple "utility" procedures.  Beginner entries include
all procedures that you must absolutely know about in order to use UVE.
Intermediate entries refer to commands that you will use frequently when
you've learned the ropes.  Advanced entries, finally, provide a lower
lever interface to UVE and, as such, are more powerful and allow for
extensive customization.  You do not have to know any of the advanced
commands, but they do come in handy in certain situations.


General Purpose Commands

function i2s (i:integer):string;
Purpose        Convert an integer to its string representation
Parameters     i    Integer variable to be converted
Return         String representation of i
Notes          -
See also       l2s, w2s


function w2s (w:word):string;
Purpose        Convert a word to its string representation
Parameters     w    Word variable to be converted
Return         String representation of w
Notes          i2s, l2s
See also       -


function l2s (l:longint):string;
Purpose        Convert an long integer to its string representation
Parameters     l    Longint variable to be converted
Return         String representation of l
Notes          -
See also       i2s, w2s


function max (a,b:integer):integer;
Purpose        Return greater of two numbers
Parameters     a,b  Integers to be compared
Return         Greatest integer
Notes          -
See also       min


function min (a,b:integer):integer;
Purpose        Return smaller of two numbers
Parameters     a,b  Integers to be compared
Return         Smallest integer
Notes          -
See also       max


procedure ia_checkfile (s:string);
Purpose        Check whether a file exists
Parameters     s    Filename
Return         -
Notes          Calls ia_error if file does not exist
See also       -


procedure ia_checkmem (w:word);
Purpose        Check for available memory
Parameters     w    Requested memory in bytes
Return         -
Notes          Calls ia_error if free memory is less than w
See also       -



Beginner Commands

procedure ia_loadtil (filename:string; n:integer);
Purpose        Load a tile
Parameters     filename  UVT or UVL file name
          n    Index of tile
Return         -
Notes          Calls ia_error if file is not found
See also       ia_setscrolling, ia_puttile, ia_gettile, ia_displaytile


procedure ia_loadspr (filename:string; n:integer);
Purpose        Load a sprite
Parameters     filename  UVS or UVL file name
          n    Index of sprite
Return         -
Notes          Calls ia_error if file is not found
          Sets spritesloaded to the number of sprites loaded
See also       ia_delspr


procedure ia_loadpcx (where:word; filename:string; seethru:byte);
Purpose        Load a PCX file
Parameters     where     Page segment
          filename  PCX file name
          seethru   Transparent color (255=none)
Return         -
Notes          PCX palette is stored in pcxpal
See also       -


procedure ia_setpalette (pal:palette);
Purpose        Set the current palette
Parameters     pal  Palette to set
Return         -
Notes          -
See also       ia_loadpalette, ia_getpalette


procedure ia_loadpalette (filename:string; var p:palette);
Purpose        Load a palette
Parameters     filename  PAL file name
          p    Palette variable to store result into
Return         -
Notes          -
See also       ia_setpalette, ia_getpalette


procedure ia_setscrolling;
Purpose        Set scrolling (tiled) background
Parameters     -
Return         -
Notes          Allocates tiles page first time it's called
See also       ia_setstatic


procedure ia_setscrollarea (x1,y1,w,h:integer);
Purpose        Set scrolling area
Parameters     x1,y1     Coordinates of top-left corner in pixels
          w,h  Width and height of scroll area in tiles
Return         -
Notes          x1,y1 are automatically aligned on a 16x16 grid
See also       ia_setscrolling


procedure ia_puttile (x,y:integer; n:byte);
Purpose        Set a tile on the scroll area
Parameters     x,y  Coordinates to place tile in pixels
          n    Tile number
Return         -
Notes          x1,y1 are automatically aligned on a 16x16 grid
See also       ia_gettile


procedure ia_setfontattr (a:byte; b:fonttextures; c:fontcpi;
d:fontorient; e:byte; f:boolean; g:byte);
Purpose        Set font attribute
Parameters     a    Font number
          b    Font texture.  Can be plain or fire
          c    Character spacing.  Can be fixed or proportional
          d    Font orienation.  Can be horizontal or vertical
          e    Background color.  Use 255 for transparent background
          f    True for a font shadow to be displayed
          g    Shadow color
Return         -
Notes          Fire uses color indices 224-245
          Font must be loaded first
See also       ia_loadfont, ia_strlength, ia_printstr


procedure ia_printstr (where:word; xx,yy:integer, s:string; clr:byte);
Purpose        Print a string
Parameters     where     Page segment
          xx,yy     Coordinates where string printing will start in
pixels
          s    String to print
          clr  String color
Return         -
Notes          Use xx=-32768 to center the string on screen
          No clipping is performed
See also       ia_loadfont, ia_setfontattr, ia_strlength


procedure ia_loadfont (i:byte);
Purpose        Load a font
Parameters     i    Font number
Return         -
Notes          Calls ia_error if UVE.UVW is not present in the current
directory
See also       ia_setfontattr, ia_strlength, ia_printstr


procedure ia_powerup;
Purpose        Switch to graphics mode 0x13
Parameters     -
Return         -
Notes          -
See also       ia_shutdown


procedure ia_shutdown;
Purpose        Switch to text mode 0x03
Parameters     -
Return         -
Notes          -
See also       ia_powerup


procedure ia_playfli (s:string);
Purpose        Play a FLI file
Parameters     s    FLI file name
Return         -
Notes          Calls ia_error if file cannot be found
See also       ia_setfliparameters, ia_openfli, ia_playframe,
ia_wait4nextframe, ia_closefli


procedure ia_inituve;
Purpose        Initialize UVE32
Parameters     -
Return         -
Notes          Call before anything else.
          Call once only.
See also       -


procedure ia_doframe;
Purpose        Execute frame
Parameters     -
Return         -
Notes          Calls parts 1,2 and 3
See also       ia_doframepart1, ia_doframepart2, ia_doframepart3



Intermediate Commands

procedure ia_getpalette (var pal:palette);
Purpose        Get the current palette
Parameters     pal  Palette variable to store result into
Return         -
Notes          -
See also       ia_loadpalette, ia_setpalette


procedure ia_setwadfile (s:string);
Purpose        Set the current UVW file
Parameters     s    Filename
Return         -
Notes          When s is specified, all PCX/UVS/UVL/FLI/UVT files will
be loaded from s
          To load files normally, call ia_setwadfile('');
          Calls ia_error if file does not exist
See also       ia_searchinwad


procedure ia_copypage (source,dest:word);
Purpose        Copy a page
Parameters     source    Source page segment
          target    Target page segment
Return         -
Notes          -
See also       -


procedure ia_cls (where:word; col:byte);
Purpose        Clear a page
Parameters     where     Page segment
          col  Color to use
Return         -
Notes          -
See also       -


procedure ia_putpixel (where,x,y:word; c:byte);
Purpose        Plot a pixel
Parameters     where     Page segment
          x,y  Point coordinates
          c    Color to use
Return         -
Notes          No clipping is performed
See also       ia_getpixel


function ia_getpixel (where,x,y:word):byte;
Purpose        Read a pixel
Parameters     where     Page segment
          x,y  Point coordinates
Return         Color of specified pixel
Notes          No clipping is performed
See also       ia_putpixel


procedure ia_hline (where:word; x,y,len:integer; c:byte);
Purpose        Draw a horizontal line
Parameters     where     Page segment
          x,y  Starting point coordinates
          len  Line length in pixels
Return         -
Notes          Performs no clipping
See also       ia_vline, ia_line


procedure ia_vline (where:word; x,y,len:integer; c:byte);
Purpose        Draw a vertical line
Parameters     where     Page segment
          x,y  Starting point coordinates
          len  Line height in pixels
Return         -
Notes          Performs no clipping
See also       ia_hline, ia_line


procedure ia_line (where:word; x1,y1,x2,y2:integer; color:byte);
Purpose        Draw a line using Bresenham's algorithm
Parameters     where     Page segment
          x1,y1     Start point coordinates
          x2,y2     End point coordinates
          color     Color to use
Return         -
Notes          Performs no clipping
See also       ia_vline, ia_hline


procedure ia_box (where:word; x1,y1,x2,y2:integer; c:byte;
filled:boolean);
Purpose        Draw a box
Parameters     where     Page segment
          x1,y1     Start point coordinates
          x2,y2     End point coordinates
          c    Color to use
          filled    If true, box is filled with color c
               If false, only a hollow rectangle is drawn
Return         -
Notes          Performs no clipping
See also       ia_box_clip


procedure ia_box_clip (where:word; x1,y1,x2,y2:integer; c:byte;
filled:boolean);
Purpose        Draw a box
Parameters     where     Page segment
          x1,y1     Start point coordinates
          x2,y2     End point coordinates
          c    Color to use
          filled    If true, box is filled with color c
               If false, only a hollow rectangle is drawn
Return         -
Notes          Same as ia_box, but performs clipping as well
See also       ia_box


procedure ia_fadein (where,time:word);
Purpose        Dissolve a page onto the VGA page
Parameters     where     Page to be dissolved
          time Speed of transition
Return         -
Notes          Very slow on some 386s
See also       -


procedure ia_makeshadowmap (var p:palette; percent:integer; var
mapped:maptab);
Purpose        Make a shadow map
Parameters     p    Palette variable to use as a base for mapping
          percent   Shadow percentage
          mapped    Map table variable to store result into
Return         -
Notes          Percent can be more than 100, which will result in a
brighter colormap
See also       ia_findclosestcolor, ia_makeglenzmap


procedure ia_makeglenzmap (var p:palette; xr,xg,xb:integer; var
mapped:maptab);
Purpose        Make a glenz (translucency) map
Parameters     p    Palette variable to use as a base for mapping
          xr,xg,xb  RGB components to add to each color
          mapped    Map table variable to store result into
Return         -
Notes          xr,xg,xb can be positive or negative
See also       ia_findclosestcolor, ia_makeshadowmap


procedure ia_fadeto (s,t:palette; steps:word);
Purpose        Cross-fade between two palettes using linear
     interpolation
Parameters     s    Source palette
          t    Target palette
          steps     Cross-fade duration
Return         -
Notes          -
See also       ia_weirdfadeto, ia_loadpalette, ia_setpalette,
ia_getpalette


procedure ia_weirdfadeto (s,t:palette; d:integer);
Purpose        Cross-fade between two palettes using integer RGB
     matching
Parameters     s    Source palette
          t    Target palette
          steps     Cross-fade duration
Return         -
Notes          Faster than ia_fadeto but may result in slight hue shifts
while cross-fading
See also       ia_fadeto, ia_loadpalette, ia_setpalette, ia_getpalette


function ia_visible (i:integer):boolean;
Purpose        Test for sprite visibility in both axes
Parameters     i    Sprite number
Return         True if sprite is visible on screen
Notes          Uses bounding box, not actual sprite geometry
See also       ia_visiblex, ia_visibley


procedure ia_center (i,x,y:integer; pos:byte);
Purpose        Center a sprite around a point
Parameters     i    Sprite number
          x,y  Coordinates of center point
          pos  Centering method:
               2    (x,y) becomes mid-bottom of sprite
               4    (x,y) becomes mid-left of sprite
               5    (x,y) becomes center of sprite
               6    (x,y) becomes mid-right of sprite
               8    (x,y) becomes mid-top of sprite
Return         -
Notes          Hint for centering method: Look at the numeric keypad.
Sprite boundary is
          123/456/789 "box".  Centering method corresponds to number on
each key.
See also       -


function ia_collide (a,b:integer):boolean;
Purpose        Test for sprite collision
Parameters     a,b  Sprite numbers
Return         True if there is a collision between sprites a and b
Notes          Collision can be off-screen
          Both sprites must have collision data embedded in their
format!
See also       -


procedure ia_chainsprite (nr,len,endloop:integer);
Purpose        Chain sprites together
Parameters     nr   Start sprite number for chain
          len  Length of chain
          endloop   Final chain sprite link
Return         -
Notes          Sprite will cycle nr nr+1  nr+2  ...  nr+len  endloop
          Infinite loops can be created by setting endloop=nr
See also       ia_unchainsprite


procedure ia_unchainsprite (nr,len:integer);
Purpose        Unchain previously chained sprites
Parameters     nr   Start sprite number for chain
          len  Unchain length
Return         -
Notes          -
See also       ia_chainsprite


function ia_gettile (x,y:integer):byte;
Purpose        Get a tile from the scroll area
Parameters     x,y  Coordinates to get tile from in pixels
Return         Tile number under x,y
Notes          x1,y1 are automatically aligned on a 16x16 grid
See also       ia_puttile


procedure ia_setstatic;
Purpose        Set static background (disable scrolling background)
Parameters     -
Return         -
Notes          Tiles page is not deallocated
See also       ia_setscrolling


procedure ia_setcycletime (i:integer);
Purpose        Set frame duration
Parameters     i    Frame duration in milliseconds
Return         -
Notes          Frame rate will be limited to i.
          Has no effect if frames cannot be displayed as fast as i
See also       -


procedure ia_setwindow (x1,y1,x2,y2:integer);
Purpose        Set animation window
Parameters     x1,y1     Coordinates of top-left corner of animation
window
          x2,y2     Coordinates of bottom-right corner of animation
window
Return         -
Notes          -
See also       -



Advanced Commands

function clock:longint;
Purpose        Return the current time
Parameters     -
Return         Milliseconds elapsed since midnight
Notes          -
See also       -


procedure ia_error (caller:string; errornum:byte; param:string);
Purpose        Error handling routine
Parameters     caller    Procedure that generated the error
          errornum Error number
          param     Parameter that caused the error
Return         -
Notes          Program halts after printing error.
          Possible error numbers are.
               1    Parameter(s) out of range.
               2    File does not exist.
               3    Not an UVE file.
               4    Wrong file type.
               5    Not enough memory.
               6    Attempt to dereference nil pointer.
               7    No VGA display adapter detected.
               8    Scrolling area too large.
               9    Tile placed out of scroll area.
See also       -


function ia_detectvga:boolean;
Purpose        Detect the presence of a VGA adapter
Parameters     -
Return         True if VGA present
Notes          -
See also       -


procedure ia_drawasciiscreen;
Purpose        Draw UVE opening text screen
Parameters     -
Return         -
Notes          -
See also       ia_uvetext


procedure ia_uvetext (caller, strng, parameter: string);
Purpose        Print out text in UVE standardized format
Parameters     caller    Calling procedure
          parameter Text to print
Return         -
Notes          -
See also       ia_drawasciiscreen


procedure ia_waitretrace;
Purpose        Wait for VGA vertical retrace signal
Parameters     -
Return         -
Notes          Not needed, but included for completeness
See also       -


procedure ia_delspr (n:integer);
Purpose        Delete a sprite from memory
Parameters     n    Sprite number to delete
Return         -
Notes          -
See also       ia_loadspr


function ia_searchinwad (var fp:file; lookingfor:string):longint;
Purpose        Position a file pointer to a specified entry in a UVW
     file
Parameters     fp   File handle (must be of type FILE)
Return         Size of file requested
Notes          Calls ia_error if entry is not present in UVW
See also       ia_setwadfile


function ia_findclosestcolor (var p:palette; r,g,b:integer):byte;
Purpose        Find closest color using least squares method
Parameters     p    Palette var to search in
          r,g,b     Desired RGB components
Return         Index of closest color matching RGB components specified
Notes          -
See also       ia_makeshadowmap, ia_makeglenzmap


function ia_visiblex (i:integer):boolean;
Purpose        Test for sprite visibility in x-axis
Parameters     i    Sprite number
Return         True if sprite is visible on screen x-extents
Notes          Uses bounding box, not actual sprite geometry
See also       ia_visibley, ia_visible


function ia_visibley (i:integer):boolean;
Purpose        Test for sprite visibility in y-axis
Parameters     i    Sprite number
Return         True if sprite is visible on screen y-extents
Notes          Uses bounding box, not actual sprite geometry
See also       ia_visiblex, ia_visible


procedure ia_displaysprite (n:integer; destofs,destseg:word);
Purpose        Display a sprite
Parameters     n    Sprite number
          destofs   Destination offset
          destseg   Destination segment
Return         -
Notes          Performs no clipping
See also       ia_displaysprite_mapped, ia_displaysprite_transparent


procedure ia_displaysprite_mapped (n:integer; destofs,destseg:word;
m:byte);
Purpose        Display a sprite using a map table
Parameters     n    Sprite number
          destofs   Destination offset
          destseg   Destination segment
          m    Map table index
Return         -
Notes          Performs no clipping
See also       ia_displaysprite, ia_displaysprite_transparent


procedure ia_displaysprite_transparent (n:integer; destofs,destseg:word;
m:byte);
Purpose        Display a transparent sprite using a map table
Parameters     n    Sprite number
          destofs   Destination offset
          destseg   Destination segment
          m    Map table index
Return         -
Notes          Performs no clipping
See also       ia_displaysprite, ia_displaysprite_mapped


procedure ia_displaytile (n:byte; lineofs,destseg:word);
Purpose        Display a tile
Parameters     n    Tile number
          lineofs   Destination offset
          destseg   Destination segment
Return         -
Notes          Performs no clipping
See also       ia_loadtil, ia_gettile, ia_puttile


procedure ia_doframepart1;
Purpose        Execute first part of frame cycle
Parameters     -
Return         -
Notes          Copies background page onto hidden page and/or constructs
tiled background
See also       ia_doframepart2, ia_doframepart3, ia_doframe


procedure ia_doframepart2;
Purpose        Execute second part of frame cycle
Parameters     -
Return         -
Notes          Display all sprites onto hidden page
See also       ia_doframepart1, ia_doframepart3, ia_doframe


procedure ia_doframepart3;
Purpose        Execute third part of frame cycle
Parameters     -
Return         -
Notes          Copy  hidden page to vga page
See also       ia_doframepart1, ia_doframepart2, ia_doframe


procedure ia_setspritesused (i:integer);
Purpose        Set number of active sprites
Parameters     i    Sprite number
Return         -
Notes          Only sprites with number between 1 and i will be
displayed
See also       -


procedure ia_setclipdepth (i:integer);
Purpose        Set number of sprites clipped to screen ("in front" of
     animation window)
Parameters     i    Sprite number
Return         -
Notes          Sprites with number between 1 and i will be clipped to
320x200 screen
          Sprites with number between i+1 and nmax will be clipped to
animation window
See also       -


function ia_strlength (s:string):word;
Purpose        Get string length
Parameters     s    String to calculate length of
Return         Length of string in pixels when printed out in the
current font attributes
Notes          -
See also       ia_loadfont, ia_setfontattr, ia_printstr


procedure ia_newpage (var p:pagepointer; var where:word);
Purpose        Allocate a new page
Parameters     -
Return         p    points to the new allocated page
          where     holds new page segment
Notes          Calls ia_error if there is not enough memory left
See also       -


procedure ia_openfli (filename:string);
Purpose        Open a FLI file
Parameters     filename  FLI file name
Return         -
Notes          Calls ia_error if file is cannot be found
See also       ia_setfliparameters, ia_playframe, ia_wait4nextframe,
ia_closefli, ia_playfli


procedure ia_playframe;
Purpose        Play the next frame of a FLI file
Parameters     -
Return         -
Notes          -
See also       ia_setfliparameters, ia_openfli, ia_wait4nextframe,
ia_closefli, ia_playfli


procedure ia_wait4nextframe;
Purpose        Wait until current FLI frame time expires
Parameters     -
Return         -
Notes          -
See also       ia_setfliparameters, ia_openfli, ia_playframe,
ia_closefli, ia_playfli


procedure ia_closefli;
Purpose        Close a FLI file
Parameters     -
Return         -
Notes          -
See also       ia_setfliparameters, ia_openfli, ia_playframe,
ia_wait4nextframe, ia_playfli


procedure ia_setfliparameters (a:integer; b:word; c:boolean);
Purpose        Set FLI play parameters
Parameters     a    FLI playing speed
          b    FLI loop times
          c    True if FLI can be interrupted by a keypress
Return         -
Notes          Use a=-1 to use FLI internal speed
See also       ia_openfli, ia_playframe, ia_wait4nextframe, ia_closefli,
ia_playfli




Supporting Utilities

UVSINFO

UVSINFO is a simple utility that gives information about sprites
contained in a single/multiple sprite file (.UVS/.UVL).



PCX2UVS

PCX2UVS is a utility that converts a PCX file into a sprite (.UVS) file.
It can also optionally save the PCX's palette for use with that sprite.
The PCX's background must be black (color index 0).  UVE32 automatically
calculates the bounding box from the PCX image and saves the sprite.
Any part of the sprite that is drawn in black will be completely
transparent when displayed on the screen.  A PCX file can only hold a
single sprite.  (You can get a far more powerful version of PCX2UVS if
you register).



PCX2UVT

PCX2UVT is a utility that converts a PCX file into a tile (.UVT) file.
The PCX's background must be black (color index 0).  No bounding box is
used.  The first non-black pixel found is assumed to be the top-left
pixel of a 16x16 tile.  You can have multiple tiles in the PCX.  In such
a case, the tiles will all be saved in a single file in the order they
were detected (top-to-bottom, left-to-right).

Note:  As no bounding box is used when saving tiles, the top-left pixel
of each tile must not be black, so that the start of a tile is properly
detected.



LIBMAKER

LIBMAKER is a powerful utility that allows you to combine multiple files
together.  Its most obvious use is to concatenate .UVS or .UVT files
into library (.UVL) files.  It cannot be used to create wad files
(.UVW).  The files will be concatenated in the order they were selected.

There is a maximum of 100 files that can be simultaneously displayed.
This means that your UVL files can contain at most 100 sprites/tiles.
Workaround:  First split your files into groups of (at most) 100 files,
combine them separately, and then combine the resultant UVL files into
your final master UVL file.  Hope you got that.




Known bugs... err.. features

The following list contains all the bugs/limitations I am aware of (but
don't intend to fix, at least in the near future).

    When using a scrolling (tiled) background, the animation window
  must be at least 16 pixels long in both dimensions, i.e. it must be able
  to contain at least one full tile.  Tiles are not clipped properly if
  the animation window is smaller than that.




UVE32 Frequently Asked Questions

    Why is there still only one question in the UVE32 FAQ?
     What can I say?  There haven't been any frequently asked questions.
     This can only mean two things: Either UVE32 is flawless, or nobody
     has bothered downloading it :-)



UVE mailing list

Starting with v1.40 there is a mailing list which you can join.  You can
use  the  mailing  list to communicate with other UVE users  world-wide,
post  problems,  comments, suggestions and so on.  To join  the  mailing
list send e-mail to:

                      majordomo@lennon.shim.org.sg

with  the  words "subscribe uve" in the body of the message (subject  is
not  important).   You should get a confimation back  upon  joining  the
list, along with some additional information.




Where can I get UVE32?

The two primary sites where the lastest version of UVE32 will be
uploaded first are:

    The SimTel archive in the US (ftp to ftp.simtel.net or
  ftp.cdrom.com) in pub/simtelnet/msdos/turbopas/
    The X2 archive in Finland (ftp to x2ftp.oulu.fi) in
  pub/msdos/programming/libs/

Of course, since everybody mirrors everybody else these days, it should
soon spread to just about everywhere.  If you cannot access these sites,
try getting UVE32 from a nearby mirror site.  At the time of this
writing, the official mirroring sites are:

For the SimTel archive:

Country   Host           Directory
Argentina           ftp.satlink.com               /pub/mirrors/simtelnet
Australia           ftp.bhp.com.au                /pub/simtelnet
Australia           ftp.iniaccess.net.au          /pub/simtelnet
Australia           ftp.tas.gov.au                /pub/simtelnet
Australia           sunsite.anu.edu.au            /pub/pc/simtelnet
Austria             ftp.univie.ac.at              /mirror/simtelnet
Belgium             ftp.linkline.be               /mirror/simtelnet
Belgium             ftp.tornado.be                /pub/simtelnet
Bulgaria            ftp.eunet.bg                  /pub/simtelnet
Brazil              ftp.iis.com.br                /pub/simtelnet
Brazil              ftp.unicamp.br                /pub/simtelnet
Canada,Ottawa       ftp.crc.doc.ca
/systems/ibmpc/simtelnet
Canada,Vancvr  ftp.direct.ca                 /pub/simtelnet
Chile               sunsite.dcc.uchile.cl         /pub/Mirror/simtelnet
China               ftp.pku.edu.cn                /pub/simtelnet
Czech Republic      ftp.eunet.cz                  /pub/simtelnet
Czech Republic      pub.vse.cz                    /pub/simtelnet
Czech Republic      ftp.zcu.cz                    /pub/simtelnet
Finland             ftp.funet.fi
/mirrors/ftp.simtel.net/pub/simtelnet
France              ftp.grolier.fr                /pub/simtelnet
France              ftp.ibp.fr                    /pub/simtelnet
Germany             ftp.mpi-sb.mpg.de             /pub/simtelnet
Germany             ftp.rz.ruhr-uni-bochum.de     /pub/simtelnet
Germany             ftp.tu-chemnitz.de            /pub/simtelnet
Germany             ftp.uni-heidelberg.de         /pub/simtelnet
Germany             ftp.uni-magdeburg.de          /pub/mirrors/simtelnet
Germany             ftp.uni-paderborn.de          /pub/simtelnet
Germany             ftp.uni-trier.de
/pub/pc/mirrors/simtelnet
Germany             ftp.rz.uni-wuerzburg.de       /pub/pc/simtelnet
Greece              ftp.ntua.gr                   /pub/pc/simtelnet
Hong Kong           ftp.cs.cuhk.hk                /pub/simtelnet
Hong Kong           ftp.hkstar.com                /pub/simtelnet
Hong Kong           sunsite.ust.hk                /pub/simtelnet
Israel              ftp.huji.ac.il                /pub/simtelnet
Italy               cis.utovrm.it                 /simtelnet
Italy               ftp.flashnet.it               /pub/simtelnet
Italy               ftp.unina.it                  /pub/simtelnet
Italy               mcftp.mclink.it               /pub/simtelnet
Japan               ftp.iij.ad.jp                 /pub/simtelnet
Japan               ftp.riken.go.jp               /pub/simtelnet
Japan               ftp.saitama-u.ac.jp           /pub/simtelnet
Japan               ftp.u-aizu.ac.jp              /pub/PC/simtelnet
Japan               ring.aist.go.jp               /pub/simtelnet
Japan               ring.asahi-net.or.jp          /pub/simtelnet
Latvia              ftp.lanet.lv                  /pub/mirror/simtelnet
Malaysia            ftp.jaring.my                 /pub/simtelnet
Malaysia            ftp.mimos.my                  /pub/simtelnet
Mexico              ftp.gdl.iteso.mx              /pub/simtelnet
Netherlands         ftp.euro.net                  /d5/simtelnet
Netherlands         ftp.nic.surfnet.nl            /mirror-
archive/software/simtelnet
New Zealand         ftp.vuw.ac.nz                 /pub/simtelnet
Norway              ftp.bitcon.no                 /pub/simtelnet
Poland              ftp.cyf-kr.edu.pl             /pub/mirror/Simtel.Net
Poland              ftp.icm.edu.pl                /pub/simtelnet
Poland              ftp.man.poznan.pl             /pub/simtelnet
Portugal             ftp.ip.pt                    /pub/simtelnet
Portugal            ftp.ua.pt                     /pub/simtelnet
Romania             ftp.sorostm.ro                /pub/simtelnet
Singapore           ftp.nus.sg                    /pub/simtelnet
Slovakia            ftp.uakom.sk                  /pub/simtelnet
Slovenia            ftp.arnes.si                  /software/simtelnet
South Africa        ftp.is.co.za                  /pub/simtelnet
South Africa        ftp.sun.ac.za                 /pub/simtelnet
South Korea         ftp.nuri.net                  /pub/simtelnet
South Korea         ftp.sogang.ac.kr              /pub/simtelnet
South Korea         sunsite.snu.ac.kr             /pub/simtelnet
Spain               ftp.rediris.es                /mirror/simtelnet
Sweden              ftp.sunet.se                  /pub/simtelnet
Switzerland         ftp.switch.ch                 /mirror/simtelnet
Taiwan              ftp.ncu.edu.tw                /Packages/simtelnet
Taiwan              nctuccca.edu.tw               /mirror/simtelnet
Thailand            ftp.nectec.or.th              /pub/mirrors/simtelnet
UK, Edinburgh       emwac.ed.ac.uk                /mirrors/simtelnet
UK, London          ftp.demon.co.uk               /pub/simtelnet
UK, Lancaster       micros.hensa.ac.uk            /pub/simtelnet
UK, London          sunsite.doc.ic.ac.uk          /packages/simtelnet
US, California      ftp.cdrom.com                 /pub/simtelnet
US, California      ftp.digital.com
/pub/micro/pc/simtelnet
US, Illinois        uiarchive.cso.uiuc.edu
/pub/systems/pc/simtelnet
US, Mass.           ftp.bu.edu                    /pub/mirrors/simtelnet
US, Michigan        oak.oakland.edu               /pub/simtelnet
US, New York        ftp.rge.com                   /pub/systems/simtelnet
US, Oklahoma        ftp.ou.edu                    /pub/simtelnet
US, Oregon          ftp.orst.edu                  /pub/simtelnet
US, Utah            ftp.cyber-naut.com            /pub/simtelnet
US, Virginia        mirrors.aol.com               /pub/simtelnet


For the X2 archive:

     Site: ftp.ibp.fr (France)
     Path: /pub/pc/x2ftp/
     Mirror: books,msdos
     Update: 0330 GMT

     Site: ftp.rz.uni-karlsruhe.de (Germany)
     Path: /pub/programming/mirror.x2ftp/
     Mirror: books,msdos,unix
     Update: 0200 GMT

     Site: ftp.infomagic.com (USA)
     Path: /pub/mirrors/x2ftp/
     Mirror: amiga,books,console,msdos,standards
     Update: 0400 GMT

     Site: illusion.shiny.it (Italy)
     Path: /pub/x2ftp/
     Mirror: msdos
     Update: 0500 GMT

     Site: mirrors.aol.com (USA)
     Path: /pub/x2ftp
     Mirror: books,console,msdos
     Update: 2330 GMT

     Site: ftp.ee.techpta.ac.za (168.172.8.5) (South Africa)
     Path: /pub/mirrors/x2ftp/
     Mirror:
     Update: 2300 GMT

     Site: ftp.lanet.lv (Latvia)
     Path: /pub/mirror/x2ftp/
     Mirror: books,msdos
     Update: 0130 GMT

     Site: http://www2.cp.eng.chula.ac.th/mirror1/x2ftp
     Mirror: msdos
     Update: 2100 GMT
     Freq.: weekly (Friday)
     Admin: ftpadm@cpu.cp.eng.chula.ac.th




Contacting InterAction software

You can contact me for any reason, whether it's to ask for registration
information, to ask if/how you can do something in UVE32, to report a
bug, or just to say what you like/don't like about UVE32.
Alternatively, you can join the UVE mailing list (more information in
the relevant section).

When writing in to report a bug or ask something about a command, please
take the time to read through the manual.  There is a good chance the
answer you are looking for is already there.  Also, check the FAQ for
answers to frequently asked questions.  When you writing in with a
question, please include in your mail information about the version of
UVE32 you are using.

Okay, by far the best way to contact me is through e-mail.  My address
is:

                            cbp@doc.ic.ac.uk

Note that this address is only valid until the 1st of July, 1998.
Furthermore, avoid sending mail to that address during the Christmas,
Easter, and Summer holidays as I will not be checking my mail regularly
during those times.  You can alternatively send mail to the following
address:

                              robin@hol.net

It's probably A Good Idea to send your mail to both addresses to
minimize latency time (I love it when I use jargon).  Some e-mail
bounces, so if you haven't heard from me in 15 days, send your mail
again, perhaps including more complete or alternative reply addresses.

If you have no access to the Internet, You can use conventional surface
mail, although I can't guarantee how long it's going to take before you
get an answer.  You can write to:

                          Constantine Prouskas
                         4 Adrianoupoleos street
                            GR 156-69 Papagou
                             Athens, GREECE




Registering

UVE32 is shareware.  It is not free.  If you use UVE32 for 21 days and
wish to continue using it, you must register it.  Read the License
Agreement for more information on what you are and are not allowed to
do.  By registering you are acknowledging all he work that has gone
behind this product and supporting further development.

By registering, you get:

    My eternal gratitude :-)
    The latest version of UVE32 for Turbo Pascal 6.0 & 7.0 on disk,
  plus the protected mode version for Borland Pascal 7.0.
    Unlimited mail/e-mail support (including hot tips on how it's
possible to have a 2-player split screen in UVE32).
    The complete documentation in Rich Text Format (.RTF), complete
with advanced formatting and tables.
    Registered versions of all the supporting utilities, including
UVSINFO, PCX2UVS, PCX2UVT, LIBMAKER.
    An advanced, powerful version of PCX2UVS that allows multiple
sprites per PCX and complete control over the save region.
    WADMAKER:  An extra utility that can create .UVW files.
    WADVIEW:  An extra utility that shows the contents of .UVW files.
    FLI2UVL:  An extra utility that directly creates a UVL file from a
FLI file.
    *NEW!* UVE_KEY: A unit to handle multiple keypresses.  An absolute
must for games.

Registration is US$25.  There is a 20% discount (US$20) to any cheque or
cash orders. The latest version available at the time of registration
will be sent to you on a HD floppy (default is 3.5", specify if you want
5.25") via first class mail.

Starting with v1.40, it is now possible to register using your credit
card.  Since this has made things a bit more complicated, I have split
up the registration to credit card and non-credit card orders.



Credit card orders

For your convenience, I have contacted another company, NorthStar
solutions, to process any orders you may wish to place with your Visa,
MasterCard or Discover card.  NorthStar Solutions can be easily
contacted for orders only via any of the following methods:

    PHONE ORDERS
  Available 10am-8pm, EST, Monday through Saturday
  1-800-699-6395 (Calls from the U.S. only.)
  1-803-699-6395 (Calls from outside the U.S.)

    FAX ORDERS
  1-803-699-5465 (Available 24 hours.  International and business orders
  encouraged.)

    INTERNET ORDERS
  Simply fill out the online order form at
  http://ourworld.compuserve.com/homepages/starmail

    E-MAIL ORDERS
  You can send your order via e-mail to:
  America Online: STARMAIL
  CompuServe: 71561,2751
  Internet: 71561.2751@compuserve.com

Please provide (or be prepared to provide) the following information
when ordering:
    The program you are registering (Ultra VGA Engine 32).  You might
  find it easier to refer to UVE32 by using its product ID, which is
  #1126.
    Your mailing address.
    Your Visa, MasterCard or Discover card number and its expiration
date.
    Your drive type (if other than 3.5").
    Your e-mail address (so that NorthStar Solutions can send you
confirmation of your order).

IMPORTANT!
Please note that NorthStar Solutions only handles credit card orders.
They will not be able to offer any assistance or technical support with
any problems you may have with UVE32.  Please refer any such problems to
me directly.

Feel free to e-mail me as well, notifying me that you placed an order
with NorthStar Solutions if you want to speed up the sending out of the
disk.



Other (non-credit card) orders

Apart from using a credit card, you can also register by the following
alternative methods:

    Cheque.  Must be in U.S. Dollars, British Pounds or Greek Drachmas
  and made payable to "Constantine Prouskas".
    Cash.  Again, must be U.S. Dollars, British Pounds or Greek
  Drachmas.  For other currencies, visit your local bank or travel
  exchange.  You should be able to convert to one of the above currencies
  using international exchange rates.
    Direct deposit in a bank account (U.S., U.K. or Greece only).

Since addresses, bank accounts and the like are subject to change,
however, it's best if you contact me at the above address first (see the
"Contacting InterAction software" section).  Apart from notifying me
that you want to register, it is possible to work out the payment method
that is most convenient on a case-by-case basis (even using a method not
described above).  Additionally,  I can arrange sending out the
registered version via e-mail, if you so wish.




Epilogue

This, as they say, is it.  You can take it from here.  I've supplied the
tools, you supply the inspiration.

One final request:  If you ever use UVE32 to write any sort of
application (especially what it was meant for, games), I ask that you
send me a piece of mail saying what it is and where I can get it.  It's
your way of letting me know that all the hours of hard work that have
gone into UVE32 have really paid off.

Go forth and make some magic.



