Files
FC1/CryGame/XArea.cpp
romkazvo 34d6c5d489 123
2023-08-07 19:29:24 +08:00

1300 lines
36 KiB
C++

//////////////////////////////////////////////////////////////////////
//
// Game Source Code
//
// File: XArea.cpp
// Description: 2D area class. Area is in XY plane.
// 2D areas manager
//
// History:
// - Feb 24, 2002: Created by Kirill Bulatsev
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "xarea.h"
#include "xPlayer.h"
#include "TagPoint.h"
#ifdef _XBOX
#ifdef k2
#undef k2
#endif
#endif
//////////////////////////////////////////////////////////////////////////
CXArea::CXArea(void):
m_VOrigin(0.0f),
m_VSize(0.0f),
m_PrevFade(-1.0f),
m_Proximity(5.0f),
m_AreaGroupID(-1),
m_AreaID(-1),
m_bIsActive(false)
{
m_InvMatrix.SetIdentity();
}
//////////////////////////////////////////////////////////////////////////
CXArea::~CXArea(void)
{
ClearPoints();
}
// resets area - clears all segments in area
//////////////////////////////////////////////////////////////////////////
void CXArea::ClearPoints()
{
a2DSegment* carSegment;
for(unsigned int sIdx=0; sIdx<m_vpSegments.size(); sIdx++)
{
carSegment = m_vpSegments[sIdx];
delete carSegment;
}
m_vpSegments.clear();
}
//////////////////////////////////////////////////////////////////////////
unsigned CXArea::MemStat()
{
unsigned memSize = sizeof *this;
memSize += m_vpSegments.size()*(sizeof(a2DSegment) + sizeof(a2DSegment*)) ;
return memSize;
}
//adds segment to area, calculates line parameters y=kx+b, sets horizontal/vertical flags
//////////////////////////////////////////////////////////////////////////
void CXArea::AddSegment(const a2DPoint& p0, const a2DPoint& p1)
{
a2DSegment* newSegment = new a2DSegment;
//if this is horizontal line set flag. This segment is needed only for distance calculations
if(p1.y == p0.y)
newSegment->isHorizontal = true;
else
newSegment->isHorizontal = false;
if( p0.x < p1.x )
{
newSegment->bbox.min.x = p0.x;
newSegment->bbox.max.x = p1.x;
}
else
{
newSegment->bbox.min.x = p1.x;
newSegment->bbox.max.x = p0.x;
}
if( p0.y < p1.y )
{
newSegment->bbox.min.y = p0.y;
newSegment->bbox.max.y = p1.y;
}
else
{
newSegment->bbox.min.y = p1.y;
newSegment->bbox.max.y = p0.y;
}
if(!newSegment->isHorizontal)
{
//if this is vertical line - spesial case
if(p1.x == p0.x)
{
newSegment->k = 0;
newSegment->b = p0.x;
}
else
{
newSegment->k = (p1.y - p0.y)/(p1.x - p0.x);
newSegment->b = p0.y - newSegment->k*p0.x;
}
}
else
{
newSegment->k = 0;
newSegment->b = 0;
}
m_vpSegments.push_back( newSegment );
}
// calculates min distance from point within area to the border of area
// returns fade coefficient: Distance/m_Proximity
// [0 - on the very border of area, 1 - inside area, distance to border is more than m_Proximity]
//////////////////////////////////////////////////////////////////////////
float CXArea::CalcDistToPoint(const a2DPoint& point) const
{
if (m_Proximity==0.0f)
return 1.0f;
float distMin = m_Proximity*m_Proximity;
float curDist;
a2DBBox proximityBox;
proximityBox.max.x = point.x + m_Proximity;
proximityBox.max.y = point.y + m_Proximity;
proximityBox.min.x = point.x - m_Proximity;
proximityBox.min.y = point.y - m_Proximity;
for(unsigned int sIdx=0; sIdx<m_vpSegments.size(); sIdx++)
{
a2DSegment *curSg = m_vpSegments[sIdx];
if(!m_vpSegments[sIdx]->bbox.BBoxOutBBox2D( proximityBox ))
{
if(m_vpSegments[sIdx]->isHorizontal)
{
if( point.x<m_vpSegments[sIdx]->bbox.min.x )
curDist = m_vpSegments[sIdx]->bbox.min.DistSqr( point );
else if( point.x>m_vpSegments[sIdx]->bbox.max.x )
curDist = m_vpSegments[sIdx]->bbox.max.DistSqr( point );
else
curDist = fabsf( point.y-m_vpSegments[sIdx]->bbox.max.y );
curDist *= curDist;
}
else
if(m_vpSegments[sIdx]->k==0.0f)
{
if( point.y<m_vpSegments[sIdx]->bbox.min.y )
curDist = m_vpSegments[sIdx]->bbox.min.DistSqr( point );
else if( point.y>m_vpSegments[sIdx]->bbox.max.y )
curDist = m_vpSegments[sIdx]->bbox.max.DistSqr( point );
else
curDist = fabsf( point.x-m_vpSegments[sIdx]->b );
curDist *= curDist;
}
else
{
a2DPoint intersection;
float b2, k2;
k2 = -1.0f/m_vpSegments[sIdx]->k;
b2 = point.y - k2*point.x;
intersection.x = (b2 - m_vpSegments[sIdx]->b)/(m_vpSegments[sIdx]->k - k2);
intersection.y = k2*intersection.x + b2;
if( intersection.x<m_vpSegments[sIdx]->bbox.min.x)
if( m_vpSegments[sIdx]->k<0 )
curDist = point.DistSqr( m_vpSegments[sIdx]->bbox.min.x, m_vpSegments[sIdx]->bbox.max.y );
else
curDist = point.DistSqr( m_vpSegments[sIdx]->bbox.min );
else if( intersection.x>m_vpSegments[sIdx]->bbox.max.x)
if( m_vpSegments[sIdx]->k<0 )
curDist = point.DistSqr( m_vpSegments[sIdx]->bbox.max.x, m_vpSegments[sIdx]->bbox.min.y );
else
curDist = point.DistSqr( m_vpSegments[sIdx]->bbox.max );
else
curDist = intersection.DistSqr( point );
}
if(curDist<distMin)
distMin = curDist;
}
}
return cry_sqrtf(distMin)/m_Proximity;
}
// check if the point is within the area
// first BBox check, then count number of intersections for horizontal ray from point and area segments
// if the number is odd - the point is inside
//////////////////////////////////////////////////////////////////////////
bool CXArea::IsPointWithin(const Vec3d& point3d) const
{
/*
if( m_AreaType == ATP_SECTOR )
{
if( building == m_Building && sector == m_Sector )
return true;
return false;
}
*/
if( m_AreaType == ATP_SPHERE )
{
Vec3d sPnt = point3d - m_Center;
if( GetLengthSquared(sPnt) < m_Radius2 )
return true;
return false;
}
if( m_AreaType == ATP_BOX )
{
Vec3d p3d=m_InvMatrix.TransformPointOLD(point3d);
if ( (p3d.x<m_Min.x) ||
(p3d.y<m_Min.y) ||
(p3d.z<m_Min.z) ||
(p3d.x>m_Max.x) ||
(p3d.y>m_Max.y) ||
(p3d.z>m_Max.z))
return false;
return true;
}
if( m_AreaType == ATP_SHAPE )
{
if( m_VSize>0.0f )
if( point3d.z<m_VOrigin || point3d.z>m_VOrigin + m_VSize )
return false;
a2DPoint *point = (CXArea::a2DPoint*)(&point3d);
if( m_BBox.PointOutBBox2D( *point ) )
return false;
int cntr=0;
for(unsigned int sIdx=0; sIdx<m_vpSegments.size(); sIdx++)
{
if(m_vpSegments[sIdx]->isHorizontal)
continue;
if(!m_vpSegments[sIdx]->bbox.PointOutBBox2DVertical( *point ))
if( m_vpSegments[sIdx]->IntersectsXPosVertical( *point ) )
cntr++;
else
if(!m_vpSegments[sIdx]->bbox.PointOutBBox2DVertical( *point ))
if( m_vpSegments[sIdx]->IntersectsXPos( *point ) )
cntr++;
}
return (cntr & 0x1);
}
// to keep the compiler happy and give more robustness to the code with the assert...
assert(0); // can never reach this part
return false; // default behavior
}
// for editor use - if point is withing - returns min hor distance to border
// if point out - returns -1
//////////////////////////////////////////////////////////////////////////
float CXArea::IsPointWithinDist(const a2DPoint& point) const
{
int cntr=0;
float minDist=-1;
float dist;
if( m_BBox.PointOutBBox2D( point ) )
return -1;
for(unsigned int sIdx=0; sIdx<m_vpSegments.size(); sIdx++)
{
if(m_vpSegments[sIdx]->isHorizontal)
continue;
if( m_vpSegments[sIdx]->k==0 )
dist = m_vpSegments[sIdx]->b - point.x;
else
if(!m_vpSegments[sIdx]->bbox.PointOutBBox2D( point ))
dist = m_vpSegments[sIdx]->GetIntersectX( point );
else
continue;
if( dist<0 )
dist = -dist;
else
cntr++;
if( minDist>dist || minDist<0)
minDist = dist;
}
if(cntr & 0x1)
return minDist;
else
return -1;
}
//////////////////////////////////////////////////////////////////////////
void CXArea::SetPoints(const Vec3d* vPoints, const int count)
{
ClearPoints();
// at least three points needed to create closed shape
if(count>2)
{
int pIdx;
for(pIdx=1; pIdx<count; pIdx++)
{
AddSegment( *((CXArea::a2DPoint*)(vPoints+pIdx-1)), *((CXArea::a2DPoint*)(vPoints+pIdx)) );
}
AddSegment( *((CXArea::a2DPoint*)(vPoints+pIdx-1)), *((CXArea::a2DPoint*)(vPoints)) );
CalcBBox( );
}
}
//////////////////////////////////////////////////////////////////////////
void CXArea::CalcBBox( )
{
m_BBox.min.x = m_vpSegments[0]->bbox.min.x;
m_BBox.min.y = m_vpSegments[0]->bbox.min.y;
m_BBox.max.x = m_vpSegments[0]->bbox.max.x;
m_BBox.max.y = m_vpSegments[0]->bbox.max.y;
for(unsigned int sIdx=1; sIdx<m_vpSegments.size(); sIdx++)
{
if( m_BBox.min.x>m_vpSegments[sIdx]->bbox.min.x )
m_BBox.min.x = m_vpSegments[sIdx]->bbox.min.x;
if( m_BBox.min.y>m_vpSegments[sIdx]->bbox.min.y )
m_BBox.min.y = m_vpSegments[sIdx]->bbox.min.y;
if( m_BBox.max.x<m_vpSegments[sIdx]->bbox.max.x )
m_BBox.max.x = m_vpSegments[sIdx]->bbox.max.x;
if( m_BBox.max.y<m_vpSegments[sIdx]->bbox.max.y )
m_BBox.max.y = m_vpSegments[sIdx]->bbox.max.y;
}
}
//////////////////////////////////////////////////////////////////////////
void CXArea::SetTM( const Matrix44& TM )
{
m_InvMatrix=TM;
m_InvMatrix.Invert44();
}
//////////////////////////////////////////////////////////////////////////
void CXArea::AddEntity( const char* const clsName )
{
string name = clsName;
m_vsEntityName.push_back( name );
m_vEntityID.push_back( 0 );
}
//////////////////////////////////////////////////////////////////////////
void CXArea::AddEntites( const std::vector<string> &names )
{
for(unsigned int i=0; i<names.size(); i++)
AddEntity(names[i].c_str());
}
//////////////////////////////////////////////////////////////////////////
void CXArea::AddEntity( const EntityId entId )
{
m_vEntityID.push_back( entId );
}
//////////////////////////////////////////////////////////////////////////
void CXArea::AddEntites( const std::vector<EntityId> &entIDs )
{
for(unsigned int i=0; i<entIDs.size(); i++)
AddEntity(entIDs[i]);
}
//////////////////////////////////////////////////////////////////////////
void CXArea::ClearEntities( )
{
m_vsEntityName.clear();
m_vEntityID.clear();
m_PrevFade = -1.0f;
}
// init attached entities IDs list using entites names list
//////////////////////////////////////////////////////////////////////////
void CXArea::UpdateIDs( ISystem * pSystem )
{
IEntity* pEntity;
for( unsigned int eIdx=0; eIdx<m_vEntityID.size(); eIdx++ )
{
//char* tmp_name = (char *)m_vsEntityName[eIdx].c_str();
pEntity = pSystem->GetIEntitySystem()->GetEntity(m_vEntityID[eIdx]);
if(!pEntity)
{
pEntity = pSystem->GetIEntitySystem()->GetEntity((char *)m_vsEntityName[eIdx].c_str());
if(!pEntity)
{
//FIXME
// m_vsEntityName.erase( m_vsEntityName.begin() + eIdx );
// m_vEntityID.erase( m_vEntityID.begin() + eIdx );
// eIdx--;
m_vEntityID[eIdx] = 0;
continue;
}
m_vEntityID[eIdx] = pEntity->GetId();
}
}
}
// do enter area - player was outside, now is inside
// calls entity OnEnterArea wich calls script OnEnterArea( player, AreaID )
//////////////////////////////////////////////////////////////////////////
void CXArea::EnterArea( CXAreaUser& user )
{
IEntity* pEntity;
if (user.m_pGame->m_bIsLoadingLevelFromFile)
return;
m_PrevFade = -1;
m_bIsActive = true;
UpdateIDs( user.m_pGame->GetSystem() );
for( unsigned int eIdx=0; eIdx<m_vEntityID.size(); eIdx++ )
{
pEntity = user.m_pGame->GetSystem()->GetIEntitySystem()->GetEntity(m_vEntityID[eIdx]);
// ASSERT(pEntity);
if (pEntity)
{
if(user.m_pGame->a_LogArea->GetIVal())
user.m_pGame->m_pLog->Log("\001 CXArea::EnterArea calling %s\n", pEntity->GetName() );
pEntity->OnEnterArea( pEntity, m_AreaID);
}
}
}
// do enter area - player was outside, now is inside
// calls entity OnEnterArea wich calls script OnEnterArea( player, AreaID )
//////////////////////////////////////////////////////////////////////////
void CXArea::EnterArea( IEntity* const pEntity, ISystem *pSystem )
{
IEntity* pAreaAttachedEntity;
m_PrevFade = -1;
m_bIsActive = true;
UpdateIDs( pSystem );
for( unsigned int eIdx=0; eIdx<m_vEntityID.size(); eIdx++ )
{
pAreaAttachedEntity = pSystem->GetIEntitySystem()->GetEntity(m_vEntityID[eIdx]);
// ASSERT(pEntity);
if (pAreaAttachedEntity)
{
pAreaAttachedEntity->OnEnterArea( pEntity, m_AreaID);
// if(a_LogArea->GetIVal())
// m_pGame->m_pLog->Log("\001 CXArea::EnterArea reenter calling %s\n", pAreaAttachedEntity->GetName() );
}
}
}
// do leave area - player was inside, now is outside
// calls entity OnLeaveArea wich calls script OnLeaveArea( player, AreaID )
//////////////////////////////////////////////////////////////////////////
void CXArea::LeaveArea( CXAreaUser& user )
{
IEntity* pEntity;
if (user.m_pGame->m_bIsLoadingLevelFromFile)
return;
UpdateIDs( user.m_pGame->GetSystem() );
for( unsigned int eIdx=0; eIdx<m_vEntityID.size(); eIdx++ )
{
pEntity = user.m_pGame->GetSystem()->GetIEntitySystem()->GetEntity(m_vEntityID[eIdx]);
// ASSERT(pEntity);
if (pEntity)
{
pEntity->OnLeaveArea( user.m_pEntity, m_AreaID);
if(user.m_pGame->a_LogArea->GetIVal())
user.m_pGame->m_pLog->Log("\001 CXArea::LeaveArea calling %s\n", pEntity->GetName() );
}
}
m_PrevFade = -1;
m_bIsActive = false;
}
//calculate distance to area - proceed fade. player is inside of the area
//////////////////////////////////////////////////////////////////////////
void CXArea::UpdateArea( CXAreaUser& user )
{
ProceedFade( user, CalculateFade( user.m_vPos ));
}
//calculate distance to area. player is inside of the area
//////////////////////////////////////////////////////////////////////////
float CXArea::CalculateFade( const Vec3d& pos3D )
{
// a2DPoint pos = *((CXArea::a2DPoint*)(&pos3D));
a2DPoint pos = CXArea::a2DPoint(pos3D);
float fadeCoeff;
switch(m_AreaType)
{
case ATP_SHAPE:
fadeCoeff = CalcDistToPoint( pos );
break;
case ATP_SPHERE:
{
if (m_Proximity<=0.0f)
{
fadeCoeff=1.0f;
break;
}
Vec3d Delta=pos3D-m_Center;
fadeCoeff=(m_Radius-Delta.Length())/m_Proximity;
if (fadeCoeff>1.0f)
fadeCoeff=1.0f;
break;
}
case ATP_BOX:
{
if (m_Proximity<=0.0f)
{
fadeCoeff=1.0f;
break;
}
Vec3d p3D=m_InvMatrix.TransformPointOLD(pos3D);
Vec3d MinDelta=p3D-m_Min;
Vec3d MaxDelta=m_Max-p3D;
Vec3d EdgeDist=(m_Max-m_Min)/2.0f;
if ((!EdgeDist.x) ||
(!EdgeDist.y) ||
(!EdgeDist.z))
{
fadeCoeff=1.0f;
break;
}
float fFadeScale=m_Proximity/100.0f;
EdgeDist*=fFadeScale;
//float fFade=MinDelta.x/EdgeDist.x;
float fMinFade=MinDelta.x/EdgeDist.x;
for (int k=0;k<3;k++)
{
float fFade1=MinDelta[k]/EdgeDist[k];
float fFade2=MaxDelta[k]/EdgeDist[k];
fMinFade=min(fMinFade,min(fFade1,fFade2));
} //k
/*
fFade=MinDelta.y/EdgeDist.y;
if (fFade<fMinFade)
fMinFade=fFade;
fFade=MinDelta.z/EdgeDist.z;
if (fFade<fMinFade)
fMinFade=fFade;
fFade=MaxDelta.x/EdgeDist.x;
if (fFade<fMinFade)
fMinFade=fFade;
fFade=MaxDelta.y/EdgeDist.y;
if (fFade<fMinFade)
fMinFade=fFade;
fFade=MaxDelta.z/EdgeDist.z;
if (fFade<fMinFade)
fMinFade=fFade;
*/
fadeCoeff=fMinFade;
if (fadeCoeff>1.0f)
fadeCoeff=1.0f;
break;
}
}
return fadeCoeff;
}
//proceed fade. player is inside of the area
//////////////////////////////////////////////////////////////////////////
void CXArea::ProceedFade( CXAreaUser& user, const float fadeCoeff )
{
// no update if fade coefficient hasn't changed
if(m_PrevFade == fadeCoeff)
return;
m_PrevFade = fadeCoeff;
UpdateIDs( user.m_pGame->GetSystem() );
IEntity* pEntity;
for( unsigned int eIdx=0; eIdx<m_vEntityID.size(); eIdx++ )
{
pEntity = user.m_pGame->GetSystem()->GetIEntitySystem()->GetEntity(m_vEntityID[eIdx]);
// ASSERT(pEntity);
if (pEntity)
pEntity->OnProceedFadeArea( user.m_pEntity, m_AreaID, fadeCoeff);
}
}
//////////////////////////////////////////////////////////////////////////
void CXArea::Draw(const ISystem * const pSystem, const int idx)
{
CFColor colorsArray[] = { CFColor(1.0f, 0.0f, 0.0f, 1.0f ),
CFColor(0.0f, 1.0f, 0.0f, 1.0f),
CFColor(0.0f, 0.0f, 1.0f, 1.0f),
CFColor(1.0f, 1.0f, 0.0f, 1.0f),
CFColor(1.0f, 0.0f, 1.0f, 1.0f),
CFColor(0.0f, 1.0f, 1.0f, 1.0f),
CFColor(1.0f, 1.0f, 1.0f, 1.0f),
};
CFColor color = colorsArray[idx%(sizeof(colorsArray)/sizeof(CFColor))];
CFColor color1 = colorsArray[(idx+1)%(sizeof(colorsArray)/sizeof(CFColor))];
CFColor color2 = colorsArray[(idx+2)%(sizeof(colorsArray)/sizeof(CFColor))];
Vec3d v0, v1;
float deltaZ=1.5f;
if(m_AreaType != ATP_SHAPE)
return;
for(unsigned int sIdx=0; sIdx<m_vpSegments.size(); sIdx++)
{
if( m_vpSegments[sIdx]->k<0 )
{
v0.x = m_vpSegments[sIdx]->bbox.min.x;
v0.y = m_vpSegments[sIdx]->bbox.max.y;
v1.x = m_vpSegments[sIdx]->bbox.max.x;
v1.y = m_vpSegments[sIdx]->bbox.min.y;
}
else
{
v0.x = m_vpSegments[sIdx]->bbox.min.x;
v0.y = m_vpSegments[sIdx]->bbox.min.y;
v1.x = m_vpSegments[sIdx]->bbox.max.x;
v1.y = m_vpSegments[sIdx]->bbox.max.y;
}
if( m_VSize>0.0f )
{
v0.z = m_VOrigin;
v1.z = m_VOrigin;
}
else
{
v0.z = ((ISystem *)pSystem)->GetI3DEngine()->GetTerrainElevation(v0.x, v0.y) + deltaZ;
v1.z = ((ISystem *)pSystem)->GetI3DEngine()->GetTerrainElevation(v1.x, v1.y) + deltaZ;
}
if(m_vpSegments[sIdx]->isHorizontal) // horizontal segment
((ISystem *)pSystem)->GetIRenderer()->DrawLineColor( v0, color2, v1, color2);
else if(m_vpSegments[sIdx]->k==0) // vertical segment
((ISystem *)pSystem)->GetIRenderer()->DrawLineColor( v0, color1, v1, color1);
else
{
((ISystem *)pSystem)->GetIRenderer()->DrawLineColor( v0, color, v1, color);
if( m_VSize>0.0f )
{
v0.z = m_VOrigin + m_VSize;
v1.z = m_VOrigin + m_VSize;
((ISystem *)pSystem)->GetIRenderer()->DrawLineColor( v0, color, v1, color);
}
}
}
}
//////////////////////////////////////////////////////////////////////////
//*************************************************************************************************************
//
// AREAs MANEGER
//
//////////////////////////////////////////////////////////////////////////
//
//////////////////////////////////////////////////////////////////////////
CXAreaMgr::CXAreaMgr(void):
m_sCurStep(3)
{
m_lastUpdatePos.Set(0,0,0);
}
//////////////////////////////////////////////////////////////////////////
CXAreaMgr::~CXAreaMgr(void)
{
Clear();
}
//////////////////////////////////////////////////////////////////////////
void CXAreaMgr::Clear()
{
CXArea *carArea;
for(unsigned int sIdx=0; sIdx<m_vpAreas.size(); sIdx++)
{
carArea = m_vpAreas[sIdx];
delete carArea;
}
m_vpAreas.clear();
}
//-------------------------------------------------------------------------------------------------------------
// adding area BOX
// min - Min of box
// max - Max of box
// names - attached entities names
// width - fade width (m_Proximity), from border to withing
//////////////////////////////////////////////////////////////////////////
CXArea* CXAreaMgr::AddArea(const Vec3d& min, const Vec3d& max, const Matrix44& TM, const std::vector<string> &names,
const int id, const int groupId, const float width)
{
CXArea *newArea;
newArea = new CXArea;
newArea->SetMin( min );
newArea->SetMax( max );
newArea->SetTM( TM );
newArea->SetID(id);
newArea->SetGroup(groupId);
newArea->SetAreaType( CXArea::ATP_BOX );
// newArea->SetID( id );
newArea->AddEntites( names );
newArea->SetProximity( width );
m_vpAreas.push_back( newArea );
return newArea;
}
//-------------------------------------------------------------------------------------------------------------
// adding area SPHERE
// center - pos of center of sphere
// radius - radius of the sphere
// names - attached entities names
// width - fade width (m_Proximity), from border to withing
//////////////////////////////////////////////////////////////////////////
CXArea* CXAreaMgr::AddArea(const Vec3d& center, const float radius, const std::vector<string> &names,
const int id, const int groupId, const float width)
{
CXArea *newArea;
// no building/sector must be outside
if(radius<=0)
return NULL;
newArea = new CXArea;
newArea->SetCenter( center );
newArea->SetRadius( radius );
newArea->SetID(id);
newArea->SetGroup(groupId);
newArea->SetAreaType( CXArea::ATP_SPHERE );
// newArea->SetID( id );
newArea->AddEntites( names );
newArea->SetProximity( width );
m_vpAreas.push_back( newArea );
return newArea;
}
//-------------------------------------------------------------------------------------------------------------
// adding area SECTOR
// nBuilding - building number
// nSectorId - sector ID
// entityID - attached entity id
// width - fade width (m_Proximity), from border to withing
//////////////////////////////////////////////////////////////////////////
/*
CXArea* CXAreaMgr::AddArea(const int nBuilding, const int nSectorId, const EntityId entityID, const float width)
{
CXArea *newArea;
// no building/sector must be outside
if(nBuilding<0 || nSectorId<0)
return NULL;
newArea = new CXArea;
newArea->SetBuilding( nBuilding );
newArea->SetSector( nSectorId );
newArea->SetAreaType( CXArea::ATP_SECTOR );
// newArea->SetID( id );
newArea->AddEntity( entityID );
newArea->SetProximity( width );
m_vpAreas.push_back( newArea );
return newArea;
}
*/
// adding area SHAPE
// vPoint - border points
// count - number of border points
// names - attached entities names
// id - area ID, used for exclusive areas
// id - group area belongs to, used for exclusive areas (exclusive withing group)
// width - fade width (m_Proximity), from border to withing
//////////////////////////////////////////////////////////////////////////
CXArea* CXAreaMgr::AddArea(const Vec3d* const vPoints, const int count, const std::vector<string> &names,
const int id, const int groupId, const float width)
{
CXArea *newArea;
// at least three points needed to create closed shape
if(count<3)
return NULL;
newArea = new CXArea;
//
//find the lowest point - to use for vertical origin in 3D area
float minZ=vPoints[0].z;
for( int idx=1; idx<count; idx++ )
if(minZ>vPoints[idx].z)
minZ = vPoints[idx].z;
newArea->SetVOrigin( minZ );
newArea->SetPoints( vPoints, count );
newArea->SetAreaType( CXArea::ATP_SHAPE );
newArea->SetID( id );
newArea->SetGroup(groupId);
newArea->AddEntites( names );
newArea->SetProximity( width );
m_vpAreas.push_back( newArea );
return newArea;
}
/*
// gets area by building/sector/entityID
//////////////////////////////////////////////////////////////////////////
CXArea* CXAreaMgr::GetArea(const int nBuilding, const int nSectorId, const EntityId entityID)
{
for(unsigned int aIdx=0; aIdx<m_vpAreas.size(); aIdx++)
{
CXArea* curArea = m_vpAreas[aIdx];
if( curArea->GetAreaType() == CXArea::ATP_SECTOR )
{
if( curArea->GetBuilding() == nBuilding
&& curArea->GetSector() == nSectorId
// &&curArea->Get
)
return curArea;
}
}
return NULL;
}
*/
// gets area by point
//////////////////////////////////////////////////////////////////////////
CXArea* CXAreaMgr::GetArea( const Vec3d& point )
{
float dist;
float closeDist=-1;
CXArea* closeArea=NULL;
for(unsigned int aIdx=0; aIdx<m_vpAreas.size(); aIdx++)
{
if( m_vpAreas[aIdx]->GetAreaType() == CXArea::ATP_SHAPE )
{
dist = m_vpAreas[aIdx]->IsPointWithinDist( *((CXArea::a2DPoint*)&point) );
if(dist>0)
{
if( !closeArea || closeDist>dist )
{
closeDist = dist;
closeArea = m_vpAreas[aIdx];
}
}
}
}
return closeArea;
}
//////////////////////////////////////////////////////////////////////////
void CXAreaMgr::DeleteArea( const IXArea* aPtr )
{
for(unsigned int aIdx=0; aIdx<m_vpAreas.size(); aIdx++)
if( m_vpAreas[aIdx]== aPtr)
{
delete aPtr;
m_vpAreas.erase( m_vpAreas.begin() + aIdx );
}
}
// check player's position for all the areas. Do onEnter, onLeave, updateFade
//////////////////////////////////////////////////////////////////////////
void CXAreaMgr::UpdatePlayer(CXAreaUser& user)
{
if (user.m_pGame->m_bIsLoadingLevelFromFile)
return;
FUNCTION_PROFILER( GetISystem(),PROFILE_GAME );
unsigned int aIdx;
// int building=0, sector=0;
// IVisArea *sector=NULL;
//fixme - localplayer check should be earlier
// if( player->GetEntity() != player->GetGame()->GetMyPlayer() )
// return;
// pos = player->GetGame()->GetSystem()->GetViewCamera().GetPos();
// get check player position - not foot
user.m_vPos = user.m_pEntity->GetPos() + Vec3d(0,0,1);
if (user.m_vPos == m_lastUpdatePos)
return;
m_lastUpdatePos = user.m_vPos;
// player->GetGame()->GetSystem()->GetI3DEngine()->GetBuildingManager()->CheckInside( pos, building, sector );
// sector = (int)player->GetGame()->GetSystem()->GetI3DEngine()->GetVisAreaFromPos(pos);
m_sCurStep++;
// check all the areas player is in already
for(unsigned int inIdx=0; inIdx<user.m_HostedAreasIdx.size(); inIdx++)
{
aIdx = user.m_HostedAreasIdx[inIdx];
//safecheck for editor
if( aIdx>=m_vpAreas.size() )
return;
m_vpAreas[aIdx]->m_stepID = m_sCurStep;
if(!m_vpAreas[aIdx]->IsPointWithin(user.m_vPos))
{
// was inside, now is out - do OnLeaveArea
ProceedExclusiveLeave( user, aIdx );
user.m_HostedAreasIdx.erase( user.m_HostedAreasIdx.begin() + inIdx );
inIdx--;
}
}
// check all the rest areas (player is outside of them)
for(aIdx=0; aIdx<m_vpAreas.size(); aIdx++)
if( m_vpAreas[aIdx]->m_stepID != m_sCurStep )
if(m_vpAreas[aIdx]->IsPointWithin(user.m_vPos))
{
// was outside, now inside - do enter area
if(ProceedExclusiveEnter( user, aIdx ))
m_vpAreas[aIdx]->EnterArea(user);
user.m_HostedAreasIdx.push_back( aIdx );
}
//
//update fade. For all hosted areas
for(unsigned int inIdx=0; inIdx<user.m_HostedAreasIdx.size(); inIdx++)
{
aIdx = user.m_HostedAreasIdx[inIdx];
//safecheck for editor
if( aIdx>=m_vpAreas.size() )
return;
// this one already updated
if( m_vpAreas[aIdx]->m_stepID == 0 )
continue;
// this area is not active - overriden by same groupId area with higher id (exclusive areas)
if( !m_vpAreas[aIdx]->m_bIsActive )
continue;
ProceedExclusiveUpdate(user, aIdx);
}
}
// checks for areas in the same group, if entered area is lower priority (areaID) - return false
// (don't do enreArea for it)
//////////////////////////////////////////////////////////////////////////
void CXAreaMgr::ProceedExclusiveUpdate( CXAreaUser& user, unsigned int curIdx )
{
int maxArea=FindHighestHostedArea( user.m_HostedAreasIdx, curIdx );
CXArea *firstArea=m_vpAreas[curIdx];
// not grouped or the only one in a group
if( maxArea<0 )
{
firstArea->UpdateArea(user);
return;
}
CXArea *secondArea=m_vpAreas[maxArea];
if( firstArea->GetID()<secondArea->GetID() )
{
firstArea = secondArea;
int secondMaxArea=FindHighestHostedArea( user.m_HostedAreasIdx, maxArea );
secondArea=m_vpAreas[secondMaxArea];
}
float fadeBase = firstArea->CalculateFade(user.m_vPos);
if( fadeBase<1.0f )
{
if( !secondArea->m_bIsActive )
secondArea->EnterArea(user);
float fadeSecond = secondArea->CalculateFade(user.m_vPos);
fadeSecond *= (1.0f-fadeBase);
firstArea->ProceedFade(user, fadeBase);
secondArea->ProceedFade(user, fadeSecond);
}
else
{
if( secondArea->m_bIsActive )
secondArea->LeaveArea(user);
}
// mark all the areas of this group as updated
int curGroup = m_vpAreas[curIdx]->GetGroup();
for(unsigned int inIdx=0; inIdx<user.m_HostedAreasIdx.size(); inIdx++)
{
unsigned int aIdx = user.m_HostedAreasIdx[inIdx];
//safecheck for editor
if( aIdx>=m_vpAreas.size() )
continue;
if(m_vpAreas[curIdx]->GetGroup() != m_vpAreas[aIdx]->GetGroup())
continue;
m_vpAreas[aIdx]->m_stepID = 0;
}
}
// checks for areas in the same group, if entered area is lower priority (areaID) - return false
// (don't do enreArea for it)
//////////////////////////////////////////////////////////////////////////
bool CXAreaMgr::ProceedExclusiveEnter( CXAreaUser& user, unsigned int curIdx )
{
int maxArea=FindHighestHostedArea( user.m_HostedAreasIdx, curIdx );
if(maxArea<0)
return true;
int maxID = m_vpAreas[maxArea]->GetID();
if(m_vpAreas[curIdx]->GetID()<maxID)
return false;
return true;
}
// checks for areas in the same group, if entered area is lower priority (areaID) - return false
// (don't do enreArea for it)
//////////////////////////////////////////////////////////////////////////
bool CXAreaMgr::ProceedExclusiveLeave( CXAreaUser& user, unsigned int curIdx )
{
int maxArea=FindHighestHostedArea( user.m_HostedAreasIdx, curIdx );
if( maxArea<0 )
{
m_vpAreas[curIdx]->LeaveArea( user );
return true;
}
int maxID = m_vpAreas[maxArea]->GetID();
if( m_vpAreas[curIdx]->GetID()<maxID )
return false;
m_vpAreas[curIdx]->LeaveArea( user );
if(!m_vpAreas[maxArea]->m_bIsActive)
m_vpAreas[maxArea]->EnterArea( user ); // new area will owerride this one
return true;
}
// find hosted area with highest priority withing the group of curIdx area
//////////////////////////////////////////////////////////////////////////
int CXAreaMgr::FindHighestHostedArea( intVector& hostedAreas, unsigned int curIdx )
{
unsigned int aIdx;
int maxID=-1;
int maxArea=-1;
int curGroup = m_vpAreas[curIdx]->GetGroup();
if(curGroup<=0)
{
return maxArea;
}
for(unsigned int inIdx=0; inIdx<hostedAreas.size(); inIdx++)
{
aIdx = hostedAreas[inIdx];
if( aIdx == curIdx )
continue;
//safecheck for editor
if( aIdx>=m_vpAreas.size() )
continue;
if(curGroup != m_vpAreas[aIdx]->GetGroup()) // different groups
continue;
if(maxID<m_vpAreas[aIdx]->GetID())
{
maxID = m_vpAreas[aIdx]->GetID();
maxArea = aIdx;
}
}
return maxArea;
}
// do onexit for all areas player is in - do it before kill player
//////////////////////////////////////////////////////////////////////////
void CXAreaMgr::ExitAllAreas(CXAreaUser& user)
{
//fixme - is this needed here - should be check before
//
if( user.m_pEntity != user.m_pGame->GetMyPlayer() )
return;
// check all the areas player is in already
for(unsigned int inIdx=0; inIdx<user.m_HostedAreasIdx.size(); inIdx++)
{
unsigned int aIdx;
aIdx = user.m_HostedAreasIdx[inIdx];
// was inside, now is out - do OnLeaveArea
if(aIdx<m_vpAreas.size())
{
m_vpAreas[aIdx]->LeaveArea( user );
user.m_HostedAreasIdx.erase( user.m_HostedAreasIdx.begin() + inIdx );
inIdx--;
}
}
}
//////////////////////////////////////////////////////////////////////////
void CXAreaMgr::DrawAreas(const ISystem * const pSystem)
{
for(unsigned int aIdx=0; aIdx<m_vpAreas.size(); aIdx++)
m_vpAreas[aIdx]->Draw( pSystem, aIdx );
}
//////////////////////////////////////////////////////////////////////////
unsigned CXAreaMgr::MemStat()
{
unsigned memSize = sizeof *this;
for(unsigned int aIdx=0; aIdx<m_vpAreas.size(); aIdx++)
memSize += m_vpAreas[aIdx]->MemStat();
return memSize;
}
//////////////////////////////////////////////////////////////////////////
void CXAreaMgr::ReTriggerArea(IEntity* pEntity, const Vec3d &vPos,bool bIndoor)
{
intVector hostedAreasIdx;
intVector updatedID;
unsigned int aIdx;
hostedAreasIdx.clear();
updatedID.clear();
// check all the rest areas (player is outside of them)
for(aIdx=0; aIdx<m_vpAreas.size(); aIdx++)
if(m_vpAreas[aIdx]->IsPointWithin(vPos))
hostedAreasIdx.push_back(aIdx);
for(int idxIdx=0; idxIdx<(int)(hostedAreasIdx.size()); idxIdx++ )
{
if(std::find(updatedID.begin(), updatedID.end(), m_vpAreas[hostedAreasIdx[idxIdx]]->GetGroup())!=updatedID.end())
continue;
int exclIdx = FindHighestHostedArea( hostedAreasIdx, hostedAreasIdx[idxIdx] );
if(exclIdx<0)
{
m_vpAreas[hostedAreasIdx[idxIdx]]->EnterArea(pEntity, m_pSystem);
continue;
}
m_vpAreas[exclIdx]->EnterArea(pEntity, m_pSystem);
updatedID.push_back( m_vpAreas[hostedAreasIdx[idxIdx]]->GetGroup() );
}
/*
unsigned int aIdx;
int sector = 0; //(int)player->GetGame()->GetSystem()->GetI3DEngine()->GetVisAreaFromPos(pos);
int building = 0;
// check all the rest areas (player is outside of them)
for(aIdx=0; aIdx<m_vpAreas.size(); aIdx++)
if(m_vpAreas[aIdx]->IsPointWithin(vPos, building, sector))
{
m_vpAreas[aIdx]->EnterArea(pEntity, m_pSystem);
}
*/
}
//////////////////////////////////////////////////////////////////////////
void CXAreaMgr::Init( ISystem * pSystem )
{
m_pSystem = pSystem;
m_pCurrArea=m_pPrevArea=NULL;
}
// [marco] functions common for game and editor mode
// to be able to retrigger areas etc.
//////////////////////////////////////////////////////////////////////////
void CXGame::CheckSoundVisAreas()
{
m_XAreaMgr.m_pPrevArea=m_XAreaMgr.m_pCurrArea=NULL;
if (m_pSystem->GetISoundSystem())
m_XAreaMgr.m_pPrevArea=m_pSystem->GetISoundSystem()->GetListenerArea();
}
//////////////////////////////////////////////////////////////////////////
void CXGame::RetriggerAreas()
{
if (m_pSystem->GetISoundSystem())
{
m_XAreaMgr.m_pCurrArea=m_pSystem->GetISoundSystem()->GetListenerArea();
if ((!m_XAreaMgr.m_pCurrArea && m_XAreaMgr.m_pPrevArea) || (m_XAreaMgr.m_pCurrArea && !m_XAreaMgr.m_pPrevArea))
{
// if we switched between outdoor and indoor, let's
// retrigger the area to account for random ambient sounds
// indoor / outdoor only
// m_XAreaMgr.ReTriggerArea(m_pSystem->GetISoundSystem()->GetListenerPos(),false);
if( GetMyPlayer() )
m_XAreaMgr.ReTriggerArea(GetMyPlayer(), m_pSystem->GetISoundSystem()->GetListenerPos(),false);
}
}
}
//////////////////////////////////////////////////////////////////////////
IXArea *CXGame::CreateArea( const Vec3d *vPoints, const int count, const std::vector<string> &names,
const int type, const int groupId, const float width, const float height)
{
IXArea* newArea = m_XAreaMgr.AddArea( vPoints, count, names, type, groupId, width );
newArea->SetVSize( height );
return newArea;
// return (IXArea*)m_XAreaMgr.AddArea( vPoints, count, names, type, width );
}
//////////////////////////////////////////////////////////////////////////
IXArea *CXGame::CreateArea( const Vec3d& min, const Vec3d& max, const Matrix44& TM, const std::vector<string> &names,
const int type, const int groupId, const float width)
{
return m_XAreaMgr.AddArea(min, max, TM, names, type, groupId, width );
}
//////////////////////////////////////////////////////////////////////////
IXArea *CXGame::CreateArea( const Vec3d& center, const float radius, const std::vector<string> &names,
const int type, const int groupId, const float width)
{
return m_XAreaMgr.AddArea(center, radius, names, type, groupId, width );
}
//////////////////////////////////////////////////////////////////////////
void CXGame::DeleteArea( const IXArea *pArea )
{
m_XAreaMgr.DeleteArea( (CXArea*) pArea);
}
//finds closest area the point is in
//////////////////////////////////////////////////////////////////////////
IXArea *CXGame::GetArea( const Vec3d &point )
{
return (IXArea*)m_XAreaMgr.GetArea( point );
}
//////////////////////////////////////////////////////////////////////////
void CXAreaMgr::RetriggerAreas()
{
if (m_pSystem->GetISoundSystem())
{
m_pCurrArea = m_pSystem->GetISoundSystem()->GetListenerArea();
if ((!m_pCurrArea && m_pPrevArea) || ( m_pCurrArea && !m_pPrevArea ))
{
// if we switched between outdoor and indoor, let's
// retrigger the area to account for random ambient sounds
// indoor / outdoor only
// m_XAreaMgr.ReTriggerArea(m_pSystem->GetISoundSystem()->GetListenerPos(),false);
if ( GetIXGame( m_pSystem->GetIGame() )->GetMyPlayer() )
ReTriggerArea( GetIXGame( m_pSystem->GetIGame() )->GetMyPlayer(), m_pSystem->GetISoundSystem()->GetListenerPos(), false );
}
}
}
//////////////////////////////////////////////////////////////////////////
IXArea *CXAreaMgr::CreateArea( const Vec3d *vPoints, const int count, const std::vector<string> &names,
const int type, const int groupId, const float width, const float height)
{
IXArea* newArea = AddArea( vPoints, count, names, type, groupId, width );
newArea->SetVSize( height );
return newArea;
// return (IXArea*)m_XAreaMgr.AddArea( vPoints, count, names, type, width );
}
//////////////////////////////////////////////////////////////////////////
IXArea *CXAreaMgr::CreateArea( const Vec3d& min, const Vec3d& max, const Matrix44& TM, const std::vector<string> &names,
const int type, const int groupId, const float width)
{
return AddArea(min, max, TM, names, type, groupId, width );
}
//////////////////////////////////////////////////////////////////////////
IXArea *CXAreaMgr::CreateArea( const Vec3d& center, const float radius, const std::vector<string> &names,
const int type, const int groupId, const float width)
{
return AddArea(center, radius, names, type, groupId, width );
}