/*
 * 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 <http://www.gnu.org/licenses/>.
 */

#include "Creature.h"
#include "BattlegroundMgr.h"
#include "CellImpl.h"
#include "Common.h"
#include "CreatureAI.h"
#include "CreatureAISelector.h"
#include "CreatureGroups.h"
#include "DatabaseEnv.h"
#include "Formulas.h"
#include "GameEventMgr.h"
#include "GameTime.h"
#include "GridNotifiers.h"
#include "Group.h"
#include "GroupMgr.h"
#include "Log.h"
#include "LootMgr.h"
#include "ObjectMgr.h"
#include "Opcodes.h"
#include "Pet.h"
#include "Player.h"
#include "PoolMgr.h"
#include "ScriptMgr.h"
#include "ScriptedGossip.h"
#include "SpellAuraEffects.h"
#include "SpellMgr.h"
#include "TemporarySummon.h"
#include "Transport.h"
#include "Util.h"
#include "Vehicle.h"
#include "WaypointMovementGenerator.h"
#include "World.h"
#include "WorldPacket.h"

/// @todo: this import is not necessary for compilation and marked as unused by the IDE
//  however, for some reasons removing it would cause a damn linking issue
//  there is probably some underlying problem with imports which should properly addressed
//  see: https://github.com/azerothcore/azerothcore-wotlk/issues/9766
#include "GridNotifiersImpl.h"

CreatureMovementData::CreatureMovementData() : Ground(CreatureGroundMovementType::Run), Flight(CreatureFlightMovementType::None),
                                               Swim(true), Rooted(false), Chase(CreatureChaseMovementType::Run),
                                               Random(CreatureRandomMovementType::Walk), InteractionPauseTimer(sWorld->getIntConfig(CONFIG_CREATURE_STOP_FOR_PLAYER)) {}

std::string CreatureMovementData::ToString() const
{
    constexpr std::array<char const*, 3> GroundStates = {"None", "Run", "Hover"};
    constexpr std::array<char const*, 3> FlightStates = {"None", "DisableGravity", "CanFly"};
    constexpr std::array<char const*, 3> ChaseStates  = {"Run", "CanWalk", "AlwaysWalk"};
    constexpr std::array<char const*, 3> RandomStates = {"Walk", "CanRun", "AlwaysRun"};

    std::ostringstream str;
    str << std::boolalpha
        << "Ground: " << GroundStates[AsUnderlyingType(Ground)]
        << ", Swim: " << Swim
        << ", Flight: " << FlightStates[AsUnderlyingType(Flight)]
        << ", Chase: " << ChaseStates[AsUnderlyingType(Chase)]
        << ", Random: " << RandomStates[AsUnderlyingType(Random)];
    if (Rooted)
        str << ", Rooted";
    str << ", InteractionPauseTimer: " << InteractionPauseTimer;

    return str.str();
}

TrainerSpell const* TrainerSpellData::Find(uint32 spell_id) const
{
    TrainerSpellMap::const_iterator itr = spellList.find(spell_id);
    if (itr != spellList.end())
        return &itr->second;

    return nullptr;
}

bool VendorItemData::RemoveItem(uint32 item_id)
{
    bool found = false;
    for (VendorItemList::iterator i = m_items.begin(); i != m_items.end();)
    {
        if ((*i)->item == item_id)
        {
            i = m_items.erase(i++);
            found = true;
        }
        else
            ++i;
    }
    return found;
}

VendorItemCount::VendorItemCount(uint32 _item, uint32 _count)
    : itemId(_item), count(_count), lastIncrementTime(GameTime::GetGameTime().count()) { }

VendorItem const* VendorItemData::FindItemCostPair(uint32 item_id, uint32 extendedCost) const
{
    for (VendorItemList::const_iterator i = m_items.begin(); i != m_items.end(); ++i)
        if ((*i)->item == item_id && (*i)->ExtendedCost == extendedCost)
            return *i;
    return nullptr;
}

CreatureModel const CreatureModel::DefaultInvisibleModel(11686, 1.0f, 1.0f);
CreatureModel const CreatureModel::DefaultVisibleModel(17519, 1.0f, 1.0f);

CreatureModel const* CreatureTemplate::GetModelByIdx(uint32 idx) const
{
    return idx < Models.size() ? &Models[idx] : nullptr;
}

CreatureModel const* CreatureTemplate::GetRandomValidModel() const
{
    if (!Models.size())
        return nullptr;

    // If only one element, ignore the Probability (even if 0)
    if (Models.size() == 1)
        return &Models[0];

    auto selectedItr = Acore::Containers::SelectRandomWeightedContainerElement(Models, [](CreatureModel const& model)
        {
            return model.Probability;
        });

    return &(*selectedItr);
}

CreatureModel const* CreatureTemplate::GetFirstValidModel() const
{
    for (CreatureModel const& model : Models)
        if (model.CreatureDisplayID)
            return &model;

    return nullptr;
}

CreatureModel const* CreatureTemplate::GetModelWithDisplayId(uint32 displayId) const
{
    for (CreatureModel const& model : Models)
        if (displayId == model.CreatureDisplayID)
            return &model;

    return nullptr;
}

CreatureModel const* CreatureTemplate::GetFirstInvisibleModel() const
{
    for (CreatureModel const& model : Models)
        if (CreatureModelInfo const* modelInfo = sObjectMgr->GetCreatureModelInfo(model.CreatureDisplayID))
            if (modelInfo && modelInfo->is_trigger)
                return &model;

    return &CreatureModel::DefaultInvisibleModel;
}

CreatureModel const* CreatureTemplate::GetFirstVisibleModel() const
{
    for (CreatureModel const& model : Models)
        if (CreatureModelInfo const* modelInfo = sObjectMgr->GetCreatureModelInfo(model.CreatureDisplayID))
            if (modelInfo && !modelInfo->is_trigger)
                return &model;

    return &CreatureModel::DefaultVisibleModel;
}

void CreatureTemplate::InitializeQueryData()
{
    queryData.Initialize(SMSG_CREATURE_QUERY_RESPONSE, 1);

    queryData << uint32(Entry);                                   // creature entry
    queryData << Name;
    queryData << uint8(0) << uint8(0) << uint8(0);                // name2, name3, name4, always empty
    queryData << SubName;
    queryData << IconName;                                        // "Directions" for guard, string for Icons 2.3.0
    queryData << uint32(type_flags);                              // flags
    queryData << uint32(type);                                    // CreatureType.dbc
    queryData << uint32(family);                                  // CreatureFamily.dbc
    queryData << uint32(rank);                                    // Creature Rank (elite, boss, etc)
    queryData << uint32(KillCredit[0]);                           // new in 3.1, kill credit
    queryData << uint32(KillCredit[1]);                           // new in 3.1, kill credit
    if (GetModelByIdx(0))
        queryData << uint32(GetModelByIdx(0)->CreatureDisplayID); // Modelid1
    else
        queryData << uint32(0);                                   // Modelid1
    if (GetModelByIdx(1))
        queryData << uint32(GetModelByIdx(1)->CreatureDisplayID); // Modelid2
    else
        queryData << uint32(0);                                   // Modelid2
    if (GetModelByIdx(2))
        queryData << uint32(GetModelByIdx(2)->CreatureDisplayID); // Modelid3
    else
        queryData << uint32(0);                                   // Modelid3
    if (GetModelByIdx(3))
        queryData << uint32(GetModelByIdx(3)->CreatureDisplayID); // Modelid4
    else
        queryData << uint32(0);                                   // Modelid4
    queryData << float(ModHealth);                                // dmg/hp modifier
    queryData << float(ModMana);                                  // dmg/mana modifier
    queryData << uint8(RacialLeader);
    queryData << uint32(movementId);                              // CreatureMovementInfo.dbc
}

bool AssistDelayEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/)
{
    if (Unit* victim = ObjectAccessor::GetUnit(*m_owner, m_victim))
    {
        while (!m_assistants.empty())
        {
            Creature* assistant = ObjectAccessor::GetCreature(*m_owner, *m_assistants.begin());
            m_assistants.pop_front();

            if (assistant && assistant->CanAssistTo(m_owner, victim))
            {
                assistant->SetNoCallAssistance(true);
                assistant->CombatStart(victim);
                if (assistant->IsAIEnabled)
                {
                    assistant->AI()->AttackStart(victim);

                    // When nearby mobs aggro from another mob's initial call for assistance
                    // their leash timers become linked and attacking one will keep the rest from evading.
                    if (assistant->GetVictim())
                        assistant->SetLastLeashExtensionTimePtr(m_owner->GetLastLeashExtensionTimePtr());
                }
            }
        }
    }
    return true;
}

CreatureBaseStats const* CreatureBaseStats::GetBaseStats(uint8 level, uint8 unitClass)
{
    return sObjectMgr->GetCreatureBaseStats(level, unitClass);
}

bool ForcedDespawnDelayEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/)
{
    m_owner.DespawnOrUnsummon(0s, m_respawnTimer);    // since we are here, we are not TempSummon as object type cannot change during runtime
    return true;
}

bool TemporaryThreatModifierEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/)
{
    if (Unit* victim = ObjectAccessor::GetUnit(m_owner, m_threatVictimGUID))
    {
        if (m_owner.IsInCombatWith(victim))
        {
            m_owner.GetThreatMgr().ModifyThreatByPercent(victim, -100); // Reset threat to zero.
            m_owner.GetThreatMgr().AddThreat(victim, m_threatValue);  // Set to the previous value it had, first before modification.
        }
    }

    return true;
}

Creature::Creature(bool isWorldObject): Unit(isWorldObject), MovableMapObject(), m_groupLootTimer(0), lootingGroupLowGUID(0), m_lootRecipientGroup(0),
    m_corpseRemoveTime(0), m_respawnTime(0), m_respawnDelay(300), m_corpseDelay(60), m_wanderDistance(0.0f), m_boundaryCheckTime(2500),
    m_transportCheckTimer(1000), lootPickPocketRestoreTime(0), m_combatPulseTime(0), m_combatPulseDelay(0), m_reactState(REACT_AGGRESSIVE), m_defaultMovementType(IDLE_MOTION_TYPE),
    m_spawnId(0), m_equipmentId(0), m_originalEquipmentId(0), m_AlreadyCallAssistance(false),
    m_AlreadySearchedAssistance(false), m_regenHealth(true), m_regenPower(true), m_AI_locked(false), m_meleeDamageSchoolMask(SPELL_SCHOOL_MASK_NORMAL), m_originalEntry(0), m_moveInLineOfSightDisabled(false), m_moveInLineOfSightStrictlyDisabled(false),
    m_homePosition(), m_transportHomePosition(), m_creatureInfo(nullptr), m_creatureData(nullptr), m_detectionDistance(20.0f), m_waypointID(0), m_path_id(0), m_formation(nullptr), m_lastLeashExtensionTime(nullptr), m_cannotReachTimer(0),
    _isMissingSwimmingFlagOutOfCombat(false), m_assistanceTimer(0), _playerDamageReq(0), _damagedByPlayer(false), _isCombatMovementAllowed(true)
{
    m_regenTimer = CREATURE_REGEN_INTERVAL;
    m_valuesCount = UNIT_END;

    for (uint8 i = 0; i < MAX_CREATURE_SPELLS; ++i)
        m_spells[i] = 0;

    for (uint8 i = SPELL_SCHOOL_NORMAL; i < MAX_SPELL_SCHOOL; ++i)
        m_ProhibitSchoolTime[i] = 0;

    m_CreatureSpellCooldowns.clear();
    DisableReputationGain = false;

    m_SightDistance = sWorld->getFloatConfig(CONFIG_SIGHT_MONSTER);
    m_CombatDistance = 0.0f;

    ResetLootMode(); // restore default loot mode
    TriggerJustRespawned = false;
    m_isTempWorldObject = false;
    _focusSpell = nullptr;

    m_respawnedTime = time_t(0);
}

Creature::~Creature()
{
    m_vendorItemCounts.clear();

    delete i_AI;
    i_AI = nullptr;
}

void Creature::AddToWorld()
{
    ///- Register the creature for guid lookup
    if (!IsInWorld())
    {
        // pussywizard: motion master needs to be initialized before OnCreatureCreate, which may set death state to JUST_DIED, to prevent crash
        // it's also initialized in AIM_Initialize(), few lines below, but it's not a problem
        Motion_Initialize();

        GetMap()->GetObjectsStore().Insert<Creature>(GetGUID(), this);
        if (m_spawnId)
        {
            GetMap()->GetCreatureBySpawnIdStore().insert(std::make_pair(m_spawnId, this));
        }
        Unit::AddToWorld();

        SearchFormation();

        AIM_Initialize();

        if (IsVehicle())
        {
            GetVehicleKit()->Install();
        }

        if (GetZoneScript())
        {
            GetZoneScript()->OnCreatureCreate(this);
        }

        loot.sourceWorldObjectGUID = GetGUID();

        sScriptMgr->OnCreatureAddWorld(this);
    }
}

void Creature::RemoveFromWorld()
{
    if (IsInWorld())
    {
        sScriptMgr->OnCreatureRemoveWorld(this);

        if (GetZoneScript())
            GetZoneScript()->OnCreatureRemove(this);

        if (m_formation)
            sFormationMgr->RemoveCreatureFromGroup(m_formation, this);

        if (Transport* transport = GetTransport())
            transport->RemovePassenger(this, true);

        Unit::RemoveFromWorld();

        if (m_spawnId)
            Acore::Containers::MultimapErasePair(GetMap()->GetCreatureBySpawnIdStore(), m_spawnId, this);

        GetMap()->GetObjectsStore().Remove<Creature>(GetGUID());
    }
}

void Creature::DisappearAndDie()
{
    DestroyForNearbyPlayers();
    //SetVisibility(VISIBILITY_OFF);
    //ObjectAccessor::UpdateObjectVisibility(this);
    if (IsAlive())
        setDeathState(DeathState::JustDied, true);
    RemoveCorpse(false, true);
}

void Creature::SearchFormation()
{
    if (IsSummon())
    {
        return;
    }

    ObjectGuid::LowType spawnId = GetSpawnId();
    if (!spawnId)
    {
        return;
    }

    CreatureGroupInfoType::const_iterator frmdata = sFormationMgr->CreatureGroupMap.find(spawnId);
    if (frmdata != sFormationMgr->CreatureGroupMap.end())
    {
        sFormationMgr->AddCreatureToGroup(frmdata->second.leaderGUID, this);
    }
}

void Creature::RemoveCorpse(bool setSpawnTime, bool skipVisibility)
{
    if (getDeathState() != DeathState::Corpse)
        return;

    m_corpseRemoveTime = GameTime::GetGameTime().count();
    setDeathState(DeathState::Dead);
    RemoveAllAuras();
    if (!skipVisibility) // pussywizard
        DestroyForNearbyPlayers(); // pussywizard: previous UpdateObjectVisibility()
    loot.clear();
    uint32 respawnDelay = m_respawnDelay;
    if (IsAIEnabled)
        AI()->CorpseRemoved(respawnDelay);

    // Should get removed later, just keep "compatibility" with scripts
    if (setSpawnTime)
    {
        m_respawnTime = GameTime::GetGameTime().count() + respawnDelay;
        //SaveRespawnTime();
    }

    float x, y, z, o;
    GetRespawnPosition(x, y, z, &o);
    SetHomePosition(x, y, z, o);
    SetPosition(x, y, z, o);

    // xinef: relocate notifier
    m_last_notify_position.Relocate(-5000.0f, -5000.0f, -5000.0f, 0.0f);

    // pussywizard: if corpse was removed during falling then the falling will continue after respawn, so stop falling is such case
    if (IsFalling())
        StopMoving();
}

/**
 * change the entry of creature until respawn
 */
bool Creature::InitEntry(uint32 Entry, const CreatureData* data)
{
    CreatureTemplate const* normalInfo = sObjectMgr->GetCreatureTemplate(Entry);
    if (!normalInfo)
    {
        LOG_ERROR("sql.sql", "Creature::InitEntry creature entry {} does not exist.", Entry);
        return false;
    }

    // get difficulty 1 mode entry
    // Xinef: Skip for pets!
    CreatureTemplate const* cinfo = normalInfo;
    for (uint8 diff = uint8(GetMap()->GetSpawnMode()); diff > 0 && !IsPet();)
    {
        // we already have valid Map pointer for current creature!
        if (normalInfo->DifficultyEntry[diff - 1])
        {
            cinfo = sObjectMgr->GetCreatureTemplate(normalInfo->DifficultyEntry[diff - 1]);
            if (cinfo)
                break;                                      // template found

            // check and reported at startup, so just ignore (restore normalInfo)
            cinfo = normalInfo;
        }

        // for instances heroic to normal, other cases attempt to retrieve previous difficulty
        if (diff >= RAID_DIFFICULTY_10MAN_HEROIC && GetMap()->IsRaid())
            diff -= 2;                                      // to normal raid difficulty cases
        else
            --diff;
    }

    SetEntry(Entry);                                        // normal entry always
    m_creatureInfo = cinfo;                                 // map mode related always

    // equal to player Race field, but creature does not have race
    SetByteValue(UNIT_FIELD_BYTES_0, 0, 0);

    // known valid are: CLASS_WARRIOR, CLASS_PALADIN, CLASS_ROGUE, CLASS_MAGE
    SetByteValue(UNIT_FIELD_BYTES_0, 1, uint8(cinfo->unit_class));

    // Cancel load if no model defined
    if (!(cinfo->GetFirstValidModel()))
    {
        LOG_ERROR("sql.sql", "Creature (Entry: {}) has no model defined in table `creature_template_model`, can't load. ", Entry);
        return false;
    }

    CreatureModel model = *ObjectMgr::ChooseDisplayId(cinfo, data);
    CreatureModelInfo const* mInfo = sObjectMgr->GetCreatureModelRandomGender(&model, cinfo);
    if (!mInfo)                                             // Cancel load if no model defined
    {
        LOG_ERROR("sql.sql", "Creature (Entry: {}) has no model {} defined in table `creature_template_model`, can't load. ", Entry, model.CreatureDisplayID);
        return false;
    }

    SetDisplayId(model.CreatureDisplayID, model.DisplayScale);
    SetNativeDisplayId(model.CreatureDisplayID);

    // Load creature equipment
    if (!data)
    {
        LoadEquipment();             // use default from the template
    }
    else if (data->equipmentId == 0)
    {
        LoadEquipment(0);            // 0 means no equipment for creature table
    }
    else
    {
        m_originalEquipmentId = data->equipmentId;
        LoadEquipment(data->equipmentId);
    }

    SetName(normalInfo->Name);                              // at normal entry always

    SetFloatValue(UNIT_MOD_CAST_SPEED, 1.0f);

    SetSpeed(MOVE_WALK, cinfo->speed_walk);
    SetSpeed(MOVE_RUN, cinfo->speed_run);
    SetSpeed(MOVE_SWIM, cinfo->speed_swim);
    SetSpeed(MOVE_FLIGHT, cinfo->speed_flight);

    // Will set UNIT_FIELD_BOUNDINGRADIUS and UNIT_FIELD_COMBATREACH
    SetObjectScale(GetNativeObjectScale());

    SetFloatValue(UNIT_FIELD_HOVERHEIGHT, cinfo->HoverHeight);

    SetCanDualWield(cinfo->flags_extra & CREATURE_FLAG_EXTRA_USE_OFFHAND_ATTACK);

    // checked at loading
    m_defaultMovementType = MovementGeneratorType(cinfo->MovementType);
    if (!m_wanderDistance && m_defaultMovementType == RANDOM_MOTION_TYPE)
        m_defaultMovementType = IDLE_MOTION_TYPE;

    for (uint8 i = 0; i < MAX_CREATURE_SPELLS; ++i)
        m_spells[i] = GetCreatureTemplate()->spells[i];

    return true;
}

bool Creature::UpdateEntry(uint32 Entry, const CreatureData* data, bool changelevel, bool updateAI)
{
    if (!InitEntry(Entry, data))
        return false;

    CreatureTemplate const* cInfo = GetCreatureTemplate();

    m_regenHealth = cInfo->RegenHealth;

    // creatures always have melee weapon ready if any unless specified otherwise
    if (!GetCreatureAddon())
        SetSheath(SHEATH_STATE_MELEE);

    SetFaction(cInfo->faction);

    uint32 npcflag, unit_flags, dynamicflags;
    ObjectMgr::ChooseCreatureFlags(cInfo, npcflag, unit_flags, dynamicflags, data);

    if (cInfo->flags_extra & CREATURE_FLAG_EXTRA_WORLDEVENT)
        ReplaceAllNpcFlags(NPCFlags(npcflag | sGameEventMgr->GetNPCFlag(this)));
    else
        ReplaceAllNpcFlags(NPCFlags(npcflag));

    // Xinef: NPC is in combat, keep this flag!
    unit_flags &= ~UNIT_FLAG_IN_COMBAT;
    if (IsInCombat())
        unit_flags |= UNIT_FLAG_IN_COMBAT;

    ReplaceAllUnitFlags(UnitFlags(unit_flags));
    ReplaceAllUnitFlags2(UnitFlags2(cInfo->unit_flags2));

    ReplaceAllDynamicFlags(dynamicflags);

    SetCanDualWield(cInfo->flags_extra & CREATURE_FLAG_EXTRA_USE_OFFHAND_ATTACK);

    SetAttackTime(BASE_ATTACK,   cInfo->BaseAttackTime);
    SetAttackTime(OFF_ATTACK,    cInfo->BaseAttackTime);
    SetAttackTime(RANGED_ATTACK, cInfo->RangeAttackTime);

    uint32 previousHealth = GetHealth();
    uint32 previousMaxHealth = GetMaxHealth();
    uint32 previousPlayerDamageReq = _playerDamageReq;

    SelectLevel(changelevel);
    if (previousHealth > 0)
    {
        SetHealth(previousHealth);

        if (previousMaxHealth && previousMaxHealth > GetMaxHealth())
        {
            _playerDamageReq = (uint32)(previousPlayerDamageReq * GetMaxHealth() / previousMaxHealth);
        }
        else
        {
            _playerDamageReq = previousPlayerDamageReq;
        }
    }

    SetMeleeDamageSchool(SpellSchools(cInfo->dmgschool));
    CreatureBaseStats const* stats = sObjectMgr->GetCreatureBaseStats(GetLevel(), cInfo->unit_class);
    float armor = stats->GenerateArmor(cInfo);
    SetModifierValue(UNIT_MOD_ARMOR,             BASE_VALUE, armor);
    SetModifierValue(UNIT_MOD_RESISTANCE_HOLY,   BASE_VALUE, float(cInfo->resistance[SPELL_SCHOOL_HOLY]));
    SetModifierValue(UNIT_MOD_RESISTANCE_FIRE,   BASE_VALUE, float(cInfo->resistance[SPELL_SCHOOL_FIRE]));
    SetModifierValue(UNIT_MOD_RESISTANCE_NATURE, BASE_VALUE, float(cInfo->resistance[SPELL_SCHOOL_NATURE]));
    SetModifierValue(UNIT_MOD_RESISTANCE_FROST,  BASE_VALUE, float(cInfo->resistance[SPELL_SCHOOL_FROST]));
    SetModifierValue(UNIT_MOD_RESISTANCE_SHADOW, BASE_VALUE, float(cInfo->resistance[SPELL_SCHOOL_SHADOW]));
    SetModifierValue(UNIT_MOD_RESISTANCE_ARCANE, BASE_VALUE, float(cInfo->resistance[SPELL_SCHOOL_ARCANE]));

    SetCanModifyStats(true);
    UpdateAllStats();

    // checked and error show at loading templates
    if (FactionTemplateEntry const* factionTemplate = sFactionTemplateStore.LookupEntry(cInfo->faction))
    {
        if (factionTemplate->factionFlags & FACTION_TEMPLATE_FLAG_ASSIST_PLAYERS)
            SetPvP(true);
        else
            SetPvP(false);
    }

    // updates spell bars for vehicles and set player's faction - should be called here, to overwrite faction that is set from the new template
    if (IsVehicle())
    {
        if (Player* owner = Creature::GetCharmerOrOwnerPlayerOrPlayerItself()) // this check comes in case we don't have a player
        {
            SetFaction(owner->GetFaction()); // vehicles should have same as owner faction
            owner->VehicleSpellInitialize();
        }
    }

    // trigger creature is always not selectable and can not be attacked
    if (IsTrigger())
        SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE);

    InitializeReactState();

    if (!IsPet() && cInfo->flags_extra & CREATURE_FLAG_EXTRA_NO_TAUNT)
    {
        ApplySpellImmune(0, IMMUNITY_STATE, SPELL_AURA_MOD_TAUNT, true);
        ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_ATTACK_ME, true);
    }

    SetDetectionDistance(cInfo->detection_range);

    // Update movement
    if (IsRooted())
        SetControlled(true, UNIT_STATE_ROOT);
    UpdateMovementFlags();

    LoadSpellTemplateImmunity();

    if (updateAI)
    {
        AIM_Initialize();
    }

    return true;
}

void Creature::Update(uint32 diff)
{
    if (IsAIEnabled && TriggerJustRespawned)
    {
        TriggerJustRespawned = false;
        AI()->JustRespawned();
        if (m_vehicleKit)
            m_vehicleKit->Reset();
    }

    switch (m_deathState)
    {
        case DeathState::JustRespawned:
            // Must not be called, see Creature::setDeathState JUST_RESPAWNED -> ALIVE promoting.
            LOG_ERROR("entities.unit", "Creature ({}) in wrong state: DeathState::JustRespawned (4)", GetGUID().ToString());
            break;
        case DeathState::JustDied:
            // Must not be called, see Creature::setDeathState JUST_DIED -> CORPSE promoting.
            LOG_ERROR("entities.unit", "Creature ({}) in wrong state: DeathState::JustDead (1)", GetGUID().ToString());
            break;
        case DeathState::Dead:
        {
            time_t now = GameTime::GetGameTime().count();
            if (m_respawnTime <= now)
            {
                Respawn();
            }
            break;
        }
        case DeathState::Corpse:
        {
            Unit::Update(diff);
            // deathstate changed on spells update, prevent problems
            if (m_deathState != DeathState::Corpse)
                break;

            if (m_groupLootTimer && lootingGroupLowGUID)
            {
                if (m_groupLootTimer <= diff)
                {
                    Group* group = sGroupMgr->GetGroupByGUID(lootingGroupLowGUID);
                    if (group)
                        group->EndRoll(&loot, GetMap());
                    m_groupLootTimer = 0;
                    lootingGroupLowGUID = 0;
                }
                else
                {
                    m_groupLootTimer -= diff;
                }
            }
            else if (m_corpseRemoveTime <= GameTime::GetGameTime().count())
            {
                RemoveCorpse(false);
                LOG_DEBUG("entities.unit", "Removing corpse... {} ", GetUInt32Value(OBJECT_FIELD_ENTRY));
            }
            break;
        }
        case DeathState::Alive:
        {
            Unit::Update(diff);

            // creature can be dead after Unit::Update call
            // CORPSE/DEAD state will processed at next tick (in other case death timer will be updated unexpectedly)
            if (!IsAlive())
                break;

            // if creature is charmed, switch to charmed AI
            if (NeedChangeAI)
            {
                UpdateCharmAI();
                NeedChangeAI = false;
                IsAIEnabled = true;

                // xinef: update combat state, if npc is not in combat - return to spawn correctly by calling EnterEvadeMode
                SelectVictim();
            }

            // if periodic combat pulse is enabled and we are both in combat and in a dungeon, do this now
            if (m_combatPulseDelay > 0 && IsInCombat() && GetMap()->IsDungeon())
            {
                if (diff > m_combatPulseTime)
                    m_combatPulseTime = 0;
                else
                    m_combatPulseTime -= diff;

                if (m_combatPulseTime == 0)
                {
                    if (AI())
                        AI()->DoZoneInCombat();
                    else
                        SetInCombatWithZone();
                    m_combatPulseTime = m_combatPulseDelay * IN_MILLISECONDS;
                }
            }

            // periodic check to see if the creature has passed an evade boundary
            if (IsAIEnabled && !IsInEvadeMode() && IsEngaged())
            {
                if (diff >= m_boundaryCheckTime)
                {
                    AI()->CheckInRoom();
                    m_boundaryCheckTime = 2500;
                }
                else
                    m_boundaryCheckTime -= diff;
            }

            Unit* owner = GetCharmerOrOwner();
            if (IsCharmed() && !IsWithinDistInMap(owner, GetMap()->GetVisibilityRange(), true, false))
            {
                RemoveCharmAuras();
            }

            if (Unit* victim = GetVictim())
            {
                // If we are closer than 50% of the combat reach we are going to reposition the victim
                if (diff >= m_moveBackwardsMovementTime)
                {
                    float MaxRange = GetCollisionRadius() + GetVictim()->GetCollisionRadius();

                    if (IsInDist(victim, MaxRange))
                        AI()->MoveBackwardsChecks();

                    m_moveBackwardsMovementTime = urand(MOVE_BACKWARDS_CHECK_INTERVAL, MOVE_BACKWARDS_CHECK_INTERVAL * 3);
                }
                else
                {
                    m_moveBackwardsMovementTime -= diff;
                }

                // Circling the target
                if (diff >= m_moveCircleMovementTime)
                {
                    AI()->MoveCircleChecks();
                    m_moveCircleMovementTime = urand(MOVE_CIRCLE_CHECK_INTERVAL, MOVE_CIRCLE_CHECK_INTERVAL * 2);
                }
                else
                {
                    m_moveCircleMovementTime -= diff;
                }
            }

            // Call for assistance if not disabled
            if (m_assistanceTimer)
            {
                if (m_assistanceTimer <= diff)
                {
                    if (CanPeriodicallyCallForAssistance())
                    {
                        SetNoCallAssistance(false);
                        CallAssistance();
                    }
                    m_assistanceTimer = sWorld->getIntConfig(CONFIG_CREATURE_FAMILY_ASSISTANCE_PERIOD);
                }
                else
                {
                    m_assistanceTimer -= diff;
                }
            }

            if (!IsInEvadeMode() && IsAIEnabled)
            {
                // do not allow the AI to be changed during update
                m_AI_locked = true;
                i_AI->UpdateAI(diff);
                m_AI_locked = false;
            }

            // creature can be dead after UpdateAI call
            // CORPSE/DEAD state will processed at next tick (in other case death timer will be updated unexpectedly)
            if (!IsAlive())
                break;

            m_regenTimer -= diff;
            if (m_regenTimer <= 0)
            {
                if (!IsInEvadeMode())
                {
                    // regenerate health if not in combat or if polymorphed)
                    if (!IsInCombat() || IsPolymorphed())
                        RegenerateHealth();
                    else if (IsNotReachableAndNeedRegen())
                    {
                        // regenerate health if cannot reach the target and the setting is set to do so.
                        // this allows to disable the health regen of raid bosses if pathfinding has issues for whatever reason
                        if (sWorld->getBoolConfig(CONFIG_REGEN_HP_CANNOT_REACH_TARGET_IN_RAID) || !GetMap()->IsRaid())
                        {
                            RegenerateHealth();
                            LOG_DEBUG("entities.unit", "RegenerateHealth() enabled because Creature cannot reach the target. Detail: {}", GetDebugInfo());
                        }
                        else
                            LOG_DEBUG("entities.unit", "RegenerateHealth() disabled even if the Creature cannot reach the target. Detail: {}", GetDebugInfo());
                    }
                }

                if (getPowerType() == POWER_ENERGY)
                    Regenerate(POWER_ENERGY);
                else
                    Regenerate(POWER_MANA);

                m_regenTimer += CREATURE_REGEN_INTERVAL;
            }

            if (CanNotReachTarget() && !IsInEvadeMode())
            {
                m_cannotReachTimer += diff;
                if (m_cannotReachTimer >= (sWorld->getIntConfig(CONFIG_NPC_EVADE_IF_NOT_REACHABLE) * IN_MILLISECONDS))
                {
                    Player* cannotReachPlayer = ObjectAccessor::GetPlayer(*this, m_cannotReachTarget);
                    if (cannotReachPlayer && IsEngagedBy(cannotReachPlayer) && IsAIEnabled && AI()->OnTeleportUnreacheablePlayer(cannotReachPlayer))
                    {
                        SetCannotReachTarget();
                    }
                    else if (!GetMap()->IsRaid())
                    {
                        auto EnterEvade = [&]()
                        {
                            if (CreatureAI* ai = AI())
                            {
                                ai->EnterEvadeMode(CreatureAI::EvadeReason::EVADE_REASON_NO_PATH);
                            }
                        };

                        if (GetThreatMgr().GetThreatListSize() <= 1)
                        {
                            EnterEvade();
                        }
                        else
                        {
                            if (HostileReference* ref = GetThreatMgr().GetOnlineContainer().getReferenceByTarget(m_cannotReachTarget))
                            {
                                ref->removeReference();
                                SetCannotReachTarget();
                            }
                            else
                            {
                                EnterEvade();
                            }
                        }
                    }
                }
            }
            break;
        }
        default:
            break;
    }

    if (IsInWorld() && !IsDuringRemoveFromWorld())
    {
        // pussywizard:
        if (GetOwnerGUID().IsPlayer())
        {
            if (m_transportCheckTimer <= diff)
            {
                m_transportCheckTimer = 1000;
                Transport* newTransport = GetMap()->GetTransportForPos(GetPhaseMask(), GetPositionX(), GetPositionY(), GetPositionZ(), this);
                if (newTransport != GetTransport())
                {
                    if (GetTransport())
                        GetTransport()->RemovePassenger(this, true);
                    if (newTransport)
                        newTransport->AddPassenger(this, true);
                    this->StopMovingOnCurrentPos();
                    //SendMovementFlagUpdate();
                }
            }
            else
                m_transportCheckTimer -= diff;
        }

        sScriptMgr->OnCreatureUpdate(this, diff);
    }
}

bool Creature::IsFreeToMove()
{
    uint32 moveFlags = m_movementInfo.GetMovementFlags();
    // Do not reposition ourself when we are not allowed to move
    if ((IsMovementPreventedByCasting() || isMoving() || !CanFreeMove() || !IsCombatMovementAllowed()) &&
        (GetMotionMaster()->GetCurrentMovementGeneratorType() != CHASE_MOTION_TYPE ||
        moveFlags & MOVEMENTFLAG_SPLINE_ENABLED))
    {
        return false;
    }

    return true;
}

void Creature::Regenerate(Powers power)
{
    uint32 curValue = GetPower(power);
    uint32 maxValue = GetMaxPower(power);

    // Xinef: implement power regeneration flag
    if (!HasUnitFlag2(UNIT_FLAG2_REGENERATE_POWER) && !GetOwnerGUID().IsPlayer())
        return;

    if (!m_regenPower)
    {
        return;
    }

    if (curValue >= maxValue)
        return;

    float addvalue = 0.0f;

    switch (power)
    {
        case POWER_FOCUS:
            {
                // For hunter pets.
                addvalue = 24 * sWorld->getRate(RATE_POWER_FOCUS);
                break;
            }
        case POWER_ENERGY:
            {
                // For deathknight's ghoul.
                addvalue = 20;
                break;
            }
        case POWER_MANA:
            {
                // Combat and any controlled creature
                if (IsInCombat() || GetCharmerOrOwnerGUID())
                {
                    if (GetEntry() == NPC_IMP || GetEntry() == NPC_WATER_ELEMENTAL_TEMP || GetEntry() == NPC_WATER_ELEMENTAL_PERM)
                    {
                        addvalue = uint32((GetStat(STAT_SPIRIT) / (IsUnderLastManaUseEffect() ? 8.0f : 5.0f) + 17.0f));
                    }
                    else if (!IsUnderLastManaUseEffect())
                    {
                        float ManaIncreaseRate = sWorld->getRate(RATE_POWER_MANA);
                        float Spirit = GetStat(STAT_SPIRIT);

                        addvalue = uint32((Spirit / 5.0f + 17.0f) * ManaIncreaseRate);
                    }
                }
                else
                    addvalue = maxValue / 3;
                break;
            }
        default:
            return;
    }

    // Apply modifiers (if any).
    AuraEffectList const& ModPowerRegenPCTAuras = GetAuraEffectsByType(SPELL_AURA_MOD_POWER_REGEN_PERCENT);
    for (AuraEffectList::const_iterator i = ModPowerRegenPCTAuras.begin(); i != ModPowerRegenPCTAuras.end(); ++i)
        if (Powers((*i)->GetMiscValue()) == power)
            AddPct(addvalue, (*i)->GetAmount());

    addvalue += GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_POWER_REGEN, power) * (power == POWER_FOCUS ? PET_FOCUS_REGEN_INTERVAL.count() : CREATURE_REGEN_INTERVAL) / (5 * IN_MILLISECONDS);

    ModifyPower(power, int32(addvalue));
}

void Creature::RegenerateHealth()
{
    if (!isRegeneratingHealth())
        return;

    uint32 curValue = GetHealth();
    uint32 maxValue = GetMaxHealth();

    if (curValue >= maxValue)
        return;

    uint32 addvalue = 0;

    // Not only pet, but any controlled creature
    // Xinef: fix polymorph rapid regen
    if (!GetCharmerOrOwnerGUID() || IsPolymorphed())
        addvalue = maxValue / 3;
    else //if (GetCharmerOrOwnerGUID())
    {
        float HealthIncreaseRate = sWorld->getRate(RATE_HEALTH);
        float Spirit = GetStat(STAT_SPIRIT);

        if (GetPower(POWER_MANA) > 0)
            addvalue = uint32(Spirit * 0.25 * HealthIncreaseRate);
        else
            addvalue = uint32(Spirit * 0.80 * HealthIncreaseRate);
    }

    // Apply modifiers (if any).
    AuraEffectList const& ModPowerRegenPCTAuras = GetAuraEffectsByType(SPELL_AURA_MOD_HEALTH_REGEN_PERCENT);
    for (AuraEffectList::const_iterator i = ModPowerRegenPCTAuras.begin(); i != ModPowerRegenPCTAuras.end(); ++i)
        AddPct(addvalue, (*i)->GetAmount());

    addvalue += GetTotalAuraModifier(SPELL_AURA_MOD_REGEN) * CREATURE_REGEN_INTERVAL  / (5 * IN_MILLISECONDS);

    ModifyHealth(addvalue);
}

void Creature::DoFleeToGetAssistance()
{
    if (!GetVictim())
        return;

    if (HasAuraType(SPELL_AURA_PREVENTS_FLEEING))
        return;

    float radius = sWorld->getFloatConfig(CONFIG_CREATURE_FAMILY_FLEE_ASSISTANCE_RADIUS);
    if (radius > 0)
    {
        Creature* creature = nullptr;

        Acore::NearestAssistCreatureInCreatureRangeCheck u_check(this, GetVictim(), radius);
        Acore::CreatureLastSearcher<Acore::NearestAssistCreatureInCreatureRangeCheck> searcher(this, creature, u_check);

        Cell::VisitGridObjects(this, searcher, radius);

        SetNoSearchAssistance(true);
        UpdateSpeed(MOVE_RUN, false);

        if (!creature)
            //SetFeared(true, GetVictim()->GetGUID(), 0, sWorld->getIntConfig(CONFIG_CREATURE_FAMILY_FLEE_DELAY));
            //TODO: use 31365
            SetControlled(true, UNIT_STATE_FLEEING, GetVictim());
        else
            GetMotionMaster()->MoveSeekAssistance(creature->GetPositionX(), creature->GetPositionY(), creature->GetPositionZ());
    }
}

bool Creature::AIM_Initialize(CreatureAI* ai)
{
    // make sure nothing can change the AI during AI update
    if (m_AI_locked)
    {
        LOG_DEBUG("scripts.ai", "AIM_Initialize: failed to init, locked.");
        return false;
    }

    UnitAI* oldAI = i_AI;

    // Xinef: called in add to world
    //Motion_Initialize();

    i_AI = ai ? ai : FactorySelector::SelectAI(this);
    delete oldAI;
    IsAIEnabled = true;
    i_AI->InitializeAI();

    // Xinef: Initialize vehicle if it is not summoned!
    if (GetVehicleKit() && m_spawnId)
        GetVehicleKit()->Reset();
    return true;
}

void Creature::Motion_Initialize()
{
    if (!m_formation)
        GetMotionMaster()->Initialize();
    else if (m_formation->GetLeader() == this)
    {
        m_formation->FormationReset(false, true);
        GetMotionMaster()->Initialize();
    }
    else if (m_formation->IsFormed())
        GetMotionMaster()->MoveIdle(); //wait the order of leader
    else
        GetMotionMaster()->Initialize();
}

bool Creature::Create(ObjectGuid::LowType guidlow, Map* map, uint32 phaseMask, uint32 Entry, uint32 vehId, float x, float y, float z, float ang, const CreatureData* data)
{
    ASSERT(map);
    SetMap(map);
    SetPhaseMask(phaseMask, false);

    CreatureTemplate const* cinfo = sObjectMgr->GetCreatureTemplate(Entry);
    if (!cinfo)
    {
        LOG_ERROR("sql.sql", "Creature::Create(): creature template (guidlow: {}, entry: {}) does not exist.", guidlow, Entry);
        return false;
    }

    //! Relocate before CreateFromProto, to initialize coords and allow
    //! returning correct zone id for selecting OutdoorPvP/Battlefield script
    Relocate(x, y, z, ang);

    if (!IsPositionValid())
    {
        LOG_ERROR("entities.unit", "Creature::Create(): given coordinates for creature (guidlow {}, entry {}) are not valid (X: {}, Y: {}, Z: {}, O: {})", guidlow, Entry, x, y, z, ang);
        return false;
    }

    // area/zone id is needed immediately for ZoneScript::GetCreatureEntry hook before it is known which creature template to load (no model/scale available yet)
    PositionFullTerrainStatus terrainData;
    GetMap()->GetFullTerrainStatusForPosition(GetPhaseMask(), GetPositionX(), GetPositionY(), GetPositionZ(), DEFAULT_COLLISION_HEIGHT, terrainData);
    ProcessPositionDataChanged(terrainData);

    //oX = x;     oY = y;    dX = x;    dY = y;    m_moveTime = 0;    m_startMove = 0;
    if (!CreateFromProto(guidlow, Entry, vehId, data))
        return false;

    UpdateMovementFlags();

    switch (GetCreatureTemplate()->rank)
    {
        case CREATURE_ELITE_RARE:
            m_corpseDelay = sWorld->getIntConfig(CONFIG_CORPSE_DECAY_RARE);
            break;
        case CREATURE_ELITE_ELITE:
            m_corpseDelay = sWorld->getIntConfig(CONFIG_CORPSE_DECAY_ELITE);
            break;
        case CREATURE_ELITE_RAREELITE:
            m_corpseDelay = sWorld->getIntConfig(CONFIG_CORPSE_DECAY_RAREELITE);
            break;
        case CREATURE_ELITE_WORLDBOSS:
            // Xinef: Reduce corpse delay for bossess outside of instance
            if (!GetInstanceId())
                m_corpseDelay = sWorld->getIntConfig(CONFIG_CORPSE_DECAY_ELITE) * 2;
            else
                m_corpseDelay = sWorld->getIntConfig(CONFIG_CORPSE_DECAY_WORLDBOSS);
            break;
        default:
            m_corpseDelay = sWorld->getIntConfig(CONFIG_CORPSE_DECAY_NORMAL);
            break;
    }

    CreatureModel display(GetNativeDisplayId(), GetNativeObjectScale(), 1.0f);
    CreatureModelInfo const* minfo = sObjectMgr->GetCreatureModelRandomGender(&display, cinfo);
    if (minfo && !IsTotem())                               // Cancel load if no model defined or if totem
    {
        SetDisplayId(display.CreatureDisplayID, display.DisplayScale);
        SetNativeDisplayId(display.CreatureDisplayID);
    }

    LoadCreaturesAddon();

    //! Need to be called after LoadCreaturesAddon - MOVEMENTFLAG_HOVER is set there
    m_positionZ += GetHoverHeight();

    LastUsedScriptID = GetScriptId();

    if (IsSpiritHealer() || IsSpiritGuide() || (GetCreatureTemplate()->flags_extra & CREATURE_FLAG_EXTRA_GHOST_VISIBILITY))
    {
        m_serverSideVisibility.SetValue(SERVERSIDE_VISIBILITY_GHOST, GHOST_VISIBILITY_GHOST);
        m_serverSideVisibilityDetect.SetValue(SERVERSIDE_VISIBILITY_GHOST, GHOST_VISIBILITY_GHOST);
    }
    else if (cinfo->type_flags & CREATURE_TYPE_FLAG_VISIBLE_TO_GHOSTS) // Xinef: Add ghost visibility for ghost units
        m_serverSideVisibility.SetValue(SERVERSIDE_VISIBILITY_GHOST, GHOST_VISIBILITY_ALIVE | GHOST_VISIBILITY_GHOST);

    if (Entry == VISUAL_WAYPOINT)
        SetVisible(false);

    if (GetCreatureTemplate()->flags_extra & CREATURE_FLAG_EXTRA_IGNORE_PATHFINDING)
        AddUnitState(UNIT_STATE_IGNORE_PATHFINDING);

    return true;
}

bool Creature::isCanInteractWithBattleMaster(Player* player, bool msg) const
{
    if (!IsBattleMaster())
        return false;

    BattlegroundTypeId bgTypeId = sBattlegroundMgr->GetBattleMasterBG(GetEntry());
    if (!msg)
        return player->GetBGAccessByLevel(bgTypeId);

    if (!player->GetBGAccessByLevel(bgTypeId))
    {
        ClearGossipMenuFor(player);
        switch (bgTypeId)
        {
            case BATTLEGROUND_AV:
                SendGossipMenuFor(player, 7616, this);
                break;
            case BATTLEGROUND_WS:
                SendGossipMenuFor(player, 7599, this);
                break;
            case BATTLEGROUND_AB:
                SendGossipMenuFor(player, 7642, this);
                break;
            case BATTLEGROUND_EY:
            case BATTLEGROUND_NA:
            case BATTLEGROUND_BE:
            case BATTLEGROUND_AA:
            case BATTLEGROUND_RL:
            case BATTLEGROUND_SA:
            case BATTLEGROUND_DS:
            case BATTLEGROUND_RV:
                SendGossipMenuFor(player, 10024, this);
                break;
            default:
                break;
        }
        return false;
    }
    return true;
}

bool Creature::isCanTrainingAndResetTalentsOf(Player* player) const
{
    return player->GetLevel() >= 10
        && GetCreatureTemplate()->trainer_type == TRAINER_TYPE_CLASS
        && player->IsClass((Classes)GetCreatureTemplate()->trainer_class, CLASS_CONTEXT_CLASS_TRAINER);
}

bool Creature::IsValidTrainerForPlayer(Player* player, uint32* npcFlags /*= nullptr*/) const
{
    if (!IsTrainer())
    {
        return false;
    }

    switch (m_creatureInfo->trainer_type)
    {
        case TRAINER_TYPE_CLASS:
        case TRAINER_TYPE_PETS:
            if (m_creatureInfo->trainer_class && !player->IsClass((Classes)m_creatureInfo->trainer_class, CLASS_CONTEXT_CLASS_TRAINER))
            {
                if (npcFlags)
                    *npcFlags &= ~UNIT_NPC_FLAG_TRAINER_CLASS;

                return false;
            }
            break;
        case TRAINER_TYPE_MOUNTS:
            if (m_creatureInfo->trainer_race && m_creatureInfo->trainer_race != player->getRace())
            {
                return false;
            }
            break;
        case TRAINER_TYPE_TRADESKILLS:
            if (m_creatureInfo->trainer_spell && !player->HasSpell(m_creatureInfo->trainer_spell))
            {
                if (npcFlags)
                    *npcFlags &= ~UNIT_NPC_FLAG_TRAINER_PROFESSION;

                return false;
            }
            break;
        default:
            break;
    }

    return true;
}

Player* Creature::GetLootRecipient() const
{
    if (!m_lootRecipient)
        return nullptr;
    return ObjectAccessor::FindConnectedPlayer(m_lootRecipient);
}

Group* Creature::GetLootRecipientGroup() const
{
    if (!m_lootRecipientGroup)
        return nullptr;
    return sGroupMgr->GetGroupByGUID(m_lootRecipientGroup);
}

void Creature::SetLootRecipient(Unit* unit, bool withGroup)
{
    // set the player whose group should receive the right
    // to loot the creature after it dies
    // should be set to nullptr after the loot disappears

    if (!unit)
    {
        m_lootRecipient.Clear();
        m_lootRecipientGroup = 0;
        RemoveDynamicFlag(UNIT_DYNFLAG_LOOTABLE | UNIT_DYNFLAG_TAPPED);
        ResetAllowedLooters();
        return;
    }

    Player* player = unit->GetCharmerOrOwnerPlayerOrPlayerItself();
    if (!player)                                             // normal creature, no player involved
        return;

    m_lootRecipient = player->GetGUID();

    Map* map = GetMap();
    if (map && map->IsDungeon() && (isWorldBoss() || IsDungeonBoss()))
    {
        AddAllowedLooter(m_lootRecipient);
    }

    if (withGroup)
    {
        if (Group* group = player->GetGroup())
        {
            m_lootRecipientGroup = group->GetGUID().GetCounter();

            if (map && map->IsDungeon() && (isWorldBoss() || IsDungeonBoss()))
            {
                Map::PlayerList const& PlayerList = map->GetPlayers();
                for (Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i)
                {
                    if (Player* groupMember = i->GetSource())
                    {
                        if (groupMember->IsGameMaster() || groupMember->IsSpectator())
                        {
                            continue;
                        }

                        if (groupMember->GetGroup() == group)
                        {
                            AddAllowedLooter(groupMember->GetGUID());
                        }
                    }
                }
            }
        }
    }
    else
        m_lootRecipientGroup = 0;

    SetDynamicFlag(UNIT_DYNFLAG_TAPPED);
}

// return true if this creature is tapped by the player or by a member of his group.
bool Creature::isTappedBy(Player const* player) const
{
    if (player->GetGUID() == m_lootRecipient)
        return true;

    Group const* playerGroup = player->GetGroup();
    if (!playerGroup || playerGroup != GetLootRecipientGroup()) // if we dont have a group we arent the recipient
        return false;                                           // if creature doesnt have group bound it means it was solo killed by someone else

    return true;
}

void Creature::SaveToDB()
{
    // this should only be used when the creature has already been loaded
    // preferably after adding to map, because mapid may not be valid otherwise
    CreatureData const* data = sObjectMgr->GetCreatureData(m_spawnId);
    if (!data)
    {
        LOG_ERROR("entities.unit", "Creature::SaveToDB failed, cannot get creature data!");
        return;
    }

    uint32 mapId = GetTransport() && GetTransport()->ToMotionTransport() ? GetTransport()->GetGOInfo()->moTransport.mapID : GetMapId();
    SaveToDB(mapId, data->spawnMask, GetPhaseMask());
}

void Creature::SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask)
{
    // update in loaded data
    if (!m_spawnId)
        m_spawnId = sObjectMgr->GenerateCreatureSpawnId();

    CreatureData& data = sObjectMgr->NewOrExistCreatureData(m_spawnId);

    uint32 displayId = GetNativeDisplayId();
    uint32 npcflag = GetNpcFlags();
    uint32 unit_flags = GetUnitFlags();
    uint32 dynamicflags = GetDynamicFlags();

    // check if it's a custom model and if not, use 0 for displayId
    CreatureTemplate const* cinfo = GetCreatureTemplate();
    if (cinfo)
    {
        for (CreatureModel model : cinfo->Models)
            if (displayId && displayId == model.CreatureDisplayID)
                displayId = 0;

        if (npcflag == cinfo->npcflag)
            npcflag = 0;

        if (unit_flags == cinfo->unit_flags)
            unit_flags = 0;

        if (dynamicflags == cinfo->dynamicflags)
            dynamicflags = 0;
    }

    data.id1 = GetEntry();
    data.mapid = mapid;
    data.phaseMask = phaseMask;
    data.displayid = displayId;
    data.equipmentId = GetCurrentEquipmentId();
    if (!GetTransport())
    {
        data.posX = GetPositionX();
        data.posY = GetPositionY();
        data.posZ = GetPositionZ();
        data.orientation = GetOrientation();
    }
    else
    {
        data.posX = GetTransOffsetX();
        data.posY = GetTransOffsetY();
        data.posZ = GetTransOffsetZ();
        data.orientation = GetTransOffsetO();
    }

    data.spawntimesecs = m_respawnDelay;
    // prevent add data integrity problems
    data.wander_distance = GetDefaultMovementType() == IDLE_MOTION_TYPE ? 0.0f : m_wanderDistance;
    data.currentwaypoint = 0;
    data.curhealth = GetHealth();
    data.curmana = GetPower(POWER_MANA);
    // prevent add data integrity problems
    data.movementType = !m_wanderDistance && GetDefaultMovementType() == RANDOM_MOTION_TYPE
                        ? IDLE_MOTION_TYPE : GetDefaultMovementType();
    data.spawnMask = spawnMask;
    data.npcflag = npcflag;
    data.unit_flags = unit_flags;
    data.dynamicflags = dynamicflags;

    // update in DB
    WorldDatabaseTransaction trans = WorldDatabase.BeginTransaction();

    WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_CREATURE);
    stmt->SetData(0, m_spawnId);
    trans->Append(stmt);

    uint8 index = 0;

    stmt = WorldDatabase.GetPreparedStatement(WORLD_INS_CREATURE);
    stmt->SetData(index++, m_spawnId);
    stmt->SetData(index++, GetEntry());
    stmt->SetData(index++, 0);
    stmt->SetData(index++, 0);
    stmt->SetData(index++, uint16(mapid));
    stmt->SetData(index++, spawnMask);
    stmt->SetData(index++, GetPhaseMask());
    stmt->SetData(index++, int32(GetCurrentEquipmentId()));
    stmt->SetData(index++, GetPositionX());
    stmt->SetData(index++, GetPositionY());
    stmt->SetData(index++, GetPositionZ());
    stmt->SetData(index++, GetOrientation());
    stmt->SetData(index++, m_respawnDelay);
    stmt->SetData(index++, m_wanderDistance);
    stmt->SetData(index++, 0);
    stmt->SetData(index++, GetHealth());
    stmt->SetData(index++, GetPower(POWER_MANA));
    stmt->SetData(index++, uint8(GetDefaultMovementType()));
    stmt->SetData(index++, npcflag);
    stmt->SetData(index++, unit_flags);
    stmt->SetData(index++, dynamicflags);
    trans->Append(stmt);

    WorldDatabase.CommitTransaction(trans);
    sScriptMgr->OnCreatureSaveToDB(this);
}

void Creature::SelectLevel(bool changelevel)
{
    CreatureTemplate const* cInfo = GetCreatureTemplate();

    uint32 rank = IsPet() ? 0 : cInfo->rank;

    // level
    uint8 minlevel = std::min(cInfo->maxlevel, cInfo->minlevel);
    uint8 maxlevel = std::max(cInfo->maxlevel, cInfo->minlevel);
    uint8 level = minlevel == maxlevel ? minlevel : urand(minlevel, maxlevel);

    sScriptMgr->OnBeforeCreatureSelectLevel(cInfo, this, level);

    if (changelevel)
        SetLevel(level);

    CreatureBaseStats const* stats = sObjectMgr->GetCreatureBaseStats(level, cInfo->unit_class);

    // health
    float healthmod = _GetHealthMod(rank);

    uint32 basehp = std::max<uint32>(1, stats->GenerateHealth(cInfo));
    uint32 health = uint32(basehp * healthmod);

    SetCreateHealth(health);
    SetMaxHealth(health);
    SetHealth(health);
    ResetPlayerDamageReq();

    // mana
    uint32 mana = stats->GenerateMana(cInfo);

    SetCreateMana(mana);
    SetMaxPower(POWER_MANA, mana);                          //MAX Mana
    SetPower(POWER_MANA, mana);

    /// @todo: set UNIT_FIELD_POWER*, for some creature class case (energy, etc)

    SetModifierValue(UNIT_MOD_HEALTH, BASE_VALUE, (float)health);
    SetModifierValue(UNIT_MOD_MANA, BASE_VALUE, (float)mana);

    // damage

    float basedamage = stats->GenerateBaseDamage(cInfo);

    float weaponBaseMinDamage = basedamage;
    float weaponBaseMaxDamage = basedamage * 1.5;

    SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, weaponBaseMinDamage);
    SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, weaponBaseMaxDamage);

    SetBaseWeaponDamage(OFF_ATTACK, MINDAMAGE, weaponBaseMinDamage);
    SetBaseWeaponDamage(OFF_ATTACK, MAXDAMAGE, weaponBaseMaxDamage);

    SetBaseWeaponDamage(RANGED_ATTACK, MINDAMAGE, weaponBaseMinDamage);
    SetBaseWeaponDamage(RANGED_ATTACK, MAXDAMAGE, weaponBaseMaxDamage);

    SetModifierValue(UNIT_MOD_ATTACK_POWER, BASE_VALUE, stats->AttackPower);
    SetModifierValue(UNIT_MOD_ATTACK_POWER_RANGED, BASE_VALUE, stats->RangedAttackPower);

    sScriptMgr->Creature_SelectLevel(cInfo, this);
}

float Creature::_GetHealthMod(int32 Rank)
{
    switch (Rank)                                           // define rates for each elite rank
    {
        case CREATURE_ELITE_NORMAL:
            return sWorld->getRate(RATE_CREATURE_NORMAL_HP);
        case CREATURE_ELITE_ELITE:
            return sWorld->getRate(RATE_CREATURE_ELITE_ELITE_HP);
        case CREATURE_ELITE_RAREELITE:
            return sWorld->getRate(RATE_CREATURE_ELITE_RAREELITE_HP);
        case CREATURE_ELITE_WORLDBOSS:
            return sWorld->getRate(RATE_CREATURE_ELITE_WORLDBOSS_HP);
        case CREATURE_ELITE_RARE:
            return sWorld->getRate(RATE_CREATURE_ELITE_RARE_HP);
        default:
            return sWorld->getRate(RATE_CREATURE_ELITE_ELITE_HP);
    }
}

float Creature::_GetDamageMod(int32 Rank)
{
    switch (Rank)                                           // define rates for each elite rank
    {
        case CREATURE_ELITE_NORMAL:
            return sWorld->getRate(RATE_CREATURE_NORMAL_DAMAGE);
        case CREATURE_ELITE_ELITE:
            return sWorld->getRate(RATE_CREATURE_ELITE_ELITE_DAMAGE);
        case CREATURE_ELITE_RAREELITE:
            return sWorld->getRate(RATE_CREATURE_ELITE_RAREELITE_DAMAGE);
        case CREATURE_ELITE_WORLDBOSS:
            return sWorld->getRate(RATE_CREATURE_ELITE_WORLDBOSS_DAMAGE);
        case CREATURE_ELITE_RARE:
            return sWorld->getRate(RATE_CREATURE_ELITE_RARE_DAMAGE);
        default:
            return sWorld->getRate(RATE_CREATURE_ELITE_ELITE_DAMAGE);
    }
}

float Creature::GetSpellDamageMod(int32 Rank)
{
    switch (Rank)                                           // define rates for each elite rank
    {
        case CREATURE_ELITE_NORMAL:
            return sWorld->getRate(RATE_CREATURE_NORMAL_SPELLDAMAGE);
        case CREATURE_ELITE_ELITE:
            return sWorld->getRate(RATE_CREATURE_ELITE_ELITE_SPELLDAMAGE);
        case CREATURE_ELITE_RAREELITE:
            return sWorld->getRate(RATE_CREATURE_ELITE_RAREELITE_SPELLDAMAGE);
        case CREATURE_ELITE_WORLDBOSS:
            return sWorld->getRate(RATE_CREATURE_ELITE_WORLDBOSS_SPELLDAMAGE);
        case CREATURE_ELITE_RARE:
            return sWorld->getRate(RATE_CREATURE_ELITE_RARE_SPELLDAMAGE);
        default:
            return sWorld->getRate(RATE_CREATURE_ELITE_ELITE_SPELLDAMAGE);
    }
}

bool Creature::CreateFromProto(ObjectGuid::LowType guidlow, uint32 Entry, uint32 vehId, const CreatureData* data)
{
    SetZoneScript();
    if (GetZoneScript() && data)
    {
        uint32 FirstEntry = GetZoneScript()->GetCreatureEntry(guidlow, data);
        if (!FirstEntry)
            return false;
    }

    CreatureTemplate const* normalInfo = sObjectMgr->GetCreatureTemplate(Entry);
    if (!normalInfo)
    {
        LOG_ERROR("sql.sql", "Creature::CreateFromProto(): creature template (guidlow: {}, entry: {}) does not exist.", guidlow, Entry);
        return false;
    }

    SetOriginalEntry(Entry);

    Object::_Create(guidlow, Entry, (vehId || normalInfo->VehicleId) ? HighGuid::Vehicle : HighGuid::Unit);

    // Xinef: select proper vehicle id
    if (!vehId)
    {
        CreatureTemplate const* cinfo = normalInfo;
        for (uint8 diff = uint8(GetMap()->GetSpawnMode()); diff > 0 && !IsPet();)
        {
            // we already have valid Map pointer for current creature!
            if (cinfo->DifficultyEntry[diff - 1])
            {
                cinfo = sObjectMgr->GetCreatureTemplate(normalInfo->DifficultyEntry[diff - 1]);
                if (cinfo && cinfo->VehicleId)
                    break;                                      // template found

                // check and reported at startup, so just ignore (restore normalInfo)
                cinfo = normalInfo;
            }

            // for instances heroic to normal, other cases attempt to retrieve previous difficulty
            if (diff >= RAID_DIFFICULTY_10MAN_HEROIC && GetMap()->IsRaid())
                diff -= 2;                                      // to normal raid difficulty cases
            else
                --diff;
        }

        if (cinfo->VehicleId)
            CreateVehicleKit(cinfo->VehicleId, (cinfo->VehicleId != normalInfo->VehicleId ? cinfo->Entry : normalInfo->Entry));
    }
    else
        CreateVehicleKit(vehId, Entry);

    if (!UpdateEntry(Entry, data))
        return false;

    return true;
}

bool Creature::isVendorWithIconSpeak() const
{
    return m_creatureInfo->IconName == "Speak" && m_creatureData->npcflag & UNIT_NPC_FLAG_VENDOR;
}

bool Creature::LoadCreatureFromDB(ObjectGuid::LowType spawnId, Map* map, bool addToMap, bool allowDuplicate /*= false*/)
{
    if (!allowDuplicate)
    {
        // If an alive instance of this spawnId is already found, skip creation
        // If only dead instance(s) exist, despawn them and spawn a new (maybe also dead) version
        const auto creatureBounds = map->GetCreatureBySpawnIdStore().equal_range(spawnId);
        std::vector <Creature*> despawnList;

        if (creatureBounds.first != creatureBounds.second)
        {
            for (auto itr = creatureBounds.first; itr != creatureBounds.second; ++itr)
            {
                if (itr->second->IsAlive())
                {
                    LOG_DEBUG("maps", "Would have spawned {} but {} already exists", spawnId, creatureBounds.first->second->GetGUID().ToString());
                    return false;
                }
                else
                {
                    despawnList.push_back(itr->second);
                    LOG_DEBUG("maps", "Despawned dead instance of spawn {} ({})", spawnId, itr->second->GetGUID().ToString());
                }
            }

            for (Creature* despawnCreature : despawnList)
            {
                despawnCreature->AddObjectToRemoveList();
            }
        }
    }

    CreatureData const* data = sObjectMgr->GetCreatureData(spawnId);
    if (!data)
    {
        LOG_ERROR("sql.sql", "Creature (SpawnId: {}) not found in table `creature`, can't load. ", spawnId);
        return false;
    }

    // xinef: this has to be assigned before Create function, properly loads equipment id from DB
    m_creatureData = data;
    m_spawnId = spawnId;

    // Add to world
    uint32 entry = GetRandomId(data->id1, data->id2, data->id3);

    if (!Create(map->GenerateLowGuid<HighGuid::Unit>(), map, data->phaseMask, entry, 0, data->posX, data->posY, data->posZ, data->orientation, data))
        return false;

    //We should set first home position, because then AI calls home movement
    SetHomePosition(data->posX, data->posY, data->posZ, data->orientation);

    m_wanderDistance = data->wander_distance;

    m_respawnDelay = data->spawntimesecs;
    m_deathState = DeathState::Alive;

    m_respawnTime  = GetMap()->GetCreatureRespawnTime(m_spawnId);
    if (m_respawnTime)                          // respawn on Update
    {
        m_deathState = DeathState::Dead;
        if (CanFly())
        {
            float tz = map->GetHeight(GetPhaseMask(), data->posX, data->posY, data->posZ, true, MAX_FALL_DISTANCE);
            if (data->posZ - tz > 0.1f && Acore::IsValidMapCoord(tz))
            {
                Relocate(data->posX, data->posY, tz);
            }
        }
    }

    uint32 curhealth;

    if (!m_regenHealth)
    {
        curhealth = data->curhealth;
        if (curhealth)
        {
            curhealth = uint32(curhealth * _GetHealthMod(GetCreatureTemplate()->rank));
            if (curhealth < 1)
                curhealth = 1;
        }
        SetPower(POWER_MANA, data->curmana);
    }
    else
    {
        curhealth = GetMaxHealth();
        SetPower(POWER_MANA, GetMaxPower(POWER_MANA));
    }

    SetHealth(m_deathState == DeathState::Alive ? curhealth : 0);

    // checked at creature_template loading
    m_defaultMovementType = MovementGeneratorType(data->movementType);

    if (addToMap && !GetMap()->AddToMap(this))
        return false;
    return true;
}

void Creature::SetCanDualWield(bool value)
{
    Unit::SetCanDualWield(value);
    UpdateDamagePhysical(OFF_ATTACK);
}

void Creature::LoadEquipment(int8 id, bool force /*= false*/)
{
    if (id == 0)
    {
        if (force)
        {
            for (uint8 i = 0; i < MAX_EQUIPMENT_ITEMS; ++i)
                SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + i, 0);
            m_equipmentId = 0;
        }
        return;
    }

    EquipmentInfo const* einfo = sObjectMgr->GetEquipmentInfo(GetEntry(), id);
    if (!einfo)
        return;

    m_equipmentId = id;
    for (uint8 i = 0; i < 3; ++i)
        SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + i, einfo->ItemEntry[i]);
}

bool Creature::hasQuest(uint32 quest_id) const
{
    QuestRelationBounds qr = sObjectMgr->GetCreatureQuestRelationBounds(GetEntry());
    for (QuestRelations::const_iterator itr = qr.first; itr != qr.second; ++itr)
    {
        if (itr->second == quest_id)
            return true;
    }
    return false;
}

bool Creature::hasInvolvedQuest(uint32 quest_id) const
{
    QuestRelationBounds qir = sObjectMgr->GetCreatureQuestInvolvedRelationBounds(GetEntry());
    for (QuestRelations::const_iterator itr = qir.first; itr != qir.second; ++itr)
    {
        if (itr->second == quest_id)
            return true;
    }
    return false;
}

void Creature::DeleteFromDB()
{
    if (!m_spawnId)
    {
        LOG_ERROR("entities.unit", "Trying to delete not saved creature: {}", GetGUID().ToString());
        return;
    }

    GetMap()->RemoveCreatureRespawnTime(m_spawnId);
    sObjectMgr->DeleteCreatureData(m_spawnId);

    WorldDatabaseTransaction trans = WorldDatabase.BeginTransaction();

    WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_CREATURE);
    stmt->SetData(0, m_spawnId);
    trans->Append(stmt);

    stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_CREATURE_ADDON);
    stmt->SetData(0, m_spawnId);
    trans->Append(stmt);

    stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_GAME_EVENT_CREATURE);
    stmt->SetData(0, m_spawnId);
    trans->Append(stmt);

    stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_GAME_EVENT_MODEL_EQUIP);
    stmt->SetData(0, m_spawnId);
    trans->Append(stmt);

    WorldDatabase.CommitTransaction(trans);
}

bool Creature::IsInvisibleDueToDespawn() const
{
    if (Unit::IsInvisibleDueToDespawn())
        return true;

    if (IsAlive() || isDying() || m_corpseRemoveTime > GameTime::GetGameTime().count())
        return false;

    return true;
}

bool Creature::CanAlwaysSee(WorldObject const* obj) const
{
    if (IsAIEnabled && AI()->CanSeeAlways(obj))
        return true;

    return false;
}

bool Creature::IsAlwaysDetectableFor(WorldObject const* seer) const
{
    if (Unit::IsAlwaysDetectableFor(seer))
    {
        return true;
    }

    if (IsAIEnabled && AI()->CanAlwaysBeDetectable(seer))
    {
        return true;
    }

    return false;
}

bool Creature::CanStartAttack(Unit const* who) const
{
    if (IsCivilian())
        return false;

    // This set of checks is should be done only for creatures
    if ((IsImmuneToNPC() && !who->IsPlayer()) ||      // flag is valid only for non player characters
        (IsImmuneToPC() && who->IsPlayer()))         // immune to PC and target is a player, return false
    {
        return false;
    }

    if (Unit* owner = who->GetOwner())
        if (owner->IsPlayer() && IsImmuneToPC())     // immune to PC and target has player owner
            return false;

    // Do not attack non-combat pets
    if (who->IsCreature() && who->GetCreatureType() == CREATURE_TYPE_NON_COMBAT_PET)
        return false;

    if (!CanFly() && (GetDistanceZ(who) > CREATURE_Z_ATTACK_RANGE + m_CombatDistance))                    // too much Z difference, skip very costy vmap calculations here
        return false;

    if (!_IsTargetAcceptable(who))
        return false;

    // pussywizard: at this point we are either hostile to who or friendly to who->getAttackerForHelper()
    // pussywizard: if who is in combat and has an attacker, help him if the distance is right (help because who is hostile or help because attacker is friendly)
    bool assist = false;
    if (who->IsEngaged() && IsWithinDist(who, ATTACK_DISTANCE))
        if (Unit* victim = who->getAttackerForHelper())
            if (IsWithinDistInMap(victim, sWorld->getFloatConfig(CONFIG_CREATURE_FAMILY_ASSISTANCE_RADIUS)))
                assist = true;

    if (!assist)
        if (IsNeutralToAll() || !IsWithinDistInMap(who, GetAggroRange(who) + m_CombatDistance, true, false)) // pussywizard: +m_combatDistance for turrets and similar
            return false;

    if (!CanCreatureAttack(who))
        return false;

    if (HasUnitState(UNIT_STATE_STUNNED))
        return false;

    return IsWithinLOSInMap(who);
}

/**
 * @brief A creature can be in 4 different states: Alive, JustDied, Corpse, and JustRespawned. The cycle follows the next order:
 * - Alive: The creature is alive and has spawned in the world
 * - JustDied: The creature has just died. This is the state just before his body appeared
 * - Corpse: The creature has been removed from the world, and this corpse has been spawned
 * - JustRespawned: The creature has just respawned. Follow when the corpse disappears and the respawn timer is finished
 *
 * @param state Specify one of 4 states above
 * @param despawn Despawn the creature immediately, without waiting for any movement to finish
 */
void Creature::setDeathState(DeathState state, bool despawn)
{
    Unit::setDeathState(state, despawn);

    if (state == DeathState::JustDied)
    {
        m_corpseRemoveTime = GameTime::GetGameTime().count() + m_corpseDelay;
        m_respawnTime = GameTime::GetGameTime().count() + m_respawnDelay + m_corpseDelay;

        // always save boss respawn time at death to prevent crash cheating
        if (GetMap()->IsDungeon() || isWorldBoss() || GetCreatureTemplate()->rank >= CREATURE_ELITE_ELITE)
            SaveRespawnTime();

        SetTarget();    // remove target selection in any cases (can be set at aura remove in Unit::setDeathState)
        ReplaceAllNpcFlags(UNIT_NPC_FLAG_NONE);

        Dismount();     // if creature is mounted on a virtual mount, remove it at death

        setActive(false);

        if (HasSearchedAssistance())
        {
            SetNoSearchAssistance(false);
        }

        //Dismiss group if is leader
        if (m_formation && m_formation->GetLeader() == this)
            m_formation->FormationReset(true, false);

        bool needsFalling = !despawn && (IsFlying() || IsHovering()) && !IsUnderWater();
        SetHover(false, false, false);
        SetDisableGravity(false, false, false);

        if (needsFalling)
            GetMotionMaster()->MoveFall(0, true);

        Unit::setDeathState(DeathState::Corpse, despawn);
    }
    else if (state == DeathState::JustRespawned)
    {
        SetFullHealth();
        SetLootRecipient(nullptr);
        ResetPlayerDamageReq();
        SetCannotReachTarget();
        CreatureTemplate const* cinfo = GetCreatureTemplate();
        // Xinef: npc run by default
        //SetWalk(true);

        // pussywizard:
        if (HasUnitMovementFlag(MOVEMENTFLAG_FALLING))
            RemoveUnitMovementFlag(MOVEMENTFLAG_FALLING);

        UpdateMovementFlags();

        ReplaceAllNpcFlags(NPCFlags(cinfo->npcflag));
        ClearUnitState(uint32(UNIT_STATE_ALL_STATE & ~(UNIT_STATE_IGNORE_PATHFINDING | UNIT_STATE_NO_ENVIRONMENT_UPD)));
        SetMeleeDamageSchool(SpellSchools(cinfo->dmgschool));

        Unit::setDeathState(DeathState::Alive, despawn);

        Motion_Initialize();
        LoadCreaturesAddon(true);
        if (GetCreatureData() && GetPhaseMask() != GetCreatureData()->phaseMask)
            SetPhaseMask(GetCreatureData()->phaseMask, false);
    }
}

/**
 * @param force Force the respawn by killing the creature.
 */
void Creature::Respawn(bool force)
{
    if (force)
    {
        if (IsAlive())
        {
            setDeathState(DeathState::JustDied);
        }
        else if (getDeathState() != DeathState::Corpse)
        {
            setDeathState(DeathState::Corpse);
        }
    }

    ConditionList conditions = sConditionMgr->GetConditionsForNotGroupedEntry(CONDITION_SOURCE_TYPE_CREATURE_RESPAWN, GetEntry());

    if (!sConditionMgr->IsObjectMeetToConditions(this, conditions) && !force)
    {
        // Creature should not respawn, reset respawn timer. Conditions will be checked again the next time it tries to respawn.
        m_respawnTime = GameTime::GetGameTime().count() + m_respawnDelay;
        return;
    }

    bool allowed = !IsAIEnabled || AI()->CanRespawn(); // First check if there are any scripts that prevent us respawning
    if (!allowed && !force) // Will be rechecked on next Update call
        return;

    ObjectGuid dbtableHighGuid = ObjectGuid::Create<HighGuid::Unit>(m_creatureData ? m_creatureData->id1 : GetEntry(), m_spawnId);
    time_t linkedRespawntime = GetMap()->GetLinkedRespawnTime(dbtableHighGuid);

    CreatureTemplate const* cInfo = sObjectMgr->GetCreatureTemplate(GetEntry());

    if (!linkedRespawntime || (cInfo && cInfo->HasFlagsExtra(CREATURE_FLAG_EXTRA_HARD_RESET)) || force)          // Should respawn
    {
        RemoveCorpse(false, false);

        if (getDeathState() == DeathState::Dead)
        {
            if (m_spawnId)
            {
                GetMap()->RemoveCreatureRespawnTime(m_spawnId);
                CreatureData const* data = sObjectMgr->GetCreatureData(m_spawnId);
                // Respawn check if spawn has 2 entries
                if (data->id2)
                {
                    uint32 entry = GetRandomId(data->id1, data->id2, data->id3);
                    UpdateEntry(entry, data, true);  // Select Random Entry
                    m_defaultMovementType = MovementGeneratorType(data->movementType);                    // Reload Movement Type
                    LoadEquipment(data->equipmentId);                                                     // Reload Equipment
                    AIM_Initialize();                                                                     // Reload AI
                }
                else
                {
                    if (m_originalEntry != GetEntry())
                        UpdateEntry(m_originalEntry);
                }
            }

            LOG_DEBUG("entities.unit", "Respawning creature {} (SpawnId: {}, {})", GetName(), GetSpawnId(), GetGUID().ToString());
            m_respawnTime = 0;
            ResetPickPocketLootTime();
            loot.clear();
            SelectLevel();

            m_respawnedTime = GameTime::GetGameTime().count();
            setDeathState(DeathState::JustRespawned);

            // MDic - Acidmanifesto: Do not override transform auras
            if (GetAuraEffectsByType(SPELL_AURA_TRANSFORM).empty())
            {
                CreatureModel display(GetNativeDisplayId(), GetNativeObjectScale(), 1.0f);
                CreatureModelInfo const* minfo = sObjectMgr->GetCreatureModelRandomGender(&display, GetCreatureTemplate());
                if (minfo)                                             // Cancel load if no model defined
                {
                    SetDisplayId(display.CreatureDisplayID, display.DisplayScale);
                    SetNativeDisplayId(display.CreatureDisplayID);
                }
            }

            GetMotionMaster()->InitDefault();

            //Call AI respawn virtual function
            if (IsAIEnabled)
            {
                //reset the AI to be sure no dirty or uninitialized values will be used till next tick
                AI()->Reset();
                TriggerJustRespawned = true;  //delay event to next tick so all creatures are created on the map before processing
            }

            uint32 poolid = m_spawnId ? sPoolMgr->IsPartOfAPool<Creature>(m_spawnId) : 0;
            if (poolid)
                sPoolMgr->UpdatePool<Creature>(poolid, m_spawnId);

            //Re-initialize reactstate that could be altered by movementgenerators
            InitializeReactState();

        }
        m_respawnedTime = GameTime::GetGameTime().count();
        // xinef: relocate notifier, fixes npc appearing in corpse position after forced respawn (instead of spawn)
        m_last_notify_position.Relocate(-5000.0f, -5000.0f, -5000.0f, 0.0f);
        UpdateObjectVisibility(false);

    }
    else   // the master is dead
    {
        ObjectGuid targetGuid = sObjectMgr->GetLinkedRespawnGuid(dbtableHighGuid);
        if (targetGuid == dbtableHighGuid) // if linking self, never respawn (check delayed to next day)
        {
            SetRespawnTime(DAY);
        }
        else
        {
            time_t now = GameTime::GetGameTime().count();
            m_respawnTime = (now > linkedRespawntime ? now : linkedRespawntime) + urand(5, MINUTE); // else copy time from master and add a little
        }
        SaveRespawnTime(); // also save to DB immediately
    }
}

void Creature::ForcedDespawn(uint32 timeMSToDespawn, Seconds forceRespawnTimer)
{
    if (timeMSToDespawn)
    {
        ForcedDespawnDelayEvent* pEvent = new ForcedDespawnDelayEvent(*this, forceRespawnTimer);
        m_Events.AddEvent(pEvent, m_Events.CalculateTime(timeMSToDespawn));
        return;
    }

    if (IsAlive())
        setDeathState(DeathState::JustDied, true);

    // Xinef: Set new respawn time, ignore corpse decay time...
    RemoveCorpse(true);

    if (forceRespawnTimer > Seconds::zero())
    {
        if (GetMap())
        {
            GetMap()->ScheduleCreatureRespawn(GetGUID(), forceRespawnTimer);
        }
    }
}

void Creature::DespawnOrUnsummon(Milliseconds msTimeToDespawn /*= 0*/, Seconds forcedRespawnTimer)
{
    if (TempSummon* summon = this->ToTempSummon())
        summon->UnSummon(msTimeToDespawn.count());
    else
        ForcedDespawn(msTimeToDespawn.count(), forcedRespawnTimer);
}

void Creature::DespawnOnEvade(Seconds respawnDelay)
{
    AI()->SummonedCreatureDespawnAll();

    if (respawnDelay < 2s)
    {
        LOG_WARN("entities.unit", "DespawnOnEvade called with delay of {} seconds, defaulting to 2.", respawnDelay.count());
        respawnDelay = 2s;
    }

    if (TempSummon* whoSummon = ToTempSummon())
    {
        LOG_WARN("entities.unit", "DespawnOnEvade called on a temporary summon.");
        whoSummon->UnSummon();
        return;
    }

    DespawnOrUnsummon(Milliseconds(0), respawnDelay);
}

void Creature::InitializeReactState()
{
    if ((IsTotem() || IsTrigger() || IsCritter() || IsSpiritService()) && GetAIName() != "SmartAI" && !GetScriptId())
        SetReactState(REACT_PASSIVE);
    else
        SetReactState(REACT_AGGRESSIVE);
}

bool Creature::HasMechanicTemplateImmunity(uint32 mask) const
{
    return !GetOwnerGUID().IsPlayer() && (GetCreatureTemplate()->MechanicImmuneMask & mask);
}

void Creature::LoadSpellTemplateImmunity()
{
    // uint32 max used for "spell id", the immunity system will not perform SpellInfo checks against invalid spells
    // used so we know which immunities were loaded from template
    static uint32 const placeholderSpellId = std::numeric_limits<uint32>::max();

    // unapply template immunities (in case we're updating entry)
    for (uint8 i = SPELL_SCHOOL_NORMAL; i <= SPELL_SCHOOL_ARCANE; ++i)
    {
        ApplySpellImmune(placeholderSpellId, IMMUNITY_SCHOOL, i, false);
    }

    // don't inherit immunities for hunter pets
    if (GetOwnerGUID().IsPlayer() && IsHunterPet())
    {
        return;
    }

    if (uint8 mask = GetCreatureTemplate()->SpellSchoolImmuneMask)
    {
        for (uint8 i = SPELL_SCHOOL_NORMAL; i <= SPELL_SCHOOL_ARCANE; ++i)
        {
            if (mask & (1 << i))
            {
                ApplySpellImmune(placeholderSpellId, IMMUNITY_SCHOOL, 1 << i, true);
            }
        }
    }
}

bool Creature::IsImmunedToSpell(SpellInfo const* spellInfo, Spell const* spell)
{
    if (!spellInfo)
        return false;

    if (spellInfo->HasAttribute(SPELL_ATTR0_CU_BYPASS_MECHANIC_IMMUNITY))
    {
        return false;
    }

    // Xinef: this should exclude self casts...
    // Spells that don't have effectMechanics.
    if (spellInfo->Mechanic > MECHANIC_NONE && HasMechanicTemplateImmunity(1 << (spellInfo->Mechanic - 1)))
        return true;

    // This check must be done instead of 'if (GetCreatureTemplate()->MechanicImmuneMask & (1 << (spellInfo->Mechanic - 1)))' for not break
    // the check of mechanic immunity on DB (tested) because GetCreatureTemplate()->MechanicImmuneMask and m_spellImmune[IMMUNITY_MECHANIC] don't have same data.
    bool immunedToAllEffects = true;
    for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
        if (spellInfo->Effects[i].IsEffect() && !IsImmunedToSpellEffect(spellInfo, i))
        {
            immunedToAllEffects = false;
            break;
        }
    if (immunedToAllEffects)
        return true;

    return Unit::IsImmunedToSpell(spellInfo, spell);
}

bool Creature::IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index) const
{
    // Xinef: this should exclude self casts...
    if (spellInfo->Effects[index].Mechanic > MECHANIC_NONE && HasMechanicTemplateImmunity(1 << (spellInfo->Effects[index].Mechanic - 1)))
        return true;

    if (GetCreatureTemplate()->type == CREATURE_TYPE_MECHANICAL && spellInfo->Effects[index].Effect == SPELL_EFFECT_HEAL)
        return true;

    return Unit::IsImmunedToSpellEffect(spellInfo, index);
}

SpellInfo const* Creature::reachWithSpellAttack(Unit* victim)
{
    if (!victim)
        return nullptr;

    for (uint32 i = 0; i < MAX_CREATURE_SPELLS; ++i)
    {
        if (!m_spells[i])
            continue;
        SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(m_spells[i]);
        if (!spellInfo)
        {
            LOG_ERROR("entities.unit", "WORLD: unknown spell id {}", m_spells[i]);
            continue;
        }

        bool bcontinue = true;
        for (uint32 j = 0; j < MAX_SPELL_EFFECTS; j++)
        {
            if ((spellInfo->Effects[j].Effect == SPELL_EFFECT_SCHOOL_DAMAGE)       ||
                    (spellInfo->Effects[j].Effect == SPELL_EFFECT_INSTAKILL)            ||
                    (spellInfo->Effects[j].Effect == SPELL_EFFECT_ENVIRONMENTAL_DAMAGE) ||
                    (spellInfo->Effects[j].Effect == SPELL_EFFECT_HEALTH_LEECH)
               )
            {
                bcontinue = false;
                break;
            }
        }
        if (bcontinue)
            continue;

        if (spellInfo->ManaCost > GetPower(POWER_MANA))
            continue;
        float range = spellInfo->GetMaxRange(false);
        float minrange = spellInfo->GetMinRange(false);
        float dist = GetDistance(victim);
        if (dist > range || dist < minrange)
            continue;
        if (spellInfo->PreventionType == SPELL_PREVENTION_TYPE_SILENCE && HasUnitFlag(UNIT_FLAG_SILENCED))
            continue;
        if (spellInfo->PreventionType == SPELL_PREVENTION_TYPE_PACIFY && HasUnitFlag(UNIT_FLAG_PACIFIED))
            continue;
        return spellInfo;
    }
    return nullptr;
}

SpellInfo const* Creature::reachWithSpellCure(Unit* victim)
{
    if (!victim)
        return nullptr;

    for (uint32 i = 0; i < MAX_CREATURE_SPELLS; ++i)
    {
        if (!m_spells[i])
            continue;
        SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(m_spells[i]);
        if (!spellInfo)
        {
            LOG_ERROR("entities.unit", "WORLD: unknown spell id {}", m_spells[i]);
            continue;
        }

        bool bcontinue = true;
        for (uint32 j = 0; j < MAX_SPELL_EFFECTS; j++)
        {
            if ((spellInfo->Effects[j].Effect == SPELL_EFFECT_HEAL))
            {
                bcontinue = false;
                break;
            }
        }
        if (bcontinue)
            continue;

        if (spellInfo->ManaCost > GetPower(POWER_MANA))
            continue;

        float range = spellInfo->GetMaxRange(true);
        float minrange = spellInfo->GetMinRange(true);
        float dist = GetDistance(victim);

        if (dist > range || dist < minrange)
            continue;
        if (spellInfo->PreventionType == SPELL_PREVENTION_TYPE_SILENCE && HasUnitFlag(UNIT_FLAG_SILENCED))
            continue;
        if (spellInfo->PreventionType == SPELL_PREVENTION_TYPE_PACIFY && HasUnitFlag(UNIT_FLAG_PACIFIED))
            continue;
        return spellInfo;
    }
    return nullptr;
}

/**
 * @brief Select nearest hostile unit within the given distance (regardless of threat list).
 */
Unit* Creature::SelectNearestTarget(float dist, bool playerOnly /* = false */) const
{
    if (dist == 0.0f)
    {
        dist = MAX_VISIBILITY_DISTANCE;
    }

    Unit* target = nullptr;

    Acore::NearestHostileUnitCheck u_check(this, dist, playerOnly);
    Acore::UnitLastSearcher<Acore::NearestHostileUnitCheck> searcher(this, target, u_check);
    Cell::VisitAllObjects(this, searcher, dist);
    return target;
}

/**
 * @brief Select nearest hostile unit within the given attack distance (i.e. distance is ignored if > than ATTACK_DISTANCE), regardless of threat list.
 */
Unit* Creature::SelectNearestTargetInAttackDistance(float dist) const
{
    if (dist < ATTACK_DISTANCE)
        dist = ATTACK_DISTANCE;
    if (dist > MAX_SEARCHER_DISTANCE)
        dist = MAX_SEARCHER_DISTANCE;

    Unit* target = nullptr;
    Acore::NearestHostileUnitInAttackDistanceCheck u_check(this, dist);
    Acore::UnitLastSearcher<Acore::NearestHostileUnitInAttackDistanceCheck> searcher(this, target, u_check);
    Cell::VisitAllObjects(this, searcher, std::max(dist, ATTACK_DISTANCE));

    return target;
}

void Creature::SendAIReaction(AiReaction reactionType)
{
    WorldPacket data(SMSG_AI_REACTION, 12);

    data << GetGUID();
    data << uint32(reactionType);

    ((WorldObject*)this)->SendMessageToSet(&data, true);

    LOG_DEBUG("network", "WORLD: Sent SMSG_AI_REACTION, type {}.", reactionType);
}

void Creature::CallAssistance(Unit* target /*= nullptr*/)
{
    if (!target)
    {
        target = GetVictim();
    }

    if (!m_AlreadyCallAssistance && target && !IsPet() && !IsCharmed())
    {
        SetNoCallAssistance(true);

        float radius = sWorld->getFloatConfig(CONFIG_CREATURE_FAMILY_ASSISTANCE_RADIUS);

        if (radius > 0)
        {
            std::list<Creature*> assistList;

            Acore::AnyAssistCreatureInRangeCheck u_check(this, target, radius);
            Acore::CreatureListSearcher<Acore::AnyAssistCreatureInRangeCheck> searcher(this, assistList, u_check);
            Cell::VisitGridObjects(this, searcher, radius);

            if (!assistList.empty())
            {
                AssistDelayEvent* e = new AssistDelayEvent(target->GetGUID(), this);
                while (!assistList.empty())
                {
                    // Pushing guids because in delay can happen some creature gets despawned => invalid pointer
                    e->AddAssistant((*assistList.begin())->GetGUID());
                    assistList.pop_front();
                }
                m_Events.AddEvent(e, m_Events.CalculateTime(sWorld->getIntConfig(CONFIG_CREATURE_FAMILY_ASSISTANCE_DELAY)));
            }
        }
    }
}

void Creature::CallForHelp(float radius, Unit* target /*= nullptr*/)
{
    if (radius <= 0.0f || IsPet() || IsCharmed())
    {
        return;
    }

    if (!target)
    {
        target = GetVictim();
    }

    if (!target)
    {
        return;
    }

    Acore::CallOfHelpCreatureInRangeDo u_do(this, target, radius);
    Acore::CreatureWorker<Acore::CallOfHelpCreatureInRangeDo> worker(this, u_do);
    Cell::VisitGridObjects(this, worker, radius);
}

bool Creature::CanAssistTo(Unit const* u, Unit const* enemy, bool checkfaction /*= true*/) const
{
    // is it true?
    if (!HasReactState(REACT_AGGRESSIVE))
        return false;

    // we don't need help from zombies :)
    if (!IsAlive())
        return false;

    // Xinef: we cannot assist in evade mode
    if (IsInEvadeMode())
        return false;

    // pussywizard: or if enemy is in evade mode
    if (enemy && enemy->IsCreature() && enemy->ToCreature()->IsInEvadeMode())
        return false;

    // we don't need help from non-combatant ;)
    if (IsCivilian())
        return false;

    if (HasUnitFlag(UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE) || IsImmuneToNPC())
        return false;

    // skip fighting creature
    if (IsEngaged())
        return false;

    // only free creature
    if (GetCharmerOrOwnerGUID())
        return false;

    /// @todo: Implement aggro range, detection range and assistance range templates
    if (m_creatureInfo->HasFlagsExtra(CREATURE_FLAG_EXTRA_IGNORE_ALL_ASSISTANCE_CALLS))
    {
        return false;
    }

    // only from same creature faction
    if (checkfaction)
    {
        if (GetFaction() != u->GetFaction())
            return false;
    }
    else
    {
        if (!IsFriendlyTo(u))
            return false;
    }

    // skip non hostile to caster enemy creatures
    if (!IsHostileTo(enemy))
        return false;

    // Check if can see the enemy
    if (!CanSeeOrDetect(enemy))
    {
        return false;
    }

    return true;
}

// use this function to avoid having hostile creatures attack
// friendlies and other mobs they shouldn't attack
bool Creature::_IsTargetAcceptable(Unit const* target) const
{
    ASSERT(target);

    // if the target cannot be attacked, the target is not acceptable
    if (IsFriendlyTo(target) || !target->isTargetableForAttack(false, this) || (m_vehicle && (IsOnVehicle(target) || m_vehicle->GetBase()->IsOnVehicle(target))))
        return false;

    if (target->HasUnitState(UNIT_STATE_DIED))
    {
        // some creatures can detect fake death
        if (CanIgnoreFeignDeath() && target->HasUnitFlag2(UNIT_FLAG2_FEIGN_DEATH))
            return true;
        else
            return false;
    }

    Unit const* targetVictim = target->getAttackerForHelper();

    // if I'm already fighting target, or I'm hostile towards the target, the target is acceptable
    if (IsEngagedBy(target) || IsHostileTo(target))
        return true;

    // if the target's victim is friendly, and the target is neutral, the target is acceptable
    if (targetVictim && !IsNeutralToAll() && IsFriendlyTo(targetVictim))
        return true;

    // if the target's victim is not friendly, or the target is friendly, the target is not acceptable
    return false;
}

void Creature::UpdateMoveInLineOfSightState()
{
    // xinef: pets, guardians and units with scripts / smartAI should be skipped
    if (IsPet() || HasUnitTypeMask(UNIT_MASK_MINION | UNIT_MASK_SUMMON | UNIT_MASK_GUARDIAN | UNIT_MASK_CONTROLABLE_GUARDIAN) ||
            GetScriptId() || GetAIName() == "SmartAI")
    {
        m_moveInLineOfSightStrictlyDisabled = false;
        m_moveInLineOfSightDisabled = false;
        return;
    }

    if (IsTrigger() || IsCivilian() || GetCreatureType() == CREATURE_TYPE_NON_COMBAT_PET || IsCritter() || GetAIName() == "NullCreatureAI")
    {
        m_moveInLineOfSightDisabled = true;
        m_moveInLineOfSightStrictlyDisabled = true;
        return;
    }

    bool nonHostile = true;
    if (FactionTemplateEntry const* factionTemplate = sFactionTemplateStore.LookupEntry(GetFaction()))
        if (factionTemplate->hostileMask || factionTemplate->enemyFaction[0] || factionTemplate->enemyFaction[1] || factionTemplate->enemyFaction[2] || factionTemplate->enemyFaction[3])
            nonHostile = false;

    if (nonHostile)
        m_moveInLineOfSightDisabled = true;
    else
        m_moveInLineOfSightDisabled = false;
}

void Creature::SaveRespawnTime()
{
    if (IsSummon() || !m_spawnId || (m_creatureData && !m_creatureData->dbData))
        return;

    GetMap()->SaveCreatureRespawnTime(m_spawnId, m_respawnTime);
}

bool Creature::CanCreatureAttack(Unit const* victim, bool skipDistCheck) const
{
    if (!victim->IsInMap(this))
        return false;

    if (!IsValidAttackTarget(victim))
        return false;

    if (!victim->isInAccessiblePlaceFor(this))
        return false;

    if (IsAIEnabled && !AI()->CanAIAttack(victim))
        return false;

    // pussywizard: we cannot attack in evade mode
    if (IsInEvadeMode())
        return false;

    // pussywizard: or if enemy is in evade mode
    if (victim->IsCreature() && victim->ToCreature()->IsInEvadeMode())
        return false;

    // cannot attack if is during 5 second grace period, unless being attacked
    if (m_respawnedTime && (GameTime::GetGameTime().count() - m_respawnedTime) < 5 && !IsEngagedBy(victim))
    {
        return false;
    }

    // if victim is in FD and we can't see that
    if (victim->HasUnitFlag2(UNIT_FLAG2_FEIGN_DEATH) && !CanIgnoreFeignDeath())
    {
        return false;
    }

    if (!GetCharmerOrOwnerGUID().IsPlayer())
    {
        if (GetMap()->IsDungeon())
            return true;

        float visibility = std::max<float>(GetVisibilityRange(), victim->GetVisibilityRange());

        // if outside visibility
        if (!IsWithinDist(victim, visibility))
            return false;

        // pussywizard: don't check distance to home position if recently damaged (allow kiting away from spawnpoint!)
        // xinef: this should include taunt auras
        if (!isWorldBoss() && (GetLastLeashExtensionTime() + 12 > GameTime::GetGameTime().count() || HasAuraType(SPELL_AURA_MOD_TAUNT)))
            return true;
    }

    if (skipDistCheck)
        return true;

    // xinef: added size factor for huge npcs
    float dist = std::min<float>(GetDetectionRange() + GetObjectSize() * 2, 150.0f);

    if (Unit* unit = GetCharmerOrOwner())
    {
        dist = std::min<float>(GetMap()->GetVisibilityRange() + GetObjectSize() * 2, 150.0f);
        return victim->IsWithinDist(unit, dist);
    }
    else
    {
        // to prevent creatures in air ignore attacks because distance is already too high...
        if (GetMovementTemplate().IsFlightAllowed())
            return victim->IsInDist2d(&m_homePosition, dist);
        else
            return victim->IsInDist(&m_homePosition, dist);
    }
}

CreatureAddon const* Creature::GetCreatureAddon() const
{
    if (m_spawnId)
    {
        if (CreatureAddon const* addon = sObjectMgr->GetCreatureAddon(m_spawnId))
            return addon;
    }

    // dependent from difficulty mode entry
    return sObjectMgr->GetCreatureTemplateAddon(GetCreatureTemplate()->Entry);
}

//creature_addon table
bool Creature::LoadCreaturesAddon(bool reload)
{
    CreatureAddon const* cainfo = GetCreatureAddon();
    if (!cainfo)
        return false;

    if (cainfo->mount != 0)
        Mount(cainfo->mount);

    if (cainfo->bytes1 != 0)
    {
        // 0 StandState
        // 1 FreeTalentPoints   Pet only, so always 0 for default creature
        // 2 StandFlags
        // 3 StandMiscFlags

        SetByteValue(UNIT_FIELD_BYTES_1, UNIT_BYTES_1_OFFSET_STAND_STATE, uint8(cainfo->bytes1 & 0xFF));
        //SetByteValue(UNIT_FIELD_BYTES_1, UNIT_BYTES_1_OFFSET_PET_TALENTS, uint8((cainfo->bytes1 >> 8) & 0xFF));
        SetByteValue(UNIT_FIELD_BYTES_1, UNIT_BYTES_1_OFFSET_PET_TALENTS, 0);
        SetByteValue(UNIT_FIELD_BYTES_1, UNIT_BYTES_1_OFFSET_VIS_FLAG, uint8((cainfo->bytes1 >> 16) & 0xFF));
        SetByteValue(UNIT_FIELD_BYTES_1, UNIT_BYTES_1_OFFSET_ANIM_TIER, uint8((cainfo->bytes1 >> 24) & 0xFF));

        //! Suspected correlation between UNIT_FIELD_BYTES_1, offset 3, value 0x2:
        //! If no inhabittype_fly (if no MovementFlag_DisableGravity or MovementFlag_CanFly flag found in sniffs)
        //! Check using InhabitType as movement flags are assigned dynamically
        //! basing on whether the creature is in air or not
        //! Set MovementFlag_Hover. Otherwise do nothing.
        if (CanHover())
            AddUnitMovementFlag(MOVEMENTFLAG_HOVER);
    }

    if (cainfo->bytes2 != 0)
    {
        // 0 SheathState
        // 1 Bytes2Flags
        // 2 UnitRename         Pet only, so always 0 for default creature
        // 3 ShapeshiftForm     Must be determined/set by shapeshift spell/aura

        SetByteValue(UNIT_FIELD_BYTES_2, 0, uint8(cainfo->bytes2 & 0xFF));
        //SetByteValue(UNIT_FIELD_BYTES_2, 1, uint8((cainfo->bytes2 >> 8) & 0xFF));
        //SetByteValue(UNIT_FIELD_BYTES_2, 2, uint8((cainfo->bytes2 >> 16) & 0xFF));
        SetByteValue(UNIT_FIELD_BYTES_2, 2, 0);
        //SetByteValue(UNIT_FIELD_BYTES_2, 3, uint8((cainfo->bytes2 >> 24) & 0xFF));
        SetByteValue(UNIT_FIELD_BYTES_2, 3, 0);
    }

    SetUInt32Value(UNIT_NPC_EMOTESTATE, cainfo->emote);

    // Check if visibility distance different
    if (cainfo->visibilityDistanceType != VisibilityDistanceType::Normal)
    {
        SetVisibilityDistanceOverride(cainfo->visibilityDistanceType);
    }

    //Load Path
    if (cainfo->path_id != 0)
    {
        if (sWorld->getBoolConfig(CONFIG_SET_ALL_CREATURES_WITH_WAYPOINT_MOVEMENT_ACTIVE))
            setActive(true);
        m_path_id = cainfo->path_id;
    }

    if (!cainfo->auras.empty())
    {
        for (std::vector<uint32>::const_iterator itr = cainfo->auras.begin(); itr != cainfo->auras.end(); ++itr)
        {
            SpellInfo const* AdditionalSpellInfo = sSpellMgr->GetSpellInfo(*itr);
            if (!AdditionalSpellInfo)
            {
                LOG_ERROR("sql.sql", "Creature ({}) has wrong spell {} defined in `auras` field.", GetGUID().ToString(), *itr);
                continue;
            }

            // skip already applied aura
            if (HasAura(*itr))
            {
                if (!reload)
                    LOG_ERROR("sql.sql", "Creature ({}) has duplicate aura (spell {}) in `auras` field.", GetGUID().ToString(), *itr);

                continue;
            }

            AddAura(*itr, this);
            LOG_DEBUG("entities.unit", "Spell: {} added to creature ({})", *itr, GetGUID().ToString());
        }
    }

    return true;
}

/// Send a message to LocalDefense channel for players opposition team in the zone
void Creature::SendZoneUnderAttackMessage(Player* attacker)
{
    WorldPacket data(SMSG_ZONE_UNDER_ATTACK, 4);
    data << (uint32)GetAreaId();
    sWorld->SendGlobalMessage(&data, nullptr, (attacker->GetTeamId() == TEAM_ALLIANCE ? TEAM_HORDE : TEAM_ALLIANCE));
}

/**
 * @brief Set in combat all units in the dungeon/raid. Affect only units with IsAIEnabled.
 */
void Creature::SetInCombatWithZone()
{
    if (IsAIEnabled)
        AI()->DoZoneInCombat();
}

void Creature::ProhibitSpellSchool(SpellSchoolMask idSchoolMask, uint32 unTimeMs)
{
    for (uint8 i = SPELL_SCHOOL_NORMAL; i < MAX_SPELL_SCHOOL; ++i)
    {
        if (idSchoolMask & (1 << i))
        {
            m_ProhibitSchoolTime[i] = GameTime::GetGameTimeMS().count() + unTimeMs;
        }
    }
}

bool Creature::IsSpellProhibited(SpellSchoolMask idSchoolMask) const
{
    for (uint8 i = SPELL_SCHOOL_NORMAL; i < MAX_SPELL_SCHOOL; ++i)
    {
        if (idSchoolMask & (1 << i))
        {
            if (m_ProhibitSchoolTime[i] >= GameTime::GetGameTimeMS().count())
            {
                return true;
            }
        }
    }

    return false;
}

void Creature::ClearProhibitedSpellTimers()
{
    for (uint8 i = SPELL_SCHOOL_NORMAL; i < MAX_SPELL_SCHOOL; ++i)
    {
        m_ProhibitSchoolTime[i] = 0;
    }
}

void Creature::_AddCreatureSpellCooldown(uint32 spell_id, uint16 categoryId, uint32 end_time)
{
    CreatureSpellCooldown spellCooldown;
    spellCooldown.category = categoryId;
    spellCooldown.end = GameTime::GetGameTimeMS().count() + end_time;
    m_CreatureSpellCooldowns[spell_id] = std::move(spellCooldown);
}

void Creature::AddSpellCooldown(uint32 spell_id, uint32 /*itemid*/, uint32 end_time, bool /*needSendToClient*/, bool /*forceSendToSpectator*/)
{
    SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spell_id);
    if (!spellInfo)
        return;

    // used in proc system, otherwise normal creature cooldown
    if (end_time)
    {
        _AddCreatureSpellCooldown(spellInfo->Id, 0, end_time);
        return;
    }

    uint32 spellcooldown = spellInfo->RecoveryTime;
    uint32 categoryId = spellInfo->GetCategory();
    uint32 categorycooldown = categoryId ? spellInfo->CategoryRecoveryTime : 0;
    if (Player* modOwner = GetSpellModOwner())
    {
        modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_COOLDOWN, spellcooldown);
        modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_COOLDOWN, categorycooldown);
    }

    SpellCategoryStore::const_iterator i_scstore = sSpellsByCategoryStore.find(categoryId);
    if (categorycooldown && i_scstore != sSpellsByCategoryStore.end())
    {
        for (SpellCategorySet::const_iterator i_scset = i_scstore->second.begin(); i_scset != i_scstore->second.end(); ++i_scset)
        {
            _AddCreatureSpellCooldown(i_scset->second, categoryId, categorycooldown);
        }
    }
    else if (spellcooldown)
    {
        _AddCreatureSpellCooldown(spellInfo->Id, 0, spellcooldown);
    }

    if (sSpellMgr->HasSpellCooldownOverride(spellInfo->Id))
    {
        if (IsCharmed() && GetCharmer()->IsPlayer())
        {
            WorldPacket data;
            BuildCooldownPacket(data, SPELL_COOLDOWN_FLAG_NONE, spellInfo->Id, spellcooldown);
            GetCharmer()->ToPlayer()->SendDirectMessage(&data);
        }
    }
}

uint32 Creature::GetSpellCooldown(uint32 spell_id) const
{
    CreatureSpellCooldowns::const_iterator itr = m_CreatureSpellCooldowns.find(spell_id);
    if (itr == m_CreatureSpellCooldowns.end())
        return 0;

    return itr->second.end > GameTime::GetGameTimeMS().count() ? itr->second.end - GameTime::GetGameTimeMS().count() : 0;
}

bool Creature::HasSpellCooldown(uint32 spell_id) const
{
    CreatureSpellCooldowns::const_iterator itr = m_CreatureSpellCooldowns.find(spell_id);
    return (itr != m_CreatureSpellCooldowns.end() && itr->second.end > GameTime::GetGameTimeMS().count());
}

bool Creature::HasSpell(uint32 spellID) const
{
    uint8 i;
    for (i = 0; i < MAX_CREATURE_SPELLS; ++i)
        if (spellID == m_spells[i])
            break;
    return i < MAX_CREATURE_SPELLS;                         //broke before end of iteration of known spells
}

time_t Creature::GetRespawnTimeEx() const
{
    time_t now = GameTime::GetGameTime().count();

    if (m_respawnTime > now)
        return m_respawnTime;
    else
        return now;
}

void Creature::GetRespawnPosition(float& x, float& y, float& z, float* ori, float* dist) const
{
    if (m_spawnId)
    {
        if (CreatureData const* data = sObjectMgr->GetCreatureData(m_spawnId))
        {
            x = data->posX;
            y = data->posY;
            z = data->posZ;
            if (ori)
                *ori = data->orientation;
            if (dist)
                *dist = data->wander_distance;

            return;
        }
    }

    // xinef: changed this from current position to home position, fixes world summons with infinite duration
    if (GetTransport())
    {
        x = GetPositionX();
        y = GetPositionY();
        z = GetPositionZ();
        if (ori)
            *ori = GetOrientation();
    }
    else
    {
        Position homePos = GetHomePosition();
        x = homePos.GetPositionX();
        y = homePos.GetPositionY();
        z = homePos.GetPositionZ();
        if (ori)
            *ori = homePos.GetOrientation();
    }
    if (dist)
        *dist = 0;
}

CreatureMovementData const& Creature::GetMovementTemplate() const
{
    if (CreatureMovementData const* movementOverride = sObjectMgr->GetCreatureMovementOverride(m_spawnId))
        return *movementOverride;

    return GetCreatureTemplate()->Movement;
}

void Creature::AllLootRemovedFromCorpse()
{
    if (loot.loot_type != LOOT_SKINNING && !IsPet() && GetCreatureTemplate()->SkinLootId && hasLootRecipient())
    {
        if (LootTemplates_Skinning.HaveLootFor(GetCreatureTemplate()->SkinLootId))
        {
            SetUnitFlag(UNIT_FLAG_SKINNABLE);
        }
    }

    time_t now = GameTime::GetGameTime().count();
    if (m_corpseRemoveTime <= now)
    {
        return;
    }

    float decayRate = sWorld->getRate(RATE_CORPSE_DECAY_LOOTED);
    uint32 diff = uint32((m_corpseRemoveTime - now) * decayRate);

    m_respawnTime -= diff;

    // corpse skinnable, but without skinning flag, and then skinned, corpse will despawn next update
    if (loot.loot_type == LOOT_SKINNING)
    {
        m_corpseRemoveTime = GameTime::GetGameTime().count();
    }
    else
    {
        m_corpseRemoveTime -= diff;
    }
}

uint8 Creature::getLevelForTarget(WorldObject const* target) const
{
    if (!isWorldBoss() || !target->ToUnit())
        return Unit::getLevelForTarget(target);

    uint16 level = target->ToUnit()->GetLevel() + sWorld->getIntConfig(CONFIG_WORLD_BOSS_LEVEL_DIFF);
    if (level < 1)
        return 1;
    if (level > 255)
        return 255;
    return uint8(level);
}

std::string const& Creature::GetAIName() const
{
    return sObjectMgr->GetCreatureTemplate(GetEntry())->AIName;
}

std::string Creature::GetScriptName() const
{
    return sObjectMgr->GetScriptName(GetScriptId());
}

uint32 Creature::GetScriptId() const
{
    if (CreatureData const* creatureData = GetCreatureData())
    {
        uint32 scriptId = creatureData->ScriptId;
        if (scriptId && GetEntry() == creatureData->id1)
            return scriptId;
    }

    return sObjectMgr->GetCreatureTemplate(GetEntry())->ScriptID;
}

VendorItemData const* Creature::GetVendorItems() const
{
    return sObjectMgr->GetNpcVendorItemList(GetEntry());
}

uint32 Creature::GetVendorItemCurrentCount(VendorItem const* vItem)
{
    if (!vItem->maxcount)
        return vItem->maxcount;

    VendorItemCounts::iterator itr = m_vendorItemCounts.begin();
    for (; itr != m_vendorItemCounts.end(); ++itr)
        if (itr->itemId == vItem->item)
            break;

    if (itr == m_vendorItemCounts.end())
        return vItem->maxcount;

    VendorItemCount* vCount = &*itr;

    time_t ptime = GameTime::GetGameTime().count();

    if (time_t(vCount->lastIncrementTime + vItem->incrtime) <= ptime)
    {
        ItemTemplate const* pProto = sObjectMgr->GetItemTemplate(vItem->item);

        uint32 diff = uint32((ptime - vCount->lastIncrementTime) / vItem->incrtime);
        if ((vCount->count + diff * pProto->BuyCount) >= vItem->maxcount)
        {
            m_vendorItemCounts.erase(itr);
            return vItem->maxcount;
        }

        vCount->count += diff * pProto->BuyCount;
        vCount->lastIncrementTime = ptime;
    }

    return vCount->count;
}

uint32 Creature::UpdateVendorItemCurrentCount(VendorItem const* vItem, uint32 used_count)
{
    if (!vItem->maxcount)
        return 0;

    VendorItemCounts::iterator itr = m_vendorItemCounts.begin();
    for (; itr != m_vendorItemCounts.end(); ++itr)
        if (itr->itemId == vItem->item)
            break;

    if (itr == m_vendorItemCounts.end())
    {
        uint32 new_count = vItem->maxcount > used_count ? vItem->maxcount - used_count : 0;
        m_vendorItemCounts.push_back(VendorItemCount(vItem->item, new_count));
        return new_count;
    }

    VendorItemCount* vCount = &*itr;

    time_t ptime = GameTime::GetGameTime().count();

    if (time_t(vCount->lastIncrementTime + vItem->incrtime) <= ptime)
    {
        ItemTemplate const* pProto = sObjectMgr->GetItemTemplate(vItem->item);

        uint32 diff = uint32((ptime - vCount->lastIncrementTime) / vItem->incrtime);
        if ((vCount->count + diff * pProto->BuyCount) < vItem->maxcount)
            vCount->count += diff * pProto->BuyCount;
        else
            vCount->count = vItem->maxcount;
    }

    vCount->count = vCount->count > used_count ? vCount->count - used_count : 0;
    vCount->lastIncrementTime = ptime;
    return vCount->count;
}

TrainerSpellData const* Creature::GetTrainerSpells() const
{
    return sObjectMgr->GetNpcTrainerSpells(GetEntry());
}

// overwrite WorldObject function for proper name localization
std::string const& Creature::GetNameForLocaleIdx(LocaleConstant loc_idx) const
{
    if (loc_idx != DEFAULT_LOCALE)
    {
        uint8 uloc_idx = uint8(loc_idx);
        CreatureLocale const* cl = sObjectMgr->GetCreatureLocale(GetEntry());
        if (cl)
        {
            if (cl->Name.size() > uloc_idx && !cl->Name[uloc_idx].empty())
                return cl->Name[uloc_idx];
        }
    }

    return GetName();
}

void Creature::SetPosition(float x, float y, float z, float o)
{
    if (!Acore::IsValidMapCoord(x, y, z, o))
        return;

    GetMap()->CreatureRelocation(this, x, y, z, o);
}

bool Creature::IsDungeonBoss() const
{
    if (GetOwnerGUID().IsPlayer())
        return false;

    CreatureTemplate const* cinfo = sObjectMgr->GetCreatureTemplate(GetEntry());
    return cinfo && (cinfo->flags_extra & CREATURE_FLAG_EXTRA_DUNGEON_BOSS);
}

bool Creature::IsImmuneToKnockback() const
{
    if (GetOwnerGUID().IsPlayer())
        return false;

    CreatureTemplate const* cinfo = sObjectMgr->GetCreatureTemplate(GetEntry());
    return cinfo && (cinfo->flags_extra & CREATURE_FLAG_EXTRA_IMMUNITY_KNOCKBACK);
}

bool Creature::HasWeapon(WeaponAttackType type) const
{
    const uint8 slot = uint8(type);
    ItemEntry const* item = sItemStore.LookupEntry(GetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + slot));

    return ((item && item->ClassID == ITEM_CLASS_WEAPON) || (type == OFF_ATTACK && CanDualWield()));
}

/**
 * @brief Enable or disable the creature's walk mode by removing: MOVEMENTFLAG_WALKING. Infom also the client
 */
bool Creature::SetWalk(bool enable)
{
    if (!Unit::SetWalk(enable))
        return false;

    WorldPacket data(enable ? SMSG_SPLINE_MOVE_SET_WALK_MODE : SMSG_SPLINE_MOVE_SET_RUN_MODE, 9);
    data << GetPackGUID();
    SendMessageToSet(&data, false);
    return true;
}

/**
 * @brief Enable or disable the creature's fly mode by adding or removing: MOVEMENTFLAG_FLYING. Infom also the client
 */
bool Creature::SetDisableGravity(bool disable, bool packetOnly /*= false*/, bool updateAnimationTier /*= true*/)
{
    //! It's possible only a packet is sent but moveflags are not updated
    //! Need more research on this
    if (!packetOnly && !Unit::SetDisableGravity(disable))
        return false;

    if (m_movedByPlayer)
    {
        WorldPacket data(disable ? SMSG_MOVE_GRAVITY_DISABLE : SMSG_MOVE_GRAVITY_ENABLE, 12);
        data << GetPackGUID();
        data << uint32(0); //! movement counter
        m_movedByPlayer->ToPlayer()->SendDirectMessage(&data);

        data.Initialize(MSG_MOVE_GRAVITY_CHNG, 64);
        data << GetPackGUID();
        BuildMovementPacket(&data);
        m_movedByPlayer->ToPlayer()->SendMessageToSet(&data, false);
        return true;
    }

    if (updateAnimationTier && IsAlive() && !HasUnitState(UNIT_STATE_ROOT) && !IsRooted())
    {
        if (IsLevitating())
            SetByteValue(UNIT_FIELD_BYTES_1, UNIT_BYTES_1_OFFSET_ANIM_TIER, UNIT_BYTE1_FLAG_FLY);
        else if (IsHovering())
            SetByteValue(UNIT_FIELD_BYTES_1, UNIT_BYTES_1_OFFSET_ANIM_TIER, UNIT_BYTE1_FLAG_HOVER);
        else
            SetByteValue(UNIT_FIELD_BYTES_1, UNIT_BYTES_1_OFFSET_ANIM_TIER, UNIT_BYTE1_FLAG_GROUND);
    }

    WorldPacket data(disable ? SMSG_SPLINE_MOVE_GRAVITY_DISABLE : SMSG_SPLINE_MOVE_GRAVITY_ENABLE, 9);
    data << GetPackGUID();
    SendMessageToSet(&data, false);
    return true;
}

bool Creature::SetSwim(bool enable)
{
    if (!Unit::SetSwim(enable))
        return false;

    WorldPacket data(enable ? SMSG_SPLINE_MOVE_START_SWIM : SMSG_SPLINE_MOVE_STOP_SWIM);
    data << GetPackGUID();
    SendMessageToSet(&data, true);
    return true;
}

/**
 * @brief This method check the current flag/status of a creature and its inhabit type
 *
 * Pets should swim by default to properly follow the player
 * NOTE: You can set the UNIT_FLAG_CANNOT_SWIM temporary to deny a creature to swim
 *
 */
bool Creature::CanSwim() const
{
    if (Unit::CanSwim() || (!Unit::CanSwim() && !CanFly()))
        return true;

    if (IsPet())
        return true;

    return false;
}

bool Creature::CanEnterWater() const
{
    if (CanSwim())
        return true;

    return GetMovementTemplate().IsSwimAllowed();
}

void Creature::RefreshSwimmingFlag(bool recheck)
{
    if (!_isMissingSwimmingFlagOutOfCombat || recheck)
        _isMissingSwimmingFlagOutOfCombat = !HasUnitFlag(UNIT_FLAG_SWIMMING);

    // Check if the creature has UNIT_FLAG_SWIMMING and add it if it's missing
    // Creatures must be able to chase a target in water if they can enter water
    if (_isMissingSwimmingFlagOutOfCombat && CanEnterWater())
        SetUnitFlag(UNIT_FLAG_SWIMMING);
}

bool Creature::SetCanFly(bool enable, bool  /*packetOnly*/ /* = false */)
{
    if (!Unit::SetCanFly(enable))
        return false;

    if (m_movedByPlayer)
    {
        sScriptMgr->AnticheatSetCanFlybyServer(m_movedByPlayer->ToPlayer(), enable);

        if (!enable)
            m_movedByPlayer->ToPlayer()->SetFallInformation(GameTime::GetGameTime().count(), m_movedByPlayer->ToPlayer()->GetPositionZ());

        WorldPacket data(enable ? SMSG_MOVE_SET_CAN_FLY : SMSG_MOVE_UNSET_CAN_FLY, 12);
        data << GetPackGUID();
        data << uint32(0); //! movement counter
        m_movedByPlayer->ToPlayer()->SendDirectMessage(&data);

        data.Initialize(MSG_MOVE_UPDATE_CAN_FLY, 64);
        data << GetPackGUID();
        BuildMovementPacket(&data);
        m_movedByPlayer->ToPlayer()->SendMessageToSet(&data, false);
        return true;
    }

    WorldPacket data(enable ? SMSG_SPLINE_MOVE_SET_FLYING : SMSG_SPLINE_MOVE_UNSET_FLYING, 9);
    data << GetPackGUID();
    SendMessageToSet(&data, false);
    return true;
}

bool Creature::SetWaterWalking(bool enable, bool packetOnly /* = false */)
{
    if (!packetOnly && !Unit::SetWaterWalking(enable))
        return false;

    if (m_movedByPlayer)
    {
        WorldPacket data(enable ? SMSG_MOVE_WATER_WALK : SMSG_MOVE_LAND_WALK, 12);
        data << GetPackGUID();
        data << uint32(0); //! movement counter
        m_movedByPlayer->ToPlayer()->SendDirectMessage(&data);

        data.Initialize(MSG_MOVE_WATER_WALK, 64);
        data << GetPackGUID();
        BuildMovementPacket(&data);
        m_movedByPlayer->ToPlayer()->SendMessageToSet(&data, false);
        return true;
    }

    WorldPacket data(enable ? SMSG_SPLINE_MOVE_WATER_WALK : SMSG_SPLINE_MOVE_LAND_WALK, 9);
    data << GetPackGUID();
    SendMessageToSet(&data, true);
    return true;
}

bool Creature::SetFeatherFall(bool enable, bool packetOnly /* = false */)
{
    if (!packetOnly && !Unit::SetFeatherFall(enable))
        return false;

    if (m_movedByPlayer)
    {
        WorldPacket data(enable ? SMSG_MOVE_FEATHER_FALL : SMSG_MOVE_NORMAL_FALL, 12);
        data << GetPackGUID();
        data << uint32(0); //! movement counter
        m_movedByPlayer->ToPlayer()->SendDirectMessage(&data);

        data.Initialize(MSG_MOVE_FEATHER_FALL, 64);
        data << GetPackGUID();
        BuildMovementPacket(&data);
        m_movedByPlayer->ToPlayer()->SendMessageToSet(&data, false);
        return true;
    }

    WorldPacket data(enable ? SMSG_SPLINE_MOVE_FEATHER_FALL : SMSG_SPLINE_MOVE_NORMAL_FALL, 9);
    data << GetPackGUID();
    SendMessageToSet(&data, true);
    return true;
}

bool Creature::SetHover(bool enable, bool packetOnly /*= false*/, bool updateAnimationTier /*= true*/)
{
    if (!packetOnly && !Unit::SetHover(enable))
        return false;

    if (updateAnimationTier && IsAlive() && !HasUnitState(UNIT_STATE_ROOT) && !IsRooted())
    {
        if (IsLevitating())
            SetByteValue(UNIT_FIELD_BYTES_1, UNIT_BYTES_1_OFFSET_ANIM_TIER, UNIT_BYTE1_FLAG_FLY);
        else if (IsHovering())
            SetByteValue(UNIT_FIELD_BYTES_1, UNIT_BYTES_1_OFFSET_ANIM_TIER, UNIT_BYTE1_FLAG_HOVER);
        else
            SetByteValue(UNIT_FIELD_BYTES_1, UNIT_BYTES_1_OFFSET_ANIM_TIER, UNIT_BYTE1_FLAG_GROUND);
    }

    WorldPacket data(enable ? SMSG_SPLINE_MOVE_SET_HOVER : SMSG_SPLINE_MOVE_UNSET_HOVER, 9);
    data << GetPackGUID();
    SendMessageToSet(&data, false);
    return true;
}

float Creature::GetAggroRange(Unit const* target) const
{
    // Determines the aggro range for creatures
    // Based on data from wowwiki due to lack of 3.3.5a data

    float aggroRate = sWorld->getRate(RATE_CREATURE_AGGRO);
    if (aggroRate == 0)
        return 0.0f;

    auto creatureLevel = target->getLevelForTarget(this);
    auto playerLevel  = getLevelForTarget(target);
    int32 levelDiff = int32(creatureLevel) - int32(playerLevel);

    // The maximum Aggro Radius is capped at 45 yards (25 level difference)
    if (levelDiff < -25)
        levelDiff = -25;

    // The base aggro radius for mob of same level
    auto aggroRadius = GetDetectionRange();
    if (aggroRadius < 1)
    {
        return 0.0f;
    }
    // Aggro Radius varies with level difference at a rate of roughly 1 yard/level
    aggroRadius -= (float)levelDiff;

    // detect range auras
    aggroRadius += GetTotalAuraModifier(SPELL_AURA_MOD_DETECT_RANGE);

    // detected range auras
    aggroRadius += target->GetTotalAuraModifier(SPELL_AURA_MOD_DETECTED_RANGE);

    // Just in case, we don't want pets running all over the map
    if (aggroRadius > MAX_AGGRO_RADIUS)
        aggroRadius = MAX_AGGRO_RADIUS;

    // Minimum Aggro Radius for a mob seems to be combat range (5 yards)
    // hunter pets seem to ignore minimum aggro radius so we'll default it a little higher
    float minRange = IsPet() ? 10.0f : 5.0f;
    if (aggroRadius < minRange)
        aggroRadius = minRange;

    return (aggroRadius * aggroRate);
}

void Creature::UpdateMovementFlags()
{
    // Do not update movement flags if creature is controlled by a player (charm/vehicle)
    if (m_movedByPlayer)
        return;

    CreatureTemplate const* info = GetCreatureTemplate();
    if (!info)
        return;

    // Creatures with CREATURE_FLAG_EXTRA_NO_MOVE_FLAGS_UPDATE should control MovementFlags in your own scripts
    if (info->flags_extra & CREATURE_FLAG_EXTRA_NO_MOVE_FLAGS_UPDATE)
        return;

    float ground = GetFloorZ();

    bool canHover = CanHover();
    bool isInAir  = (G3D::fuzzyGt(GetPositionZ(), ground + (canHover ? GetFloatValue(UNIT_FIELD_HOVERHEIGHT) : 0.0f) + GROUND_HEIGHT_TOLERANCE) || G3D::fuzzyLt(GetPositionZ(), ground - GROUND_HEIGHT_TOLERANCE)); // Can be underground too, prevent the falling

    if (GetMovementTemplate().IsFlightAllowed() && isInAir && !IsFalling())
    {
        if (GetMovementTemplate().Flight == CreatureFlightMovementType::CanFly)
            SetCanFly(true);
        else
            SetDisableGravity(true);

        if (!HasAuraType(SPELL_AURA_HOVER))
            SetHover(false);
    }
    else
    {
        SetCanFly(false);
        SetDisableGravity(false);
        if (IsAlive() && (CanHover() || HasAuraType(SPELL_AURA_HOVER)))
            SetHover(true);
    }

    if (!isInAir)
        RemoveUnitMovementFlag(MOVEMENTFLAG_FALLING);

    bool Swim = false;
    LiquidData const& liquidData = GetLiquidData();
    switch (liquidData.Status)
    {
        case LIQUID_MAP_WATER_WALK:
        case LIQUID_MAP_IN_WATER:
            Swim = GetPositionZ() - liquidData.DepthLevel > GetCollisionHeight() * 0.75f; // Shallow water at ~75% of collision height
            break;
        case LIQUID_MAP_UNDER_WATER:
            Swim = true;
            break;
        default:
            break;
    }

    SetSwim(CanSwim() && Swim);
}

float Creature::GetNativeObjectScale() const
{
    return ObjectMgr::ChooseDisplayId(GetCreatureTemplate())->DisplayScale;
}

void Creature::SetObjectScale(float scale)
{
    Unit::SetObjectScale(scale);

    float combatReach = DEFAULT_WORLD_OBJECT_SIZE;

    if (CreatureModelInfo const* minfo = sObjectMgr->GetCreatureModelInfo(GetDisplayId()))
    {
        SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS, (IsPet() ? 1.0f : minfo->bounding_radius) * scale);
        if (minfo->combat_reach > 0)
            combatReach = minfo->combat_reach;
    }

    if (IsPet())
        combatReach = DEFAULT_COMBAT_REACH;

    SetFloatValue(UNIT_FIELD_COMBATREACH, combatReach * scale);
}

void Creature::SetDisplayId(uint32 modelId, float displayScale /*= 1.f*/)
{
    Unit::SetDisplayId(modelId, displayScale);

    float combatReach = DEFAULT_WORLD_OBJECT_SIZE;

    if (CreatureModelInfo const* minfo = sObjectMgr->GetCreatureModelInfo(modelId))
    {
        SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS, (IsPet() ? 1.0f : minfo->bounding_radius) * GetObjectScale());
        if (minfo->combat_reach > 0)
            combatReach = minfo->combat_reach;
    }

    if (IsPet())
        combatReach = DEFAULT_COMBAT_REACH;

    SetObjectScale(displayScale);

    SetFloatValue(UNIT_FIELD_COMBATREACH, combatReach * GetObjectScale());
}

void Creature::SetDisplayFromModel(uint32 modelIdx)
{
    if (CreatureModel const* model = GetCreatureTemplate()->GetModelByIdx(modelIdx))
        SetDisplayId(model->CreatureDisplayID, model->DisplayScale);
}

void Creature::SetTarget(ObjectGuid guid)
{
    if (!_focusSpell)
        SetGuidValue(UNIT_FIELD_TARGET, guid);
}

void Creature::FocusTarget(Spell const* focusSpell, WorldObject const* target)
{
    // already focused
    if (_focusSpell)
        return;

    _focusSpell = focusSpell;

    SetGuidValue(UNIT_FIELD_TARGET, this == target ? ObjectGuid::Empty : target->GetGUID());
    if (focusSpell->GetSpellInfo()->HasAttribute(SPELL_ATTR5_AI_DOESNT_FACE_TARGET))
        AddUnitState(UNIT_STATE_ROTATING);

    // Set serverside orientation if needed (needs to be after attribute check)
    if (this == target && (movespline->Finalized() || GetMotionMaster()->GetCurrentMovementGeneratorType() == CHASE_MOTION_TYPE))
        SetFacingTo(GetOrientation());
    else
        SetInFront(target);
}

bool Creature::HasSpellFocus(Spell const* focusSpell) const
{
    if (isDead()) // dead creatures cannot focus
    {
        return false;
    }

    return focusSpell ? (focusSpell == _spellFocusInfo.Spell) : (_spellFocusInfo.Spell || _spellFocusInfo.Delay);
}

void Creature::ReleaseFocus(Spell const* focusSpell)
{
    // focused to something else
    if (focusSpell != _focusSpell)
        return;

    _focusSpell = nullptr;
    if (Unit* victim = GetVictim())
        SetGuidValue(UNIT_FIELD_TARGET, victim->GetGUID());
    else
        SetGuidValue(UNIT_FIELD_TARGET, ObjectGuid::Empty);

    if (focusSpell->GetSpellInfo()->HasAttribute(SPELL_ATTR5_AI_DOESNT_FACE_TARGET))
        ClearUnitState(UNIT_STATE_ROTATING);
}

float Creature::GetAttackDistance(Unit const* player) const
{
    float aggroRate = sWorld->getRate(RATE_CREATURE_AGGRO);

    if (aggroRate == 0)
        return 0.0f;

    if (!player)
        return 0.0f;

    uint32 playerLevel = player->getLevelForTarget(this);
    uint32 creatureLevel = getLevelForTarget(player);

    int32 levelDiff = static_cast<int32>(playerLevel) - static_cast<int32>(creatureLevel);

    // "The maximum Aggro Radius has a cap of 25 levels under. Example: A level 30 char has the same Aggro Radius of a level 5 char on a level 60 mob."
    if (levelDiff < -25)
        levelDiff = -25;

    // "The aggro radius of a mob having the same level as the player is roughly 20 yards"
    float retDistance = 20.0f;

    // "Aggro Radius varies with level difference at a rate of roughly 1 yard/level"
    // radius grow if playlevel < creaturelevel
    retDistance -= static_cast<float>(levelDiff);

    if (creatureLevel + 5 <= sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
    {
        // detect range auras
        retDistance += static_cast<float>( GetTotalAuraModifier(SPELL_AURA_MOD_DETECT_RANGE));

        // detected range auras
        retDistance += static_cast<float>( player->GetTotalAuraModifier(SPELL_AURA_MOD_DETECTED_RANGE));
    }

    // "Minimum Aggro Radius for a mob seems to be combat range (5 yards)"
    if (retDistance < 5.0f)
        retDistance = 5.0f;

    return (retDistance * aggroRate);
}

bool Creature::IsMovementPreventedByCasting() const
{
    Spell* spell = m_currentSpells[CURRENT_CHANNELED_SPELL];
    // first check if currently a movement allowed channel is active and we're not casting
    if (spell && spell->getState() != SPELL_STATE_FINISHED && spell->IsChannelActive() && spell->GetSpellInfo()->IsActionAllowedChannel())
    {
        return false;
    }

    if (HasSpellFocus())
    {
        return true;
    }

    if (HasUnitState(UNIT_STATE_CASTING))
    {
        return true;
    }

    return false;
}

void Creature::SetCannotReachTarget(ObjectGuid const& cannotReach)
{
    if (cannotReach == m_cannotReachTarget)
    {
        return;
    }

    m_cannotReachTarget = cannotReach;
    m_cannotReachTimer = 0;

    if (cannotReach)
    {
        LOG_DEBUG("entities.unit", "Creature::SetCannotReachTarget() called with true. Details: {}", GetDebugInfo());
    }
}

bool Creature::CanNotReachTarget() const
{
    return m_cannotReachTarget;
}

bool Creature::IsNotReachableAndNeedRegen() const
{
    if (CanNotReachTarget())
    {
        return m_cannotReachTimer >= (sWorld->getIntConfig(CONFIG_NPC_REGEN_TIME_IF_NOT_REACHABLE_IN_RAID) * IN_MILLISECONDS);
    }

    return false;
}

std::shared_ptr<time_t> const& Creature::GetLastLeashExtensionTimePtr() const
{
    if (m_lastLeashExtensionTime == nullptr)
        m_lastLeashExtensionTime = std::make_shared<time_t>(GameTime::GetGameTime().count());
    return m_lastLeashExtensionTime;
}

void Creature::SetLastLeashExtensionTimePtr(std::shared_ptr<time_t> const& timer)
{
    m_lastLeashExtensionTime = timer;
}

void Creature::ClearLastLeashExtensionTimePtr()
{
    m_lastLeashExtensionTime.reset();
}

time_t Creature::GetLastLeashExtensionTime() const
{
    return *GetLastLeashExtensionTimePtr();
}

void Creature::UpdateLeashExtensionTime()
{
    (*GetLastLeashExtensionTimePtr()) = GameTime::GetGameTime().count();
}

bool Creature::CanPeriodicallyCallForAssistance() const
{
    if (!IsInCombat())
        return false;

    if (HasUnitState(UNIT_STATE_DIED | UNIT_STATE_POSSESSED))
        return false;

    if (!CanHaveThreatList())
        return false;

    if (IsSummon() && GetMap()->Instanceable())
        return false;

    return true;
}

uint32 Creature::GetRandomId(uint32 id1, uint32 id2, uint32 id3)
{
    uint32 id = id1;
    uint8 ids = 0;

    if (id2)
    {
        ++ids;
        if (id3) ++ids;
    }

    if (ids)
    {
        uint8 idNumber = urand(0, ids);
        switch (idNumber)
        {
            case 0:
                id = id1;
                break;
            case 1:
                id = id2;
                break;
            case 2:
                id = id3;
                break;
        }
    }
    return id;
}

void Creature::SetPickPocketLootTime()
{
    lootPickPocketRestoreTime = GameTime::GetGameTime().count() + MINUTE + GetCorpseDelay() + GetRespawnTime();
}

bool Creature::CanGeneratePickPocketLoot() const
{
    return (lootPickPocketRestoreTime == 0 || lootPickPocketRestoreTime < GameTime::GetGameTime().count());
}

void Creature::SetRespawnTime(uint32 respawn)
{
    m_respawnTime = respawn ? GameTime::GetGameTime().count() + respawn : 0;
}

void Creature::SetCorpseRemoveTime(uint32 delay)
{
    m_corpseRemoveTime = GameTime::GetGameTime().count() + delay;
}

void Creature::ModifyThreatPercentTemp(Unit* victim, int32 percent, Milliseconds duration)
{
    if (victim)
    {
        float currentThreat = GetThreatMgr().GetThreat(victim);

        if (percent != 0.0f)
        {
            GetThreatMgr().ModifyThreatByPercent(victim, percent);
        }

        TemporaryThreatModifierEvent* pEvent = new TemporaryThreatModifierEvent(*this, victim->GetGUID(), currentThreat);
        m_Events.AddEvent(pEvent, m_Events.CalculateTime(duration.count()));
    }
}

bool Creature::IsDamageEnoughForLootingAndReward() const
{
    return (m_creatureInfo->flags_extra & CREATURE_FLAG_EXTRA_NO_PLAYER_DAMAGE_REQ) || (_playerDamageReq == 0 && _damagedByPlayer);
}

void Creature::LowerPlayerDamageReq(uint32 unDamage, bool damagedByPlayer /*= true*/)
{
    if (_playerDamageReq)
        _playerDamageReq > unDamage ? _playerDamageReq -= unDamage : _playerDamageReq = 0;

    if (!_damagedByPlayer)
    {
        _damagedByPlayer = damagedByPlayer;
    }
}

void Creature::ResetPlayerDamageReq()
{
    _playerDamageReq = GetHealth() / 2;
    _damagedByPlayer = false;
}

uint32 Creature::GetPlayerDamageReq() const
{
    return _playerDamageReq;
}

bool Creature::CanCastSpell(uint32 spellID) const
{
    SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellID);
    int32 currentPower = GetPower(getPowerType());

    if (HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SILENCED) || IsSpellProhibited(spellInfo->GetSchoolMask()))
    {
        return false;
    }

    if (spellInfo && (currentPower < spellInfo->CalcPowerCost(this, spellInfo->GetSchoolMask())))
    {
        return false;
    }

    return true;
}

void Creature::SetCombatMovement(bool allowMovement)
{
    _isCombatMovementAllowed = allowMovement;
}

ObjectGuid Creature::GetSummonerGUID() const
{
    if (TempSummon const* temp = ToTempSummon())
        return temp->GetSummonerGUID();

    LOG_DEBUG("entities.unit", "Creature::GetSummonerGUID() called by creature that is not a summon. Creature: {} ({})", GetEntry(), GetName());
    return ObjectGuid::Empty;
}

std::string Creature::GetDebugInfo() const
{
    std::stringstream sstr;
    sstr << Unit::GetDebugInfo() << "\n"
        << "AIName: " << GetAIName() << " ScriptName: " << GetScriptName()
        << " WaypointPath: " << GetWaypointPath() << " SpawnId: " << GetSpawnId();
    return sstr.str();
}
