Files
GTASource/game/Peds/Area.cpp
expvintl 419f2e4752 init
2025-02-23 17:40:52 +08:00

323 lines
10 KiB
C++

// File header
#include "Peds/Area.h"
#include "ai/aichannel.h"
// Framework headers
#include "grcore/debugdraw.h"
#include "fwmaths/angle.h"
// Game headers
#include "scene/Entity.h"
AI_OPTIMISATIONS()
//-------------------------------------------------------------------------
// Constructor
//-------------------------------------------------------------------------
CArea::CArea()
:m_vVec1(0.0f, 0.0f, 0.0f),
m_vVec2(0.0f, 0.0f, 0.0f),
m_fWidth(0.0f),
m_fMaxRadius(0.0f),
m_pAttachedEntity(NULL),
m_bActive(false),
m_bOrientateWithEntity(false)
{
}
//-------------------------------------------------------------------------
// Destructor
//-------------------------------------------------------------------------
CArea::~CArea()
{
}
//-------------------------------------------------------------------------
// Resets the area
//-------------------------------------------------------------------------
void CArea::Reset()
{
m_bActive = false;
m_pAttachedEntity = NULL;
OnReset();
}
//-------------------------------------------------------------------------
// Gets the information about the area
//-------------------------------------------------------------------------
bool CArea::Get( Vector3& vAreaStart, Vector3& vAreaEnd, float& fAreaWidth ) const
{
if( !m_bActive )
{
return false;
}
if( m_pAttachedEntity )
{
if( m_bOrientateWithEntity )
{
vAreaStart = VEC3V_TO_VECTOR3(m_pAttachedEntity->GetTransform().Transform3x3(VECTOR3_TO_VEC3V(m_vVec1)));
vAreaEnd = VEC3V_TO_VECTOR3(m_pAttachedEntity->GetTransform().Transform3x3(VECTOR3_TO_VEC3V(m_vVec2)));
}
else
{
vAreaStart = m_vVec1;
vAreaEnd = m_vVec2;
}
const Vector3 vAttachedEntityPosition = VEC3V_TO_VECTOR3(m_pAttachedEntity->GetTransform().GetPosition());
vAreaStart += vAttachedEntityPosition;
vAreaEnd += vAttachedEntityPosition;
}
else
{
vAreaStart = m_vVec1;
vAreaEnd = m_vVec2;
}
fAreaWidth = m_fWidth;
return true;
}
//-------------------------------------------------------------------------
// Gets the setup vectors, without altering them into global coordinates if attached, for debug printing
//-------------------------------------------------------------------------
bool CArea::GetSetupVectors( Vector3& vAreaStart, Vector3& vAreaEnd, float& fAreaWidth ) const
{
if( !m_bActive )
{
return false;
}
vAreaStart = m_vVec1;
vAreaEnd = m_vVec2;
fAreaWidth = m_fWidth;
return true;
}
//-------------------------------------------------------------------------
// Returns true if the point is within the area, or if there is no area.
//-------------------------------------------------------------------------
bool CArea::IsPointInArea( const Vector3& vPoint, const bool bVisualiseArea, const bool DEBUG_DRAW_ONLY(bIsDead), const bool DEBUG_DRAW_ONLY(bEditing) ) const
{
bool bReturn = false;
if( !m_bActive )
{
return false;
}
if( m_eType == AT_AngledArea )
{
Vector3 vAreaStart;
Vector3 vAreaEnd;
float fAreaWidth;
Get(vAreaStart, vAreaEnd, fAreaWidth);
if(!bVisualiseArea)
{
Vector3 vCenter = (vAreaStart + vAreaEnd) * 0.5f;
float fDistSq = vCenter.Dist2(vPoint);
if(fDistSq > square(m_fMaxRadius))
{
return false;
}
}
const float RadiansBetweenFirstTwoPoints = fwAngle::GetRadianAngleBetweenPoints(vAreaStart.x, vAreaStart.y, vAreaEnd.x, vAreaEnd.y);
float RadiansBetweenPoints1and4 = RadiansBetweenFirstTwoPoints + (PI / 2);
while (RadiansBetweenPoints1and4 < 0.0f) {RadiansBetweenPoints1and4 += (2 * PI);}
while (RadiansBetweenPoints1and4 > (2 * PI)){RadiansBetweenPoints1and4 -= (2 * PI);}
const Vector2 vBottomLeft( ( ( fAreaWidth * 0.5f ) * rage::Sinf(RadiansBetweenPoints1and4)) + vAreaStart.x, (( fAreaWidth * 0.5f ) * -rage::Cosf(RadiansBetweenPoints1and4)) + vAreaStart.y );
const Vector2 vBottomRight( ( ( fAreaWidth * 0.5f ) * rage::Sinf(RadiansBetweenPoints1and4)) + vAreaEnd.x, (( fAreaWidth * 0.5f ) * -rage::Cosf(RadiansBetweenPoints1and4)) + vAreaEnd.y );
const Vector2 vTopLeft( vAreaStart.x, vAreaStart.y );
const Vector2 vTopRight( vAreaEnd.x, vAreaEnd.y );
Vector2 vec1To2 = vTopRight - vTopLeft;
Vector2 vec1To4 = vTopLeft - vBottomLeft;
const float DistanceFrom1To2 = vec1To2.Mag();
const float DistanceFrom1To4Test = vec1To4.Mag();
const Vector2 vec1ToPed((vPoint.x - vTopLeft.x), (vPoint.y - vTopLeft.y));
vec1To2.Normalize();
float TestDistance = DotProduct(vec1ToPed, vec1To2);
// Check that the point
if ((TestDistance >= 0.0f)
&& (TestDistance <= DistanceFrom1To2))
{
vec1To4.Normalize();
TestDistance = DotProduct(vec1ToPed, vec1To4);
// The width of the box is from teh center, so either infront or behind on this axis is fine
if ( fabsf(TestDistance) <= DistanceFrom1To4Test )
{
if ((vPoint.z >= vAreaStart.z) && (vPoint.z <= vAreaEnd.z))
{
bReturn = true;
}
}
}
if( bVisualiseArea )
{
#if DEBUG_DRAW
Color32 col = bIsDead ? Color32( 0.5f, 0.5f, 0.5f ) : Color32( 0.0f, 0.0f, 1.0f );
if( bEditing )
col = Color_red;
// might not have been normalised in an early out
vec1To4.Normalize();
Vector3 vDir3d(vec1To4.x, vec1To4.y, 0.0f);
const Vector3 vVec1B = vAreaStart;
const Vector3 vVec1T = Vector3( vAreaStart.x, vAreaStart.y, vAreaEnd.z);
const Vector3 vVec2B = Vector3( vAreaEnd.x, vAreaEnd.y, vAreaStart.z);
const Vector3 vVec2T = vAreaEnd;
// bottom rectangle
grcDebugDraw::Line( vVec1B - ( vDir3d * ( fAreaWidth * 0.5f ) ), vVec1B + ( vDir3d * ( fAreaWidth * 0.5f ) ), col );
grcDebugDraw::Line( vVec2B - ( vDir3d * ( fAreaWidth * 0.5f ) ), vVec2B + ( vDir3d * ( fAreaWidth * 0.5f ) ), col );
grcDebugDraw::Line( vVec1B - ( vDir3d * ( fAreaWidth * 0.5f ) ), vVec2B - ( vDir3d * ( fAreaWidth * 0.5f ) ), col );
grcDebugDraw::Line( vVec1B + ( vDir3d * ( fAreaWidth * 0.5f ) ), vVec2B + ( vDir3d * ( fAreaWidth * 0.5f ) ), col );
// top rectangle
grcDebugDraw::Line( vVec1T - ( vDir3d * ( fAreaWidth * 0.5f ) ), vVec1T + ( vDir3d * ( fAreaWidth * 0.5f ) ), col );
grcDebugDraw::Line( vVec2T - ( vDir3d * ( fAreaWidth * 0.5f ) ), vVec2T + ( vDir3d * ( fAreaWidth * 0.5f ) ), col );
grcDebugDraw::Line( vVec1T - ( vDir3d * ( fAreaWidth * 0.5f ) ), vVec2T - ( vDir3d * ( fAreaWidth * 0.5f ) ), col );
grcDebugDraw::Line( vVec1T + ( vDir3d * ( fAreaWidth * 0.5f ) ), vVec2T + ( vDir3d * ( fAreaWidth * 0.5f ) ), col );
// vertical links
grcDebugDraw::Line( vVec1B - ( vDir3d * ( fAreaWidth * 0.5f ) ), vVec1T - ( vDir3d * ( fAreaWidth * 0.5f ) ), col );
grcDebugDraw::Line( vVec2B - ( vDir3d * ( fAreaWidth * 0.5f ) ), vVec2T - ( vDir3d * ( fAreaWidth * 0.5f ) ), col );
grcDebugDraw::Line( vVec2B + ( vDir3d * ( fAreaWidth * 0.5f ) ), vVec2T + ( vDir3d * ( fAreaWidth * 0.5f ) ), col );
grcDebugDraw::Line( vVec1B + ( vDir3d * ( fAreaWidth * 0.5f ) ), vVec1T + ( vDir3d * ( fAreaWidth * 0.5f ) ), col );
// Draw a line from the point to the area
Color32 LineColour;
LineColour = bReturn ? Color32( 0.0f, 1.0f, 0.0f ) : Color32( 1.0f, 0.0f, 0.0f );
grcDebugDraw::Line( (vAreaStart+vAreaEnd)*0.5f, vPoint, LineColour );
#endif
}
}
else if(m_eType == AT_Sphere)
{
Vector3 vAreaStart;
Vector3 vAreaEnd;
float fAreaWidth;
Get(vAreaStart, vAreaEnd, fAreaWidth);
// For small spheres allow points up to 2m higher, essentially changing the sphere to a 2m capsule
// This is to fix Z height offset issues between the coverpoints and the ped.
if( fAreaWidth < 2.0f )
{
Vector3 vDiff = vAreaStart - vPoint;
if( ABS(vDiff.z) < 2.0f )
{
bReturn = vDiff.XYMag2() < rage::square(fAreaWidth);
}
}
else
{
bReturn = vAreaStart.Dist2(vPoint) < rage::square(fAreaWidth);
}
if( bVisualiseArea )
{
#if DEBUG_DRAW
Color32 col = bIsDead ? Color32( 0.5f, 0.5f, 0.5f ) : Color32( 0.0f, 0.0f, 1.0f );
if( bEditing )
col = Color_red;
grcDebugDraw::Sphere(vAreaStart, m_fWidth, col, false );
#endif
}
}
return bReturn;
}
//-------------------------------------------------------------------------
// Set angled area
//-------------------------------------------------------------------------
void CArea::Set( const Vector3& vAreaStart, const Vector3& vAreaEnd, float fAreaWidth, const CEntity* pAttachedEntity, bool bOrientateWithEntity )
{
Reset();
m_pAttachedEntity = pAttachedEntity;
m_bActive = true;
m_bOrientateWithEntity = bOrientateWithEntity;
m_vVec1 = vAreaStart;
m_vVec2 = vAreaEnd;
m_fWidth = fAreaWidth;
// Calculate and store our max radius (non-squared)
Vector3 vCentre = (vAreaStart + vAreaEnd) * 0.5f;
m_fMaxRadius = rage::Sqrtf(vAreaStart.Dist2(vCentre) + rage::square(fAreaWidth * 0.5f));
// make sure the lowest point is always first
if( m_vVec1.z > m_vVec2.z )
{
Vector3 vSwap = m_vVec2;
m_vVec2 = m_vVec1;
m_vVec1 = vSwap;
}
m_eType = AT_AngledArea;
OnSet();
}
//-------------------------------------------------------------------------
// Set sphere area
//-------------------------------------------------------------------------
void CArea::SetAsSphere( const Vector3& vAreaStart, float fAreaRadius, const CEntity* pAttachedEntity)
{
Reset();
m_pAttachedEntity = pAttachedEntity;
m_bActive = true;
m_vVec1 = vAreaStart;
m_fWidth = fAreaRadius;
m_fMaxRadius = fAreaRadius;
m_vVec2.Zero();
m_eType = AT_Sphere;
OnSetSphere();
}
//-------------------------------------------------------------------------
// Returns the areas center and estimated max radius
//-------------------------------------------------------------------------
void CArea::GetCentreAndMaxRadius( Vector3& vCentre, float& fMaxRadiusSq ) const
{
GetCentre(vCentre);
fMaxRadiusSq = m_fMaxRadius * m_fMaxRadius;
}
void CArea::GetCentre( Vector3& vCentre ) const
{
Vector3 vAreaStart;
Vector3 vAreaEnd;
float fAreaWidth;
Get(vAreaStart, vAreaEnd, fAreaWidth);
// Should this really be aiAssertf? It's the only assert in the file though so your guess is as good as mine.
aiAssertf( m_bActive, "Attempting to get the center of a defensive area that is not active" );
if( m_eType == AT_Sphere )
{
vCentre = vAreaStart;
}
else
{
vCentre = (vAreaStart + vAreaEnd) * 0.5f;
}
}
void CArea::GetMaxRadius( float& fMaxRadius ) const
{
fMaxRadius = m_fMaxRadius;
}