2 * Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 #ifndef MANGOS_CELLIMPL_H
20 #define MANGOS_CELLIMPL_H
26 inline Cell::Cell(CellPair
const& p
)
28 data
.Part
.grid_x
= p
.x_coord
/ MAX_NUMBER_OF_CELLS
;
29 data
.Part
.grid_y
= p
.y_coord
/ MAX_NUMBER_OF_CELLS
;
30 data
.Part
.cell_x
= p
.x_coord
% MAX_NUMBER_OF_CELLS
;
31 data
.Part
.cell_y
= p
.y_coord
% MAX_NUMBER_OF_CELLS
;
32 data
.Part
.nocreate
= 0;
33 data
.Part
.reserved
= 0;
36 template<class T
, class CONTAINER
>
38 Cell::Visit(const CellPair
&standing_cell
, TypeContainerVisitor
<T
, CONTAINER
> &visitor
, Map
&m
) const
40 if (standing_cell
.x_coord
>= TOTAL_NUMBER_OF_CELLS_PER_MAP
|| standing_cell
.y_coord
>= TOTAL_NUMBER_OF_CELLS_PER_MAP
)
43 uint16 district
= (District
)this->data
.Part
.reserved
;
44 if(district
== CENTER_DISTRICT
)
46 m
.Visit(*this, visitor
);
50 // set up the cell range based on the district
51 // the overloaded operators handle range checking
52 CellPair begin_cell
= standing_cell
;
53 CellPair end_cell
= standing_cell
;
59 begin_cell
<< 1; begin_cell
-= 1; // upper left
60 end_cell
>> 1; end_cell
+= 1; // lower right
63 case UPPER_LEFT_DISTRICT
:
65 begin_cell
<< 1; begin_cell
-= 1; // upper left
68 case UPPER_RIGHT_DISTRICT
:
70 begin_cell
-= 1; // up
71 end_cell
>> 1; // right
74 case LOWER_LEFT_DISTRICT
:
76 begin_cell
<< 1; // left
77 end_cell
+= 1; // down
80 case LOWER_RIGHT_DISTRICT
:
82 end_cell
>> 1; end_cell
+= 1; // lower right
87 begin_cell
-= 1; // up
88 end_cell
>> 1; end_cell
+= 1; // lower right
93 begin_cell
<< 1; begin_cell
-= 1; // upper left
94 end_cell
+= 1; // down
99 begin_cell
<< 1; begin_cell
-= 1; // upper left
100 end_cell
>> 1; // right
105 begin_cell
<< 1; // left
106 end_cell
>> 1; end_cell
+= 1; // lower right
116 // loop the cell range
117 for(uint32 x
= begin_cell
.x_coord
; x
<= end_cell
.x_coord
; x
++)
119 for(uint32 y
= begin_cell
.y_coord
; y
<= end_cell
.y_coord
; y
++)
121 CellPair
cell_pair(x
,y
);
122 Cell
r_zone(cell_pair
);
123 r_zone
.data
.Part
.nocreate
= data
.Part
.nocreate
;
124 m
.Visit(r_zone
, visitor
);
129 inline int CellHelper(const float radius
)
134 return (int)ceilf(radius
/SIZE_OF_GRID_CELL
);
137 inline CellArea
Cell::CalculateCellArea(const WorldObject
&obj
, float radius
)
142 //we should increase search radius by object's radius, otherwise
143 //we could have problems with huge creatures, which won't attack nearest players etc
144 radius
+= obj
.GetObjectSize();
145 //lets calculate object coord offsets from cell borders.
146 //TODO: add more correct/generic method for this task
147 const float x_offset
= (obj
.GetPositionX() - CENTER_GRID_CELL_OFFSET
)/SIZE_OF_GRID_CELL
;
148 const float y_offset
= (obj
.GetPositionY() - CENTER_GRID_CELL_OFFSET
)/SIZE_OF_GRID_CELL
;
150 const float x_val
= floor(x_offset
+ CENTER_GRID_CELL_ID
+ 0.5f
);
151 const float y_val
= floor(y_offset
+ CENTER_GRID_CELL_ID
+ 0.5f
);
153 const float x_off
= (x_offset
- x_val
+ CENTER_GRID_CELL_ID
) * SIZE_OF_GRID_CELL
;
154 const float y_off
= (y_offset
- y_val
+ CENTER_GRID_CELL_ID
) * SIZE_OF_GRID_CELL
;
156 const float tmp_diff
= radius
- CENTER_GRID_CELL_OFFSET
;
157 //lets calculate upper/lower/right/left corners for cell search
158 int right
= CellHelper(tmp_diff
+ x_off
);
159 int left
= CellHelper(tmp_diff
- x_off
);
160 int upper
= CellHelper(tmp_diff
+ y_off
);
161 int lower
= CellHelper(tmp_diff
- y_off
);
163 return CellArea(right
, left
, upper
, lower
);
166 template<class T
, class CONTAINER
>
168 Cell::Visit(const CellPair
&standing_cell
, TypeContainerVisitor
<T
, CONTAINER
> &visitor
, Map
&m
, const WorldObject
&obj
, float radius
) const
170 if (standing_cell
.x_coord
>= TOTAL_NUMBER_OF_CELLS_PER_MAP
|| standing_cell
.y_coord
>= TOTAL_NUMBER_OF_CELLS_PER_MAP
)
173 //no jokes here... Actually placing ASSERT() here was good idea, but
174 //we had some problems with DynamicObjects, which pass radius = 0.0f (DB issue?)
175 //maybe it is better to just return when radius <= 0.0f?
178 m
.Visit(*this, visitor
);
181 //lets limit the upper value for search radius
185 //lets calculate object coord offsets from cell borders.
186 CellArea area
= Cell::CalculateCellArea(obj
, radius
);
187 //if radius fits inside standing cell
190 m
.Visit(*this, visitor
);
194 CellPair begin_cell
= standing_cell
;
195 CellPair end_cell
= standing_cell
;
197 area
.ResizeBorders(begin_cell
, end_cell
);
198 //visit all cells, found in CalculateCellArea()
199 //if radius is known to reach cell area more than 4x4 then we should call optimized VisitCircle
200 //currently this technique works with MAX_NUMBER_OF_CELLS 16 and higher, with lower values
201 //there are nothing to optimize because SIZE_OF_GRID_CELL is too big...
202 if(((end_cell
.x_coord
- begin_cell
.x_coord
) > 4) && ((end_cell
.y_coord
- begin_cell
.y_coord
) > 4))
204 VisitCircle(visitor
, m
, begin_cell
, end_cell
);
208 //ALWAYS visit standing cell first!!! Since we deal with small radiuses
209 //it is very essential to call visitor for standing cell firstly...
210 m
.Visit(*this, visitor
);
212 // loop the cell range
213 for(uint32 x
= begin_cell
.x_coord
; x
<= end_cell
.x_coord
; x
++)
215 for(uint32 y
= begin_cell
.y_coord
; y
<= end_cell
.y_coord
; y
++)
217 CellPair
cell_pair(x
,y
);
218 //lets skip standing cell since we already visited it
219 if(cell_pair
!= standing_cell
)
221 Cell
r_zone(cell_pair
);
222 r_zone
.data
.Part
.nocreate
= data
.Part
.nocreate
;
223 m
.Visit(r_zone
, visitor
);
229 template<class T
, class CONTAINER
>
231 Cell::VisitCircle(TypeContainerVisitor
<T
, CONTAINER
> &visitor
, Map
&m
, const CellPair
& begin_cell
, const CellPair
& end_cell
) const
233 //here is an algorithm for 'filling' circum-squared octagon
234 uint32 x_shift
= (uint32
)ceilf((end_cell
.x_coord
- begin_cell
.x_coord
) * 0.3f
- 0.5f
);
235 //lets calculate x_start/x_end coords for central strip...
236 const uint32 x_start
= begin_cell
.x_coord
+ x_shift
;
237 const uint32 x_end
= end_cell
.x_coord
- x_shift
;
239 //visit central strip with constant width...
240 for(uint32 x
= x_start
; x
<= x_end
; ++x
)
242 for(uint32 y
= begin_cell
.y_coord
; y
<= end_cell
.y_coord
; ++y
)
244 CellPair
cell_pair(x
,y
);
245 Cell
r_zone(cell_pair
);
246 r_zone
.data
.Part
.nocreate
= data
.Part
.nocreate
;
247 m
.Visit(r_zone
, visitor
);
251 //if x_shift == 0 then we have too small cell area, which were already
252 //visited at previous step, so just return from procedure...
256 uint32 y_start
= end_cell
.y_coord
;
257 uint32 y_end
= begin_cell
.y_coord
;
258 //now we are visiting borders of an octagon...
259 for (uint32 step
= 1; step
<= (x_start
- begin_cell
.x_coord
); ++step
)
261 //each step reduces strip height by 2 cells...
264 for (uint32 y
= y_start
; y
>= y_end
; --y
)
266 //we visit cells symmetrically from both sides, heading from center to sides and from up to bottom
267 //e.g. filling 2 trapezoids after filling central cell strip...
268 CellPair
cell_pair_left(x_start
- step
, y
);
269 Cell
r_zone_left(cell_pair_left
);
270 r_zone_left
.data
.Part
.nocreate
= data
.Part
.nocreate
;
271 m
.Visit(r_zone_left
, visitor
);
273 //right trapezoid cell visit
274 CellPair
cell_pair_right(x_end
+ step
, y
);
275 Cell
r_zone_right(cell_pair_right
);
276 r_zone_right
.data
.Part
.nocreate
= data
.Part
.nocreate
;
277 m
.Visit(r_zone_right
, visitor
);