home *** CD-ROM | disk | FTP | other *** search
- /**************** ACK-3D ( Animation Construction Kit 3D ) *******************/
- /* Engine Code */
- /* Author: Lary Myers */
-
- #include <stdlib.h>
- #include <stdio.h>
- #include <dos.h>
- #include <mem.h>
- #include <alloc.h>
- #include <io.h>
- #include <fcntl.h>
- #include <time.h>
- #include <string.h>
- #include <sys\stat.h>
- #include "ack3d.h"
- #include "ackext.h"
-
- /****************************************************************************
- ** **
- ****************************************************************************/
- char xRay(int x,int y,int angle)
- {
- char Color;
- int i,j,mx,my;
- int TablePosn;
- int MapPosn,CurPosn;
- int xBeg;
- long xPos,xNext;
- int BitmapColumn;
- int xCenter,yCenter,xAdj;
- int ObjPosn;
- int oBegX,oBegY;
- long yPos;
- long yNext;
- long xd,yd,sy;
- long ObjDist;
-
- xBeg = x & 0xFFC0; /* Get upper left corner of square */
- yNext = yNextTable[angle]; /* PreCalc'd value of 64 * Tan(angle) */
-
- if (angle > INT_ANGLE_270 || angle < INT_ANGLE_90)
- {
- xPos = xBeg + GRID_SIZE; /* Looking to the right */
- xNext = GRID_SIZE; /* Positive direction */
- }
- else
- {
- xPos = xBeg; /* Looking to the left */
- xNext = -GRID_SIZE; /* Negative direction */
- yNext = -yNext;
- }
-
- /* Calculate the Y coordinate for the current square */
- yPos = (((long)xPos - (long)x) * LongTanTable[angle]) + ((long)y << FP_SHIFT);
-
- while (1)
- {
- if (xPos < 0 || xPos > GRID_XMAX ||
- yPos < 0 || yPos > GRID_YMAXLONG)
- break;
-
- /************** Fixed point Y/64 * 64 X / 64 ***********/
- MapPosn = ((yPos >> FP_SHIFT) & 0xFFC0) + (xPos >> 6);
-
- /* First check for an object that has not been seen yet */
- if ((Color = xObjGrid[MapPosn]) > 0 && !(ObjFlags[Color] & 3))
- {
- ObjFlags[Color] |= 1; /* Indicate object has been seen */
-
- xd = ObjList[Color].x - x;
- yd = ObjList[Color].y - y;
- ObjDist = (xd * xd) + (yd * yd); /* Calc relative distance */
-
- /* Place the object in the array according to distance, with the ones closer */
- /* being drawn last. This will allow closer objects to hide farther objects */
-
- j = TotalObjects;
- for (i = 0; i < TotalObjects; i++)
- {
- if (ObjDist > ObjRelDist[i])
- {
-
- for (j = TotalObjects; j > i; j--)
- {
- ObjRelDist[j] = ObjRelDist[j-1];
- ObjNumber[j] = ObjNumber[j-1];
- ObjMapPosn[j] = ObjMapPosn[j-1];
- }
-
- j = i;
- i = TotalObjects;
- }
- }
-
- /* Hold onto relavant data for the object found */
- ObjNumber[j] = Color;
- ObjRelDist[j] = ObjDist;
- ObjMapPosn[j] = MapPosn;
- TotalObjects++;
- ObjRelDist[TotalObjects] = 3200000;
- }
-
- /* Now check to see if a wall is being struck by the ray */
- if ((Color = xGrid[MapPosn]) != 0)
- {
- xMapPosn = MapPosn; /* Hold onto the map location */
- iLastX = xPos;
- LastY1 = yPos;
- if (Color == DOOR_XCODE) /* Is this a door? */
- {
- yd = (yPos >> FP_SHIFT) & 0xFFC0; /* Get the left side */
- xd = yd + GRID_SIZE; /* And the right side */
- ObjDist = (yPos + (yNext >> 1)) >> FP_SHIFT; /* Calc door distance */
- if (ObjDist < yd || ObjDist > xd) /* Is door visible? */
- {
- xPos += xNext; /* Nope, continue casting */
- yPos += yNext; /* the ray as before */
- continue;
- }
-
- LastY1 = yPos + (yNext >> 1); /* Adjust the X,Y values so */
- iLastX += (xNext >> 1); /* the door is halfway in sq. */
- }
-
- if (Color == DOOR_SECRETCODE)
- {
- if (xSecretColumn != 0)
- {
- sy = xSecretColumn * LongTanTable[angle];
- ObjDist = (yPos + sy) >> FP_SHIFT;
- yd = (yPos >> FP_SHIFT) & 0xFFC0; /* Get the left side */
- xd = yd + GRID_SIZE; /* And the right side */
- if (ObjDist < yd || ObjDist > xd) /* Is door visible? */
- {
- xPos += xNext; /* Nope, continue casting */
- yPos += yNext; /* the ray as before */
- continue;
- }
- LastY1 = yPos + sy;
- iLastX += xSecretColumn;
- }
- }
-
- return(Color);
- }
-
- xPos += xNext; /* Next X coordinate (fixed at 64 or -64) */
- yPos += yNext; /* Next calculated Y coord for a delta of X */
- }
-
- return(0); /* Return that no wall was found */
- }
-
- /****************************************************************************
- ** **
- ****************************************************************************/
- char yRay(int x,int y,int angle)
- {
- char Color;
- int i,j,mx,my;
- int MapPosn;
- int yBeg;
- long yPos,yNext;
- int BitmapColumn;
- int xCenter,yCenter,yAdj;
- int ObjPosn;
- int oBegX;
- long xPos;
- long xNext;
- long xd,yd,ObjDist,sx;
-
- yBeg = y & 0xFFC0; /* Same as div 64 then mul 64 */
- xNext = xNextTable[angle]; /* Pre-calc'd value of 64 / tan(angle) */
-
- if (angle < INT_ANGLE_180)
- {
- yPos = yBeg + GRID_SIZE; /* Looking down */
- yNext = GRID_SIZE; /* Positive direction */
- }
- else
- {
- yPos = yBeg; /* Looking up */
- yNext = -GRID_SIZE; /* Negative direction */
- xNext = -xNext;
- }
-
- /* Calculate the X coordinate for the current square */
- xPos = (((long)yPos - (long)y) * LongInvTanTable[angle]) + ((long)x << FP_SHIFT);
-
- oBegX = 0;
-
- while (1)
- {
- if (xPos < 0 || xPos > GRID_XMAXLONG ||
- yPos < 0 || yPos > GRID_YMAX)
- break;
-
- /*********** Y/64 * 64 Fixed point and /64 ******/
- MapPosn = (yPos & 0xFFC0) + (xPos >> (FP_SHIFT+6));
-
- /* Check for unseen objects first */
- if ((Color = yObjGrid[MapPosn]) > 0 && !(ObjFlags[Color] & 3))
- {
- ObjFlags[Color] |= 2; /* Signal object seen on Y cast */
-
- xd = ObjList[Color].x - x;
- yd = ObjList[Color].y - y;
- yd = (xd * xd) + (yd * yd); /* Calc relative distance to object */
-
- /* Place the object in the array according to distance, with the ones closer */
- /* being drawn last. This will allow closer objects to hide farther objects */
- j = TotalObjects;
- for (i = 0; i < TotalObjects; i++)
- {
- if (yd > ObjRelDist[i])
- {
- for (j = TotalObjects; j > i; j--)
- {
- ObjRelDist[j] = ObjRelDist[j-1];
- ObjNumber[j] = ObjNumber[j-1];
- ObjMapPosn[j] = ObjMapPosn[j-1];
- }
-
- j = i;
- i = TotalObjects;
- }
- }
-
- ObjNumber[j] = Color; /* Hold onto object data for later */
- ObjRelDist[j] = yd;
- ObjMapPosn[j] = MapPosn;
- TotalObjects++;
- ObjRelDist[TotalObjects] = 3200000;
- }
-
- /** Check for a wall being struck **/
- if ((Color = yGrid[MapPosn]) != 0)
- {
- yMapPosn = MapPosn; /* Hold onto map position */
- LastX1 = xPos;
- iLastY = yPos;
-
- if (Color == DOOR_YCODE) /* Is this a door? */
- {
- yd = (xPos >> FP_SHIFT) & 0xFFC0; /* Calc top side of square */
- xd = yd + GRID_SIZE; /* And bottom side of square */
- ObjDist = (xPos + (xNext >> 1)) >> FP_SHIFT;
- if (ObjDist < yd || ObjDist > xd) /* Is door visible? */
- {
- xPos += xNext; /* No, continue on with ray cast */
- yPos += yNext;
- continue;
- }
-
- LastX1 = xPos + (xNext >> 1); /* Adjust coordinates so door is */
- iLastY += (yNext >> 1); /* Halfway into wall */
- }
-
- if (Color == DOOR_SECRETCODE)
- {
- if (ySecretColumn != 0)
- {
- sx = ySecretColumn * LongInvTanTable[angle];
- ObjDist = (xPos + sx) >> FP_SHIFT;
- yd = (xPos >> FP_SHIFT) & 0xFFC0; /* Get the top side */
- xd = yd + GRID_SIZE; /* And the bottom side */
- if (ObjDist < yd || ObjDist > xd) /* Is door visible? */
- {
- xPos += xNext; /* Nope, continue casting */
- yPos += yNext; /* the ray as before */
- continue;
- }
- LastX1 = xPos + sx;
- iLastY += ySecretColumn;
- }
-
- }
-
- return(Color);
- }
-
- xPos += xNext; /* Next calculated X value for delta Y */
- yPos += yNext; /* Next fixed value of 64 or -64 */
-
- if (++oBegX > 64) /* Check a maximum number of squares */
- break;
- }
-
- return(0); /* Return here if no Y wall is found */
- }
-
-
- /****************************************************************************
- ** This is the heart of the engine. This routine will cast two rays for **
- ** each column of the screen. The first ray will look for any walls that **
- ** fall on the X boundaries (vertical walls). The second ray will look for **
- ** walls on the Y boundaries (horizontal walls). The wall with the shorter **
- ** distance will be used to draw a sliver of the bitmap at the current **
- ** screen column (0-319). A 60 degree field of view was selected to give **
- ** a realistic perspective to the walls. **
- ** **
- ****************************************************************************/
- void DrawView(int xPlayer,int yPlayer,int PlayerAngle)
- {
- int i,j,index;
- unsigned char xBitmap,yBitmap,BitmapNumber;
- int ViewAngle;
- long xDistance,yDistance;
- long WallDistance;
- int distance;
- unsigned int BitmapColumn,yBitmapColumn;
- int WallCode,OldMapPosn,OldMapPosn1;
- long xd,yd;
- unsigned char *Video;
- unsigned int offset;
-
- /* Clear out the object seen flags so objects can be found */
- memset(ObjFlags,0,MaxObjects);
-
-
- /* Begin looking 30 degrees to the left of our current angle */
- ViewAngle = PlayerAngle - INT_ANGLE_30;
-
- if (ViewAngle < 0)
- ViewAngle += INT_ANGLE_360;
-
- /* Reset total objects found on last DrawView pass */
- TotalObjects = 0;
-
- MaxDistance = 0;
-
-
- /* Cast two rays for each column of the video display */
-
- for (i = 0; i < VIEW_WIDTH; i++)
- {
- WallDistance = 3000000; /* Set to a ridiculous distance */
- BitmapColumn = -1; /* Set to no walls found */
- WallCode = 0; /* Set to no shadowing */
-
- /* Don't even cast an X ray if impossible to intersect the X walls */
- if (ViewAngle != INT_ANGLE_90 && ViewAngle != INT_ANGLE_270)
- {
- yd = CLOCK_PTR;
- BitmapNumber = xRay(xPlayer,yPlayer,ViewAngle);
-
- if (BitmapNumber) /* A wall was found */
- {
- /* Use the Y intercept to determine the wall column */
- BitmapColumn = (LastY1 >> FP_SHIFT) & 0x3F;
-
- /* Keep the orientation the same no matter which side we're on */
- if ((int)iLastX < xPlayer)
- BitmapColumn = 63 - BitmapColumn;
-
- /* Did we strike a door? */
- if (BitmapNumber == DOOR_XCODE)
- {
- index = FindDoor(xMapPosn);
- if (index >= 0) /* This is a valid door */
- {
- j = Door[index].ColOffset; /* Get its current pos */
- if ((int)iLastX > xPlayer) /* Handle orientation */
- j = -j;
- BitmapColumn += j; /* Adjust column to show */
- if (BitmapColumn > 63) /* Door is opening */
- {
- /* Get the grid coordinates for this door */
- OldMapPosn = Door[index].mPos;
- OldMapPosn1 = Door[index].mPos1;
-
- /* Fake the engine into thinking no door is there */
- xGrid[OldMapPosn] = 0;
- xGrid[OldMapPosn1] = 0;
-
- /* Cast the ray to get walls beyond the door */
- BitmapNumber = xRay(xPlayer,yPlayer,ViewAngle);
-
- /* Put back the door codes if not fully open */
- if (Door[index].ColOffset < 63)
- {
- xGrid[OldMapPosn] = Door[index].mCode;
- xGrid[OldMapPosn1] = Door[index].mCode1;
- }
-
- /* Calc the new bitmap column of wall behind door */
- BitmapColumn = (LastY1 >> FP_SHIFT) & 0x3F;
- if ((int)iLastX < xPlayer)
- BitmapColumn = 63 - BitmapColumn;
- }
- }
- }
-
- /* Calculate the distance to the wall. Since the X position was */
- /* fixed to move 64 or -64 we can use it to determine the actual */
- /* wall distance. The InvCosTable values were stored with a fixed */
- /* point of 20 decimal places. At this time we'll knock off 14 of */
- /* them so we can later multiple with a fixed point value of 16 */
- xd = iLastX - xPlayer;
- WallDistance = (xd * InvCosTable[ViewAngle]) >> 14;
-
-
- /* Still looking for the reason this may occur. But this check */
- /* will force the distance to a ridiculous value so no wall is */
- /* seen later on when the X and Y walls are compared. */
- if (WallDistance < 0)
- WallDistance = 120000L;
-
- /* Walls that are not doors will have shadowing */
- if (BitmapNumber < DOOR_XCODE)
- WallCode = 1;
- }
-
- txRay += CLOCK_PTR - yd;
- }
-
- /* Don't cast a Y ray if its impossible to intercept any Y walls */
- if (ViewAngle != 0 && ViewAngle != INT_ANGLE_180)
- {
- xd = CLOCK_PTR;
- yBitmap = yRay(xPlayer,yPlayer,ViewAngle);
-
- if (yBitmap) /* A wall was found */
- {
-
- /* Use the X intercept to determine the column of the bitmap */
- yBitmapColumn = (LastX1 >> FP_SHIFT) & 0x3F;
-
- /* Handle orientation from either side of the wall */
- if ((int)iLastY > yPlayer)
- yBitmapColumn = 63 - yBitmapColumn;
-
- /* Did we strike a door? */
- if (yBitmap == DOOR_YCODE)
- {
- index = FindDoor(yMapPosn);
- if (index >= 0) /* This is a valid door */
- {
- /* Get the current door column offset */
- j = Door[index].ColOffset;
-
- /* Deal with orientation */
- if ((int)iLastY < yPlayer)
- j = -j;
-
- yBitmapColumn += j;
-
- /* If beyond width of bitmap than cast again */
- if (yBitmapColumn > 63)
- {
-
- /* Get the yGrid coordinates for this door */
- OldMapPosn = Door[index].mPos;
- OldMapPosn1 = Door[index].mPos1;
-
- /* Fool the engine into thinking no door is there */
- yGrid[OldMapPosn] = 0;
- yGrid[OldMapPosn1] = 0;
-
- /* Cast again for walls beyond the door */
- yBitmap = yRay(xPlayer,yPlayer,ViewAngle);
-
- /* Put door code back if not fully open */
- if (Door[index].ColOffset < 63)
- {
- yGrid[OldMapPosn] = Door[index].mCode;
- yGrid[OldMapPosn1] = Door[index].mCode1;
- }
-
- /* Get the bitmap column of wall beyond door */
- yBitmapColumn = (LastX1 >> FP_SHIFT) & 0x3F;
- if ((int)iLastY > yPlayer)
- yBitmapColumn = 63 - yBitmapColumn;
-
- }
- }
- }
-
-
- /* Calculate the distance to the wall. Since the Y position was */
- /* fixed to move 64 or -64 we can use it to determine the actual */
- /* wall distance. The InvSinTable values were stored with a fixed */
- /* point of 20 decimal places. At this time we'll knock off 14 of */
- /* them so we can later multiple with a fixed point value of 16 */
- yd = iLastY - yPlayer;
- yDistance = (yd * InvSinTable[ViewAngle]) >> 14;
-
- /* Don't know the reason but change negative value into ridiculous */
- if (yDistance < 0)
- yDistance = 120000L;
-
-
- /* At this point check the distance to the Y wall against the X */
- /* wall to see which one is closer. The closer one is the one */
- /* we'll draw at this column of the screen. */
- if (yDistance < WallDistance)
- {
- WallCode = 0; /* Turn off shadowing if on */
- WallDistance = yDistance;
- BitmapNumber = yBitmap;
- BitmapColumn = yBitmapColumn;
- }
-
- }
-
- tyRay += CLOCK_PTR - xd;
- }
-
- if (BitmapColumn < 64) /* A wall was found (either X or Y) */
- {
-
- /* To avoid a fishbowl affect we need to adjust the distance so */
- /* it appears perpendicular to the center point of the display */
- /* which is relative angle 0 from the players current angle. We */
- /* started at -30 degrees for the first screen column and will */
- /* cycle from -30 down to 0 then back up to +30 degrees. This */
- /* cosine value was pre-calculated and placed in ViewCosTable. */
- WallDistance *= ViewCosTable[i];
-
- /* Now we strip off somemore decimal points and check round-up */
- xd = WallDistance >> 14;
- if (WallDistance - (xd << 14) >= 8096)
- xd++;
-
- /* The last decimal points from the multiplication after the X and */
- /* Y rays is stripped off and checked for round-up. */
- WallDistance = xd >> 6;
- if (xd - (WallDistance << 6) >= 32)
- WallDistance++;
-
- /* Don't really need to, but put it into an integer for fast compare */
- distance = WallDistance;
-
- /* This is an arbitrary minimum distance to look for */
- if (distance < 10)
- distance = 10;
-
- /* Don't want it to go outside our table boundaries */
- if (distance >= MAX_DISTANCE)
- distance = MAX_DISTANCE - 1;
-
- /* Save the wall data to display when done with entire screen */
- Walls[i].Distance = distance;
- Walls[i].Number = (BitmapNumber & 0x3F) + WallCode;
- Walls[i].Column = BitmapColumn;
-
- if (distance > MaxDistance)
- MaxDistance = distance;
- }
-
- ViewAngle++;
- if (ViewAngle >= INT_ANGLE_360)
- ViewAngle -= INT_ANGLE_360;
-
- }
-
- xd = CLOCK_PTR;
- /* This routine gets the Walls structure and draws all of the walls */
- DrawWalls();
- tDOW += CLOCK_PTR - xd;
-
- /* Now we look at any objects that may be closer than the walls */
- FindObject(xPlayer,yPlayer,PlayerAngle);
-
- xd = CLOCK_PTR;
- /* Reset mask to allow all planes then flip to the page we just drew to */
- outp(0x3c5,255);
- flippage();
- PageNum ^= 1;
- usepage(PageNum);
-
- tFlip += CLOCK_PTR - xd;
-
- }
-
- /****************************************************************************
- ** This routine is used when the player moves location as well as by the **
- ** moveable objects. Its purpose is to determine if a valid obstacle is **
- ** in front of the player or object. **
- ** **
- ****************************************************************************/
- int CheckHit(int xPlayer,int yPlayer,int ViewAngle)
- {
- int BitmapColumn,BitmapNumber,yBitmap,distance;
- int i,WallCode;
- long WallDistance,xd,yd,yDistance;
- long CheckDist;
-
- WallDistance = 3000000; /* Set to a ridiculous value */
- WallCode = 3; /* Initialize to strike an object */
- CheckDist = 48L; /* Initial minimum distance to look for */
- BitmapNumber = 0; /* Initialize to no bitmap found */
-
- /* Clear out any objects seen from previous passes */
- memset(ObjFlags,0,MaxObjects);
-
- /* Set number of objects seen on this pass */
- TotalObjects = 0;
-
- /* Don't allow one of these angles, causes either the X or Y ray to not be */
- /* cast which gives a false reading about an obstacle. */
- if (ViewAngle == INT_ANGLE_45 ||
- ViewAngle == INT_ANGLE_135 ||
- ViewAngle == INT_ANGLE_225 ||
- ViewAngle == INT_ANGLE_315)
- ViewAngle++;
-
- /* Don't cast an X ray if no chance of striking a X wall */
- if (ViewAngle != INT_ANGLE_90 && ViewAngle != INT_ANGLE_270)
- {
- BitmapNumber = xRay(xPlayer,yPlayer,ViewAngle);
-
- if (BitmapNumber)
- {
- xd = iLastX - xPlayer;
-
- /* Use the delta X to determine the distance to the wall */
- WallDistance = (xd * InvCosTable[ViewAngle]) >> 14;
-
- if (WallDistance < 0)
- WallDistance = 120000L;
-
- /* Set the wall struck code to an X wall */
- WallCode = 1;
- }
- }
-
- /* Don't cast a Y ray if impossible to strike a Y wall */
- if (ViewAngle != 0 && ViewAngle != INT_ANGLE_180)
- {
- yBitmap = yRay(xPlayer,yPlayer,ViewAngle);
-
- if (yBitmap)
- {
- yd = iLastY - yPlayer;
-
- /* Use the delta Y to determine distance to the wall */
- yDistance = (yd * InvSinTable[ViewAngle]) >> 14;
- if (yDistance < 0)
- yDistance = 120000L;
-
- /* If Y wall closer than X wall then use Y wall data */
- if (yDistance < WallDistance)
- {
- WallDistance = yDistance;
-
- /* Indicate the wall struck was a Y wall */
- WallCode = 2;
- BitmapNumber = yBitmap;
- }
-
- }
-
- }
-
- /* Since doors appear in the middle of the wall, adjust the minimum distance */
- /* to it. This handles walking up close to a door. */
- if (BitmapNumber >= DOOR_XCODE)
- CheckDist += 34L;
-
- if (WallCode)
- {
- /* Adjust the distance based on the center of the screen */
- WallDistance *= ViewCosTable[160];
-
- /* Remove fixed point and round-up */
- xd = WallDistance >> 14;
- if (WallDistance - (xd << 14) >= 8096)
- xd++;
-
- /* Remove initial fixed point and round-up */
- WallDistance = xd >> 6;
- if (xd - (WallDistance << 6) >= 32)
- WallDistance++;
-
- /* Get minimum distance to any objects between player and wall */
- yDistance = CheckObjects(xPlayer,yPlayer,ViewAngle);
-
- /* An object was found, if closer than use object as obstacle */
- if (yDistance > 0L && yDistance < WallDistance)
- {
- /* Return code for an object struck */
- WallCode = 3;
- /* Adjust minimum distance to keep object from getting too close */
- CheckDist += 32L;
- WallDistance = yDistance;
- }
-
- /* If the wall or object is further than the minimum distance, we can */
- /* continue moving in this direction. */
- if (WallDistance > CheckDist)
- WallCode = 0;
- }
-
- return(WallCode);
- }
-
-
-