home *** CD-ROM | disk | FTP | other *** search
- /******************* ( Animation Construction Kit 3D ) ***********************/
- /* Point of View Routines */
- /* CopyRight (c) 1993 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 "ackeng.h"
- #include "ackext.h"
-
- UINT xRay(int x,int y,int angle,ACKENG *ae);
- UINT yRay(int x,int y,int angle,ACKENG *ae);
- long long_sqrt(long v);
-
- /****************************************************************************
- ** Normally an internal call made by AckMovePOV() to determine if a hit **
- ** has occurred with an object. The application can make this call if **
- ** desired at other times than moving the POV. If POV_OBJECT is returned **
- ** then the app can call AckGetObjectHit() to retrieve the number of the **
- ** object hit. **
- ** **
- ****************************************************************************/
- int AckCheckObjPosn(ACKENG *ae,int xPlayer,int yPlayer,int PlayerAngle)
- {
- int i,mPos,result;
- int j,count,SaveCenter;
- int ObjX,ObjY,ObjNum;
- int NewX,NewY,MapPosn;
- int MaxOpp,Column,ColBeg,ColEnd;
- long deltax,deltay;
- long xp,yp,MinDistance,distance;
- long SinValue,CosValue;
-
- result = POV_NOTHING;
- MinDistance = 3000000L;
- SinValue = SinTable[PlayerAngle];
- CosValue = CosTable[PlayerAngle];
- MapPosn = (yPlayer & 0xFFC0) + (xPlayer >> 6);
-
- for (i = 0; i < MAX_OBJECTS; i++)
- {
-
- if (!ae->ObjList[i].Active || ae->ObjList[i].Flags & OF_PASSABLE)
- continue;
-
- mPos = (ae->ObjList[i].y & 0xFFC0) + (ae->ObjList[i].x >> 6);
- if (mPos == MapPosn)
- {
- ObjX = ae->ObjList[i].x;
- ObjY = ae->ObjList[i].y;
- NewX = ObjX - xPlayer;
- NewY = ObjY - yPlayer;
-
- if (PlayerAngle > INT_ANGLE_180 && (NewY-63) > 0)
- continue;
-
- if (PlayerAngle < INT_ANGLE_180 && (NewY+63) < 0)
- continue;
-
- if (PlayerAngle > INT_ANGLE_270 || PlayerAngle < INT_ANGLE_90)
- {
- if ((NewX+63) < 0)
- continue;
- }
-
- if (PlayerAngle < INT_ANGLE_270 && PlayerAngle > INT_ANGLE_90)
- {
- if ((NewX-63) > 0)
- continue;
- }
-
-
- if ((PlayerAngle == 0 || PlayerAngle == INT_ANGLE_180) && NewX == 0)
- continue;
-
- if ((PlayerAngle == INT_ANGLE_90 || PlayerAngle == INT_ANGLE_270) &&
- NewY == 0)
- continue;
-
-
- /* Rotate coordinates to current player angle */
- deltax = ((NewX * CosValue) + (NewY * SinValue)) >> FP_SHIFT;
- deltay = ((NewY * CosValue) - (NewX * SinValue)) >> FP_SHIFT;
-
- MaxOpp = ((LongTanTable[INT_ANGLE_30] * (long)deltax) >> FP_SHIFT);
-
- if (NewY < ObjY)
- {
- MaxOpp = -MaxOpp;
- deltay = -deltay;
- }
-
- if ((deltay+32) < MaxOpp)
- continue;
-
- if (NewX < 0) NewX = -NewX;
- if (NewY < 0) NewY = -NewY;
- distance = NewX + NewY;
- distance -= min(NewX,NewY) / 2;
-
- if (distance > MAX_DISTANCE)
- continue;
-
- if (distance < MinDistance)
- {
- LastObjectHit = i;
- MinDistance = distance;
- result = POV_OBJECT;
- }
- }
- }
-
- return(result);
- }
-
-
-
- /****************************************************************************
- ** The application should make this call whenever the POV is moved. The **
- ** purpose of this function is to check if the move will strike a wall or **
- ** object and if not, then to actually move the coordinates of the POV to **
- ** the new location. When called, Angle must be in the range from 0 to **
- ** INT_ANGLE_360-1 and Amount should be a positive value (normally less **
- ** than 64 - values greater than around 44 could cause the POV to move **
- ** through walls!). The result code returned indicates whether the move **
- ** was successful (if zero) or what the POV actually hit (Xwall, Ywall, **
- ** or object). **
- ** **
- ****************************************************************************/
- int AckMovePOV(ACKENG *ae,int Angle,int Amount)
- {
- int x1,y1,HitResult,NewAngle;
- UCHAR gCode;
- int MapPosn;
-
- x1 = ae->xPlayer + (int)((CosTable[Angle] * Amount) >> FP_SHIFT);
- y1 = ae->yPlayer + (int)((SinTable[Angle] * Amount) >> FP_SHIFT);
-
- HitResult = AckCheckHit(ae->xPlayer,ae->yPlayer,Angle,ae);
-
- if (!HitResult)
- {
- MapPosn = (y1 & 0xFFC0) + (x1 >> 6);
-
- if (AckCheckObjPosn(ae,x1,y1,Angle))
- return(POV_OBJECT);
-
- gCode = Grid[MapPosn] & 0xFF;
- if (gCode > 0 && gCode < DOOR_XCODE)
- {
- if (!(Grid[MapPosn] & DOOR_TYPE_SECRET))
- return(POV_XWALL);
- }
-
- ae->xPlayer = x1;
- ae->yPlayer = y1;
- return(HitResult);
- }
-
- if (HitResult == POV_OBJECT)
- return(HitResult);
-
-
- if (HitResult == POV_XWALL)
- {
- x1 = ae->xPlayer;
- if (Angle < INT_ANGLE_180)
- NewAngle = INT_ANGLE_90;
- else
- NewAngle = INT_ANGLE_270;
-
- }
- else
- {
- y1 = ae->yPlayer;
- if (Angle > INT_ANGLE_270 || Angle < INT_ANGLE_90)
- NewAngle = 0;
- else
- NewAngle = INT_ANGLE_180;
- }
-
-
- if (!AckCheckHit(ae->xPlayer,ae->yPlayer,NewAngle,ae))
- {
- MapPosn = (y1 & 0xFFC0) + (x1 >> 6);
-
- if (AckCheckObjPosn(ae,x1,y1,Angle))
- return(POV_OBJECT);
-
- gCode = Grid[MapPosn] & 0xFF;
- if (gCode > 0 && gCode < DOOR_XCODE)
- if (!(Grid[MapPosn] & DOOR_TYPE_SECRET))
- return(POV_XWALL);
-
- ae->xPlayer = x1;
- ae->yPlayer = y1;
- return(POV_NOTHING);
- }
-
- return(HitResult);
- }
-
-
- /****************************************************************************
- ** Similiar to the AckMovePOV() above except ignores collision with the **
- ** same object being moved, and also checks for a collision with the **
- ** player. Angle should be the direction to move the object, Amount is the **
- ** map unit distance the object is to be moved. **
- ** **
- ****************************************************************************/
- int AckMoveObjectPOV(ACKENG *ae,int ObjIndex,int Angle,int Amount)
- {
- int x1,y1,HitResult,NewAngle,oNum;
- UCHAR gCode;
- int MapPosn,PlayerPosn;
-
- x1 = ae->ObjList[ObjIndex].x + (int)((CosTable[Angle] * Amount) >> FP_SHIFT);
- y1 = ae->ObjList[ObjIndex].y + (int)((SinTable[Angle] * Amount) >> FP_SHIFT);
-
- HitResult = AckCheckHit(ae->ObjList[ObjIndex].x,ae->ObjList[ObjIndex].y,Angle,ae);
-
- if (!HitResult)
- {
- MapPosn = (y1 & 0xFFC0) + (x1 >> 6);
-
- oNum = AckCheckObjPosn(ae,x1,y1,Angle);
- if (oNum > 0 && LastObjectHit != ObjIndex)
- return(POV_OBJECT);
-
- gCode = Grid[MapPosn] & 0xFF;
- if (gCode > 0 && gCode < DOOR_XCODE)
- if (!(Grid[MapPosn] & DOOR_TYPE_SECRET))
- return(POV_XWALL);
-
- ae->ObjList[ObjIndex].x = x1;
- ae->ObjList[ObjIndex].y = y1;
-
- PlayerPosn = (ae->yPlayer & 0xFFC0) + (ae->xPlayer >> 6);
- if (MapPosn == PlayerPosn)
- return(POV_PLAYER);
-
- }
-
- return(HitResult);
- }
-
-
-
-
- /****************************************************************************
- ** Internal call used by AckMovePOV() and AckMoveObjectPOV() to determine **
- ** if a wall was hit. This routine does NOT check for hits with objects. **
- ** ViewAngle is the angle to check against (usually the angle the POV is **
- ** facing, but could also be 180 degrees from the facing angle to see if **
- ** the POV hits something while backing up). **
- ** **
- ****************************************************************************/
- int AckCheckHit(int xPlayer,int yPlayer,int ViewAngle,ACKENG *ae)
- {
- int BitmapColumn,BitmapNumber,yBitmap,distance;
- int i,WallCode;
- long WallDistance,xd,yd,yDistance;
- long CheckDist;
-
- WallDistance = 3000000; /* Set to a ridiculous value */
- WallCode = POV_NOTHING;
- CheckDist = 48L; /* Initial minimum distance to look for */
- BitmapNumber = 0; /* Initialize to no bitmap found */
-
- /* 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,ae);
-
- 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 = POV_XWALL;
- LastMapPosn = xMapPosn;
- }
- }
-
- /* 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,ae);
-
- 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 = POV_YWALL;
- BitmapNumber = yBitmap;
- LastMapPosn = yMapPosn;
- }
-
- }
-
- }
-
- /* 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 += 64L;
-
- 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++;
-
- /* If the wall or object is further than the minimum distance, we can */
- /* continue moving in this direction. */
- if (WallDistance > CheckDist)
- WallCode = POV_NOTHING;
- }
-
- return(WallCode);
- }
-
-
- /****************************************************************************
- ** Obsolete routine, was used for internal movement by the ACK engine. **
- ** **
- ****************************************************************************/
- void AckMoveObject(int Index,int dx,int dy,ACKENG *ae)
- {
- int Pos,NewPos,x1,y1;
-
- ae->ObjList[Index].y += dy;
- ae->ObjList[Index].x += dx;
- x1 = ae->ObjList[Index].x >> 6;
- y1 = ae->ObjList[Index].y >> 6;
- NewPos = (y1 * GRID_WIDTH) + x1;
- ae->ObjList[Index].mPos = NewPos;
-
- }
-
- /****************************************************************************
- ** This routine can be called by the application to automatically update **
- ** any objects that have multiple bitmaps to display. (Note: This is **
- ** different than objects that have multiple sides). Any objects that need **
- ** a new bitmap displayed will be updated by this routine. **
- ** **
- ****************************************************************************/
- void AckCheckObjectMovement(ACKENG *ae)
- {
- int i,dx;
-
- for (i = 1; i < ae->MaxObjects; i++)
- {
- if (!ae->ObjList[i].Active)
- continue;
-
- if (!ae->ObjList[i].Speed)
- continue;
-
- if (!ae->ObjList[i].Flags & OF_ANIMATE)
- continue;
-
- dx = ae->ObjList[i].CurNum + 1;
- if (dx > ae->ObjList[i].MaxNum)
- dx = 0;
-
- ae->ObjList[i].CurNum = dx;
- }
-
- }
-
-