diff options
author | maximius <none@none> | 2009-09-27 02:24:25 -0700 |
---|---|---|
committer | maximius <none@none> | 2009-09-27 02:24:25 -0700 |
commit | f980dd9ac6c1679caac7a41d806d65c90a02939f (patch) | |
tree | add2748b3fcfd38d00883789dc692c087deed77f /src/game/CellImpl.h | |
parent | a635bdd0ccdf77c56c45ee1a6d456b3a2ef43ff3 (diff) |
*Merge [8524] New cell search algorithm implemented. You can now choose different
visibility distances on continents, in BG/Arenas and instances. Author: Ambal
*Some warning cleanup
--HG--
branch : trunk
Diffstat (limited to 'src/game/CellImpl.h')
-rw-r--r-- | src/game/CellImpl.h | 156 |
1 files changed, 156 insertions, 0 deletions
diff --git a/src/game/CellImpl.h b/src/game/CellImpl.h index 362c12579bb..d86091ee40a 100644 --- a/src/game/CellImpl.h +++ b/src/game/CellImpl.h @@ -25,6 +25,7 @@ #include "Cell.h" #include "Map.h" +#include "Object.h" inline Cell::Cell(CellPair const& p) { @@ -178,5 +179,160 @@ Cell::Visit(const CellLock<LOCK_TYPE> &l, TypeContainerVisitor<T, CONTAINER> &vi } } } + +inline int CellHelper(const float radius) +{ + if(radius < 1.0f) + return 0; + + return (int)ceilf(radius/SIZE_OF_GRID_CELL); +} + +inline CellArea Cell::CalculateCellArea(const WorldObject &obj, float radius) +{ + if(radius <= 0.0f) + return CellArea(); + + //we should increase search radius by object's radius, otherwise + //we could have problems with huge creatures, which won't attack nearest players etc + radius += obj.GetObjectSize(); + //lets calculate object coord offsets from cell borders. + //TODO: add more correct/generic method for this task + const float x_offset = (obj.GetPositionX() - CENTER_GRID_CELL_OFFSET)/SIZE_OF_GRID_CELL; + const float y_offset = (obj.GetPositionY() - CENTER_GRID_CELL_OFFSET)/SIZE_OF_GRID_CELL; + + const float x_val = floor(x_offset + CENTER_GRID_CELL_ID + 0.5f); + const float y_val = floor(y_offset + CENTER_GRID_CELL_ID + 0.5f); + + const float x_off = (x_offset - x_val + CENTER_GRID_CELL_ID) * SIZE_OF_GRID_CELL; + const float y_off = (y_offset - y_val + CENTER_GRID_CELL_ID) * SIZE_OF_GRID_CELL; + + const float tmp_diff = radius - CENTER_GRID_CELL_OFFSET; + //lets calculate upper/lower/right/left corners for cell search + int right = CellHelper(tmp_diff + x_off); + int left = CellHelper(tmp_diff - x_off); + int upper = CellHelper(tmp_diff + y_off); + int lower = CellHelper(tmp_diff - y_off); + + return CellArea(right, left, upper, lower); +} + +template<class LOCK_TYPE, class T, class CONTAINER> +inline void +Cell::Visit(const CellLock<LOCK_TYPE> &l, TypeContainerVisitor<T, CONTAINER> &visitor, Map &m, const WorldObject &obj, float radius) const +{ + const CellPair &standing_cell = l.i_cellPair; + if (standing_cell.x_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP || standing_cell.y_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP) + return; + + //no jokes here... Actually placing ASSERT() here was good idea, but + //we had some problems with DynamicObjects, which pass radius = 0.0f (DB issue?) + //maybe it is better to just return when radius <= 0.0f? + if(radius <= 0.0f) + { + m.Visit(l, visitor); + return; + } + + //lets calculate object coord offsets from cell borders. + CellArea area = Cell::CalculateCellArea(obj, radius); + //if radius fits inside standing cell + if(!area) + { + m.Visit(l, visitor); + return; + } + + CellPair begin_cell = standing_cell; + CellPair end_cell = standing_cell; + + area.ResizeBorders(begin_cell, end_cell); + //visit all cells, found in CalculateCellArea() + //if radius is known to reach cell area more than 4x4 then we should call optimized VisitCircle + //currently this technique works with MAX_NUMBER_OF_CELLS 16 and higher, with lower values + //there are nothing to optimize because SIZE_OF_GRID_CELL is too big... + if(((end_cell.x_coord - begin_cell.x_coord) > 4) && ((end_cell.y_coord - begin_cell.y_coord) > 4)) + { + VisitCircle(l, visitor, m, begin_cell, end_cell); + return; + } + + //ALWAYS visit standing cell first!!! Since we deal with small radiuses + //it is very essential to call visitor for standing cell firstly... + m.Visit(l, visitor); + + // loop the cell range + for(uint32 x = begin_cell.x_coord; x <= end_cell.x_coord; x++) + { + for(uint32 y = begin_cell.y_coord; y <= end_cell.y_coord; y++) + { + CellPair cell_pair(x,y); + //lets skip standing cell since we already visited it + if(cell_pair != standing_cell) + { + Cell r_zone(cell_pair); + r_zone.data.Part.nocreate = l->data.Part.nocreate; + CellLock<LOCK_TYPE> lock(r_zone, cell_pair); + m.Visit(lock, visitor); + } + } + } +} + +template<class LOCK_TYPE, class T, class CONTAINER> +inline void +Cell::VisitCircle(const CellLock<LOCK_TYPE> &l, TypeContainerVisitor<T, CONTAINER> &visitor, Map &m, const CellPair& begin_cell, const CellPair& end_cell) const +{ + //here is an algorithm for 'filling' circum-squared octagon + uint32 x_shift = (uint32)ceilf((end_cell.x_coord - begin_cell.x_coord) * 0.3f - 0.5f); + //lets calculate x_start/x_end coords for central strip... + const uint32 x_start = begin_cell.x_coord + x_shift; + const uint32 x_end = end_cell.x_coord - x_shift; + + //visit central strip with constant width... + for(uint32 x = x_start; x <= x_end; ++x) + { + for(uint32 y = begin_cell.y_coord; y <= end_cell.y_coord; ++y) + { + CellPair cell_pair(x,y); + Cell r_zone(cell_pair); + r_zone.data.Part.nocreate = l->data.Part.nocreate; + CellLock<LOCK_TYPE> lock(r_zone, cell_pair); + m.Visit(lock, visitor); + } + } + + //if x_shift == 0 then we have too small cell area, which were already + //visited at previous step, so just return from procedure... + if(x_shift == 0) + return; + + uint32 y_start = end_cell.y_coord; + uint32 y_end = begin_cell.y_coord; + //now we are visiting borders of an octagon... + for (uint32 step = 1; step <= (x_start - begin_cell.x_coord); ++step) + { + //each step reduces strip height by 2 cells... + y_end += 1; + y_start -= 1; + for (uint32 y = y_start; y >= y_end; --y) + { + //we visit cells symmetrically from both sides, heading from center to sides and from up to bottom + //e.g. filling 2 trapezoids after filling central cell strip... + CellPair cell_pair_left(x_start - step, y); + Cell r_zone_left(cell_pair_left); + r_zone_left.data.Part.nocreate = l->data.Part.nocreate; + CellLock<LOCK_TYPE> lock_left(r_zone_left, cell_pair_left); + m.Visit(lock_left, visitor); + + //right trapezoid cell visit + CellPair cell_pair_right(x_end + step, y); + Cell r_zone_right(cell_pair_right); + r_zone_right.data.Part.nocreate = l->data.Part.nocreate; + CellLock<LOCK_TYPE> lock_right(r_zone_right, cell_pair_right); + m.Visit(lock_right, visitor); + } + } +} #endif |