home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
DP Tool Club 18
/
CD_ASCQ_18_111294_W.iso
/
dos
/
prg
/
c
/
ack3d
/
engsrc
/
ackpov.c
< prev
next >
Wrap
Text File
|
1993-08-22
|
12KB
|
419 lines
/******************* ( 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;
}
}