/* * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information * * 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 General Public License along * with this program. If not, see . */ #include "FleeingMovementGenerator.h" #include "Creature.h" #include "CreatureAI.h" #include "MapMgr.h" #include "MoveSplineInit.h" #include "ObjectAccessor.h" #include "Player.h" #include "VMapFactory.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_STATE_NOT_MOVE) || owner->IsMovementPreventedByCasting()) { return; } if (!_setMoveData(owner)) return; float x, y, z; if (!_getPoint(owner, x, y, z)) { i_nextCheckTime.Reset(100); return; } owner->AddUnitState(UNIT_STATE_FLEEING_MOVE); Movement::MoveSplineInit init(owner); init.MoveTo(x, y, z, true); init.SetWalk(false); init.Launch(); } template bool FleeingMovementGenerator::_getPoint(T* owner, float& x, float& y, float& z) { if (!owner) return false; const Map* _map = owner->GetMap(); x = owner->GetPositionX(); y = owner->GetPositionY(); z = owner->GetPositionZ(); float temp_x, temp_y, angle; // 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 + static_cast(M_PI / 4); break; case 4: angle = i_cur_angle - static_cast(M_PI / 4); break; case 5: angle = i_cur_angle + static_cast(M_PI / 4); distance /= 2; break; case 6: angle = i_cur_angle - static_cast(M_PI / 4); distance /= 2; break; case 7: angle = i_cur_angle + static_cast(M_PI / 2); break; case 8: angle = i_cur_angle - static_cast(M_PI / 2); break; case 9: angle = i_cur_angle + static_cast(M_PI / 2); distance /= 2; break; case 10: angle = i_cur_angle - static_cast(M_PI / 2); distance /= 2; break; case 11: angle = i_cur_angle + static_cast(M_PI / 4); distance /= 4; break; case 12: angle = i_cur_angle - static_cast(M_PI / 4); distance /= 4; break; case 13: angle = i_cur_angle + static_cast(M_PI / 2); distance /= 4; break; case 14: angle = i_cur_angle - static_cast(M_PI / 2); distance /= 4; break; case 15: angle = i_cur_angle + static_cast(3 * M_PI / 4); distance /= 2; break; case 16: angle = i_cur_angle - static_cast(3 * M_PI / 4); distance /= 2; break; case 17: angle = i_cur_angle + static_cast(M_PI); distance /= 2; break; default: angle = 0.0f; distance = 0.0f; break; } temp_x = x + distance * cos(angle); temp_y = y + distance * std::sin(angle); float temp_z = z; if (!_map->CanReachPositionAndGetValidCoords(owner, temp_x, temp_y, temp_z, true, true)) { break; } if (!(temp_z - z) || distance / std::fabs(temp_z - z) > 1.0f) { float temp_z_left = _map->GetHeight(owner->GetPhaseMask(), temp_x + 1.0f * cos(angle + static_cast(M_PI / 2)), temp_y + 1.0f * std::sin(angle + static_cast(M_PI / 2)), z, true); float temp_z_right = _map->GetHeight(owner->GetPhaseMask(), temp_x + 1.0f * cos(angle - static_cast(M_PI / 2)), temp_y + 1.0f * std::sin(angle - static_cast(M_PI / 2)), z, true); if (std::fabs(temp_z_left - temp_z) < 1.2f && std::fabs(temp_z_right - temp_z) < 1.2f) { // use new values x = temp_x; y = temp_y; z = temp_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; if (Unit* fright = ObjectAccessor::GetUnit(*owner, i_frightGUID)) { 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) + static_cast(M_PI); } } else { cur_dist = cur_dist_xyz; angle_to_caster = owner->GetAngle(i_caster_x, i_caster_y) + static_cast(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 = (float)rand_norm() * (1.0f - cur_dist / MIN_QUIET_DISTANCE) * static_cast(M_PI / 3) + (float)rand_norm() * static_cast(M_PI * 2 / 3); i_to_distance_from_caster = MIN_QUIET_DISTANCE; i_only_forward = true; } else if (cur_dist < MIN_QUIET_DISTANCE) { angle = static_cast(M_PI / 6) + (float)rand_norm() * static_cast(M_PI * 2 / 3); i_to_distance_from_caster = cur_dist * 2 / 3 + (float)rand_norm() * (MIN_QUIET_DISTANCE - cur_dist * 2 / 3); } else if (cur_dist > MAX_QUIET_DISTANCE) { angle = (float)rand_norm() * static_cast(M_PI / 3) + static_cast(M_PI * 2 / 3); i_to_distance_from_caster = MIN_QUIET_DISTANCE + 2.5f + (float)rand_norm() * (MAX_QUIET_DISTANCE - MIN_QUIET_DISTANCE - 2.5f); } else { angle = (float)rand_norm() * static_cast(M_PI); i_to_distance_from_caster = MIN_QUIET_DISTANCE + 2.5f + (float)rand_norm() * (MAX_QUIET_DISTANCE - MIN_QUIET_DISTANCE - 2.5f); } int8 sign = (float)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::DoInitialize(T* owner) { if (!owner) return; owner->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_FLEEING); owner->AddUnitState(UNIT_STATE_FLEEING | UNIT_STATE_FLEEING_MOVE); _Init(owner); if (Unit* fright = ObjectAccessor::GetUnit(*owner, i_frightGUID)) { i_caster_x = fright->GetPositionX(); i_caster_y = fright->GetPositionY(); i_caster_z = fright->GetPositionZ(); } else { i_caster_x = owner->GetPositionX(); i_caster_y = owner->GetPositionY(); i_caster_z = owner->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->SetTargetGuid(ObjectGuid()); is_water_ok = owner->CanEnterWater(); is_land_ok = owner->CanWalk(); } template<> void FleeingMovementGenerator::_Init(Player* ) { is_water_ok = true; is_land_ok = true; } template<> void FleeingMovementGenerator::DoFinalize(Player* owner) { owner->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_FLEEING); owner->ClearUnitState(UNIT_STATE_FLEEING | UNIT_STATE_FLEEING_MOVE); } template<> void FleeingMovementGenerator::DoFinalize(Creature* owner) { owner->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_FLEEING); owner->ClearUnitState(UNIT_STATE_FLEEING | UNIT_STATE_FLEEING_MOVE); if (owner->GetVictim()) owner->SetTarget(owner->GetVictim()->GetGUID()); } template void FleeingMovementGenerator::DoReset(T* owner) { DoInitialize(owner); } template bool FleeingMovementGenerator::DoUpdate(T* owner, uint32 time_diff) { if (!owner || !owner->IsAlive()) return false; if (owner->HasUnitState(UNIT_STATE_NOT_MOVE) || owner->IsMovementPreventedByCasting()) { owner->StopMoving(); return true; } i_nextCheckTime.Update(time_diff); if (i_nextCheckTime.Passed() && owner->movespline->Finalized()) _setTargetLocation(owner); return true; } template void FleeingMovementGenerator::DoInitialize(Player*); template void FleeingMovementGenerator::DoInitialize(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::DoReset(Player*); template void FleeingMovementGenerator::DoReset(Creature*); template bool FleeingMovementGenerator::DoUpdate(Player*, uint32); template bool FleeingMovementGenerator::DoUpdate(Creature*, uint32); void TimedFleeingMovementGenerator::Finalize(Unit* owner) { owner->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_FLEEING); owner->ClearUnitState(UNIT_STATE_FLEEING | UNIT_STATE_FLEEING_MOVE); if (owner->GetVictim()) owner->SetTarget(owner->GetVictim()->GetGUID()); } bool TimedFleeingMovementGenerator::Update(Unit* owner, uint32 time_diff) { if (!owner->IsAlive()) return false; if (owner->HasUnitState(UNIT_STATE_NOT_MOVE) || owner->IsMovementPreventedByCasting()) { owner->StopMoving(); return true; } i_totalFleeTime.Update(time_diff); if (i_totalFleeTime.Passed()) return false; // This calls grant-parent Update method hiden by FleeingMovementGenerator::Update(Creature &, uint32) version // This is done instead of casting Unit& to Creature& and call parent method, then we can use Unit directly return MovementGeneratorMedium< Creature, FleeingMovementGenerator >::Update(owner, time_diff); }