/************************************************************************************************ * * Copyright (C) 2001-2002 Raven Software * * RM_Area.cpp * ************************************************************************************************/ #include "../server/exe_headers.h" #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}; vec3_t zerodvec; VectorClear(zerodvec); 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, zerodvec, zerodvec, 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, zerodvec, zerodvec, 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