/* * Copyright (C) 2005-2008 MaNGOS * * Copyright (C) 2008 Trinity * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "Creature.h" #include "MapManager.h" #include "FleeingMovementGenerator.h" #include "DestinationHolderImp.h" #include "ObjectAccessor.h" #define MIN_QUIET_DISTANCE 28.0f #define MAX_QUIET_DISTANCE 43.0f template void FleeingMovementGenerator::_setTargetLocation(T &owner) { if( !&owner ) return; if( owner.hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNNED) ) return; if(!_setMoveData(owner)) return; float x, y, z; if(!_getPoint(owner, x, y, z)) return; owner.addUnitState(UNIT_STAT_FLEEING); Traveller traveller(owner); i_destinationHolder.SetDestination(traveller, x, y, z); } template bool FleeingMovementGenerator::_getPoint(T &owner, float &x, float &y, float &z) { if(!&owner) return false; x = owner.GetPositionX(); y = owner.GetPositionY(); z = owner.GetPositionZ(); float temp_x, temp_y, angle; const Map * _map = MapManager::Instance().GetBaseMap(owner.GetMapId()); //primitive path-finding for(uint8 i = 0; i < 18; i++) { if(i_only_forward && i > 2) break; float distance = 5.0f; switch(i) { case 0: angle = i_cur_angle; break; case 1: angle = i_cur_angle; distance /= 2; break; case 2: angle = i_cur_angle; distance /= 4; break; case 3: angle = i_cur_angle + M_PI/4.0f; break; case 4: angle = i_cur_angle - M_PI/4.0f; break; case 5: angle = i_cur_angle + M_PI/4.0f; distance /= 2; break; case 6: angle = i_cur_angle - M_PI/4.0f; distance /= 2; break; case 7: angle = i_cur_angle + M_PI/2.0f; break; case 8: angle = i_cur_angle - M_PI/2.0f; break; case 9: angle = i_cur_angle + M_PI/2.0f; distance /= 2; break; case 10: angle = i_cur_angle - M_PI/2.0f; distance /= 2; break; case 11: angle = i_cur_angle + M_PI/4.0f; distance /= 4; break; case 12: angle = i_cur_angle - M_PI/4.0f; distance /= 4; break; case 13: angle = i_cur_angle + M_PI/2.0f; distance /= 4; break; case 14: angle = i_cur_angle - M_PI/2.0f; distance /= 4; break; case 15: angle = i_cur_angle + M_PI*3/4.0f; distance /= 2; break; case 16: angle = i_cur_angle - M_PI*3/4.0f; distance /= 2; break; case 17: angle = i_cur_angle + M_PI; distance /= 2; break; } temp_x = x + distance * cos(angle); temp_y = y + distance * sin(angle); Trinity::NormalizeMapCoord(temp_x); Trinity::NormalizeMapCoord(temp_y); if( owner.IsWithinLOS(temp_x,temp_y,z)) { bool is_water_now = _map->IsInWater(x,y,z); if(is_water_now && _map->IsInWater(temp_x,temp_y,z)) { x = temp_x; y = temp_y; return true; } float new_z = _map->GetHeight(temp_x,temp_y,z,true); if(new_z <= INVALID_HEIGHT) continue; bool is_water_next = _map->IsInWater(temp_x,temp_y,new_z); if((is_water_now && !is_water_next && !is_land_ok) || (!is_water_now && is_water_next && !is_water_ok)) continue; if( !(new_z - z) || distance / fabs(new_z - z) > 1.0f) { 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); 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); if(fabs(new_z_left - new_z) < 1.2f && fabs(new_z_right - new_z) < 1.2f) { x = temp_x; y = temp_y; z = new_z; return true; } } } } i_to_distance_from_caster = 0.0f; i_nextCheckTime.Reset( urand(500,1000) ); return false; } template bool FleeingMovementGenerator::_setMoveData(T &owner) { float cur_dist_xyz = owner.GetDistance(i_caster_x, i_caster_y, i_caster_z); if(i_to_distance_from_caster > 0.0f) { if((i_last_distance_from_caster > i_to_distance_from_caster && cur_dist_xyz < i_to_distance_from_caster) || // if we reach lower distance (i_last_distance_from_caster > i_to_distance_from_caster && cur_dist_xyz > i_last_distance_from_caster) || // if we can't be close (i_last_distance_from_caster < i_to_distance_from_caster && cur_dist_xyz > i_to_distance_from_caster) || // if we reach bigger distance (cur_dist_xyz > MAX_QUIET_DISTANCE) || // if we are too far (i_last_distance_from_caster > MIN_QUIET_DISTANCE && cur_dist_xyz < MIN_QUIET_DISTANCE) ) // if we leave 'quiet zone' { // we are very far or too close, stopping i_to_distance_from_caster = 0.0f; i_nextCheckTime.Reset( urand(500,1000) ); return false; } else { // now we are running, continue i_last_distance_from_caster = cur_dist_xyz; return true; } } float cur_dist; float angle_to_caster; Unit * fright = ObjectAccessor::GetUnit(owner, i_frightGUID); if(fright) { cur_dist = fright->GetDistance(&owner); if(cur_dist < cur_dist_xyz) { i_caster_x = fright->GetPositionX(); i_caster_y = fright->GetPositionY(); i_caster_z = fright->GetPositionZ(); angle_to_caster = fright->GetAngle(&owner); } else { cur_dist = cur_dist_xyz; angle_to_caster = owner.GetAngle(i_caster_x, i_caster_y) + M_PI; } } else { cur_dist = cur_dist_xyz; angle_to_caster = owner.GetAngle(i_caster_x, i_caster_y) + M_PI; } // if we too close may use 'path-finding' else just stop i_only_forward = cur_dist >= MIN_QUIET_DISTANCE/3; //get angle and 'distance from caster' to run float angle; if(i_cur_angle == 0.0f && i_last_distance_from_caster == 0.0f) //just started, first time { angle = rand_norm()*(1.0f - cur_dist/MIN_QUIET_DISTANCE) * M_PI/3 + rand_norm()*M_PI*2/3; i_to_distance_from_caster = MIN_QUIET_DISTANCE; i_only_forward = true; } else if(cur_dist < MIN_QUIET_DISTANCE) { angle = M_PI/6 + rand_norm()*M_PI*2/3; i_to_distance_from_caster = cur_dist*2/3 + rand_norm()*(MIN_QUIET_DISTANCE - cur_dist*2/3); } else if(cur_dist > MAX_QUIET_DISTANCE) { angle = rand_norm()*M_PI/3 + M_PI*2/3; i_to_distance_from_caster = MIN_QUIET_DISTANCE + 2.5f + rand_norm()*(MAX_QUIET_DISTANCE - MIN_QUIET_DISTANCE - 2.5f); } else { angle = rand_norm()*M_PI; i_to_distance_from_caster = MIN_QUIET_DISTANCE + 2.5f + rand_norm()*(MAX_QUIET_DISTANCE - MIN_QUIET_DISTANCE - 2.5f); } int8 sign = rand_norm() > 0.5f ? 1 : -1; i_cur_angle = sign*angle + angle_to_caster; // current distance i_last_distance_from_caster = cur_dist; return true; } template void FleeingMovementGenerator::Initialize(T &owner) { if(!&owner) return; Unit * fright = ObjectAccessor::GetUnit(owner, i_frightGUID); if(!fright) return; _Init(owner); owner.RemoveUnitMovementFlag(MOVEMENTFLAG_WALK_MODE); i_caster_x = fright->GetPositionX(); i_caster_y = fright->GetPositionY(); i_caster_z = fright->GetPositionZ(); i_only_forward = true; i_cur_angle = 0.0f; i_last_distance_from_caster = 0.0f; i_to_distance_from_caster = 0.0f; _setTargetLocation(owner); } template<> void FleeingMovementGenerator::_Init(Creature &owner) { if(!&owner) return; owner.SetUInt64Value(UNIT_FIELD_TARGET, 0); is_water_ok = owner.canSwim(); is_land_ok = owner.canWalk(); } template<> void FleeingMovementGenerator::_Init(Player &) { is_water_ok = true; is_land_ok = true; } template void FleeingMovementGenerator::Finalize(T &owner) { owner.clearUnitState(UNIT_STAT_FLEEING); } template void FleeingMovementGenerator::Reset(T &owner) { Initialize(owner); } template bool FleeingMovementGenerator::Update(T &owner, const uint32 & time_diff) { if( !&owner || !owner.isAlive() ) return false; if( owner.hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNNED) ) return true; Traveller traveller(owner); i_nextCheckTime.Update(time_diff); if( (owner.IsStopped() && !i_destinationHolder.HasArrived()) || !i_destinationHolder.HasDestination() ) { _setTargetLocation(owner); return true; } if (i_destinationHolder.UpdateTraveller(traveller, time_diff, false)) { i_destinationHolder.ResetUpdate(50); if(i_nextCheckTime.Passed() && i_destinationHolder.HasArrived()) { _setTargetLocation(owner); return true; } } return true; } template void FleeingMovementGenerator::Initialize(Player &); template void FleeingMovementGenerator::Initialize(Creature &); template bool FleeingMovementGenerator::_setMoveData(Player &); template bool FleeingMovementGenerator::_setMoveData(Creature &); template bool FleeingMovementGenerator::_getPoint(Player &, float &, float &, float &); template bool FleeingMovementGenerator::_getPoint(Creature &, float &, float &, float &); template void FleeingMovementGenerator::_setTargetLocation(Player &); template void FleeingMovementGenerator::_setTargetLocation(Creature &); template void FleeingMovementGenerator::Finalize(Player &); template void FleeingMovementGenerator::Finalize(Creature &); template void FleeingMovementGenerator::Reset(Player &); template void FleeingMovementGenerator::Reset(Creature &); template bool FleeingMovementGenerator::Update(Player &, const uint32 &); template bool FleeingMovementGenerator::Update(Creature &, const uint32 &);