478 lines
13 KiB
C++
478 lines
13 KiB
C++
//Anything above this #include will be ignored by the compiler
|
|
#include "../qcommon/exe_headers.h"
|
|
|
|
/************************************************************************************************
|
|
*
|
|
* Copyright (C) 2001-2002 Raven Software
|
|
*
|
|
* RM_Area.cpp
|
|
*
|
|
************************************************************************************************/
|
|
|
|
#include "RM_Headers.h"
|
|
|
|
#ifdef _WIN32
|
|
#pragma optimize("p", on)
|
|
#endif
|
|
|
|
/************************************************************************************************
|
|
* CRMArea::CRMArea
|
|
* constructor
|
|
*
|
|
* inputs:
|
|
* none
|
|
*
|
|
* return:
|
|
* none
|
|
*
|
|
************************************************************************************************/
|
|
CRMArea::CRMArea (
|
|
float spacingRadius,
|
|
float paddingSize,
|
|
float confineRadius,
|
|
vec3_t confineOrigin,
|
|
vec3_t lookAtOrigin,
|
|
bool flatten,
|
|
int symmetric
|
|
)
|
|
{
|
|
mMoveCount = 0;
|
|
mAngle = 0;
|
|
mCollision = true;
|
|
mConfineRadius = confineRadius;
|
|
mPaddingSize = paddingSize;
|
|
mSpacingRadius = spacingRadius;
|
|
mFlatten = flatten;
|
|
mLookAt = true;
|
|
mLockOrigin = false;
|
|
mSymmetric = symmetric;
|
|
mRadius = spacingRadius;
|
|
|
|
VectorCopy ( confineOrigin, mConfineOrigin );
|
|
VectorCopy ( lookAtOrigin, mLookAtOrigin );
|
|
}
|
|
|
|
/************************************************************************************************
|
|
* CRMArea::LookAt
|
|
* Angle the area towards the given point
|
|
*
|
|
* inputs:
|
|
* lookat - the origin to look at
|
|
*
|
|
* return:
|
|
* the angle in radians that was calculated
|
|
*
|
|
************************************************************************************************/
|
|
float CRMArea::LookAt ( vec3_t lookat )
|
|
{
|
|
if (mLookAt)
|
|
{ // this area orients itself towards a point
|
|
vec3_t a;
|
|
|
|
VectorCopy ( lookat, mLookAtOrigin );
|
|
VectorSubtract ( lookat, mOrigin, a );
|
|
|
|
mAngle = atan2 ( a[1], a[0] );
|
|
}
|
|
|
|
return mAngle;
|
|
}
|
|
|
|
/************************************************************************************************
|
|
* CRMArea::Mirror
|
|
* Mirrors the area to the other side of the map. This includes mirroring the confine origin
|
|
* and lookat origin
|
|
*
|
|
* inputs:
|
|
* none
|
|
*
|
|
* return:
|
|
* none
|
|
*
|
|
************************************************************************************************/
|
|
void CRMArea::Mirror ( void )
|
|
{
|
|
mOrigin[0] = -mOrigin[0];
|
|
mOrigin[1] = -mOrigin[1];
|
|
|
|
mConfineOrigin[0] = -mConfineOrigin[0];
|
|
mConfineOrigin[1] = -mConfineOrigin[1];
|
|
|
|
mLookAtOrigin[0] = -mLookAtOrigin[0];
|
|
mLookAtOrigin[1] = -mLookAtOrigin[1];
|
|
}
|
|
|
|
/************************************************************************************************
|
|
* CRMAreaManager::CRMAreaManager
|
|
* constructor
|
|
*
|
|
* inputs:
|
|
* none
|
|
*
|
|
* return:
|
|
* none
|
|
*
|
|
************************************************************************************************/
|
|
CRMAreaManager::CRMAreaManager ( const vec3_t mins, const vec3_t maxs)
|
|
{
|
|
VectorCopy ( mins, mMins );
|
|
VectorCopy ( maxs, mMaxs );
|
|
|
|
mWidth = mMaxs[0] - mMins[0];
|
|
mHeight = mMaxs[1] - mMins[1];
|
|
}
|
|
|
|
/************************************************************************************************
|
|
* CRMAreaManager::~CRMAreaManager
|
|
* Removes all managed areas
|
|
*
|
|
* inputs:
|
|
* none
|
|
*
|
|
* return:
|
|
* none
|
|
*
|
|
************************************************************************************************/
|
|
CRMAreaManager::~CRMAreaManager ( )
|
|
{
|
|
int i;
|
|
|
|
for ( i = mAreas.size() - 1; i >=0; i -- )
|
|
{
|
|
delete mAreas[i];
|
|
}
|
|
|
|
mAreas.clear();
|
|
}
|
|
|
|
/************************************************************************************************
|
|
* CRMAreaManager::MoveArea
|
|
* Moves an area within the area manager thus shifting any other areas as needed
|
|
*
|
|
* inputs:
|
|
* area - area to be moved
|
|
* origin - new origin to attempt to move to
|
|
*
|
|
* return:
|
|
* none
|
|
*
|
|
************************************************************************************************/
|
|
void CRMAreaManager::MoveArea ( CRMArea* movedArea, vec3_t origin)
|
|
{
|
|
int index;
|
|
int size;
|
|
|
|
// Increment the addcount (this is for infinite protection)
|
|
movedArea->AddMoveCount ();
|
|
|
|
// Infinite recursion prevention
|
|
if ( movedArea->GetMoveCount() > 250 )
|
|
{
|
|
// assert ( 0 );
|
|
movedArea->EnableCollision ( false );
|
|
return;
|
|
}
|
|
|
|
// First set the area's origin, This may cause it to be in collision with
|
|
// another area but that will get fixed later
|
|
movedArea->SetOrigin ( origin );
|
|
|
|
// when symmetric we want to ensure that no instances end up on the "other" side of the imaginary diaganol that cuts the map in two
|
|
// mSymmetric tells us which side of the map is legal
|
|
if ( movedArea->GetSymmetric ( ) )
|
|
{
|
|
const vec3pair_t& bounds = TheRandomMissionManager->GetLandScape()->GetBounds();
|
|
|
|
vec3_t point;
|
|
vec3_t dir;
|
|
vec3_t tang;
|
|
bool push;
|
|
float len;
|
|
|
|
VectorSubtract( movedArea->GetOrigin(), bounds[0], point );
|
|
VectorSubtract( bounds[1], bounds[0], dir );
|
|
VectorNormalize(dir);
|
|
|
|
dir[2] = 0;
|
|
point[2] = 0;
|
|
VectorMA( bounds[0], DotProduct(point, dir), dir, tang );
|
|
VectorSubtract ( movedArea->GetOrigin(), tang, dir );
|
|
|
|
dir[2] = 0;
|
|
push = false;
|
|
len = VectorNormalize(dir);
|
|
|
|
if ( len < movedArea->GetRadius ( ) )
|
|
{
|
|
if ( movedArea->GetLockOrigin ( ) )
|
|
{
|
|
movedArea->EnableCollision ( false );
|
|
return;
|
|
}
|
|
|
|
VectorMA ( point, (movedArea->GetSpacingRadius() - len) + TheRandomMissionManager->GetLandScape()->irand(10,movedArea->GetSpacingRadius()), dir, point );
|
|
origin[0] = point[0] + bounds[0][0];
|
|
origin[1] = point[1] + bounds[0][1];
|
|
movedArea->SetOrigin ( origin );
|
|
}
|
|
|
|
switch ( movedArea->GetSymmetric ( ) )
|
|
{
|
|
case SYMMETRY_TOPLEFT:
|
|
if ( origin[1] > origin[0] )
|
|
{
|
|
movedArea->Mirror ( );
|
|
}
|
|
break;
|
|
|
|
case SYMMETRY_BOTTOMRIGHT:
|
|
if ( origin[1] < origin[0] )
|
|
{
|
|
movedArea->Mirror ( );
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
// unknown symmetry type
|
|
assert ( 0 );
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Confine to area unless we are being pushed back by the same guy who pushed us last time (infinite loop)
|
|
if ( movedArea->GetConfineRadius() )
|
|
{
|
|
if ( movedArea->GetMoveCount() < 25 )
|
|
{
|
|
vec3_t cdiff;
|
|
float cdist;
|
|
|
|
VectorSubtract ( movedArea->GetOrigin(), movedArea->GetConfineOrigin(), cdiff );
|
|
cdiff[2] = 0;
|
|
cdist = VectorLength ( cdiff );
|
|
|
|
if ( cdist + movedArea->GetSpacingRadius() > movedArea->GetConfineRadius() )
|
|
{
|
|
cdist = movedArea->GetConfineRadius() - movedArea->GetSpacingRadius();
|
|
VectorNormalize ( cdiff );
|
|
|
|
VectorMA ( movedArea->GetConfineOrigin(), cdist, cdiff, movedArea->GetOrigin());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
index = 0;
|
|
}
|
|
}
|
|
|
|
// See if it fell off the world in the x direction
|
|
if ( movedArea->GetOrigin()[0] + movedArea->GetSpacingRadius() > mMaxs[0] )
|
|
movedArea->GetOrigin()[0] = mMaxs[0] - movedArea->GetSpacingRadius() - (TheRandomMissionManager->GetLandScape()->irand(10,200));
|
|
else if ( movedArea->GetOrigin()[0] - movedArea->GetSpacingRadius() < mMins[0] )
|
|
movedArea->GetOrigin()[0] = mMins[0] + movedArea->GetSpacingRadius() + (TheRandomMissionManager->GetLandScape()->irand(10,200));
|
|
|
|
// See if it fell off the world in the y direction
|
|
if ( movedArea->GetOrigin()[1] + movedArea->GetSpacingRadius() > mMaxs[1] )
|
|
movedArea->GetOrigin()[1] = mMaxs[1] - movedArea->GetSpacingRadius() - (TheRandomMissionManager->GetLandScape()->irand(10,200));
|
|
else if ( movedArea->GetOrigin()[1] - movedArea->GetSpacingRadius() < mMins[1] )
|
|
movedArea->GetOrigin()[1] = mMins[1] + movedArea->GetSpacingRadius() + (TheRandomMissionManager->GetLandScape()->irand(10,200));
|
|
|
|
// Look at what we need to look at
|
|
movedArea->LookAt ( movedArea->GetLookAtOrigin() );
|
|
|
|
// Dont collide against things that have no collision
|
|
// if ( !movedArea->IsCollisionEnabled ( ) )
|
|
// {
|
|
// return;
|
|
// }
|
|
|
|
// See if its colliding
|
|
for(index = 0, size = mAreas.size(); index < size; index ++ )
|
|
{
|
|
CRMArea *area = mAreas[index];
|
|
vec3_t diff;
|
|
vec3_t newOrigin;
|
|
float dist;
|
|
float targetdist;
|
|
|
|
// Skip the one that was moved in the first place
|
|
if ( area == movedArea )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if ( area->GetLockOrigin ( ) && movedArea->GetLockOrigin( ) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Dont collide against things that have no collision
|
|
if ( !area->IsCollisionEnabled ( ) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Grab the distance between the two
|
|
// only want the horizontal distance -- dmv
|
|
//dist = Distance ( movedArea->GetOrigin ( ), area->GetOrigin ( ));
|
|
vec3_t maOrigin;
|
|
vec3_t aOrigin;
|
|
VectorCopy(movedArea->GetOrigin(), maOrigin);
|
|
VectorCopy(area->GetOrigin(), aOrigin);
|
|
maOrigin[2] = aOrigin[2] = 0;
|
|
dist = Distance ( maOrigin, aOrigin );
|
|
targetdist = movedArea->GetSpacingRadius() + area->GetSpacingRadius() + maximum(movedArea->GetPaddingSize(),area->GetPaddingSize());
|
|
|
|
if ( dist == 0 )
|
|
{
|
|
area->GetOrigin()[0] += (50 * (float)(TheRandomMissionManager->GetLandScape()->irand(0,99))/100.0f);
|
|
area->GetOrigin()[1] += (50 * (float)(TheRandomMissionManager->GetLandScape()->irand(0,99))/100.0f);
|
|
|
|
VectorCopy(area->GetOrigin(), aOrigin);
|
|
aOrigin[2] = 0;
|
|
|
|
dist = Distance ( maOrigin, aOrigin );
|
|
}
|
|
|
|
// Are they are enough apart?
|
|
if ( dist >= targetdist )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Dont move a step if locked
|
|
if ( area->GetLockOrigin ( ) )
|
|
{
|
|
MoveArea ( area, area->GetOrigin ( ) );
|
|
continue;
|
|
}
|
|
|
|
// we got a collision, move the guy we hit
|
|
VectorSubtract ( area->GetOrigin(), movedArea->GetOrigin(), diff );
|
|
diff[2] = 0;
|
|
VectorNormalize ( diff );
|
|
|
|
// Push by the difference in the distance and no-collide radius
|
|
VectorMA ( area->GetOrigin(), targetdist - dist + 1 , diff, newOrigin );
|
|
|
|
// Move the area now
|
|
MoveArea ( area, newOrigin );
|
|
}
|
|
}
|
|
|
|
/************************************************************************************************
|
|
* CRMAreaManager::CreateArea
|
|
* Creates an area and adds it to the list of managed areas
|
|
*
|
|
* inputs:
|
|
* none
|
|
*
|
|
* return:
|
|
* a pointer to the newly added area class
|
|
*
|
|
************************************************************************************************/
|
|
CRMArea* CRMAreaManager::CreateArea (
|
|
vec3_t origin,
|
|
float spacingRadius,
|
|
int spacingLine,
|
|
float paddingSize,
|
|
float confineRadius,
|
|
vec3_t confineOrigin,
|
|
vec3_t lookAtOrigin,
|
|
bool flatten,
|
|
bool collide,
|
|
bool lockorigin,
|
|
int symmetric
|
|
)
|
|
{
|
|
CRMArea* area = new CRMArea ( spacingRadius, paddingSize, confineRadius, confineOrigin, lookAtOrigin, flatten, symmetric );
|
|
|
|
if ( lockorigin || spacingLine )
|
|
{
|
|
area->LockOrigin ( );
|
|
}
|
|
|
|
if (origin[0] != lookAtOrigin[0] || origin[1] != lookAtOrigin[1])
|
|
area->EnableLookAt(true);
|
|
|
|
// First add the area to the list
|
|
mAreas.push_back ( area );
|
|
|
|
area->EnableCollision(collide);
|
|
|
|
// Set the real radius which is used for center line detection
|
|
if ( spacingLine )
|
|
{
|
|
area->SetRadius ( spacingRadius + (spacingLine - 1) * spacingRadius );
|
|
}
|
|
|
|
// Now move the area around
|
|
MoveArea ( area, origin );
|
|
|
|
if ( (origin[0] != lookAtOrigin[0] || origin[1] != lookAtOrigin[1]) )
|
|
{
|
|
int i;
|
|
vec3_t linedir;
|
|
vec3_t dir;
|
|
vec3_t up = {0,0,1};
|
|
|
|
VectorSubtract ( lookAtOrigin, origin, dir );
|
|
VectorNormalize ( dir );
|
|
dir[2] = 0;
|
|
CrossProduct ( dir, up, linedir );
|
|
|
|
for ( i = 0; i < spacingLine - 1; i ++ )
|
|
{
|
|
CRMArea* linearea;
|
|
vec3_t lineorigin;
|
|
|
|
linearea = new CRMArea ( spacingRadius, paddingSize, 0, vec3_origin, vec3_origin, false, symmetric );
|
|
linearea->LockOrigin ( );
|
|
linearea->EnableCollision(collide);
|
|
|
|
VectorMA ( origin, spacingRadius + (spacingRadius * 2 * i), linedir, lineorigin );
|
|
mAreas.push_back ( linearea );
|
|
MoveArea ( linearea, lineorigin );
|
|
|
|
linearea = new CRMArea ( spacingRadius, paddingSize, 0, vec3_origin, vec3_origin, false, symmetric );
|
|
linearea->LockOrigin ( );
|
|
linearea->EnableCollision(collide);
|
|
|
|
VectorMA ( origin, -spacingRadius - (spacingRadius * 2 * i), linedir, lineorigin );
|
|
mAreas.push_back ( linearea );
|
|
MoveArea ( linearea, lineorigin );
|
|
}
|
|
}
|
|
|
|
// Return it for convienience
|
|
return area;
|
|
}
|
|
|
|
/************************************************************************************************
|
|
* CRMAreaManager::EnumArea
|
|
* Allows for enumeration through the area list. If an invalid index is given then NULL will
|
|
* be returned;
|
|
*
|
|
* inputs:
|
|
* index - current enumeration index
|
|
*
|
|
* return:
|
|
* requested area class pointer or NULL if the index was invalid
|
|
*
|
|
************************************************************************************************/
|
|
CRMArea* CRMAreaManager::EnumArea ( const int index )
|
|
{
|
|
// This isnt an assertion case because there is no size method for
|
|
// the area manager so the areas are enumerated until NULL is returned.
|
|
if ( index < 0 || index >= mAreas.size ( ) )
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
return mAreas[index];
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
#pragma optimize("p", off)
|
|
#endif
|