[8483] Implement glyph 43361.
[getmangos.git] / src / game / FleeingMovementGenerator.cpp
bloba74790d9a2ef3272c0614cc861d53009da6e0d4a
1 /*
2 * Copyright (C) 2005-2009 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 #include "Creature.h"
20 #include "CreatureAI.h"
21 #include "MapManager.h"
22 #include "FleeingMovementGenerator.h"
23 #include "DestinationHolderImp.h"
24 #include "ObjectAccessor.h"
26 #define MIN_QUIET_DISTANCE 28.0f
27 #define MAX_QUIET_DISTANCE 43.0f
29 template<class T>
30 void
31 FleeingMovementGenerator<T>::_setTargetLocation(T &owner)
33 if( !&owner )
34 return;
36 if( owner.hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNNED) )
37 return;
39 if(!_setMoveData(owner))
40 return;
42 float x, y, z;
43 if(!_getPoint(owner, x, y, z))
44 return;
46 owner.addUnitState(UNIT_STAT_FLEEING);
47 Traveller<T> traveller(owner);
48 i_destinationHolder.SetDestination(traveller, x, y, z);
51 template<class T>
52 bool
53 FleeingMovementGenerator<T>::_getPoint(T &owner, float &x, float &y, float &z)
55 if(!&owner)
56 return false;
58 x = owner.GetPositionX();
59 y = owner.GetPositionY();
60 z = owner.GetPositionZ();
62 float temp_x, temp_y, angle;
63 const Map * _map = owner.GetBaseMap();
64 //primitive path-finding
65 for(uint8 i = 0; i < 18; ++i)
67 if(i_only_forward && i > 2)
68 break;
70 float distance = 5.0f;
72 switch(i)
74 case 0:
75 angle = i_cur_angle;
76 break;
77 case 1:
78 angle = i_cur_angle;
79 distance /= 2;
80 break;
81 case 2:
82 angle = i_cur_angle;
83 distance /= 4;
84 break;
85 case 3:
86 angle = i_cur_angle + M_PI/4.0f;
87 break;
88 case 4:
89 angle = i_cur_angle - M_PI/4.0f;
90 break;
91 case 5:
92 angle = i_cur_angle + M_PI/4.0f;
93 distance /= 2;
94 break;
95 case 6:
96 angle = i_cur_angle - M_PI/4.0f;
97 distance /= 2;
98 break;
99 case 7:
100 angle = i_cur_angle + M_PI/2.0f;
101 break;
102 case 8:
103 angle = i_cur_angle - M_PI/2.0f;
104 break;
105 case 9:
106 angle = i_cur_angle + M_PI/2.0f;
107 distance /= 2;
108 break;
109 case 10:
110 angle = i_cur_angle - M_PI/2.0f;
111 distance /= 2;
112 break;
113 case 11:
114 angle = i_cur_angle + M_PI/4.0f;
115 distance /= 4;
116 break;
117 case 12:
118 angle = i_cur_angle - M_PI/4.0f;
119 distance /= 4;
120 break;
121 case 13:
122 angle = i_cur_angle + M_PI/2.0f;
123 distance /= 4;
124 break;
125 case 14:
126 angle = i_cur_angle - M_PI/2.0f;
127 distance /= 4;
128 break;
129 case 15:
130 angle = i_cur_angle + M_PI*3/4.0f;
131 distance /= 2;
132 break;
133 case 16:
134 angle = i_cur_angle - M_PI*3/4.0f;
135 distance /= 2;
136 break;
137 case 17:
138 angle = i_cur_angle + M_PI;
139 distance /= 2;
140 break;
142 temp_x = x + distance * cos(angle);
143 temp_y = y + distance * sin(angle);
144 MaNGOS::NormalizeMapCoord(temp_x);
145 MaNGOS::NormalizeMapCoord(temp_y);
146 if( owner.IsWithinLOS(temp_x,temp_y,z))
148 bool is_water_now = _map->IsInWater(x,y,z);
150 if(is_water_now && _map->IsInWater(temp_x,temp_y,z))
152 x = temp_x;
153 y = temp_y;
154 return true;
156 float new_z = _map->GetHeight(temp_x,temp_y,z,true);
158 if(new_z <= INVALID_HEIGHT)
159 continue;
161 bool is_water_next = _map->IsInWater(temp_x,temp_y,new_z);
163 if((is_water_now && !is_water_next && !is_land_ok) || (!is_water_now && is_water_next && !is_water_ok))
164 continue;
166 if( !(new_z - z) || distance / fabs(new_z - z) > 1.0f)
168 float new_z_left = _map->GetHeight(temp_x + 1.0f*cos(angle+M_PI/2),temp_y + 1.0f*sin(angle+M_PI/2),z,true);
169 float new_z_right = _map->GetHeight(temp_x + 1.0f*cos(angle-M_PI/2),temp_y + 1.0f*sin(angle-M_PI/2),z,true);
170 if(fabs(new_z_left - new_z) < 1.2f && fabs(new_z_right - new_z) < 1.2f)
172 x = temp_x;
173 y = temp_y;
174 z = new_z;
175 return true;
180 i_to_distance_from_caster = 0.0f;
181 i_nextCheckTime.Reset( urand(500,1000) );
182 return false;
185 template<class T>
186 bool
187 FleeingMovementGenerator<T>::_setMoveData(T &owner)
189 float cur_dist_xyz = owner.GetDistance(i_caster_x, i_caster_y, i_caster_z);
191 if(i_to_distance_from_caster > 0.0f)
193 if((i_last_distance_from_caster > i_to_distance_from_caster && cur_dist_xyz < i_to_distance_from_caster) ||
194 // if we reach lower distance
195 (i_last_distance_from_caster > i_to_distance_from_caster && cur_dist_xyz > i_last_distance_from_caster) ||
196 // if we can't be close
197 (i_last_distance_from_caster < i_to_distance_from_caster && cur_dist_xyz > i_to_distance_from_caster) ||
198 // if we reach bigger distance
199 (cur_dist_xyz > MAX_QUIET_DISTANCE) || // if we are too far
200 (i_last_distance_from_caster > MIN_QUIET_DISTANCE && cur_dist_xyz < MIN_QUIET_DISTANCE) )
201 // if we leave 'quiet zone'
203 // we are very far or too close, stopping
204 i_to_distance_from_caster = 0.0f;
205 i_nextCheckTime.Reset( urand(500,1000) );
206 return false;
208 else
210 // now we are running, continue
211 i_last_distance_from_caster = cur_dist_xyz;
212 return true;
216 float cur_dist;
217 float angle_to_caster;
219 Unit * fright = ObjectAccessor::GetUnit(owner, i_frightGUID);
221 if(fright)
223 cur_dist = fright->GetDistance(&owner);
224 if(cur_dist < cur_dist_xyz)
226 i_caster_x = fright->GetPositionX();
227 i_caster_y = fright->GetPositionY();
228 i_caster_z = fright->GetPositionZ();
229 angle_to_caster = fright->GetAngle(&owner);
231 else
233 cur_dist = cur_dist_xyz;
234 angle_to_caster = owner.GetAngle(i_caster_x, i_caster_y) + M_PI;
237 else
239 cur_dist = cur_dist_xyz;
240 angle_to_caster = owner.GetAngle(i_caster_x, i_caster_y) + M_PI;
243 // if we too close may use 'path-finding' else just stop
244 i_only_forward = cur_dist >= MIN_QUIET_DISTANCE/3;
246 //get angle and 'distance from caster' to run
247 float angle;
249 if(i_cur_angle == 0.0f && i_last_distance_from_caster == 0.0f) //just started, first time
251 angle = rand_norm()*(1.0f - cur_dist/MIN_QUIET_DISTANCE) * M_PI/3 + rand_norm()*M_PI*2/3;
252 i_to_distance_from_caster = MIN_QUIET_DISTANCE;
253 i_only_forward = true;
255 else if(cur_dist < MIN_QUIET_DISTANCE)
257 angle = M_PI/6 + rand_norm()*M_PI*2/3;
258 i_to_distance_from_caster = cur_dist*2/3 + rand_norm()*(MIN_QUIET_DISTANCE - cur_dist*2/3);
260 else if(cur_dist > MAX_QUIET_DISTANCE)
262 angle = rand_norm()*M_PI/3 + M_PI*2/3;
263 i_to_distance_from_caster = MIN_QUIET_DISTANCE + 2.5f + rand_norm()*(MAX_QUIET_DISTANCE - MIN_QUIET_DISTANCE - 2.5f);
265 else
267 angle = rand_norm()*M_PI;
268 i_to_distance_from_caster = MIN_QUIET_DISTANCE + 2.5f + rand_norm()*(MAX_QUIET_DISTANCE - MIN_QUIET_DISTANCE - 2.5f);
271 int8 sign = rand_norm() > 0.5f ? 1 : -1;
272 i_cur_angle = sign*angle + angle_to_caster;
274 // current distance
275 i_last_distance_from_caster = cur_dist;
277 return true;
280 template<class T>
281 void
282 FleeingMovementGenerator<T>::Initialize(T &owner)
284 if(!&owner)
285 return;
287 _Init(owner);
289 if(Unit * fright = ObjectAccessor::GetUnit(owner, i_frightGUID))
291 i_caster_x = fright->GetPositionX();
292 i_caster_y = fright->GetPositionY();
293 i_caster_z = fright->GetPositionZ();
295 else
297 i_caster_x = owner.GetPositionX();
298 i_caster_y = owner.GetPositionY();
299 i_caster_z = owner.GetPositionZ();
302 i_only_forward = true;
303 i_cur_angle = 0.0f;
304 i_last_distance_from_caster = 0.0f;
305 i_to_distance_from_caster = 0.0f;
306 _setTargetLocation(owner);
309 template<>
310 void
311 FleeingMovementGenerator<Creature>::_Init(Creature &owner)
313 if(!&owner)
314 return;
316 owner.RemoveMonsterMoveFlag(MONSTER_MOVE_WALK);
317 owner.SetUInt64Value(UNIT_FIELD_TARGET, 0);
318 is_water_ok = owner.canSwim();
319 is_land_ok = owner.canWalk();
322 template<>
323 void
324 FleeingMovementGenerator<Player>::_Init(Player &)
326 is_water_ok = true;
327 is_land_ok = true;
330 template<>
331 void FleeingMovementGenerator<Player>::Finalize(Player &owner)
333 owner.clearUnitState(UNIT_STAT_FLEEING);
336 template<>
337 void FleeingMovementGenerator<Creature>::Finalize(Creature &owner)
339 owner.AddMonsterMoveFlag(MONSTER_MOVE_WALK);
340 owner.clearUnitState(UNIT_STAT_FLEEING);
343 template<class T>
344 void
345 FleeingMovementGenerator<T>::Reset(T &owner)
347 Initialize(owner);
350 template<class T>
351 bool
352 FleeingMovementGenerator<T>::Update(T &owner, const uint32 & time_diff)
354 if( !&owner || !owner.isAlive() )
355 return false;
356 if( owner.hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNNED) )
357 return true;
359 Traveller<T> traveller(owner);
361 i_nextCheckTime.Update(time_diff);
363 if( (owner.IsStopped() && !i_destinationHolder.HasArrived()) || !i_destinationHolder.HasDestination() )
365 _setTargetLocation(owner);
366 return true;
369 if (i_destinationHolder.UpdateTraveller(traveller, time_diff, false))
371 i_destinationHolder.ResetUpdate(50);
372 if(i_nextCheckTime.Passed() && i_destinationHolder.HasArrived())
374 _setTargetLocation(owner);
375 return true;
378 return true;
381 template void FleeingMovementGenerator<Player>::Initialize(Player &);
382 template void FleeingMovementGenerator<Creature>::Initialize(Creature &);
383 template bool FleeingMovementGenerator<Player>::_setMoveData(Player &);
384 template bool FleeingMovementGenerator<Creature>::_setMoveData(Creature &);
385 template bool FleeingMovementGenerator<Player>::_getPoint(Player &, float &, float &, float &);
386 template bool FleeingMovementGenerator<Creature>::_getPoint(Creature &, float &, float &, float &);
387 template void FleeingMovementGenerator<Player>::_setTargetLocation(Player &);
388 template void FleeingMovementGenerator<Creature>::_setTargetLocation(Creature &);
389 template void FleeingMovementGenerator<Player>::Reset(Player &);
390 template void FleeingMovementGenerator<Creature>::Reset(Creature &);
391 template bool FleeingMovementGenerator<Player>::Update(Player &, const uint32 &);
392 template bool FleeingMovementGenerator<Creature>::Update(Creature &, const uint32 &);
394 void TimedFleeingMovementGenerator::Finalize(Unit &owner)
396 owner.clearUnitState(UNIT_STAT_FLEEING);
397 if (Unit* victim = owner.getVictim())
399 if (owner.isAlive())
401 owner.AttackStop(true);
402 ((Creature*)&owner)->AI()->AttackStart(victim);
407 bool TimedFleeingMovementGenerator::Update(Unit & owner, const uint32 & time_diff)
409 if( !owner.isAlive() )
410 return false;
412 if( owner.hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNNED) )
413 return true;
415 i_totalFleeTime.Update(time_diff);
416 if (i_totalFleeTime.Passed())
417 return false;
419 // This calls grant-parent Update method hiden by FleeingMovementGenerator::Update(Creature &, const uint32 &) version
420 // This is done instead of casting Unit& to Creature& and call parent method, then we can use Unit directly
421 return MovementGeneratorMedium< Creature, FleeingMovementGenerator<Creature> >::Update(owner, time_diff);