// Ryzom - MMORPG Framework // 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 . #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;i0); // 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 _Rows; /// the table of disc patterns static std::vector _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 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*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 CCellIteratorBorder : public CCellIterator { 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(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::CEntityIteratorTemplate class CAIEntityMatrix::CEntityIteratorLinear class CAIEntityMatrix::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 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(_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* _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 > { public: inline CEntityIteratorDisc(){} inline CEntityIteratorDisc(CEntityMatrix *matrix,CEntityMatrixPatternSymetrical *pattern, sint32 posX, sint32 posY, double radiusSquare) :CEntityIteratorTemplate > ( 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 > { public: inline CEntityIteratorCone(){} inline CEntityIteratorCone(CEntityMatrix *matrix, CEntityMatrixPatternRect *pattern, const CAreaQuad * cone, const CAreaCoords & center, const CEntityBase* mainTarget) :CEntityIteratorTemplate > ( 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(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 * _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 * 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(" bad action nature: %s", ACTNATURE::toString(_Nature).c_str()); } return CEntityIteratorDisc::testValidity( entity ); } const std::vector* _AddedEntities; ACTNATURE::TActionNature _Nature; TDataSetRow _ActorRowId; const CEntityBase * _MainTarget; }; /// iterator used to get entities in a disc around a point class CEntityIteratorChainBorder : public CEntityIteratorTemplate > { public: inline CEntityIteratorChainBorder(){} inline CEntityIteratorChainBorder(CEntityMatrix *matrix, CEntityMatrixPatternBorder *pattern, sint32 posX, sint32 posY, double radiusSquare, const std::vector * entities, ACTNATURE::TActionNature nature, const TDataSetRow & actor, const CEntityBase * mainTarget ) :CEntityIteratorTemplate > ( 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(" 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 * _AddedEntities; ACTNATURE::TActionNature _Nature; TDataSetRow _ActorRowId; const CEntityBase * _MainTarget; }; /// A line of the matrix class CMatrixLine { public: /// one and only accessor inline CEntityListLink &operator[](uint8 x) { return Line[x]; } private: /// data CEntityListLink Line[256]; }; /// link an entity to the matrix inline void linkToMatrix(sint32 x, sint32 y, CEntityListLink & 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 * cone, const CAreaCoords & 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 *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 *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 */