khanat-opennel-code/code/ryzom/server/src/entities_game_service/entity_matrix.h

826 lines
23 KiB
C++

// Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
// Copyright (C) 2010 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef RY_ENTITY_MATRIX_H
#define RY_ENTITY_MATRIX_H
#include "entity_list_link.h"
#include "area_geometry.h"
#include "phrase_manager/phrase_utilities_functions.h"
#include "entity_manager/entity_base.h"
/***************************
TODO :
This system was designed as a template to support special entities such as harvest deposit explosions,...
In fact, it is only used with CEntityBase. As special features had to be included in it, we use a lot of methods very specific to CEntityBase
So This class must be reworked when the new EGS classes will be designed.
trap 14.12.2004 : I have deleted the template argument of the CEntityMatrix class. This class is no more template.
****************************/
/// helper for coords conversion
/// convert world coords to matrix coords ( >> 14 is approximatively /16000 but faster )
inline static uint16 WorldtoMatrixDistance( uint32 dist )
{
return uint16(dist >> 14);
}
inline static uint8 WorldXtoMatrixX( sint32 x )
{
return uint8(x >> 14);
}
inline static uint8 WorldYtoMatrixY( sint32 y )
{
return uint8((-y) >> 14);
}
/*************************************************************
MATRIX PATTERNS
They represent an area of an entity matrix to be scanned
Linear pattern tables use a better RAM access patern than random access tables and should be used whenever possible
For template use, all pattern class must present the following method:
- uint16 size() : return the size in rows of the pattern
- uint16 runLength(uint row) : return the runLength of the specified row
- sint16 startDx(uint row) : return a value to add to an X coord to go to the beginning of the current row from the end of the previous
**************************************************************/
/// This class uses a vector of horizontal run lengths to represent a pattern
/// The pattern represented by the data is assumed to be symetrical in both x and y.
/// it is based on linear tables. To avoid the generation of too many tables, the choice of the best iterator is done with only 1 parameter
/// So it is perfect for circles, but not for most other patterns ( rectangles for example are better selected by width and length )
class CEntityMatrixPatternSymetrical
{
public:
/// ctor, used during init only
inline CEntityMatrixPatternSymetrical(uint32 *runLengths,uint32 count)
{
uint32 lastRun=0;
for(uint i=0;i<count;++i)
{
_Rows.push_back(CRow(runLengths[i],lastRun));
lastRun=runLengths[i];
}
}
static void initMatrixPatterns();
/// find the best Disc pattern according to the distance chosen
inline static CEntityMatrixPatternSymetrical* bestDiscPattern( uint16 distInMeters )
{
#ifdef NL_DEBUG
nlassert(distInMeters < _DiscPatterns.size());
#endif
return _DiscPatterns[ distInMeters ];
}
inline uint16 size() const{ return (uint16)_Rows.size(); }
inline sint16 startDx(uint row) const
{
#ifdef NL_DEBUG
nlassert( row < _Rows.size() );
#endif
return _Rows[row].StartDx;
}
inline uint16 runLength(uint row) const
{
#ifdef NL_DEBUG
nlassert( row < _Rows.size() );
#endif
return _Rows[row].RunLength;
}
private:
/// struct representing a row in our pattern
struct CRow
{
CRow() {}
inline CRow(uint32 runLength, sint32 previousRunLength)
{
RunLength=(uint16)(runLength-1); // we assume all runs are at least 1 unit long - this is the excess
StartDx=(sint16)(-(previousRunLength/2)-(((sint32)runLength)/2));
#ifdef NL_DEBUG
nlassert(runLength>0); // runs must be at least 1 unit long
nlassert(runLength<32768); // this is the limit where StartDx runs out of bits
nlassert(previousRunLength>=0);
nlassert(previousRunLength<32768);
#endif
}
// length of a run in this row
uint16 RunLength;
// dx to add to the last x of the previous line to go to the beginning of this row
sint16 StartDx;
};
/// the rows of the pattern
std::vector<CRow> _Rows;
/// the table of disc patterns
static std::vector<CEntityMatrixPatternSymetrical*> _DiscPatterns;
};
/// This pattern represents a rectangle aligned with The X and Y axis
/// the width is the norm of the side on the X axis
/// the height is the norm of the side on the Y axis
/// These pattern are built "on the fly", no tables are needed
class CEntityMatrixPatternRect
{
public:
inline CEntityMatrixPatternRect( uint32 width, uint32 height)
{
// first get the width in matrix coords
_RunLength = WorldtoMatrixDistance( width );
// if the runlength is even, add 3 ( 1 cell for the center cell and 2 because we dont know where the center is in the cell. Remove 1 because we just want the excess )
if ( (_RunLength & 1) == 0 )
_RunLength+= 2;
// if the runlength is odd, add 2( 2 because we dont know where the center is in the cell. We alredy have a center. Remove 1 because we just want the excess )
else
_RunLength+= 1;
// same for the size
_Size = WorldtoMatrixDistance( height);
if ( (_Size & 1) == 0 )
_Size+= 3;
else
_Size+= 2;
_StartDx = - (sint16)_RunLength;
}
inline uint16 size() const
{
return _Size;
}
inline sint16 startDx(uint row) const
{
return _StartDx;
}
inline uint16 runLength(uint row) const
{
return _RunLength;
}
private:
sint16 _StartDx;
uint16 _RunLength;
uint16 _Size;
};
/// This pattern represents a square border aligned with The X and Y axis :
/*
*************
* *
* *
* *
* *
*************
*/
/// the cellWidth is the width in cell of the border
/// These pattern are built "on the fly", no tables are needed
class CEntityMatrixPatternBorder
{
public:
inline CEntityMatrixPatternBorder( uint8 cellSide )
{
_Side = cellSide;
_StartDx = sint16(1) - (sint16)_Side;
}
inline uint16 size() const
{
return _Side;
}
inline sint16 startDx(uint row) const
{
return _StartDx;
}
inline uint16 runLength(uint row) const
{
if ( row == 0 || row == uint ( _Side-1) )
return _Side - 1;
return 1;
}
private:
uint16 _Side;
sint16 _StartDx;
};
/**
* Matrix used to dispatch all the entities in its entries
* The matrix size is 256*256. Each entry contains a linked list of entities ( see CEntityListLink )
* This way it is much faster to get all the entities which are within a specific distance from a point
* Entities position in the matrix must be periodically updated
* \author Nicolas Brigand
* \author Nevrax France
* \date 2003
*/
class CEntityMatrix
{
public:
/// iterator used to iterate through an entity matrix cells using a specific pattern
/// TEntity is the entity type
/// TPattern is the used pattern type
template <class TPattern >
class CCellIterator
{
public:
/// ctor only used before operator = in STL-like for loops
inline CCellIterator(){}
/// real ctor
/// matrix is the observed matrix, pattern is the pattern used to iterate
/// x and y are the world coords of the center point
inline CCellIterator( CEntityMatrix *matrix,TPattern *pattern,sint32 x, sint32 y )
:_Matrix(matrix),_Pattern(pattern)
{
#ifdef NL_DEBUG
nlassert(_Pattern!=NULL);
nlassert(_Pattern->size()>0);
nlassert(_Pattern->size()<32768); // a numeric over-run limit
nlassert(_Matrix!=NULL);
#endif
// setup the iterator to point to the start of the pattern and setup properties accordingly
_IndexInPattern = 0;
_RunLengthRemaining= _Pattern->runLength(0);
_X = uint8( WorldXtoMatrixX(x) - (sint16)(pattern->runLength(0)/2) );
_Y = uint8( WorldYtoMatrixY(y) - (sint16)(pattern->size()/2) );
}
///\return the en
inline CEntityListLink<CEntityBase>*operator*()
{
return & ( (*_Matrix)[_Y][_X] );
}
inline CCellIterator &operator++()
{
#ifdef NL_DEBUG
// make sure we aren'TEntity trying to access an uninitialised iterator
nlassert(_IndexInPattern < _Pattern->size());
#endif
// if we're not at the end of the current run continue run else move on to next line
if (_RunLengthRemaining!=0)
{
--_RunLengthRemaining;
++_X;
}
else
{
++_IndexInPattern;
// check if end not reached
if ( _IndexInPattern < _Pattern->size() )
{
_RunLengthRemaining= _Pattern->runLength(_IndexInPattern);
_X= uint8( _X + _Pattern->startDx(_IndexInPattern) );
++_Y;
}
}
return *this;
}
inline bool end() const
{
return _IndexInPattern >= _Pattern->size();
}
protected:
/// coords of the current cells in the matrix
uint8 _X;
uint8 _Y;
// matrix used by the iterator
CEntityMatrix* _Matrix;
/// current pattern used
TPattern* _Pattern;
/// iterator in the used pattern (used to see in which row of the pattern we are)
uint32 _IndexInPattern;
/// remaining run length
uint32 _RunLengthRemaining;
};
template <class TPattern >
class CCellIteratorBorder : public CCellIterator<TPattern>
{
public:
/// ctor only used before operator = in STL-like for loops
inline CCellIteratorBorder(){}
/// real ctor
/// matrix is the observed matrix, pattern is the pattern used to iterate
/// x and y are the world coords of the center point
inline CCellIteratorBorder( CEntityMatrix *matrix,TPattern *pattern,sint32 x, sint32 y )
:CCellIterator<TPattern>(matrix,pattern,x,y)
{
#ifdef NL_DEBUG
nlassert(this->_Pattern!=NULL);
nlassert(this->_Pattern->size()>0);
nlassert(this->_Pattern->size()<32768); // a numeric over-run limit
nlassert(this->_Matrix!=NULL);
#endif
// setup the iterator to point to the start of the pattern and setup properties accordingly
this->_IndexInPattern = 0;
this->_RunLengthRemaining = this->_Pattern->runLength(0);
this->_X = uint8( WorldXtoMatrixX(x) - (sint16)(pattern->runLength(0)/2) );
this->_Y = uint8( WorldYtoMatrixY(y) - (sint16)(pattern->size()/2) );
}
inline CCellIteratorBorder &operator++()
{
#ifdef NL_DEBUG
// make sure we aren'TEntity trying to access an uninitialised iterator
nlassert(this->_IndexInPattern < this->_Pattern->size());
#endif
if ( this->_RunLengthRemaining !=0 )
{
if ( this->_Pattern->runLength(this->_IndexInPattern) == 1 )
{
--this->_RunLengthRemaining;
this->_X+= this->_Pattern->size() - 1;
}
else
{
--this->_RunLengthRemaining;
++this->_X;
}
}
else
{
++this->_IndexInPattern;
// check if end not reached
if ( this->_IndexInPattern < this->_Pattern->size() )
{
this->_RunLengthRemaining= this->_Pattern->runLength(this->_IndexInPattern);
this->_X= uint8( this->_X + this->_Pattern->startDx(this->_IndexInPattern) );
++this->_Y;
}
}
return *this;
}
};
/*
-----------------------------------------------------------------------------------------------------
class CAIEntityMatrix<T>::CEntityIteratorTemplate
class CAIEntityMatrix<T>::CEntityIteratorLinear
class CAIEntityMatrix<T>::CEntityIteratorRandom
This class provides an iterator for iterating across the entities listed in the cells of a matrix
'_matrix' following the pattern described by '_Pattern'
The class is composed of a CCellIterator' _CellIt' responsible for iterating across the matrix
and an entity pointer '_Entity' used for iterating over the entities in each matrix cell
Note that unlinking, moving or deleting the entity refferenced by a CEntityIteratorLinear iterator
invalidates it.
-----------------------------------------------------------------------------------------------------
*/
template <class TPattern, class TCellIt>
class CEntityIteratorTemplate
{
public:
inline CEntityIteratorTemplate(){}
inline CEntityIteratorTemplate(CEntityMatrix *matrix,
TPattern *pattern,
sint32 centerX,
sint32 centerY)
: _CellIt(matrix,pattern,centerX,centerY),_CenterPosX(double(centerX)/1000.0),_CenterPosY(double(centerY)/1000.0)
{
// get a pointer to the list link
_Entity=(*_CellIt);
}
inline CEntityBase &operator*()
{
#ifdef NL_DEBUG
// make sure we aren't trying to access passed the end of list
nlassert(!end());
#endif
CEntityBase * entity = dynamic_cast<CEntityBase *>(_Entity->entity());
nlassert(entity);
return *entity;
}
inline const CEntityIteratorTemplate &operator++()
{
#ifdef NL_DEBUG
// if you are on a breakpoint here it is because you've tried to do a ++ on an iterator
nlassert(!_CellIt.end());
#endif
// repeat the following loop until either we come to the end of the cell iterator or we find a valid entity
do
{
// try to get the next entity in the cell
_Entity=_Entity->next();
if (_Entity==*_CellIt)
{
// we're at the end of the entity list for this cell so try to find another cell with a valid entity
do
{
++_CellIt;
}
while ( !_CellIt.end()
&& (*_CellIt)->unlinked());
_Entity=(*_CellIt)->next();
}
}
while ( !_CellIt.end()
&& ( _Entity->unlinked()
|| ! testValidity(_Entity->entity()) ) );
// as cells are tiled over the world, we need to check if our entity have a real good match with the scanned position.
return *this;
}
// method for testing iterator for end of current sequence
inline bool end()
{
// the following can only happen if there are no more entites in cell and no more deltas in cell iterator tbl
return _CellIt.end();
}
inline float getDistance(){return _Distance;}
protected:
/// test the validity of an entity
virtual bool testValidity( CEntityBase* entity ) = 0;
TCellIt _CellIt; // the cell iterator
const CEntityListLink<CEntityBase>* _Entity; // which entity are we pointing at (within the cell)
// center position
double _CenterPosX;
double _CenterPosY;
// distance to center
float _Distance;
};
/// iterator used to get entities in a disc around a point
class CEntityIteratorDisc : public CEntityIteratorTemplate<CEntityMatrixPatternSymetrical, CCellIterator<CEntityMatrixPatternSymetrical> >
{
public:
inline CEntityIteratorDisc(){}
inline CEntityIteratorDisc(CEntityMatrix *matrix,CEntityMatrixPatternSymetrical *pattern,
sint32 posX,
sint32 posY,
double radiusSquare)
:CEntityIteratorTemplate<CEntityMatrixPatternSymetrical, CCellIterator<CEntityMatrixPatternSymetrical> >
( matrix,
pattern,
posX,
posY
),
_RadiusSquare(radiusSquare){}
protected:
inline bool testValidity( CEntityBase* entity )
{
if( entity == 0 )
{
nlwarning("CEntityIteratorDisc::testValidity entity == 0 !!");
return false;
}
double dx = double(entity->getState().X) /1000.0 - _CenterPosX;
double dy = double(entity->getState().Y) /1000.0 - _CenterPosY;
double distanceSquare = dx * dx + dy * dy;
if ( distanceSquare <= _RadiusSquare )
{
_Distance = (float)sqrt(distanceSquare);
return true;
}
else
{
return false;
}
}
double _RadiusSquare;
};
/// iterator used to get entities in a truncated cone ( a trapezoid in fact )
class CEntityIteratorCone : public CEntityIteratorTemplate<CEntityMatrixPatternRect, CCellIterator<CEntityMatrixPatternRect> >
{
public:
inline CEntityIteratorCone(){}
inline CEntityIteratorCone(CEntityMatrix *matrix,
CEntityMatrixPatternRect *pattern,
const CAreaQuad<sint32> * cone,
const CAreaCoords<sint32> & center,
const CEntityBase* mainTarget)
:CEntityIteratorTemplate<CEntityMatrixPatternRect, CCellIterator<CEntityMatrixPatternRect> >
( matrix,
pattern,
center.X,
center.Y
),
_Cone(cone),
_MainTarget(mainTarget)
{
#ifdef NL_DEBUG
nlassert(cone);
#endif
}
private:
inline bool testValidity( CEntityBase* entity )
{
if( entity == 0 )
{
nlwarning("CEntityIteratorCone::testValidity entity == 0 !!");
return false;
}
if ( entity != _MainTarget && _Cone->contains( CAreaCoords<sint32>(entity->getState().X,entity->getState().Y) ) )
{
double dx = double(entity->getState().X) /1000.0 - _CenterPosX;
double dy = double(entity->getState().Y) /1000.0 - _CenterPosY;
double distanceSquare = dx * dx + dy * dy;
_Distance = (float)sqrt(distanceSquare);
return true;
}
return false;
}
const CAreaQuad<sint32> * _Cone;
const CEntityBase* _MainTarget;
};
/// iterator used to get entities in a disc around a point
class CEntityIteratorChainCenter : public CEntityIteratorDisc
{
public:
inline CEntityIteratorChainCenter(){}
inline CEntityIteratorChainCenter(CEntityMatrix *matrix,
CEntityMatrixPatternSymetrical *pattern,
sint32 posX,
sint32 posY,
double radiusSquare,
const std::vector<CEntityBase*> * addedEntities,
ACTNATURE::TActionNature nature,
const TDataSetRow & actor,
const CEntityBase * mainTarget
)
:CEntityIteratorDisc(matrix,pattern,posX,posY,radiusSquare),
_AddedEntities(addedEntities),_ActorRowId(actor),_Nature(nature),_MainTarget(mainTarget){}
private:
inline bool testValidity( CEntityBase* entity )
{
if( entity == 0 )
{
nlwarning("CEntityIteratorChainCenter::testValidity entity == 0 !!");
return false;
}
for ( uint i = 0; i < _AddedEntities->size(); i++ )
{
if ( (*_AddedEntities)[i] == entity )
return false;
}
const bool mainTarget = (entity == _MainTarget);
std::string dummy;
switch (_Nature)
{
case ACTNATURE::FIGHT:
if ( ! PHRASE_UTILITIES::testOffensiveActionAllowed(_ActorRowId, entity->getEntityRowId(), dummy, mainTarget) )
{
return false;
}
break;
case ACTNATURE::OFFENSIVE_MAGIC:
case ACTNATURE::CURATIVE_MAGIC:
if ( ! PHRASE_UTILITIES::validateSpellTarget(_ActorRowId, entity->getEntityRowId(), _Nature, dummy, mainTarget) )
{
return false;
}
break;
default:
nlwarning("<CEntityIteratorChainCenter::testValidity> bad action nature: %s", ACTNATURE::toString(_Nature).c_str());
}
return CEntityIteratorDisc::testValidity( entity );
}
const std::vector<CEntityBase*>* _AddedEntities;
ACTNATURE::TActionNature _Nature;
TDataSetRow _ActorRowId;
const CEntityBase * _MainTarget;
};
/// iterator used to get entities in a disc around a point
class CEntityIteratorChainBorder : public CEntityIteratorTemplate<CEntityMatrixPatternBorder, CCellIteratorBorder<CEntityMatrixPatternBorder> >
{
public:
inline CEntityIteratorChainBorder(){}
inline CEntityIteratorChainBorder(CEntityMatrix *matrix,
CEntityMatrixPatternBorder *pattern,
sint32 posX,
sint32 posY,
double radiusSquare,
const std::vector<CEntityBase*> * entities,
ACTNATURE::TActionNature nature,
const TDataSetRow & actor,
const CEntityBase * mainTarget
)
:CEntityIteratorTemplate<CEntityMatrixPatternBorder, CCellIteratorBorder<CEntityMatrixPatternBorder> >
( matrix,
pattern,
posX,
posY
),
_RadiusSquare(radiusSquare),_AddedEntities(entities),_ActorRowId(actor),_Nature(nature),_MainTarget(mainTarget){}
private:
inline bool testValidity( CEntityBase* entity )
{
if( entity == 0 )
{
nlwarning("CEntityIteratorChainBorder::testValidity entity == 0 !!");
return false;
}
for ( uint i = 0; i < _AddedEntities->size(); i++ )
{
if ( (*_AddedEntities)[i] == entity )
return false;
}
const bool mainTarget = (entity == _MainTarget);
std::string dummy;
switch (_Nature)
{
case ACTNATURE::FIGHT:
if ( ! PHRASE_UTILITIES::testOffensiveActionAllowed(_ActorRowId, entity->getEntityRowId(), dummy, mainTarget) )
{
return false;
}
break;
case ACTNATURE::OFFENSIVE_MAGIC:
case ACTNATURE::CURATIVE_MAGIC:
if ( ! PHRASE_UTILITIES::validateSpellTarget(_ActorRowId, entity->getEntityRowId(), _Nature, dummy, mainTarget) )
{
return false;
}
break;
default:
nlwarning("<CEntityIteratorChainBorder::testValidity> bad action nature: %s", ACTNATURE::toString(_Nature).c_str());
}
double dx = double(entity->getState().X) /1000.0 - _CenterPosX;
double dy = double(entity->getState().Y) /1000.0 - _CenterPosY;
double distanceSquare = dx * dx + dy * dy;
if ( distanceSquare <= _RadiusSquare )
{
_Distance = (float)sqrt(distanceSquare);
return true;
}
else
{
return false;
}
}
double _RadiusSquare;
const std::vector<CEntityBase*> * _AddedEntities;
ACTNATURE::TActionNature _Nature;
TDataSetRow _ActorRowId;
const CEntityBase * _MainTarget;
};
/// A line of the matrix
class CMatrixLine
{
public:
/// one and only accessor
inline CEntityListLink<CEntityBase> &operator[](uint8 x) { return Line[x]; }
private:
/// data
CEntityListLink<CEntityBase> Line[256];
};
/// link an entity to the matrix
inline void linkToMatrix(sint32 x, sint32 y, CEntityListLink<CEntityBase> & link)
{
// too slow H_AUTO(linkToMatrix);
link.link(_Matrix [(uint8)WorldYtoMatrixY(y)] [(uint8)WorldXtoMatrixX(x)]);
}
inline CEntityIteratorDisc beginEntitiesInDisc(CEntityMatrixPatternSymetrical * pattern ,sint32 x, sint32 y,double radiusSquare)
{
CEntityIteratorDisc newIt(this,pattern,x,y,radiusSquare);
++newIt;
return newIt;
}
inline CEntityIteratorCone beginEntitiesInCone(CEntityMatrixPatternRect * pattern , const CAreaQuad<sint32> * cone, const CAreaCoords<sint32> & center, const CEntityBase * mainTarget)
{
CEntityIteratorCone newIt(this,pattern, cone, center, mainTarget);
++newIt;
return newIt;
}
inline CEntityIteratorChainCenter beginEntitiesInChainCenter(CEntityMatrixPatternSymetrical * pattern ,sint32 x, sint32 y,double radiusSquare, const std::vector<CEntityBase*> *addedEntities, ACTNATURE::TActionNature nature, const TDataSetRow & actor, const CEntityBase * mainTarget)
{
CEntityIteratorChainCenter newIt(this,pattern,x,y,radiusSquare,addedEntities, nature, actor, mainTarget);
++newIt;
return newIt;
}
inline CEntityIteratorChainBorder beginEntitiesInChainBorder(CEntityMatrixPatternBorder * pattern ,sint32 x, sint32 y,double radiusSquare, const std::vector<CEntityBase*> *addedEntities, ACTNATURE::TActionNature nature, const TDataSetRow & actor, const CEntityBase * mainTarget)
{
CEntityIteratorChainBorder newIt(this,pattern,x,y,radiusSquare,addedEntities, nature, actor, mainTarget);
++newIt;
return newIt;
}
// table lookup operator - should be used as myMatrix[y][x]
inline CMatrixLine & operator[](uint8 y)
{
return _Matrix[y];
}
private:
/// the matrix data. WARNING acces is _Matrix[y][x]
CMatrixLine _Matrix[256];
};
//the entity matrix
extern CEntityMatrix EntityMatrix;
#endif // RY_ENTITY_MATRIX_H
/* End of entity_matrix.h */