                      BETATRON game library v1.00b
           Copyright 1997 Liouros Thanasis, liouros@hotmail.com

Please refer to the file readme.1st for information about using, distributing
this library and contacting the author.

This file describes how the various elements of BETATRON work.

1.0   Description of BETATRON
  1.1  Anatomy of BETATRON
  1.2  Layers
  1.3  Bitmaps, subbitmaps & frame descriptors
  1.4  Bounding system & bounding surfaces
  1.5  Sprites & action functions
  1.6  Sprite ranges and sprite handling
  1.7  Object lists & priority lists
  1.8  Clustering grids
  1.9  BETATRON kernel
  1.10 Using BETATRON under windows 95


S1.0                 Description of BETATRON:

BETATRON is a game library for use with DJGPP v2 C++ compiler. (in its
current version). It requires at least 386 compatible processor and VGA
video card. 486 with VLB or PCI card is suggested. The current version of
BETATRON (version 1.00b) is a beta version. That means that the library
has not been tested thoroughly and may contain bugs, so if you discover
a bug please let me know by dropping an email at:
   liouros@hotmail.com
with a description of your system and the procedure i have to follow to
reproduce the bug. Please report the bugs if you want this library to
become better.

BETATRON is different from other libraries because it is not just a graphic
engine. It is a very specialized and complete environment for writing
2-dimensional platform and action arcade games. The most of the tools and
things you will need to write a platform game are already embedded in this
library. Also ,because BETATRON aims at a specific type of games, the
algorithms that have been chosen are the best known in terms of efficiency
and the code has been optimized as well. So the rate of the game engine is
controlled by the vertical retrace which means ,in simple words, that the
frame rate is between 60 and 70 hz (frames/sec)!!! (depending on the mode)

Another major advantage of BETATRON is the abstraction it provides. This
abstraction can be divided in 2 categories:
- hardware abstraction, which means that the user does not have to know
  anything about how to program the hardware (the video card for example,
  or the timer chip)
- game world abstraction, which means that the user is given a "world"
  (a game field) which is compeletely controlled by BETATRON. So the
  user does not have to worry about how to draw the sprites, how to clip
  them ,how to perform scrolling , or how to bound the movement of the
  sprites etc. All of these important details are automatically handled
  by BETATRON.




S1.1                       Anatomy of BETATRON:

BETATRON consists of 2 large classes.
The first one, the TOworld structure takes care of every detail related
to the game world and the second (TOobject and TOsprite) is a simple but
powerfull enough general sprite object. You may extend this object
to meet your needs.

The "game world" is a rectangular field in which all the action of the game
takes place. Every point in this world is defined by 2 coordinates. The point
with coordinates (0,0) is the leftmost and the uppermost point of the world.
The x-coordinate is increased to the right direction and the y-coordinate
is increased downward.

The game world consists of the "background" and "foreground". Those are all
the static graphics of the game. These graphics are dumb because they cannot
move (relatively to the world) or animate and they dont have any kind of
behaviour. The background and foreground are made up from "tiles" (which are
small images (16x16 pixels) ) and are encoded in the "layers"

Besides the background and foreground in TOworld you can find the sprite
images (or "sprite frames") which are complex images consisting of one
or more simple images, the "bitmaps".

In BETATRON the sprites (or objects, object is used as synonymous to sprite)
are "intelligent" objects with their own behaviour. Every sprite has "action
functions" that determine its behaviour. Also every sprite in the game is
held in exactly 2 lists in TOworld and thus belongs to an "object list" and
a "priority list".

The flow of control in a BETATRON program is not so obvious as it is in
a programming language. Actually the user has limited access to the flow of
control and, as you will see, that is an advantage. The heart of BETATRON is
the method TOworld::animate(). In this function there is a loop that in
each cycle delivers the control over the "action functions" of the objects
that belong to the world. The animate loop is sychronized with the vertical
retrace.

The user normally writes his own code in an action function, then attaches
this action function to an object and inserts the object to the world.
Then ,in every cycle, TOworld::animate() calls the action function of the
object and thus executes the user's code. Of course you can have many
action functions per object, many objects and also many objects sharing
the same action function.




S1.2                         Layers:

To understand what a layer is, imagine that the game world is divided in
an equally spaced grid. Each cell of this grid is 16x16 pixels. The grid
can be represented by a 2-dimensional array, the layer. In each position
of the layer a tile number is hold. The corresponding tile is put in the
the appropriate position on the grid. For example if layer[10,20]=13
then the tile with number 13 will be put on grid position 10,20. Notice
that the world coordinates of grid cell (10,20) are (10*16,20*16) because
each grid cell is 16x16 pixels. We use layers to represent the background
because that way the graphics of the game are compressed and we can fit
the game graphics in the relatively small amount of memory we have available.

In BETATRON there are two graphic layers and 2 special layers.
The two graphic layers are called background layer and foreground layer.
These 2 layers contain unsigned short values that each refers to a tile
pointer in the TOworld::Ptiles array.
The exact format of those (unsigned short) values is:
bits 0-14	      tile number (cell number in TOworld::Ptiles array)
bit  15 	      if set, then BETATRON draws the tile according to
		      priority TOworld::backprior ,for the background layer
		      and TOworld::foreprior ,for the foreground layer
		      else
		      the tile is always drawn before all the sprites.
		      (priority   -1)

When working with the graphic layers you must have in mind the following
things:
1. The foreground layer is always displayed over the background layer.
2. A foreground layer tile is always displayed as a see-thru tile.
   while a background layer tile is always displayed as a solid tile.
3. the default value for TOworld::backprior and TOworld::foreprior
   is PRIORITIESNO-1==14. Thus tiles with the high bit set (in background
   and foreground layers) are drawn last (over every sprite).
   You can change the default values with TOworld::setbackprior()
   and TOworld::setforeprior().
    - foreprior can never be less than backprior
    - Sprites that belong to priority lists [0..backprior] are drawn first
      Then background tiles with the high bit 1 are drawn
      Then if backprior < foreprior the sprites that belong to priority
      lists [backprior+1..foreprior] are drawn.
      Then foreground tiles with the high bit set are drawn
      And finally if foreprior+1<=PRIORITIESNO-1 the sprites that belong
      to priority lists [foreprior+1..PRIORITIESNO-1] are drawn.
4. If a background layer tile has its high bit 1 then the corresponding
   foreground layer tile must also have its high bit 1, else the latter
   wont be displayed at all when a sprite comes into the tile's area.
   ( That's because foreground must always be drawn over background.)
5. The following applies to all layers:
   Layers in BETATRON are one-dimension arrays although they represent
   a 2-dimension scenery. For example the background layer tile with
   coordinates (i,j) is mapped on the position j*TOworld::length + i of
   the Pbacklayer layer , where TOworld::length is the length of the world
   in tiles. You must remember that; if you want to alter directly the
   contents of the layers. As an alternative you may always use the
   TOworld::setlayervalue() function.

The third layer is called background attributes layer. (LAY_ATTR)
It also contains unsigned short values but unlike the other layers it
has nothing to do with what is displayed on the screen. This layer holds
,for each tile, 16 bits of attribute information. Attribute information can
be used as you like. However the 4 highest bits of each attribute layer word
are reserved by BETATRON and used by the bounding system.
The functions that use these bits are:

howfarU():      uses bit 14 to determine if the bottom side of
		    the tile is walkable or not
howfarD() and
howfarSD():     use bit 15 to determine if the top side of
		    the tile is walkable or not
howfarL() and
howfarSL():     use bit 12 to determine if the right side of
		    the tile is walkable or not
howfarR() and
howfarSR():     use bit 13 to determine if the left side of
		    the tile is walkable or not

The rest of the bits (0-11) are available to the user.

The fourth layer is called background attributes layer 2. (LAY_ATTR2)
The lowest 9 bits of each LAY_ATTR2 word are used by the extended
bounding system of BETATRON while the rest are available to the user.
The exact format of these bits is:

if all the bits are 1:
     a bounding line is located on the top of the tile
else the bits form a number which is:
     the number of the bounding surface that intersects the tile

See also:
Bounding system & bounding surfaces
object lists & priority lists




S1.3               Bitmaps, subbitmaps & frame descriptors:

When you look at a sprite, animating on the screen, what you actually see is
a sequence of images which are displayed one after another very fast.
Those images are called sprite frames, in BETATRON terminology.
So a sprite animation consists of sprite frames, which are images.
In many cases it would be useful to divide a sprite frame into smaller
pieces (smaller images). This concept is implemented in BETATRON.
Each piece of a sprite frame is called subbitmap or subframe.


There are many advantages with this aproach:
1. we can easily draw each subframe with a different draw method.
2. we dont need to store 2 or more times a subframe that remains the same
   during the animation. We must store only a reference to that subframe.
3. we can easily change the appearance of a sprite frame by changing
   only the reference to a subbitmap not the subbitmap data themself.

There is also a disandvantage and that is, a little extra overhead. But it
is surely worth it.

Every subbitmap in a sprite frame refers to a bitmap in the TOworld::bitmaps
array. For more information see struct Tbitmap.

Every sprite frame in the world has its own unique sprite frame descriptor.
The frame descriptors contain all the necessary information about the sprite
frames, that is:
- how many subbitmaps are in the sprite frame.
- what is the bitmap number (cell number in TOworld::bitmaps array) assigned
  to each subbitmap and what is the position of each subbitmap in the sprite
  frame.
- what is the total length and height of the sprite frame.
- what is the collision mask for the sprite frame.

Because it can be tricky to alter the values in a frame descriptor directly,
i wont discuss the Tframedescr structure. But i will discuss how to use
the functions which are dedicated to that purpose.
Lets see an example:
 TOworld myworld;

Assume that the maximum number of sprite frames that you may need
in your world is 500. The first thing you have to do is to reserve
memory for 500 pointers to sprite frame descriptors:

myworld.makeframesdescrs(500);
( In a real program you must always check what the function returns)

Note that this command wont reserve memory for the frame descriptors,
it will only create an array of pointers to frame descriptors.


First, lets see the simple case.
If your sprite frame consists of a single subbitmap , then use
make1bitmapdescr()

myworld.make1bitmapdescr(0, 12, TRUE );

The last function call builds (reserves memory for) the first frame (number 0)
descriptor, assigns the bitmap number 12 to the single subbitmap of the
sprite frame and requests a collision mask for the sprite frame.

That's all.


Now lets see what happens if you have more than 1 subbitmaps in a spite
frame:
Suppose that you want to build a new descriptor and say that this
decriptor is the first one (cell number 0) in the array
that makeframedescrs() made.

Say also that:
- You want to put 3 subbitmaps in the descriptor and
- The corresponding sprite frame will have length 27 and height 19.

The following function call will make a descriptor with those specifications:
myworld.getdescrmem(0, 27, 19 , 3);

You still have to tell BETATRON which of the bitmaps in the TOworld::bitmaps
array will be assigned to each of the 3 subbitmaps of the sprite frame and
what the relative coordinates of those subbitmaps are.

Suppose that bitmap number 23 will be assigned to subbitmap number 0
 "        "          "     12    "     "          "          "     1
 "        "          "     00    "     "          "          "     2

Say also that subbitmap 0 will be put at pos (00,00) in the sprite frame area
       "        "       1            "       (08,05)       "     "       "
       "        "       2            "       (18,16)       "     "       "

myworld.setsubbitmap(0,0,23, 00,00);
myworld.setsubbitmap(0,1,12, 08,05);
myworld.setsubbitmap(0,2,00, 18,16);


	 Sprite frame area
(0,0)
Ŀ
	   			  
	   			  
    (8,5) 			  
	Ŀ		  
bitmap   	     		  
  23	  	     		  
	     		  
		     		  
	   bitmap   		  
	     12     		  
		     		  
		     		  
		     		  
		     		  
	Ĵ
		     		  
		     	 bitmap 0 
(26,18)



Be carefull with setsubbitmap().  If a bitmap doesnt fit in the sprite frame
area then setsubbitmap() will return ERR_BITMAPNOTFIT. It is your
responsibility to use bitmaps that fit in the sprite frame area or make the
sprite frame area large enough.
As an alternative you may use dprintf() to build a frame descriptor
in a very sophisticated way.
See the description of this function for more details.

sprite frame area
Ŀ
		
		
		
		
		     <-------	NOT ALLOWED
   bitmap	
   Ŀ	
   		
   		
   		

    	
    	
    

Now you have built a new sprite frame. You may optionally request a collision
mask for that sprite frame. Call TOworld::makeframecolidmask() with the number
of the corresponding frame descriptor as an argument. A collision mask will
be built and put in the frame descriptor.



S1.4                Bounding system & bounding surfaces

Surfaces are bounds that prevent sprites from going any where in the world.
To undestand better surfaces, lets first take a look at the basic bounding
system of BETATRON. This bounding system is very simple and uses the
background attributes layer (LAY_ATTR) to store the information it needs
to work. The LAY_ATTR layer has one word (16 bits) associated with each
tile. However only the 4 highest bits of each such word are used by BETATRON.
The rest are available to the user. Each one of the 4 bits corresponds to one
of the 4 tile sides. If the bit is 1 the side is solid else the side
is walkable.
 For example consider the following tile.

           | N
           | 
           v
        |------|
E       |      |       W
---->   |      |   <----
        |------| 

           ^
           |
           | S

If the bit which corresponds to the top side is 0 then a sprite can move
from north to south (function howfarD()) and pass over the tile.
Else the sprite is blocked when it tries to pass the tile. Note that
the movement of the sprite from south to north (function howfarU()) is
controlled by the bit which corresponds to the bottom side and it is
independent from the top side bit. The same is true for horizontal
movement: The left side bit controls the movement from east to west
(function howfarR()) while the right side bit controls the movement
from west to east (function howfarL()).

Notice that your sprites' movements will not be restricted by the bounding
system unless you use howfarXXX() functions. See the description of
these functions for further details. See also Layers.

Although the above bounding system is simple and efficient, it has one major
effect. The bounding surfaces are always shaped as horizontal and vertical
lines, and that happens because the surfaces are made up from tile sides which
are vertical or horizontal. So you can not have surfaces of any shape.
For example you can't  have you hero on a diagonal surface, say, climbing
a hill. For that reason i decided to expand the bounding system and make
it more complicated by adding "any shape" surfaces.
The expanded bounding system is compatible with the basic one. The only thing
that has been changed for the user is the 3 new functions:
howfarSL(), howfarSR() and howfarSD()

BETATRON can now work with 2 different bounding systems:
 - The basic one and
 - the expanded.

When working with the expanded bounding system, the bit which correspons to
the top side has a special meaning.
If it is 0 it means that the top side is walkable as before but when it is
1 it means that an "any shape" surface crosses the tile.
The number of the surface template that describes the "any shape" surface
is found in another layer, the LAY_ATTR2 layer.

A surface template is an one-dimensional array of unsigned short displacements
which define the shape of the surface. A template templ[] defines a surface
which is costructed by the points
( Sx+i, Sy+temlp[i] ) ,   0<= i < (number of shorts in templ)
(Sx,Sy) is the starting point of the template in the world.
That point is actually defined when you call TOworld::setsurface()

 Example 1:
 suppose that you have the following surface template

 unsigned short templ[]={0,1,2,3,4,5,6,7,7,7};

 This template corresponds to the following surface:
   0  1  2  3  4  5  6  7  8  9         x - direction
0  *
1     *
2        *
3           *
4              *
5                 *
6                    *
7                       *  *  *


 Example 2:
 unsigned short templ2[]={7,6,6,6,6,6,6,5,4,3}

   0  1  2  3  4  5  6  7  8  9         x - direction
0  
1   
2        
3                             *
4                          *
5                       *
6     *  *  *  *  *  *   
7  *                     



The starting point (0,0) can be any one you like so the surface can be put
anywhere in the world. But note that you cant have more than one surfaces
crossing the same tile.
The use of surfaces in BETATRON is easier than you may thought.
Assuming you have done the steps to support a basic bounding system
by initializing the world, creating the appropriate layers etc the
additional steps for the extended bounding system are the following:
 - you must create a LAY_ATTR2 layer with TOworld::makelayer()
    myworld.makelayer(LAY_ATTR2)
 - you must reserve space for the array of pointers to the surface templates
    myworld.makesurfaces(30); // reserve space for 30 surface templates
 - call setsurface() as many times as needed to insert your surface templates
   in the world.
    // insert template "templ" in position 0 of the surface templates array
    // and set the apropriate values in LAY_ATTR and LAY_ATTR2 layers
    // so the template is put in the world with starting point (40,40)
    myworld.setsurface(0,templ,40,40);
 - Use the functions howfarSL(),howfarSR(), howfarSD() instead of
   howfarL(), howfarR(), howfarD().
   The former functions will call the latter ones if TOworld is working in
   the basic bounding system mode. So you should always use the former
   functions.

Note that "any shape" surfaces are related to movements from north to south.
Also howfarSR() and howfarSL() have been coded taking in mind that the gravity
is going from north to south. Those of course, are restrictions and if you
want to use some other bounding system you must make it yourself.



S1.5                   Sprites & action functions:

A sprite that cannot move in the world, or generally do something is not
very useful. So every sprite must have some kind of behaviour. In BETATRON
every sprite may have its own behaviour which is implemented through action
functions.
 An action function is a function that takes a single argument:
 a pointer to a TOobject object.
 Here is an example of an action function which increases the x coordinate
 of a TOobject object and thus moves the object to the right one pixel (point)

  void myaction(TOobject *myobject)
  {
    myobject->x++;
  }


Here are some very important details about action functions:
- Each sprite can have as many action functions as you like.
- The fact that every action function takes as an argument a pointer
  to a TOobject object does not mean that it cannot take a pointer
  to a TOsprite object or any other object derived from the basic (TOobject)
  or its descendants. ( a real powerfull aspect of object oriented programming)
- You dont use action functions only to move sprites on the screen. You
  can do what ever you like with them.
- For each sprite:
  you cannot have more than one action functions executed per frame, but
  you can have one action function executed more than one frames.

Lets see an example:
 Suppose that you have a sprite named enemie1:

  TOsprite enemie1;

 and the following 2 action functions:

 void enemieinit(TOobject *obj)
 {
   obj->dx=1;
   obj->x=10;
   obj->y=10;
   obj->nextaction=enemierun;
  }

 void enemierun(TOobject *obj)
 {
   if ( (obj->x>90) || (obj->x < 10) ) obj->dx=-obj->dx;
   obj->x+=obj->dx;
 }


 Now tell the enemie1 object which function will be called first:

 enemie1.nextaction=enemieinit;  // start with enemieinit();

 TOsprite::nextaction holds a pointer to the action function that will be
 called in the next cycle. TOworld::animate() uses TOobject::nextaction
 to determine which action function will be called in the next frame.
 So in this example TOworld::animate() will execute enemieinit() in the
 next frame. enemieinit() will initialize some values of enemie1
 object and then it will set nextaction to enemierun.
 That means enemierun() will be executed one frame after enemieinit().
 enemierun() doesnt change the nextaction field of enemie1 so this function
 will keep being executed at the next frames.

    

S1.6                 Sprite ranges and sprite handling:

As we have already said, TOworld::animate() delivers the control to the
sprites by calling their action functions in every cycle. Usually in a game
there are a lot of sprites in the world. The most of the sprites are not even
visible. If animate() has to call the action functions of all the sprites then
too much time would be spent in that task and the game could be so slow, that
animate() would lose vertical retraces.

So there are some fields in TOobject class, that are used by animate() in
order to decide which sprites to handle. These fields are:

TOobject::deletebit
 if this field is TRUE then the sprite is not handled, that is it's action
 function is not called

TOobject::activebit
 if this field is FALSE then animate() will handle the sprite only if the
 sprite is partly or fully visible.
 if TRUE then animate() will always handle the sprite

TOobject::rangeactivebit
 if this field is TRUE then the sprite range is active else the sprite range
 is inactive. A sprite range is a box in the world. When the view window
 intersects the range box then we say that the sprite is in range.
 You set the sprite range by calling TOobject::setrange().

Here is the piece of (pseudo) code that animate() uses to decide which sprites
to handle

 if (sprite.deadbit == FALSE)
 and
 (
   ((sprite is visible) or (sprite.activebit == TRUE)) 
    or
   (( rangeactivebit == TRUE) and (the object is in range))
 )
 then (call the active action function of the sprite)





S1.7                   Object lists & priority lists:

BETATRON uses internally double linked lists to store every object
in the world. There are 15 object lists and 15 priority lists.
Each object belongs to one object list and one priority list exactly.
The object lists are used to logically seperate the objects to categories.
For example the enemie sprites can be stored in an object list while the
bullets of the main character's gun can be put in another one.
Then you can use clustering grids to perform easy collision detection of
the bullets against the enemies.

The priority lists are a mechanism to implement sprite pseudo-layers.
An object in priority list number n is always drawn after all
objects in priority lists 0..(n-1) have been drawn.
Also an object in priority list number n is always handled ( that is,
its active action function is called ) after all objects in priority
lists [0..(n-1)] have been handled.

for example:
suppose that you want to have a sprite that is always displayed over all
the rest of the sprites. ( a score sprite )
You can insert this object in priority list PRIORITIESNO-1 and the rest objects
in priority lists 0..PRIORITIESNO-2
Now you can be sure that there is no posibility for a sprite to be drawn
over your score sprite.

There are some TOsprite fields you may need to use:
TOsprite::type	     it is the number of the object list that owns the sprite
TOsprite::priority   it is the number of the priority list that owns the sprite
TOsprite::Pnext      points to the next  entry in the object list
TOsprite::Pprev      points to the previous entry in the object list
TOsprite::Pnext2     points to the next entry in the priority list
TOsprite::Pprev2     points to the previous entry in the priority list

You must never change any of these fields directly. Use instead the functions
TOworld::addobj(), TOworld::removeobj() etc.

Whenever an object is to be handled; TOworld::animate() calls the active
action function of the object and passes as argument a pointer to the
object itsself

for example:

TOsprite mysprite1,mysprite2;
TOworld myworld;

void myaction(TOobject *obj)
{
 ...		    // an action function
 ...
}

void main()
{

 ..	// initialize mysprite1,mysprite2

 mysprite1.nextaction=myaction;   //active action function: myaction()
 addobj(0,mysprite1,1);   // add mysprite1 in object list: 0, priority list: 1

 mysprite2.nextaction=myaction; 	   //active action function: myaction()
 addobj(0,mysprite2,1);   // add mysprite2 in object list: 0, priority list: 1
 ..
 ..
 myworld.animate();
 ..
 ..
}


myworld.animate() will call the active action function of mysprite1 as:

  myaction(mysprite1);

Also animate() will call the active action function of mysprite2 as:

  myaction(mysprite2);



You can have dynamically allocated objects in any of BETATRON lists.
But you should never use removeXXXX() functions to remove an object
from the lists it belongs to, or C++ delete operator to free the memory
of the object ,from inside an action function. Use instead the
TOobject::deletebit and TOobject::removebit to let BETATRON do the job,
without side effects.

for example:
void Spriteactiondie(TOobject *obj)
{
 obj->deletebit=1; // tell BETATRON to remove (by calling TOworld::removeobj()
		   //  and delete (via C++ delete operator) the sprite in
		   // the next frame (not the current)
}


If your sprite is not dynamically allocated or for some reason you dont want
to free the memory reserved for that then set removebit to 1:

void Spriteactiondie(TOobject *obj)
{
 obj->removebit=1;  // just remove the sprite from the world through removeobj()
		    // in the next frame
}


S1.8                       Clustering grids:

A clustering grid is an equally spaced grid that is used to classify
the sprites according to their position in the world. Each sprite must belong
to at least 1 grid cell but may belong to more grid cells. That depends on
the size and the position of the sprite. The clustering grids always use
the sprite box (or bounding box) to determine which grid cells a sprite
intersects. 

For example in the following case the sprite represented by the dots belongs
to 2 grid cells (the 2 ones it intersects)  


|         |
|         |  
|---------|----------
|      ...|...
|      .  |  .
|      .  |  .
|      ...|...
|---------|----------
|         |
|	  |


In BETATRON's grid implementation each grid cell has a linked list of
pointers to the sprites that belong to the specific grid cell. The cell
dimensions are powers of 2 for optimization reasons. 
 
Now lets see why a grid is useful and how it can be used.
In a typical game there is the main character and the enemies. Suppose that
the main character can fire bullets. All of the bullets must be checked for
collision against all enemies. This task can be a real headache and consumes
a lot of computing power if the sprites are not ordered somehow. Without a
grid you have to perform (number of bullets)*(number of enemies) collision
detections which of course demand a great amount of time. But if you have the
sprites classified on a grid then it suffices to check each bullet against
all the enemies that belong to the same grid cells as the bullet. That way
you can save a lot of time because in a particular moment it is more likely
that only a few enemies are nearby the bullet. For example if you have 300
enemies, the enemies will propably be spread all over the world and only
a few, say 4 or 5 will be next to the bullet. The rest of the enemies could
never collide with the bullet because in that case they had to be next to the
bullet. (in the same grid cells).

In BETATRON you can have more than one grids, each one handling some or all
of the objects. The maximum number of grids allowed is MAXGRIDSNO.
To initialize a grid call definegridlists() and pass as parameters the lists
(object lists) you want to be handled by the grid.

For example:
     TOworld::definegridlists(0, 1,5,7, -1);

The first parameter is the number of the grid you want to initialize.
The next parameters are the numbers of the lists.
The terminating parameter which is always a negative number must be used
because the function takes variable arguments, so it needs a way to find
out where the parameters end.
In the above example the grid no 0 will handle the objects of object lists
1,5,7 and only them. The actual classification is done in TOworld::animate()
in every frame. TOworld::animate() calls an appropriate function of the grid
object, and that function builds a list of pointers to objects for each grid
cell. You can use gridcollision(), getgridarea(), getgridfirst() and
getgridnext() to perform whatever job you want.





S1.9                      BETATRON kernel:

The topic discussed here applies to advanced users.
If you like to link a music library to your program to be able to play music
in the background, you will soon encounter screen synchronization problems.
The scrolling and the animation of the sprites or the playback of the music
will be jerky and the result wont be impressive at all.
In that case you must change BETATRON kernel.

BETATRON kernel is a sequence of commands that affect the hadrware of
the VGA card and it is used internally by BETATRON to perform some very
important tasks.

BETATRON kernel exists in TOworld::animate() function and by default
it is the following sequence:

1  asm cli
2  pageswap();
3  setsadr();
4  pl_retrace();
5  sethpel(hpel);
6  refreshpal();
7  asm sti


-command 1 deactivates the interrupts because nothing must interrupt the
 next commands until command 6
-command 2 swaps the offsets of the draw and the show pages.
 ( BETATRON uses page flipping for smooth animation.)
-command 3 sets the start address register of the vga card to the
 appropriate value
-command 4 waits for the next vertical retrace to start.
-command 5 puts the appropriate value in the horizontal pel panning
 register of the vga card.
-command 6 refreshes the palette if it has changed.
-finally command 7 activates the interrupts again.

This sequence works very well when no music is played in the background
but if you decide to use a music library one of the following things will
happen:
i)  An interrupt must be triggered to be handled by the music library when
    the kernel sequence is executed. The problem is that the interrupt cannot
    be triggered because of command 1. So the music library will lose one
    cycle and the music will be jerky.
ii) Suppose that you remove commands 1 and 6. Now during execution of kernel
    sequence, a music (timer) interrupt is triggered. The problem is that
    execution of the interrupt handler takes too much time. So BETATRON
    may lose one retrace and the animation will be jerky in that case.

The solution given here works nice under DOS but when it comes to windows 95
it has problems. See "Using BETATRON under win95" to see a possible solution
for both DOS and windows 95 that has not been tested yet.

The music library (not BETATRON) must sychronize its activity to the
vertical retrace, and have 3 functions called:
the first just before vertical retrace starts (lets call this preVR() )
,the second immediately after vertical retrace has started (immVR())
and the last during the vertical retrace ( inVR() )

Here is the solution:

volatile int framecount=0,oldframecount=0;
volatile char frameready =1;
TOworld world;


void preVR()
{
 if (!frameready) return;
 framecount++;
 world.pageswap();
 world.setsadr();
}

void immVR()
{
 if (!frameready) return;
 world.sethpel();
}

void inVR()
{
 if (!frameready) return;
 world.refreshpal();
 frameready=0;		    // set frameready to zero , a new frame will be built
}



short mykernel(TOworld *w)
{
 for (;framecount==oldframecount;);
 oldframecount=framecount;
 return 0;
}


void main(void)
{
   ..
   ..
   ..	  // initialization code

  // tell BETATRON to set frameready=1 each time a frame has been prepared
   world.Pframeready=&frameready;
  // Override the default kernel sequence with mykernel()
   world.Pkernelproc=mykernel;

(tell the music library to sychronize its activity to the retrace)
(tell the music library to call preVR() just before retrace)
(tell the music library to call immVR() just after retrace has started)
(tell the music library to call inVR()	during retrace )

  world.animate(); // run
  ..
  ..  // shut down code
}


Lets see how the code works:
* pageswap() and setsadr() must always be executed before the retrace.
  That's why they are in preVR().
* sethpel() must always be executed after the retrace has started
  That's why it is in immVR().
* refreshpal() must always be executed during the retrace
  That's why it is in inVR()
* Now the kernel sequence is the function mykernel()
  mykernel() actually waits for the pageswap() command to be executed.
  That happens when the framecounter changes inside preVR().
* the frameready variable is used for safety reasons. If there is a lot
  of animation on the screen, BETATRON will propably need more than one
  retraces to prepare a frame. So pageswap(),setsadr() and sethpel()
  execution must be postponed until the frame is prepared. Otherwise the
  results will be unpredictable.

I have succesfully used this method to make BETATRON cooperate with
MIDAS music library. 




S1.10                    Using BETATRON under win95.


If you want to code games that run very well under windows 95 you must be 
aware of some important issues. I have spent a great amount of time trying 
to make BETATRON compatible with win95. A lot of code has been changed and 
some parts of the library have been entirely rewritten in order to 
accomplish the desired compatibility. Besides what i have done; you, also,
must remember some important things to write windows 95 compatible programs.

There are 2 major kinds of problems concerning programming DOS programs
that run under win95:

1. Timer interrupt sychronization problems.

That means that the timer interrupt cannot be called at a constant rate
especially if that rate is high. This is a big problem when you try to
use a music library in conjuction with BETATRON.

Because BETATRON is retrace driven the music library must be synchronized
with the vertical retrace ,by measuring the period between 2 consecutive
retraces and reprogramming the timer chip.
Then the main function of the music library, will be called in every frame
just before the vertical retrace. Unfortunately that is impossible, 
because the rate of the timer interrupt is not constant, under win 95.

However i have think of a solution to this problem but i dont know if that
solution would work:

Instead of having the main function of the music library asychronously called,
call it sychronously from inside BETATRON's kernel.

Recall BETATRON's kernel sequence:
1  asm("cli");
2  pageswap();
3  setsadr();
4  pl_retrace();
5  sethpel(hpel);
6  refreshpal();
7  asm("sti");
Now we can add the call to the main function of the music library
just after command 7 like this:
8  mixing_function();

The mixing_function() needs the time between 2 consecutive retraces. That
time is stored in TOworld::retraceticks by TOworld::inittimer(). Please note
that under win95, TOworld::retraceticks is not the exact value but it is very
close to it. As long as your program does not lose vertical retraces,
by doing too much work, the mixing_function() will be called at the correct
rate.

As i have already stated i have not tested this solution and i propably
won't. So, if you do it, please let me know what would happen.


2. Screen and video card register corruption.

When you put a BETATRON program in the background and then try to restore
it ,it is possible in several cases that the screen will be corrupted or
some registers of the video card will be altered. BETATRON's code for
standard modeX 320x200 has been written especially to deal with this problem.
So the problem is more obvious in VESA 2.00 modes. For example, in my S3 card
some times the program looks like it has hanged because some important S3
registers have changed and nothing is updated on the screen.

Unfortunately there is no way for a dos program to know if it just has been
restored or if the registers or the video memory have changed.
So the only solution to the problem is to let the user refresh the screen
by pressing a special key. Use the functions

 TOworld::refresh(),
 pl_saveVESAstate(),
 pl_restoreVESAstate()

for that purpose. Use pl_saveVESAstate() at the beggining of your program
and when the user presses the special key call first pl_restoreVESAstate()
and then TOworld::refresh().











