root/trunk/src/game/Creature.cpp @ 37

Revision 37, 68.2 kB (checked in by yumileroy, 17 years ago)

[svn] * svn:eol-style native set on all files that need it

Original author: Neo2003
Date: 2008-10-11 14:16:25-05:00

Line 
1/*
2 * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17 */
18
19#include "Common.h"
20#include "Database/DatabaseEnv.h"
21#include "WorldPacket.h"
22#include "WorldSession.h"
23#include "World.h"
24#include "ObjectMgr.h"
25#include "SpellMgr.h"
26#include "Creature.h"
27#include "QuestDef.h"
28#include "GossipDef.h"
29#include "Player.h"
30#include "Opcodes.h"
31#include "Log.h"
32#include "LootMgr.h"
33#include "MapManager.h"
34#include "CreatureAI.h"
35#include "CreatureAISelector.h"
36#include "Formulas.h"
37#include "SpellAuras.h"
38#include "WaypointMovementGenerator.h"
39#include "InstanceData.h"
40#include "BattleGround.h"
41#include "Util.h"
42#include "GridNotifiers.h"
43#include "GridNotifiersImpl.h"
44#include "CellImpl.h"
45
46// apply implementation of the singletons
47#include "Policies/SingletonImp.h"
48
49void TrainerSpellData::Clear()
50{
51    for (TrainerSpellList::iterator itr = spellList.begin(); itr != spellList.end(); ++itr)
52        delete (*itr);
53    spellList.empty();
54}
55
56TrainerSpell const* TrainerSpellData::Find(uint32 spell_id) const
57{
58    for(TrainerSpellList::const_iterator itr = spellList.begin(); itr != spellList.end(); ++itr)
59        if((*itr)->spell == spell_id)
60            return *itr;
61
62    return NULL;
63}
64
65bool VendorItemData::RemoveItem( uint32 item_id )
66{
67    for(VendorItemList::iterator i = m_items.begin(); i != m_items.end(); ++i )
68    {
69        if((*i)->item==item_id)
70        {
71            m_items.erase(i);
72            return true;
73        }
74    }
75    return false;
76}
77
78size_t VendorItemData::FindItemSlot(uint32 item_id) const
79{
80    for(size_t i = 0; i < m_items.size(); ++i )
81        if(m_items[i]->item==item_id)
82            return i;
83    return m_items.size();
84}
85
86VendorItem const* VendorItemData::FindItem(uint32 item_id) const
87{
88    for(VendorItemList::const_iterator i = m_items.begin(); i != m_items.end(); ++i )
89        if((*i)->item==item_id)
90            return *i;
91    return NULL;
92}
93
94Creature::Creature() :
95Unit(), i_AI(NULL),
96lootForPickPocketed(false), lootForBody(false), m_groupLootTimer(0), lootingGroupLeaderGUID(0),
97m_lootMoney(0), m_lootRecipient(0),
98m_deathTimer(0), m_respawnTime(0), m_respawnDelay(25), m_corpseDelay(60), m_respawnradius(0.0f),
99m_gossipOptionLoaded(false),m_emoteState(0), m_isPet(false), m_isTotem(false),
100m_regenTimer(2000), m_defaultMovementType(IDLE_MOTION_TYPE), m_equipmentId(0),
101m_AlreadyCallAssistence(false), m_regenHealth(true), m_AI_locked(false), m_isDeadByDefault(false),
102m_meleeDamageSchoolMask(SPELL_SCHOOL_MASK_NORMAL),m_creatureInfo(NULL), m_DBTableGuid(0)
103{
104    m_valuesCount = UNIT_END;
105
106    for(int i =0; i<4; ++i)
107        m_spells[i] = 0;
108
109    m_CreatureSpellCooldowns.clear();
110    m_CreatureCategoryCooldowns.clear();
111    m_GlobalCooldown = 0;
112    m_unit_movement_flags = MOVEMENTFLAG_WALK_MODE;
113}
114
115Creature::~Creature()
116{
117    CleanupsBeforeDelete();
118
119    m_vendorItemCounts.clear();
120
121    delete i_AI;
122    i_AI = NULL;
123}
124
125void Creature::AddToWorld()
126{
127    ///- Register the creature for guid lookup
128    if(!IsInWorld()) ObjectAccessor::Instance().AddObject(this);
129    Unit::AddToWorld();
130}
131
132void Creature::RemoveFromWorld()
133{
134    ///- Remove the creature from the accessor
135    if(IsInWorld()) ObjectAccessor::Instance().RemoveObject(this);
136    Unit::RemoveFromWorld();
137}
138
139void Creature::RemoveCorpse()
140{
141    if( getDeathState()!=CORPSE && !m_isDeadByDefault || getDeathState()!=ALIVE && m_isDeadByDefault )
142        return;
143
144    m_deathTimer = 0;
145    setDeathState(DEAD);
146    ObjectAccessor::UpdateObjectVisibility(this);
147    loot.clear();
148    m_respawnTime = time(NULL) + m_respawnDelay;
149
150    float x,y,z,o;
151    GetRespawnCoord(x, y, z, &o);
152    MapManager::Instance().GetMap(GetMapId(), this)->CreatureRelocation(this,x,y,z,o);
153}
154
155/**
156 * change the entry of creature until respawn
157 */
158bool Creature::InitEntry(uint32 Entry, uint32 team, const CreatureData *data )
159{
160    CreatureInfo const *normalInfo = objmgr.GetCreatureTemplate(Entry);
161    if(!normalInfo)
162    {
163        sLog.outErrorDb("Creature::UpdateEntry creature entry %u does not exist.", Entry);
164        return false;
165    }
166
167    // get heroic mode entry
168    uint32 actualEntry = Entry;
169    CreatureInfo const *cinfo = normalInfo;
170    if(normalInfo->HeroicEntry)
171    {
172        Map *map = MapManager::Instance().FindMap(GetMapId(), GetInstanceId());
173        if(map && map->IsHeroic())
174        {
175            cinfo = objmgr.GetCreatureTemplate(normalInfo->HeroicEntry);
176            if(!cinfo)
177            {
178                sLog.outErrorDb("Creature::UpdateEntry creature heroic entry %u does not exist.", actualEntry);
179                return false;
180            }
181        }
182    }
183
184    SetUInt32Value(OBJECT_FIELD_ENTRY, Entry);              // normal entry always
185    m_creatureInfo = cinfo;                                 // map mode related always
186
187    if (cinfo->DisplayID_A == 0 || cinfo->DisplayID_H == 0) // Cancel load if no model defined
188    {
189        sLog.outErrorDb("Creature (Entry: %u) has no model defined for Horde or Alliance in table `creature_template`, can't load. ",Entry);
190        return false;
191    }
192
193    uint32 display_id = objmgr.ChooseDisplayId(team, GetCreatureInfo(), data);
194    CreatureModelInfo const *minfo = objmgr.GetCreatureModelRandomGender(display_id);
195    if (!minfo)
196    {
197        sLog.outErrorDb("Creature (Entry: %u) has model %u not found in table `creature_model_info`, can't load. ", Entry, display_id);
198        return false;
199    }
200    else
201        display_id = minfo->modelid;                        // it can be different (for another gender)
202
203    SetDisplayId(display_id);
204    SetNativeDisplayId(display_id);
205    SetByteValue(UNIT_FIELD_BYTES_0, 2, minfo->gender);
206
207    // Load creature equipment
208    if(!data || data->equipmentId == 0)
209    {                                                       // use default from the template
210        LoadEquipment(cinfo->equipmentId);
211    }
212    else if(data && data->equipmentId != -1)
213    {                                                       // override, -1 means no equipment
214        LoadEquipment(data->equipmentId);
215    }
216
217    SetName(normalInfo->Name);                              // at normal entry always
218
219    SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS,minfo->bounding_radius);
220    SetFloatValue(UNIT_FIELD_COMBATREACH,minfo->combat_reach );
221
222    SetFloatValue(UNIT_MOD_CAST_SPEED, 1.0f);
223
224    SetSpeed(MOVE_WALK,     cinfo->speed );
225    SetSpeed(MOVE_RUN,      cinfo->speed );
226    SetSpeed(MOVE_SWIM,     cinfo->speed );
227
228    SetFloatValue(OBJECT_FIELD_SCALE_X, cinfo->scale);
229
230    // checked at loading
231    m_defaultMovementType = MovementGeneratorType(cinfo->MovementType);
232    if(!m_respawnradius && m_defaultMovementType==RANDOM_MOTION_TYPE)
233        m_defaultMovementType = IDLE_MOTION_TYPE;
234
235    return true;
236}
237
238bool Creature::UpdateEntry(uint32 Entry, uint32 team, const CreatureData *data )
239{
240    if(!InitEntry(Entry,team,data))
241        return false;
242
243    m_regenHealth = GetCreatureInfo()->RegenHealth;
244
245    // creatures always have melee weapon ready if any
246    SetByteValue(UNIT_FIELD_BYTES_2, 0, SHEATH_STATE_MELEE );
247    SetByteValue(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_AURAS );
248
249    SelectLevel(GetCreatureInfo());
250    if (team == HORDE)
251        SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE, GetCreatureInfo()->faction_H);
252    else
253        SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE, GetCreatureInfo()->faction_A);
254
255    SetUInt32Value(UNIT_NPC_FLAGS,GetCreatureInfo()->npcflag);
256
257    SetAttackTime(BASE_ATTACK,  GetCreatureInfo()->baseattacktime);
258    SetAttackTime(OFF_ATTACK,   GetCreatureInfo()->baseattacktime);
259    SetAttackTime(RANGED_ATTACK,GetCreatureInfo()->rangeattacktime);
260
261    SetUInt32Value(UNIT_FIELD_FLAGS,GetCreatureInfo()->Flags);
262    SetUInt32Value(UNIT_DYNAMIC_FLAGS,GetCreatureInfo()->dynamicflags);
263
264    SetModifierValue(UNIT_MOD_ARMOR,             BASE_VALUE, float(GetCreatureInfo()->armor));
265    SetModifierValue(UNIT_MOD_RESISTANCE_HOLY,   BASE_VALUE, float(GetCreatureInfo()->resistance1));
266    SetModifierValue(UNIT_MOD_RESISTANCE_FIRE,   BASE_VALUE, float(GetCreatureInfo()->resistance2));
267    SetModifierValue(UNIT_MOD_RESISTANCE_NATURE, BASE_VALUE, float(GetCreatureInfo()->resistance3));
268    SetModifierValue(UNIT_MOD_RESISTANCE_FROST,  BASE_VALUE, float(GetCreatureInfo()->resistance4));
269    SetModifierValue(UNIT_MOD_RESISTANCE_SHADOW, BASE_VALUE, float(GetCreatureInfo()->resistance5));
270    SetModifierValue(UNIT_MOD_RESISTANCE_ARCANE, BASE_VALUE, float(GetCreatureInfo()->resistance6));
271
272    SetCanModifyStats(true);
273    UpdateAllStats();
274
275    FactionTemplateEntry const* factionTemplate = sFactionTemplateStore.LookupEntry(GetCreatureInfo()->faction_A);
276    if (factionTemplate)                                    // check and error show at loading templates
277    {
278        FactionEntry const* factionEntry = sFactionStore.LookupEntry(factionTemplate->faction);
279        if (factionEntry)
280            if( !(GetCreatureInfo()->flags_extra & CREATURE_FLAG_EXTRA_CIVILIAN) &&
281                (factionEntry->team == ALLIANCE || factionEntry->team == HORDE) )
282                SetPvP(true);
283    }
284
285    m_spells[0] = GetCreatureInfo()->spell1;
286    m_spells[1] = GetCreatureInfo()->spell2;
287    m_spells[2] = GetCreatureInfo()->spell3;
288    m_spells[3] = GetCreatureInfo()->spell4;
289
290    return true;
291}
292
293void Creature::Update(uint32 diff)
294{
295    if(m_GlobalCooldown <= diff)
296        m_GlobalCooldown = 0;
297    else
298        m_GlobalCooldown -= diff;
299
300    switch( m_deathState )
301    {
302        case JUST_ALIVED:
303            // Dont must be called, see Creature::setDeathState JUST_ALIVED -> ALIVE promoting.
304            sLog.outError("Creature (GUIDLow: %u Entry: %u ) in wrong state: JUST_ALIVED (4)",GetGUIDLow(),GetEntry());
305            break;
306        case JUST_DIED:
307            // Dont must be called, see Creature::setDeathState JUST_DIED -> CORPSE promoting.
308            sLog.outError("Creature (GUIDLow: %u Entry: %u ) in wrong state: JUST_DEAD (1)",GetGUIDLow(),GetEntry());
309            break;
310        case DEAD:
311        {
312            if( m_respawnTime <= time(NULL) )
313            {
314                DEBUG_LOG("Respawning...");
315                m_respawnTime = 0;
316                lootForPickPocketed = false;
317                lootForBody         = false;
318
319                if(m_originalEntry != GetUInt32Value(OBJECT_FIELD_ENTRY))
320                    UpdateEntry(m_originalEntry);
321
322                CreatureInfo const *cinfo = GetCreatureInfo();
323
324                SelectLevel(cinfo);
325                SetUInt32Value(UNIT_DYNAMIC_FLAGS, 0);
326                if (m_isDeadByDefault)
327                {
328                    setDeathState(JUST_DIED);
329                    SetHealth(0);
330                    i_motionMaster.Clear();
331                    clearUnitState(UNIT_STAT_ALL_STATE);
332                    LoadCreaturesAddon(true);
333                }
334                else
335                    setDeathState( JUST_ALIVED );
336
337                //Call AI respawn virtual function
338                i_AI->JustRespawned();
339
340                MapManager::Instance().GetMap(GetMapId(), this)->Add(this);
341            }
342            break;
343        }
344        case CORPSE:
345        {
346            if (m_isDeadByDefault)
347                break;
348
349            if( m_deathTimer <= diff )
350            {
351                RemoveCorpse();
352                DEBUG_LOG("Removing corpse... %u ", GetUInt32Value(OBJECT_FIELD_ENTRY));
353            }
354            else
355            {
356                m_deathTimer -= diff;
357                if (m_groupLootTimer && lootingGroupLeaderGUID)
358                {
359                    if(diff <= m_groupLootTimer)
360                    {
361                        m_groupLootTimer -= diff;
362                    }
363                    else
364                    {
365                        Group* group = objmgr.GetGroupByLeader(lootingGroupLeaderGUID);
366                        if (group)
367                            group->EndRoll();
368                        m_groupLootTimer = 0;
369                        lootingGroupLeaderGUID = 0;
370                    }
371                }
372            }
373
374            break;
375        }
376        case ALIVE:
377        {
378            if (m_isDeadByDefault)
379            {
380                if( m_deathTimer <= diff )
381                {
382                    RemoveCorpse();
383                    DEBUG_LOG("Removing alive corpse... %u ", GetUInt32Value(OBJECT_FIELD_ENTRY));
384                }
385                else
386                {
387                    m_deathTimer -= diff;
388                }
389            }
390
391            Unit::Update( diff );
392
393            // creature can be dead after Unit::Update call
394            // CORPSE/DEAD state will processed at next tick (in other case death timer will be updated unexpectedly)
395            if(!isAlive())
396                break;
397
398            if(!IsInEvadeMode())
399            {
400                // do not allow the AI to be changed during update
401                m_AI_locked = true;
402                i_AI->UpdateAI(diff);
403                m_AI_locked = false;
404            }
405
406            // creature can be dead after UpdateAI call
407            // CORPSE/DEAD state will processed at next tick (in other case death timer will be updated unexpectedly)
408            if(!isAlive())
409                break;
410            if(m_regenTimer > 0)
411            {
412                if(diff >= m_regenTimer)
413                    m_regenTimer = 0;
414                else
415                    m_regenTimer -= diff;
416            }
417            if (m_regenTimer != 0)
418                break;
419
420            if (!isInCombat() || IsPolymorphed())
421                RegenerateHealth();
422
423            RegenerateMana();
424
425            m_regenTimer = 2000;
426            break;
427        }
428        default:
429            break;
430    }
431}
432
433void Creature::RegenerateMana()
434{
435    uint32 curValue = GetPower(POWER_MANA);
436    uint32 maxValue = GetMaxPower(POWER_MANA);
437
438    if (curValue >= maxValue)
439        return;
440
441    uint32 addvalue = 0;
442
443    // Combat and any controlled creature
444    if (isInCombat() || GetCharmerOrOwnerGUID())
445    {
446        if(!IsUnderLastManaUseEffect())
447        {
448            float ManaIncreaseRate = sWorld.getRate(RATE_POWER_MANA);
449            float Spirit = GetStat(STAT_SPIRIT);
450
451            addvalue = uint32((Spirit/5.0f + 17.0f) * ManaIncreaseRate);
452        }
453    }
454    else
455        addvalue = maxValue/3;
456
457    ModifyPower(POWER_MANA, addvalue);
458}
459
460void Creature::RegenerateHealth()
461{
462    if (!isRegeneratingHealth())
463        return;
464
465    uint32 curValue = GetHealth();
466    uint32 maxValue = GetMaxHealth();
467
468    if (curValue >= maxValue)
469        return;
470
471    uint32 addvalue = 0;
472
473    // Not only pet, but any controelled creature
474    if(GetCharmerOrOwnerGUID())
475    {
476        float HealthIncreaseRate = sWorld.getRate(RATE_HEALTH);
477        float Spirit = GetStat(STAT_SPIRIT);
478
479        if( GetPower(POWER_MANA) > 0 )
480            addvalue = uint32(Spirit * 0.25 * HealthIncreaseRate);
481        else
482            addvalue = uint32(Spirit * 0.80 * HealthIncreaseRate);
483    }
484    else
485        addvalue = maxValue/3;
486
487    ModifyHealth(addvalue);
488}
489
490bool Creature::AIM_Initialize()
491{
492    // make sure nothing can change the AI during AI update
493    if(m_AI_locked)
494    {
495        sLog.outDebug("AIM_Initialize: failed to init, locked.");
496        return false;
497    }
498
499    CreatureAI * oldAI = i_AI;
500    i_motionMaster.Initialize();
501    i_AI = FactorySelector::selectAI(this);
502    if (oldAI)
503        delete oldAI;
504    return true;
505}
506
507bool Creature::Create (uint32 guidlow, Map *map, uint32 Entry, uint32 team, const CreatureData *data)
508{
509    SetMapId(map->GetId());
510    SetInstanceId(map->GetInstanceId());
511
512    //oX = x;     oY = y;    dX = x;    dY = y;    m_moveTime = 0;    m_startMove = 0;
513    const bool bResult = CreateFromProto(guidlow, Entry, team, data);
514
515    if (bResult)
516    {
517        switch (GetCreatureInfo()->rank)
518        {
519            case CREATURE_ELITE_RARE:
520                m_corpseDelay = sWorld.getConfig(CONFIG_CORPSE_DECAY_RARE);
521                break;
522            case CREATURE_ELITE_ELITE:
523                m_corpseDelay = sWorld.getConfig(CONFIG_CORPSE_DECAY_ELITE);
524                break;
525            case CREATURE_ELITE_RAREELITE:
526                m_corpseDelay = sWorld.getConfig(CONFIG_CORPSE_DECAY_RAREELITE);
527                break;
528            case CREATURE_ELITE_WORLDBOSS:
529                m_corpseDelay = sWorld.getConfig(CONFIG_CORPSE_DECAY_WORLDBOSS);
530                break;
531            default:
532                m_corpseDelay = sWorld.getConfig(CONFIG_CORPSE_DECAY_NORMAL);
533                break;
534        }
535        LoadCreaturesAddon();
536    }
537
538    return bResult;
539}
540
541bool Creature::isCanTrainingOf(Player* pPlayer, bool msg) const
542{
543    if(!isTrainer())
544        return false;
545
546    TrainerSpellData const* trainer_spells = GetTrainerSpells();
547
548
549    if(!trainer_spells || trainer_spells->spellList.empty())
550    {
551        sLog.outErrorDb("Creature %u (Entry: %u) have UNIT_NPC_FLAG_TRAINER but have empty trainer spell list.",
552            GetGUIDLow(),GetEntry());
553        return false;
554    }
555
556    switch(GetCreatureInfo()->trainer_type)
557    {
558        case TRAINER_TYPE_CLASS:
559            if(pPlayer->getClass()!=GetCreatureInfo()->classNum)
560            {
561                if(msg)
562                {
563                    pPlayer->PlayerTalkClass->ClearMenus();
564                    switch(GetCreatureInfo()->classNum)
565                    {
566                        case CLASS_DRUID:  pPlayer->PlayerTalkClass->SendGossipMenu( 4913,GetGUID()); break;
567                        case CLASS_HUNTER: pPlayer->PlayerTalkClass->SendGossipMenu(10090,GetGUID()); break;
568                        case CLASS_MAGE:   pPlayer->PlayerTalkClass->SendGossipMenu(  328,GetGUID()); break;
569                        case CLASS_PALADIN:pPlayer->PlayerTalkClass->SendGossipMenu( 1635,GetGUID()); break;
570                        case CLASS_PRIEST: pPlayer->PlayerTalkClass->SendGossipMenu( 4436,GetGUID()); break;
571                        case CLASS_ROGUE:  pPlayer->PlayerTalkClass->SendGossipMenu( 4797,GetGUID()); break;
572                        case CLASS_SHAMAN: pPlayer->PlayerTalkClass->SendGossipMenu( 5003,GetGUID()); break;
573                        case CLASS_WARLOCK:pPlayer->PlayerTalkClass->SendGossipMenu( 5836,GetGUID()); break;
574                        case CLASS_WARRIOR:pPlayer->PlayerTalkClass->SendGossipMenu( 4985,GetGUID()); break;
575                    }
576                }
577                return false;
578            }
579            break;
580        case TRAINER_TYPE_PETS:
581            if(pPlayer->getClass()!=CLASS_HUNTER)
582            {
583                pPlayer->PlayerTalkClass->ClearMenus();
584                pPlayer->PlayerTalkClass->SendGossipMenu(3620,GetGUID());
585                return false;
586            }
587            break;
588        case TRAINER_TYPE_MOUNTS:
589            if(GetCreatureInfo()->race && pPlayer->getRace() != GetCreatureInfo()->race)
590            {
591                if(msg)
592                {
593                    pPlayer->PlayerTalkClass->ClearMenus();
594                    switch(GetCreatureInfo()->classNum)
595                    {
596                        case RACE_DWARF:        pPlayer->PlayerTalkClass->SendGossipMenu(5865,GetGUID()); break;
597                        case RACE_GNOME:        pPlayer->PlayerTalkClass->SendGossipMenu(4881,GetGUID()); break;
598                        case RACE_HUMAN:        pPlayer->PlayerTalkClass->SendGossipMenu(5861,GetGUID()); break;
599                        case RACE_NIGHTELF:     pPlayer->PlayerTalkClass->SendGossipMenu(5862,GetGUID()); break;
600                        case RACE_ORC:          pPlayer->PlayerTalkClass->SendGossipMenu(5863,GetGUID()); break;
601                        case RACE_TAUREN:       pPlayer->PlayerTalkClass->SendGossipMenu(5864,GetGUID()); break;
602                        case RACE_TROLL:        pPlayer->PlayerTalkClass->SendGossipMenu(5816,GetGUID()); break;
603                        case RACE_UNDEAD_PLAYER:pPlayer->PlayerTalkClass->SendGossipMenu( 624,GetGUID()); break;
604                        case RACE_BLOODELF:     pPlayer->PlayerTalkClass->SendGossipMenu(5862,GetGUID()); break;
605                        case RACE_DRAENEI:      pPlayer->PlayerTalkClass->SendGossipMenu(5864,GetGUID()); break;
606                    }
607                }
608                return false;
609            }
610            break;
611        case TRAINER_TYPE_TRADESKILLS:
612            if(GetCreatureInfo()->trainer_spell && !pPlayer->HasSpell(GetCreatureInfo()->trainer_spell))
613            {
614                if(msg)
615                {
616                    pPlayer->PlayerTalkClass->ClearMenus();
617                    pPlayer->PlayerTalkClass->SendGossipMenu(11031,GetGUID());
618                }
619                return false;
620            }
621            break;
622        default:
623            return false;                                   // checked and error output at creature_template loading
624    }
625    return true;
626}
627
628bool Creature::isCanIneractWithBattleMaster(Player* pPlayer, bool msg) const
629{
630    if(!isBattleMaster())
631        return false;
632
633    uint32 bgTypeId = objmgr.GetBattleMasterBG(GetEntry());
634    if(!msg)
635        return pPlayer->GetBGAccessByLevel(bgTypeId);
636
637    if(!pPlayer->GetBGAccessByLevel(bgTypeId))
638    {
639        pPlayer->PlayerTalkClass->ClearMenus();
640        switch(bgTypeId)
641        {
642            case BATTLEGROUND_AV:  pPlayer->PlayerTalkClass->SendGossipMenu(7616,GetGUID()); break;
643            case BATTLEGROUND_WS:  pPlayer->PlayerTalkClass->SendGossipMenu(7599,GetGUID()); break;
644            case BATTLEGROUND_AB:  pPlayer->PlayerTalkClass->SendGossipMenu(7642,GetGUID()); break;
645            case BATTLEGROUND_EY:
646            case BATTLEGROUND_NA:
647            case BATTLEGROUND_BE:
648            case BATTLEGROUND_AA:
649            case BATTLEGROUND_RL:  pPlayer->PlayerTalkClass->SendGossipMenu(10024,GetGUID()); break;
650            break;
651        }
652        return false;
653    }
654    return true;
655}
656
657bool Creature::isCanTrainingAndResetTalentsOf(Player* pPlayer) const
658{
659    return pPlayer->getLevel() >= 10
660        && GetCreatureInfo()->trainer_type == TRAINER_TYPE_CLASS
661        && pPlayer->getClass() == GetCreatureInfo()->classNum;
662}
663
664void Creature::prepareGossipMenu( Player *pPlayer,uint32 gossipid )
665{
666    PlayerMenu* pm=pPlayer->PlayerTalkClass;
667    pm->ClearMenus();
668
669    // lazy loading single time at use
670    LoadGossipOptions();
671
672    GossipOption* gso;
673    GossipOption* ingso;
674
675    for( GossipOptionList::iterator i = m_goptions.begin( ); i != m_goptions.end( ); i++ )
676    {
677        gso=&*i;
678        if(gso->GossipId == gossipid)
679        {
680            bool cantalking=true;
681            if(gso->Id==1)
682            {
683                uint32 textid=GetNpcTextId();
684                GossipText * gossiptext=objmgr.GetGossipText(textid);
685                if(!gossiptext)
686                    cantalking=false;
687            }
688            else
689            {
690                switch (gso->Action)
691                {
692                    case GOSSIP_OPTION_QUESTGIVER:
693                        pPlayer->PrepareQuestMenu(GetGUID());
694                        //if (pm->GetQuestMenu()->MenuItemCount() == 0)
695                        cantalking=false;
696                        //pm->GetQuestMenu()->ClearMenu();
697                        break;
698                    case GOSSIP_OPTION_ARMORER:
699                        cantalking=false;                   // added in special mode
700                        break;
701                    case GOSSIP_OPTION_SPIRITHEALER:
702                        if( !pPlayer->isDead() )
703                            cantalking=false;
704                        break;
705                    case GOSSIP_OPTION_VENDOR:
706                    {
707                        VendorItemData const* vItems = GetVendorItems();
708                        if(!vItems || vItems->Empty())
709                        {
710                            sLog.outErrorDb("Creature %u (Entry: %u) have UNIT_NPC_FLAG_VENDOR but have empty trading item list.",
711                                GetGUIDLow(),GetEntry());
712                            cantalking=false;
713                        }
714                        break;
715                    }
716                    case GOSSIP_OPTION_TRAINER:
717                        if(!isCanTrainingOf(pPlayer,false))
718                            cantalking=false;
719                        break;
720                    case GOSSIP_OPTION_UNLEARNTALENTS:
721                        if(!isCanTrainingAndResetTalentsOf(pPlayer))
722                            cantalking=false;
723                        break;
724                    case GOSSIP_OPTION_UNLEARNPETSKILLS:
725                        if(!pPlayer->GetPet() || pPlayer->GetPet()->getPetType() != HUNTER_PET || pPlayer->GetPet()->m_spells.size() <= 1 || GetCreatureInfo()->trainer_type != TRAINER_TYPE_PETS || GetCreatureInfo()->classNum != CLASS_HUNTER)
726                            cantalking=false;
727                        break;
728                    case GOSSIP_OPTION_TAXIVENDOR:
729                        if ( pPlayer->GetSession()->SendLearnNewTaxiNode(this) )
730                            return;
731                        break;
732                    case GOSSIP_OPTION_BATTLEFIELD:
733                        if(!isCanIneractWithBattleMaster(pPlayer,false))
734                            cantalking=false;
735                        break;
736                    case GOSSIP_OPTION_SPIRITGUIDE:
737                    case GOSSIP_OPTION_INNKEEPER:
738                    case GOSSIP_OPTION_BANKER:
739                    case GOSSIP_OPTION_PETITIONER:
740                    case GOSSIP_OPTION_STABLEPET:
741                    case GOSSIP_OPTION_TABARDDESIGNER:
742                    case GOSSIP_OPTION_AUCTIONEER:
743                        break;                              // no checks
744                    default:
745                        sLog.outErrorDb("Creature %u (entry: %u) have unknown gossip option %u",GetGUIDLow(),GetEntry(),gso->Action);
746                        break;
747                }
748            }
749
750            if(!gso->Option.empty() && cantalking )
751            {                                               //note for future dev: should have database fields for BoxMessage & BoxMoney
752                pm->GetGossipMenu().AddMenuItem((uint8)gso->Icon,gso->Option, gossipid,gso->Action,"",0,false);
753                ingso=gso;
754            }
755        }
756    }
757
758    ///some gossips aren't handled in normal way ... so we need to do it this way .. TODO: handle it in normal way ;-)
759    if(pm->Empty())
760    {
761        if(HasFlag(UNIT_NPC_FLAGS,UNIT_NPC_FLAG_TRAINER))
762        {
763            isCanTrainingOf(pPlayer,true);                  // output error message if need
764        }
765        if(HasFlag(UNIT_NPC_FLAGS,UNIT_NPC_FLAG_BATTLEMASTER))
766        {
767            isCanIneractWithBattleMaster(pPlayer,true);     // output error message if need
768        }
769    }
770}
771
772void Creature::sendPreparedGossip(Player* player)
773{
774    if(!player)
775        return;
776
777    GossipMenu& gossipmenu = player->PlayerTalkClass->GetGossipMenu();
778
779    // in case empty gossip menu open quest menu if any
780    if (gossipmenu.Empty() && GetNpcTextId() == 0)
781    {
782        player->SendPreparedQuest(GetGUID());
783        return;
784    }
785
786    // in case non empty gossip menu (that not included quests list size) show it
787    // (quest entries from quest menu wiill be included in list)
788    player->PlayerTalkClass->SendGossipMenu(GetNpcTextId(), GetGUID());
789}
790
791void Creature::OnGossipSelect(Player* player, uint32 option)
792{
793    GossipMenu& gossipmenu = player->PlayerTalkClass->GetGossipMenu();
794
795    if(option >= gossipmenu.MenuItemCount())
796        return;
797
798    uint32 action=gossipmenu.GetItem(option).m_gAction;
799    uint32 zoneid=GetZoneId();
800    uint64 guid=GetGUID();
801    GossipOption const *gossip=GetGossipOption( action );
802    uint32 textid;
803    if(!gossip)
804    {
805        zoneid=0;
806        gossip=GetGossipOption( action );
807        if(!gossip)
808            return;
809    }
810    textid=GetGossipTextId( action, zoneid);
811    if(textid==0)
812        textid=GetNpcTextId();
813
814    switch (gossip->Action)
815    {
816        case GOSSIP_OPTION_GOSSIP:
817            player->PlayerTalkClass->CloseGossip();
818            player->PlayerTalkClass->SendTalking( textid );
819            break;
820        case GOSSIP_OPTION_SPIRITHEALER:
821            if( player->isDead() )
822                CastSpell(this,17251,true,NULL,NULL,player->GetGUID());
823            break;
824        case GOSSIP_OPTION_QUESTGIVER:
825            player->PrepareQuestMenu( guid );
826            player->SendPreparedQuest( guid );
827            break;
828        case GOSSIP_OPTION_VENDOR:
829        case GOSSIP_OPTION_ARMORER:
830            player->GetSession()->SendListInventory(guid);
831            break;
832        case GOSSIP_OPTION_STABLEPET:
833            player->GetSession()->SendStablePet(guid);
834            break;
835        case GOSSIP_OPTION_TRAINER:
836            player->GetSession()->SendTrainerList(guid);
837            break;
838        case GOSSIP_OPTION_UNLEARNTALENTS:
839            player->PlayerTalkClass->CloseGossip();
840            player->SendTalentWipeConfirm(guid);
841            break;
842        case GOSSIP_OPTION_UNLEARNPETSKILLS:
843            player->PlayerTalkClass->CloseGossip();
844            player->SendPetSkillWipeConfirm();
845            break;
846        case GOSSIP_OPTION_TAXIVENDOR:
847            player->GetSession()->SendTaxiMenu(this);
848            break;
849        case GOSSIP_OPTION_INNKEEPER:
850            player->PlayerTalkClass->CloseGossip();
851            player->SetBindPoint( guid );
852            break;
853        case GOSSIP_OPTION_BANKER:
854            player->GetSession()->SendShowBank( guid );
855            break;
856        case GOSSIP_OPTION_PETITIONER:
857            player->PlayerTalkClass->CloseGossip();
858            player->GetSession()->SendPetitionShowList( guid );
859            break;
860        case GOSSIP_OPTION_TABARDDESIGNER:
861            player->PlayerTalkClass->CloseGossip();
862            player->GetSession()->SendTabardVendorActivate( guid );
863            break;
864        case GOSSIP_OPTION_AUCTIONEER:
865            player->GetSession()->SendAuctionHello( guid, this );
866            break;
867        case GOSSIP_OPTION_SPIRITGUIDE:
868        case GOSSIP_GUARD_SPELLTRAINER:
869        case GOSSIP_GUARD_SKILLTRAINER:
870            prepareGossipMenu( player,gossip->Id );
871            sendPreparedGossip( player );
872            break;
873        case GOSSIP_OPTION_BATTLEFIELD:
874        {
875            uint32 bgTypeId = objmgr.GetBattleMasterBG(GetEntry());
876            player->GetSession()->SendBattlegGroundList( GetGUID(), bgTypeId );
877            break;
878        }
879        default:
880            OnPoiSelect( player, gossip );
881            break;
882    }
883
884}
885
886void Creature::OnPoiSelect(Player* player, GossipOption const *gossip)
887{
888    if(gossip->GossipId==GOSSIP_GUARD_SPELLTRAINER || gossip->GossipId==GOSSIP_GUARD_SKILLTRAINER)
889    {
890        //float x,y;
891        //bool findnpc=false;
892        Poi_Icon icon = ICON_POI_0;
893        //QueryResult *result;
894        //Field *fields;
895        uint32 mapid=GetMapId();
896        Map const* map=MapManager::Instance().GetBaseMap( mapid );
897        uint16 areaflag=map->GetAreaFlag(GetPositionX(),GetPositionY());
898        uint32 zoneid=Map::GetZoneId(areaflag,mapid);
899        std::string areaname= gossip->Option;
900        /*
901        uint16 pflag;
902
903        // use the action relate to creaturetemplate.trainer_type ?
904        result= WorldDatabase.PQuery("SELECT creature.position_x,creature.position_y FROM creature,creature_template WHERE creature.map = '%u' AND creature.id = creature_template.entry AND creature_template.trainer_type = '%u'", mapid, gossip->Action );
905        if(!result)
906            return;
907        do
908        {
909            fields = result->Fetch();
910            x=fields[0].GetFloat();
911            y=fields[1].GetFloat();
912            pflag=map->GetAreaFlag(GetPositionX(),GetPositionY());
913            if(pflag==areaflag)
914            {
915                findnpc=true;
916                break;
917            }
918        }while(result->NextRow());
919
920        delete result;
921
922        if(!findnpc)
923        {
924            player->PlayerTalkClass->SendTalking( "$NSorry", "Here no this person.");
925            return;
926        }*/
927
928        //need add more case.
929        switch(gossip->Action)
930        {
931            case GOSSIP_GUARD_BANK:
932                icon=ICON_POI_HOUSE;
933                break;
934            case GOSSIP_GUARD_RIDE:
935                icon=ICON_POI_RWHORSE;
936                break;
937            case GOSSIP_GUARD_GUILD:
938                icon=ICON_POI_BLUETOWER;
939                break;
940            default:
941                icon=ICON_POI_TOWER;
942                break;
943        }
944        uint32 textid=GetGossipTextId( gossip->Action, zoneid );
945        player->PlayerTalkClass->SendTalking( textid );
946        // how this could worked player->PlayerTalkClass->SendPointOfInterest( x, y, icon, 2, 15, areaname.c_str() );
947    }
948}
949
950uint32 Creature::GetGossipTextId(uint32 action, uint32 zoneid)
951{
952    QueryResult *result= WorldDatabase.PQuery("SELECT textid FROM npc_gossip_textid WHERE action = '%u' AND zoneid ='%u'", action, zoneid );
953
954    if(!result)
955        return 0;
956
957    Field *fields = result->Fetch();
958    uint32 id = fields[0].GetUInt32();
959
960    delete result;
961
962    return id;
963}
964
965uint32 Creature::GetNpcTextId()
966{
967    if (!m_DBTableGuid)
968        return DEFAULT_GOSSIP_MESSAGE;
969
970    if(uint32 pos = objmgr.GetNpcGossip(m_DBTableGuid))
971        return pos;
972
973    return DEFAULT_GOSSIP_MESSAGE;
974}
975
976GossipOption const* Creature::GetGossipOption( uint32 id ) const
977{
978    for( GossipOptionList::const_iterator i = m_goptions.begin( ); i != m_goptions.end( ); i++ )
979    {
980        if(i->Action==id )
981            return &*i;
982    }
983    return NULL;
984}
985
986void Creature::LoadGossipOptions()
987{
988    if(m_gossipOptionLoaded)
989        return;
990
991    uint32 npcflags=GetUInt32Value(UNIT_NPC_FLAGS);
992
993    QueryResult *result = WorldDatabase.PQuery( "SELECT id,gossip_id,npcflag,icon,action,option_text FROM npc_option WHERE (npcflag & %u)<>0", npcflags );
994
995    if(!result)
996        return;
997
998    GossipOption go;
999    do
1000    {
1001        Field *fields = result->Fetch();
1002        go.Id= fields[0].GetUInt32();
1003        go.GossipId = fields[1].GetUInt32();
1004        go.NpcFlag=fields[2].GetUInt32();
1005        go.Icon=fields[3].GetUInt32();
1006        go.Action=fields[4].GetUInt32();
1007        go.Option=fields[5].GetCppString();
1008        addGossipOption(go);
1009    }while( result->NextRow() );
1010    delete result;
1011
1012    m_gossipOptionLoaded = true;
1013}
1014
1015void Creature::AI_SendMoveToPacket(float x, float y, float z, uint32 time, uint32 MovementFlags, uint8 type)
1016{
1017    /*    uint32 timeElap = getMSTime();
1018        if ((timeElap - m_startMove) < m_moveTime)
1019        {
1020            oX = (dX - oX) * ( (timeElap - m_startMove) / m_moveTime );
1021            oY = (dY - oY) * ( (timeElap - m_startMove) / m_moveTime );
1022        }
1023        else
1024        {
1025            oX = dX;
1026            oY = dY;
1027        }
1028
1029        dX = x;
1030        dY = y;
1031        m_orientation = atan2((oY - dY), (oX - dX));
1032
1033        m_startMove = getMSTime();
1034        m_moveTime = time;*/
1035    SendMonsterMove(x, y, z, type, MovementFlags, time);
1036}
1037
1038Player *Creature::GetLootRecipient() const
1039{
1040    if (!m_lootRecipient) return NULL;
1041    else return ObjectAccessor::FindPlayer(m_lootRecipient);
1042}
1043
1044void Creature::SetLootRecipient(Unit *unit)
1045{
1046    // set the player whose group should receive the right
1047    // to loot the creature after it dies
1048    // should be set to NULL after the loot disappears
1049
1050    if (!unit)
1051    {
1052        m_lootRecipient = 0;
1053        RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_OTHER_TAGGER);
1054        return;
1055    }
1056
1057    Player* player = unit->GetCharmerOrOwnerPlayerOrPlayerItself();
1058    if(!player)                                             // normal creature, no player involved
1059        return;
1060
1061    m_lootRecipient = player->GetGUID();
1062    SetFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_OTHER_TAGGER);
1063}
1064
1065void Creature::SaveToDB()
1066{
1067    // this should only be used when the creature has already been loaded
1068    // perferably after adding to map, because mapid may not be valid otherwise
1069    CreatureData const *data = objmgr.GetCreatureData(m_DBTableGuid);
1070    if(!data)
1071    {
1072        sLog.outError("Creature::SaveToDB failed, cannot get creature data!");
1073        return;
1074    }
1075
1076    SaveToDB(GetMapId(), data->spawnMask);
1077}
1078
1079void Creature::SaveToDB(uint32 mapid, uint8 spawnMask)
1080{
1081    // update in loaded data
1082    if (!m_DBTableGuid)
1083        m_DBTableGuid = GetGUIDLow();
1084    CreatureData& data = objmgr.NewOrExistCreatureData(m_DBTableGuid);
1085
1086    uint32 displayId = GetNativeDisplayId();
1087
1088    // check if it's a custom model and if not, use 0 for displayId
1089    CreatureInfo const *cinfo = GetCreatureInfo();
1090    if(cinfo)
1091    {
1092        if(displayId != cinfo->DisplayID_A && displayId != cinfo->DisplayID_H)
1093        {
1094            CreatureModelInfo const *minfo = objmgr.GetCreatureModelInfo(cinfo->DisplayID_A);
1095            if(!minfo || displayId != minfo->modelid_other_gender)
1096            {
1097                minfo = objmgr.GetCreatureModelInfo(cinfo->DisplayID_H);
1098                if(minfo && displayId == minfo->modelid_other_gender)
1099                    displayId = 0;
1100            }
1101            else
1102                displayId = 0;
1103        }
1104        else
1105            displayId = 0;
1106    }
1107
1108    // data->guid = guid don't must be update at save
1109    data.id = GetEntry();
1110    data.mapid = mapid;
1111    data.displayid = displayId;
1112    data.equipmentId = GetEquipmentId();
1113    data.posX = GetPositionX();
1114    data.posY = GetPositionY();
1115    data.posZ = GetPositionZ();
1116    data.orientation = GetOrientation();
1117    data.spawntimesecs = m_respawnDelay;
1118    // prevent add data integrity problems
1119    data.spawndist = GetDefaultMovementType()==IDLE_MOTION_TYPE ? 0 : m_respawnradius;
1120    data.currentwaypoint = 0;
1121    data.curhealth = GetHealth();
1122    data.curmana = GetPower(POWER_MANA);
1123    data.is_dead = m_isDeadByDefault;
1124    // prevent add data integrity problems
1125    data.movementType = !m_respawnradius && GetDefaultMovementType()==RANDOM_MOTION_TYPE
1126        ? IDLE_MOTION_TYPE : GetDefaultMovementType();
1127    data.spawnMask = spawnMask;
1128
1129    // updated in DB
1130    WorldDatabase.BeginTransaction();
1131
1132    WorldDatabase.PExecuteLog("DELETE FROM creature WHERE guid = '%u'", m_DBTableGuid);
1133
1134    std::ostringstream ss;
1135    ss << "INSERT INTO creature VALUES ("
1136        << m_DBTableGuid << ","
1137        << GetEntry() << ","
1138        << mapid <<","
1139        << (uint32)spawnMask << ","
1140        << displayId <<","
1141        << GetEquipmentId() <<","
1142        << GetPositionX() << ","
1143        << GetPositionY() << ","
1144        << GetPositionZ() << ","
1145        << GetOrientation() << ","
1146        << m_respawnDelay << ","                            //respawn time
1147        << (float) m_respawnradius << ","                   //spawn distance (float)
1148        << (uint32) (0) << ","                              //currentwaypoint
1149        << GetHealth() << ","                               //curhealth
1150        << GetPower(POWER_MANA) << ","                      //curmana
1151        << (m_isDeadByDefault ? 1 : 0) << ","               //is_dead
1152        << GetDefaultMovementType() << ")";                 //default movement generator type
1153
1154    WorldDatabase.PExecuteLog( ss.str( ).c_str( ) );
1155
1156    WorldDatabase.CommitTransaction();
1157}
1158
1159void Creature::SelectLevel(const CreatureInfo *cinfo)
1160{
1161    uint32 rank = isPet()? 0 : cinfo->rank;
1162
1163    // level
1164    uint32 minlevel = std::min(cinfo->maxlevel, cinfo->minlevel);
1165    uint32 maxlevel = std::max(cinfo->maxlevel, cinfo->minlevel);
1166    uint32 level = minlevel == maxlevel ? minlevel : urand(minlevel, maxlevel);
1167    SetLevel(level);
1168
1169    float rellevel = maxlevel == minlevel ? 0 : (float(level - minlevel))/(maxlevel - minlevel);
1170
1171    // health
1172    float healthmod = _GetHealthMod(rank);
1173
1174    uint32 minhealth = std::min(cinfo->maxhealth, cinfo->minhealth);
1175    uint32 maxhealth = std::max(cinfo->maxhealth, cinfo->minhealth);
1176    uint32 health = uint32(healthmod * (minhealth + uint32(rellevel*(maxhealth - minhealth))));
1177
1178    SetCreateHealth(health);
1179    SetMaxHealth(health);
1180    SetHealth(health);
1181
1182    // mana
1183    uint32 minmana = std::min(cinfo->maxmana, cinfo->minmana);
1184    uint32 maxmana = std::max(cinfo->maxmana, cinfo->minmana);
1185    uint32 mana = minmana + uint32(rellevel*(maxmana - minmana));
1186
1187    SetCreateMana(mana);
1188    SetMaxPower(POWER_MANA, mana);                          //MAX Mana
1189    SetPower(POWER_MANA, mana);
1190
1191    SetModifierValue(UNIT_MOD_HEALTH, BASE_VALUE, health);
1192    SetModifierValue(UNIT_MOD_MANA, BASE_VALUE, mana);
1193
1194    // damage
1195    float damagemod = _GetDamageMod(rank);
1196
1197    SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, cinfo->mindmg * damagemod);
1198    SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, cinfo->maxdmg * damagemod);
1199
1200    SetFloatValue(UNIT_FIELD_MINRANGEDDAMAGE,cinfo->minrangedmg * damagemod);
1201    SetFloatValue(UNIT_FIELD_MAXRANGEDDAMAGE,cinfo->maxrangedmg * damagemod);
1202
1203    SetModifierValue(UNIT_MOD_ATTACK_POWER, BASE_VALUE, cinfo->attackpower * damagemod);
1204}
1205
1206float Creature::_GetHealthMod(int32 Rank)
1207{
1208    switch (Rank)                                           // define rates for each elite rank
1209    {
1210        case CREATURE_ELITE_NORMAL:
1211            return sWorld.getRate(RATE_CREATURE_NORMAL_HP);
1212        case CREATURE_ELITE_ELITE:
1213            return sWorld.getRate(RATE_CREATURE_ELITE_ELITE_HP);
1214        case CREATURE_ELITE_RAREELITE:
1215            return sWorld.getRate(RATE_CREATURE_ELITE_RAREELITE_HP);
1216        case CREATURE_ELITE_WORLDBOSS:
1217            return sWorld.getRate(RATE_CREATURE_ELITE_WORLDBOSS_HP);
1218        case CREATURE_ELITE_RARE:
1219            return sWorld.getRate(RATE_CREATURE_ELITE_RARE_HP);
1220        default:
1221            return sWorld.getRate(RATE_CREATURE_ELITE_ELITE_HP);
1222    }
1223}
1224
1225float Creature::_GetDamageMod(int32 Rank)
1226{
1227    switch (Rank)                                           // define rates for each elite rank
1228    {
1229        case CREATURE_ELITE_NORMAL:
1230            return sWorld.getRate(RATE_CREATURE_NORMAL_DAMAGE);
1231        case CREATURE_ELITE_ELITE:
1232            return sWorld.getRate(RATE_CREATURE_ELITE_ELITE_DAMAGE);
1233        case CREATURE_ELITE_RAREELITE:
1234            return sWorld.getRate(RATE_CREATURE_ELITE_RAREELITE_DAMAGE);
1235        case CREATURE_ELITE_WORLDBOSS:
1236            return sWorld.getRate(RATE_CREATURE_ELITE_WORLDBOSS_DAMAGE);
1237        case CREATURE_ELITE_RARE:
1238            return sWorld.getRate(RATE_CREATURE_ELITE_RARE_DAMAGE);
1239        default:
1240            return sWorld.getRate(RATE_CREATURE_ELITE_ELITE_DAMAGE);
1241    }
1242}
1243
1244float Creature::GetSpellDamageMod(int32 Rank)
1245{
1246    switch (Rank)                                           // define rates for each elite rank
1247    {
1248        case CREATURE_ELITE_NORMAL:
1249            return sWorld.getRate(RATE_CREATURE_NORMAL_SPELLDAMAGE);
1250        case CREATURE_ELITE_ELITE:
1251            return sWorld.getRate(RATE_CREATURE_ELITE_ELITE_SPELLDAMAGE);
1252        case CREATURE_ELITE_RAREELITE:
1253            return sWorld.getRate(RATE_CREATURE_ELITE_RAREELITE_SPELLDAMAGE);
1254        case CREATURE_ELITE_WORLDBOSS:
1255            return sWorld.getRate(RATE_CREATURE_ELITE_WORLDBOSS_SPELLDAMAGE);
1256        case CREATURE_ELITE_RARE:
1257            return sWorld.getRate(RATE_CREATURE_ELITE_RARE_SPELLDAMAGE);
1258        default:
1259            return sWorld.getRate(RATE_CREATURE_ELITE_ELITE_SPELLDAMAGE);
1260    }
1261}
1262
1263bool Creature::CreateFromProto(uint32 guidlow, uint32 Entry, uint32 team, const CreatureData *data)
1264{
1265    CreatureInfo const *cinfo = objmgr.GetCreatureTemplate(Entry);
1266    if(!cinfo)
1267    {
1268        sLog.outErrorDb("Error: creature entry %u does not exist.", Entry);
1269        return false;
1270    }
1271    m_originalEntry = Entry;
1272
1273    Object::_Create(guidlow, Entry, HIGHGUID_UNIT);
1274
1275    if(!UpdateEntry(Entry, team, data))
1276        return false;
1277
1278    //Notify the map's instance data.
1279    //Only works if you create the object in it, not if it is moves to that map.
1280    //Normally non-players do not teleport to other maps.
1281    Map *map = MapManager::Instance().FindMap(GetMapId(), GetInstanceId());
1282    if(map && map->IsDungeon() && ((InstanceMap*)map)->GetInstanceData())
1283    {
1284        ((InstanceMap*)map)->GetInstanceData()->OnCreatureCreate(this, Entry);
1285    }
1286
1287    return true;
1288}
1289
1290bool Creature::LoadFromDB(uint32 guid, Map *map)
1291{
1292    CreatureData const* data = objmgr.GetCreatureData(guid);
1293
1294    if(!data)
1295    {
1296        sLog.outErrorDb("Creature (GUID: %u) not found in table `creature`, can't load. ",guid);
1297        return false;
1298    }
1299
1300    m_DBTableGuid = guid;
1301    if (map->GetInstanceId() != 0) guid = objmgr.GenerateLowGuid(HIGHGUID_UNIT);
1302
1303    uint16 team = 0;
1304    if(!Create(guid,map,data->id,team,data))
1305        return false;
1306
1307    Relocate(data->posX,data->posY,data->posZ,data->orientation);
1308
1309    if(!IsPositionValid())
1310    {
1311        sLog.outError("ERROR: Creature (guidlow %d, entry %d) not loaded. Suggested coordinates isn't valid (X: %f Y: %f)",GetGUIDLow(),GetEntry(),GetPositionX(),GetPositionY());
1312        return false;
1313    }
1314
1315    m_respawnradius = data->spawndist;
1316
1317    m_respawnDelay = data->spawntimesecs;
1318    m_isDeadByDefault = data->is_dead;
1319    m_deathState = m_isDeadByDefault ? DEAD : ALIVE;
1320
1321    m_respawnTime  = objmgr.GetCreatureRespawnTime(m_DBTableGuid,GetInstanceId());
1322    if(m_respawnTime > time(NULL))                          // not ready to respawn
1323        m_deathState = DEAD;
1324    else if(m_respawnTime)                                  // respawn time set but expired
1325    {
1326        m_respawnTime = 0;
1327        objmgr.SaveCreatureRespawnTime(m_DBTableGuid,GetInstanceId(),0);
1328    }
1329
1330    uint32 curhealth = data->curhealth;
1331    if(curhealth)
1332    {
1333        curhealth = uint32(curhealth*_GetHealthMod(GetCreatureInfo()->rank));
1334        if(curhealth < 1)
1335            curhealth = 1;
1336    }
1337
1338    SetHealth(m_deathState == ALIVE ? curhealth : 0);
1339    SetPower(POWER_MANA,data->curmana);
1340
1341    SetMeleeDamageSchool(SpellSchools(GetCreatureInfo()->dmgschool));
1342
1343    // checked at creature_template loading
1344    m_defaultMovementType = MovementGeneratorType(data->movementType);
1345
1346    AIM_Initialize();
1347    return true;
1348}
1349
1350void Creature::LoadEquipment(uint32 equip_entry, bool force)
1351{
1352    if(equip_entry == 0)
1353    {
1354        if (force)
1355        {
1356            for (uint8 i=0;i<3;i++)
1357            {
1358                SetUInt32Value( UNIT_VIRTUAL_ITEM_SLOT_DISPLAY + i, 0);
1359                SetUInt32Value( UNIT_VIRTUAL_ITEM_INFO + (i * 2), 0);
1360                SetUInt32Value( UNIT_VIRTUAL_ITEM_INFO + (i * 2) + 1, 0);
1361            }
1362            m_equipmentId = 0;
1363        }
1364        return;
1365    }
1366
1367    EquipmentInfo const *einfo = objmgr.GetEquipmentInfo(equip_entry);
1368    if (!einfo)
1369        return;
1370
1371    m_equipmentId = equip_entry;
1372    for (uint8 i=0;i<3;i++)
1373    {
1374        SetUInt32Value( UNIT_VIRTUAL_ITEM_SLOT_DISPLAY + i, einfo->equipmodel[i]);
1375        SetUInt32Value( UNIT_VIRTUAL_ITEM_INFO + (i * 2), einfo->equipinfo[i]);
1376        SetUInt32Value( UNIT_VIRTUAL_ITEM_INFO + (i * 2) + 1, einfo->equipslot[i]);
1377    }
1378}
1379
1380bool Creature::hasQuest(uint32 quest_id) const
1381{
1382    QuestRelations const& qr = objmgr.mCreatureQuestRelations;
1383    for(QuestRelations::const_iterator itr = qr.lower_bound(GetEntry()); itr != qr.upper_bound(GetEntry()); ++itr)
1384    {
1385        if(itr->second==quest_id)
1386            return true;
1387    }
1388    return false;
1389}
1390
1391bool Creature::hasInvolvedQuest(uint32 quest_id) const
1392{
1393    QuestRelations const& qr = objmgr.mCreatureQuestInvolvedRelations;
1394    for(QuestRelations::const_iterator itr = qr.lower_bound(GetEntry()); itr != qr.upper_bound(GetEntry()); ++itr)
1395    {
1396        if(itr->second==quest_id)
1397            return true;
1398    }
1399    return false;
1400}
1401
1402void Creature::DeleteFromDB()
1403{
1404    if (!m_DBTableGuid)
1405    {
1406        sLog.outDebug("Trying to delete not saved creature!");
1407        return;
1408    }
1409
1410    objmgr.SaveCreatureRespawnTime(m_DBTableGuid,GetInstanceId(),0);
1411    objmgr.DeleteCreatureData(m_DBTableGuid);
1412
1413    WorldDatabase.BeginTransaction();
1414    WorldDatabase.PExecuteLog("DELETE FROM creature WHERE guid = '%u'", m_DBTableGuid);
1415    WorldDatabase.PExecuteLog("DELETE FROM creature_addon WHERE guid = '%u'", m_DBTableGuid);
1416    WorldDatabase.PExecuteLog("DELETE FROM creature_movement WHERE id = '%u'", m_DBTableGuid);
1417    WorldDatabase.PExecuteLog("DELETE FROM game_event_creature WHERE guid = '%u'", m_DBTableGuid);
1418    WorldDatabase.PExecuteLog("DELETE FROM game_event_model_equip WHERE guid = '%u'", m_DBTableGuid);
1419    WorldDatabase.CommitTransaction();
1420}
1421
1422float Creature::GetAttackDistance(Unit const* pl) const
1423{
1424    float aggroRate = sWorld.getRate(RATE_CREATURE_AGGRO);
1425    if(aggroRate==0)
1426        return 0.0f;
1427
1428    int32 playerlevel   = pl->getLevelForTarget(this);
1429    int32 creaturelevel = getLevelForTarget(pl);
1430
1431    int32 leveldif       = playerlevel - creaturelevel;
1432
1433    // "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."
1434    if ( leveldif < - 25)
1435        leveldif = -25;
1436
1437    // "The aggro radius of a mob having the same level as the player is roughly 20 yards"
1438    float RetDistance = 20;
1439
1440    // "Aggro Radius varries with level difference at a rate of roughly 1 yard/level"
1441    // radius grow if playlevel < creaturelevel
1442    RetDistance -= (float)leveldif;
1443
1444    if(creaturelevel+5 <= sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL))
1445    {
1446        // detect range auras
1447        RetDistance += GetTotalAuraModifier(SPELL_AURA_MOD_DETECT_RANGE);
1448
1449        // detected range auras
1450        RetDistance += pl->GetTotalAuraModifier(SPELL_AURA_MOD_DETECTED_RANGE);
1451    }
1452
1453    // "Minimum Aggro Radius for a mob seems to be combat range (5 yards)"
1454    if(RetDistance < 5)
1455        RetDistance = 5;
1456
1457    return (RetDistance*aggroRate);
1458}
1459
1460void Creature::setDeathState(DeathState s)
1461{
1462    if((s == JUST_DIED && !m_isDeadByDefault)||(s == JUST_ALIVED && m_isDeadByDefault))
1463    {
1464        m_deathTimer = m_corpseDelay*1000;
1465
1466        // always save boss respawn time at death to prevent crash cheating
1467        if(sWorld.getConfig(CONFIG_SAVE_RESPAWN_TIME_IMMEDIATLY) || isWorldBoss())
1468            SaveRespawnTime();
1469
1470        if(!IsStopped())
1471            StopMoving();
1472    }
1473    Unit::setDeathState(s);
1474
1475    if(s == JUST_DIED)
1476    {
1477        SetUInt64Value (UNIT_FIELD_TARGET,0);               // remove target selection in any cases (can be set at aura remove in Unit::setDeathState)
1478        SetUInt32Value(UNIT_NPC_FLAGS, 0);
1479
1480        if(!isPet() && GetCreatureInfo()->SkinLootId)
1481            if ( LootTemplates_Skinning.HaveLootFor(GetCreatureInfo()->SkinLootId) )
1482                SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE);
1483
1484        Unit::setDeathState(CORPSE);
1485    }
1486    if(s == JUST_ALIVED)
1487    {
1488        SetHealth(GetMaxHealth());
1489        SetLootRecipient(NULL);
1490        Unit::setDeathState(ALIVE);
1491        CreatureInfo const *cinfo = GetCreatureInfo();
1492        SetUInt32Value(UNIT_DYNAMIC_FLAGS, 0);
1493        RemoveFlag (UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE);
1494        AddUnitMovementFlag(MOVEMENTFLAG_WALK_MODE);
1495        SetUInt32Value(UNIT_NPC_FLAGS, cinfo->npcflag);
1496        clearUnitState(UNIT_STAT_ALL_STATE);
1497        i_motionMaster.Clear();
1498        SetMeleeDamageSchool(SpellSchools(cinfo->dmgschool));
1499        LoadCreaturesAddon(true);
1500    }
1501}
1502
1503void Creature::Respawn()
1504{
1505    RemoveCorpse();
1506
1507    // forced recreate creature object at clients
1508    UnitVisibility currentVis = GetVisibility();
1509    SetVisibility(VISIBILITY_RESPAWN);
1510    ObjectAccessor::UpdateObjectVisibility(this);
1511    SetVisibility(currentVis);                              // restore visibility state
1512    ObjectAccessor::UpdateObjectVisibility(this);
1513
1514    if(getDeathState()==DEAD)
1515    {
1516        if (m_DBTableGuid)
1517            objmgr.SaveCreatureRespawnTime(m_DBTableGuid,GetInstanceId(),0);
1518        m_respawnTime = time(NULL);                         // respawn at next tick
1519    }
1520}
1521
1522bool Creature::IsImmunedToSpell(SpellEntry const* spellInfo, bool useCharges)
1523{
1524    if (!spellInfo)
1525        return false;
1526
1527    if (GetCreatureInfo()->MechanicImmuneMask & (1 << (spellInfo->Mechanic - 1)))
1528        return true;
1529
1530    return Unit::IsImmunedToSpell(spellInfo, useCharges);
1531}
1532
1533bool Creature::IsImmunedToSpellEffect(uint32 effect, uint32 mechanic) const
1534{
1535    if (GetCreatureInfo()->MechanicImmuneMask & (1 << (mechanic-1)))
1536        return true;
1537
1538    return Unit::IsImmunedToSpellEffect(effect, mechanic);
1539}
1540
1541SpellEntry const *Creature::reachWithSpellAttack(Unit *pVictim)
1542{
1543    if(!pVictim)
1544        return NULL;
1545
1546    for(uint32 i=0; i < CREATURE_MAX_SPELLS; i++)
1547    {
1548        if(!m_spells[i])
1549            continue;
1550        SpellEntry const *spellInfo = sSpellStore.LookupEntry(m_spells[i] );
1551        if(!spellInfo)
1552        {
1553            sLog.outError("WORLD: unknown spell id %i\n", m_spells[i]);
1554            continue;
1555        }
1556
1557        bool bcontinue = true;
1558        for(uint32 j=0;j<3;j++)
1559        {
1560            if( (spellInfo->Effect[j] == SPELL_EFFECT_SCHOOL_DAMAGE )       ||
1561                (spellInfo->Effect[j] == SPELL_EFFECT_INSTAKILL)            ||
1562                (spellInfo->Effect[j] == SPELL_EFFECT_ENVIRONMENTAL_DAMAGE) ||
1563                (spellInfo->Effect[j] == SPELL_EFFECT_HEALTH_LEECH )
1564                )
1565            {
1566                bcontinue = false;
1567                break;
1568            }
1569        }
1570        if(bcontinue) continue;
1571
1572        if(spellInfo->manaCost > GetPower(POWER_MANA))
1573            continue;
1574        SpellRangeEntry const* srange = sSpellRangeStore.LookupEntry(spellInfo->rangeIndex);
1575        float range = GetSpellMaxRange(srange);
1576        float minrange = GetSpellMinRange(srange);
1577        float dist = GetDistance(pVictim);
1578        //if(!isInFront( pVictim, range ) && spellInfo->AttributesEx )
1579        //    continue;
1580        if( dist > range || dist < minrange )
1581            continue;
1582        if(HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SILENCED))
1583            continue;
1584        return spellInfo;
1585    }
1586    return NULL;
1587}
1588
1589SpellEntry const *Creature::reachWithSpellCure(Unit *pVictim)
1590{
1591    if(!pVictim)
1592        return NULL;
1593
1594    for(uint32 i=0; i < CREATURE_MAX_SPELLS; i++)
1595    {
1596        if(!m_spells[i])
1597            continue;
1598        SpellEntry const *spellInfo = sSpellStore.LookupEntry(m_spells[i] );
1599        if(!spellInfo)
1600        {
1601            sLog.outError("WORLD: unknown spell id %i\n", m_spells[i]);
1602            continue;
1603        }
1604
1605        bool bcontinue = true;
1606        for(uint32 j=0;j<3;j++)
1607        {
1608            if( (spellInfo->Effect[j] == SPELL_EFFECT_HEAL ) )
1609            {
1610                bcontinue = false;
1611                break;
1612            }
1613        }
1614        if(bcontinue) continue;
1615
1616        if(spellInfo->manaCost > GetPower(POWER_MANA))
1617            continue;
1618        SpellRangeEntry const* srange = sSpellRangeStore.LookupEntry(spellInfo->rangeIndex);
1619        float range = GetSpellMaxRange(srange);
1620        float minrange = GetSpellMinRange(srange);
1621        float dist = GetDistance(pVictim);
1622        //if(!isInFront( pVictim, range ) && spellInfo->AttributesEx )
1623        //    continue;
1624        if( dist > range || dist < minrange )
1625            continue;
1626        if(HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SILENCED))
1627            continue;
1628        return spellInfo;
1629    }
1630    return NULL;
1631}
1632
1633bool Creature::IsVisibleInGridForPlayer(Player* pl) const
1634{
1635    // gamemaster in GM mode see all, including ghosts
1636    if(pl->isGameMaster())
1637        return true;
1638
1639    // Live player (or with not release body see live creatures or death creatures with corpse disappearing time > 0
1640    if(pl->isAlive() || pl->GetDeathTimer() > 0)
1641    {
1642        if( GetEntry() == VISUAL_WAYPOINT && !pl->isGameMaster() )
1643            return false;
1644        return isAlive() || m_deathTimer > 0 || m_isDeadByDefault && m_deathState==CORPSE;
1645    }
1646
1647    // Dead player see live creatures near own corpse
1648    if(isAlive())
1649    {
1650        Corpse *corpse = pl->GetCorpse();
1651        if(corpse)
1652        {
1653            // 20 - aggro distance for same level, 25 - max additional distance if player level less that creature level
1654            if(corpse->IsWithinDistInMap(this,(20+25)*sWorld.getRate(RATE_CREATURE_AGGRO)))
1655                return true;
1656        }
1657    }
1658
1659    // Dead player see Spirit Healer or Spirit Guide
1660    if(isSpiritService())
1661        return true;
1662
1663    // and not see any other
1664    return false;
1665}
1666
1667void Creature::DoFleeToGetAssistance(float radius) // Optional parameter
1668{
1669    if (!getVictim())
1670        return;
1671
1672    Creature* pCreature = NULL;
1673
1674    CellPair p(MaNGOS::ComputeCellPair(GetPositionX(), GetPositionY()));
1675    Cell cell(p);
1676    cell.data.Part.reserved = ALL_DISTRICT;
1677    cell.SetNoCreate();
1678
1679    MaNGOS::NearestAssistCreatureInCreatureRangeCheck u_check(this,getVictim(),radius);
1680    MaNGOS::CreatureLastSearcher<MaNGOS::NearestAssistCreatureInCreatureRangeCheck> searcher(pCreature, u_check);
1681
1682    TypeContainerVisitor<MaNGOS::CreatureLastSearcher<MaNGOS::NearestAssistCreatureInCreatureRangeCheck>, GridTypeMapContainer >  grid_creature_searcher(searcher);
1683
1684    CellLock<GridReadGuard> cell_lock(cell, p);
1685    cell_lock->Visit(cell_lock, grid_creature_searcher, *(GetMap()));
1686
1687    if(!GetMotionMaster()->empty() && (GetMotionMaster()->GetCurrentMovementGeneratorType() != POINT_MOTION_TYPE))
1688        GetMotionMaster()->Clear(false);
1689    if(pCreature == NULL)
1690    {
1691        GetMotionMaster()->MoveIdle();
1692        GetMotionMaster()->MoveFleeing(getVictim());
1693    }
1694    else
1695    {
1696        GetMotionMaster()->MoveIdle();
1697        GetMotionMaster()->MovePoint(0,pCreature->GetPositionX(),pCreature->GetPositionY(),pCreature->GetPositionZ());
1698    }
1699}
1700
1701void Creature::CallAssistence()
1702{
1703    if( !m_AlreadyCallAssistence && getVictim() && !isPet() && !isCharmed())
1704    {
1705        SetNoCallAssistence(true);
1706
1707        float radius = sWorld.getConfig(CONFIG_CREATURE_FAMILY_ASSISTEMCE_RADIUS);
1708        if(radius > 0)
1709        {
1710            std::list<Creature*> assistList;
1711
1712            {
1713                CellPair p(MaNGOS::ComputeCellPair(GetPositionX(), GetPositionY()));
1714                Cell cell(p);
1715                cell.data.Part.reserved = ALL_DISTRICT;
1716                cell.SetNoCreate();
1717
1718                MaNGOS::AnyAssistCreatureInRangeCheck u_check(this, getVictim(), radius);
1719                MaNGOS::CreatureListSearcher<MaNGOS::AnyAssistCreatureInRangeCheck> searcher(assistList, u_check);
1720
1721                TypeContainerVisitor<MaNGOS::CreatureListSearcher<MaNGOS::AnyAssistCreatureInRangeCheck>, GridTypeMapContainer >  grid_creature_searcher(searcher);
1722
1723                CellLock<GridReadGuard> cell_lock(cell, p);
1724                cell_lock->Visit(cell_lock, grid_creature_searcher, *MapManager::Instance().GetMap(GetMapId(), this));
1725            }
1726
1727            for(std::list<Creature*>::iterator iter = assistList.begin(); iter != assistList.end(); ++iter)
1728            {
1729                (*iter)->SetNoCallAssistence(true);
1730                if((*iter)->AI())
1731                    (*iter)->AI()->AttackStart(getVictim());
1732            }
1733        }
1734    }
1735}
1736
1737void Creature::SaveRespawnTime()
1738{
1739    if(isPet() || !m_DBTableGuid)
1740        return;
1741
1742    if(m_respawnTime > time(NULL))                          // dead (no corpse)
1743        objmgr.SaveCreatureRespawnTime(m_DBTableGuid,GetInstanceId(),m_respawnTime);
1744    else if(m_deathTimer > 0)                               // dead (corpse)
1745        objmgr.SaveCreatureRespawnTime(m_DBTableGuid,GetInstanceId(),time(NULL)+m_respawnDelay+m_deathTimer/1000);
1746}
1747
1748bool Creature::IsOutOfThreatArea(Unit* pVictim) const
1749{
1750    if(!pVictim)
1751        return true;
1752
1753    if(!pVictim->IsInMap(this))
1754        return true;
1755
1756    if(!pVictim->isTargetableForAttack())
1757        return true;
1758
1759    if(!pVictim->isInAccessablePlaceFor(this))
1760        return true;
1761
1762    if(sMapStore.LookupEntry(GetMapId())->Instanceable())
1763        return false;
1764
1765    float length = pVictim->GetDistance(CombatStartX,CombatStartY,CombatStartZ);
1766    float AttackDist = GetAttackDistance(pVictim);
1767    uint32 ThreatRadius = sWorld.getConfig(CONFIG_THREAT_RADIUS);
1768
1769    //Use AttackDistance in distance check if threat radius is lower. This prevents creature bounce in and ouf of combat every update tick.
1770    return ( length > (ThreatRadius > AttackDist ? ThreatRadius : AttackDist));
1771}
1772
1773CreatureDataAddon const* Creature::GetCreatureAddon() const
1774{
1775    if (m_DBTableGuid)
1776    {
1777        if(CreatureDataAddon const* addon = ObjectMgr::GetCreatureAddon(m_DBTableGuid))
1778            return addon;
1779    }
1780
1781    // dependent from heroic mode entry
1782    return ObjectMgr::GetCreatureTemplateAddon(GetCreatureInfo()->Entry);
1783}
1784
1785//creature_addon table
1786bool Creature::LoadCreaturesAddon(bool reload)
1787{
1788    CreatureDataAddon const *cainfo = GetCreatureAddon();
1789    if(!cainfo)
1790        return false;
1791
1792    if (cainfo->mount != 0)
1793        Mount(cainfo->mount);
1794
1795    if (cainfo->bytes0 != 0)
1796        SetUInt32Value(UNIT_FIELD_BYTES_0, cainfo->bytes0);
1797
1798    if (cainfo->bytes1 != 0)
1799        SetUInt32Value(UNIT_FIELD_BYTES_1, cainfo->bytes1);
1800
1801    if (cainfo->bytes2 != 0)
1802        SetUInt32Value(UNIT_FIELD_BYTES_2, cainfo->bytes2);
1803
1804    if (cainfo->emote != 0)
1805        SetUInt32Value(UNIT_NPC_EMOTESTATE, cainfo->emote);
1806
1807    if (cainfo->move_flags != 0)
1808        SetUnitMovementFlags(cainfo->move_flags);
1809
1810    if(cainfo->auras)
1811    {
1812        for (CreatureDataAddonAura const* cAura = cainfo->auras; cAura->spell_id; ++cAura)
1813        {
1814            SpellEntry const *AdditionalSpellInfo = sSpellStore.LookupEntry(cAura->spell_id);
1815            if (!AdditionalSpellInfo)
1816            {
1817                sLog.outErrorDb("Creature (GUIDLow: %u Entry: %u ) has wrong spell %u defined in `auras` field.",GetGUIDLow(),GetEntry(),cAura->spell_id);
1818                continue;
1819            }
1820
1821            // skip already applied aura
1822            if(HasAura(cAura->spell_id,cAura->effect_idx))
1823            {
1824                if(!reload)
1825                    sLog.outErrorDb("Creature (GUIDLow: %u Entry: %u ) has duplicate aura (spell %u effect %u) in `auras` field.",GetGUIDLow(),GetEntry(),cAura->spell_id,cAura->effect_idx);
1826
1827                continue;
1828            }
1829
1830            Aura* AdditionalAura = CreateAura(AdditionalSpellInfo, cAura->effect_idx, NULL, this, this, 0);
1831            AddAura(AdditionalAura);
1832            sLog.outDebug("Spell: %u with Aura %u added to creature (GUIDLow: %u Entry: %u )", cAura->spell_id, AdditionalSpellInfo->EffectApplyAuraName[0],GetGUIDLow(),GetEntry());
1833        }
1834    }
1835    return true;
1836}
1837
1838/// Send a message to LocalDefense channel for players oposition team in the zone
1839void Creature::SendZoneUnderAttackMessage(Player* attacker)
1840{
1841    uint32 enemy_team = attacker->GetTeam();
1842
1843    WorldPacket data(SMSG_ZONE_UNDER_ATTACK,4);
1844    data << (uint32)GetZoneId();
1845    sWorld.SendGlobalMessage(&data,NULL,(enemy_team==ALLIANCE ? HORDE : ALLIANCE));
1846}
1847
1848void Creature::_AddCreatureSpellCooldown(uint32 spell_id, time_t end_time)
1849{
1850    m_CreatureSpellCooldowns[spell_id] = end_time;
1851}
1852
1853void Creature::_AddCreatureCategoryCooldown(uint32 category, time_t apply_time)
1854{
1855    m_CreatureCategoryCooldowns[category] = apply_time;
1856}
1857
1858void Creature::AddCreatureSpellCooldown(uint32 spellid)
1859{
1860    SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellid);
1861    if(!spellInfo)
1862        return;
1863
1864    uint32 cooldown = GetSpellRecoveryTime(spellInfo);
1865    if(cooldown)
1866        _AddCreatureSpellCooldown(spellid, time(NULL) + cooldown/1000);
1867
1868    if(spellInfo->Category)
1869        _AddCreatureCategoryCooldown(spellInfo->Category, time(NULL));
1870
1871    m_GlobalCooldown = spellInfo->StartRecoveryTime;
1872}
1873
1874bool Creature::HasCategoryCooldown(uint32 spell_id) const
1875{
1876    SpellEntry const *spellInfo = sSpellStore.LookupEntry(spell_id);
1877    if(!spellInfo)
1878        return false;
1879
1880    // check global cooldown if spell affected by it
1881    if (spellInfo->StartRecoveryCategory > 0 && m_GlobalCooldown > 0)
1882        return true;
1883
1884    CreatureSpellCooldowns::const_iterator itr = m_CreatureCategoryCooldowns.find(spellInfo->Category);
1885    return(itr != m_CreatureCategoryCooldowns.end() && time_t(itr->second + (spellInfo->CategoryRecoveryTime / 1000)) > time(NULL));
1886}
1887
1888bool Creature::HasSpellCooldown(uint32 spell_id) const
1889{
1890    CreatureSpellCooldowns::const_iterator itr = m_CreatureSpellCooldowns.find(spell_id);
1891    return (itr != m_CreatureSpellCooldowns.end() && itr->second > time(NULL)) || HasCategoryCooldown(spell_id);
1892}
1893
1894bool Creature::IsInEvadeMode() const
1895{
1896    return !i_motionMaster.empty() && i_motionMaster.GetCurrentMovementGeneratorType() == HOME_MOTION_TYPE;
1897}
1898
1899bool Creature::HasSpell(uint32 spellID) const
1900{
1901    uint8 i;
1902    for(i = 0; i < CREATURE_MAX_SPELLS; ++i)
1903        if(spellID == m_spells[i])
1904            break;
1905    return i < CREATURE_MAX_SPELLS;                         //broke before end of iteration of known spells
1906}
1907
1908time_t Creature::GetRespawnTimeEx() const
1909{
1910    time_t now = time(NULL);
1911    if(m_respawnTime > now)                                 // dead (no corpse)
1912        return m_respawnTime;
1913    else if(m_deathTimer > 0)                               // dead (corpse)
1914        return now+m_respawnDelay+m_deathTimer/1000;
1915    else
1916        return now;
1917}
1918
1919void Creature::GetRespawnCoord( float &x, float &y, float &z, float* ori, float* dist ) const
1920{
1921    if (m_DBTableGuid)
1922    {
1923        if (CreatureData const* data = objmgr.GetCreatureData(GetDBTableGUIDLow()))
1924        {
1925            x = data->posX;
1926            y = data->posY;
1927            z = data->posZ;
1928            if(ori)
1929                *ori = data->orientation;
1930            if(dist)
1931                *dist = data->spawndist;
1932
1933            return;
1934        }
1935    }
1936
1937    x = GetPositionX();
1938    y = GetPositionY();
1939    z = GetPositionZ();
1940    if(ori)
1941        *ori = GetOrientation();
1942    if(dist)
1943        *dist = 0;
1944}
1945
1946void Creature::AllLootRemovedFromCorpse()
1947{
1948    if (!HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE))
1949    {
1950        uint32 nDeathTimer;
1951
1952        CreatureInfo const *cinfo = GetCreatureInfo();
1953
1954        // corpse was not skinnable -> apply corpse looted timer
1955        if (!cinfo || !cinfo->SkinLootId)
1956            nDeathTimer = (uint32)((m_corpseDelay * 1000) * sWorld.getRate(RATE_CORPSE_DECAY_LOOTED));
1957        // corpse skinnable, but without skinning flag, and then skinned, corpse will despawn next update
1958        else
1959            nDeathTimer = 0;
1960
1961        // update death timer only if looted timer is shorter
1962        if (m_deathTimer > nDeathTimer)
1963            m_deathTimer = nDeathTimer;
1964    }
1965}
1966
1967uint32 Creature::getLevelForTarget( Unit const* target ) const
1968{
1969    if(!isWorldBoss())
1970        return Unit::getLevelForTarget(target);
1971
1972    uint32 level = target->getLevel()+sWorld.getConfig(CONFIG_WORLD_BOSS_LEVEL_DIFF);
1973    if(level < 1)
1974        return 1;
1975    if(level > 255)
1976        return 255;
1977    return level;
1978}
1979
1980char const* Creature::GetScriptName() const
1981{
1982    return ObjectMgr::GetCreatureTemplate(GetEntry())->ScriptName;
1983}
1984
1985
1986VendorItemData const* Creature::GetVendorItems() const
1987{
1988    return objmgr.GetNpcVendorItemList(GetEntry());
1989}
1990
1991uint32 Creature::GetVendorItemCurrentCount(VendorItem const* vItem)
1992{
1993    if(!vItem->maxcount)
1994        return vItem->maxcount;
1995
1996    VendorItemCounts::iterator itr = m_vendorItemCounts.begin();
1997    for(; itr != m_vendorItemCounts.end(); ++itr)
1998        if(itr->itemId==vItem->item)
1999            break;
2000
2001    if(itr == m_vendorItemCounts.end())
2002        return vItem->maxcount;
2003
2004    VendorItemCount* vCount = &*itr;
2005
2006    time_t ptime = time(NULL);
2007
2008    if( vCount->lastIncrementTime + vItem->incrtime <= ptime )
2009    {
2010        ItemPrototype const* pProto = objmgr.GetItemPrototype(vItem->item);
2011
2012        uint32 diff = uint32((ptime - vCount->lastIncrementTime)/vItem->incrtime);
2013        if((vCount->count + diff * pProto->BuyCount) >= vItem->maxcount )
2014        {
2015            m_vendorItemCounts.erase(itr);       
2016            return vItem->maxcount;
2017        }
2018
2019        vCount->count += diff * pProto->BuyCount;
2020        vCount->lastIncrementTime = ptime;
2021    }
2022
2023    return vCount->count;
2024}
2025
2026uint32 Creature::UpdateVendorItemCurrentCount(VendorItem const* vItem, uint32 used_count)
2027{
2028    if(!vItem->maxcount)
2029        return 0;
2030
2031    VendorItemCounts::iterator itr = m_vendorItemCounts.begin();
2032    for(; itr != m_vendorItemCounts.end(); ++itr)
2033        if(itr->itemId==vItem->item)
2034            break;
2035
2036    if(itr == m_vendorItemCounts.end())
2037    {
2038        uint32 new_count = vItem->maxcount > used_count ? vItem->maxcount-used_count : 0;
2039        m_vendorItemCounts.push_back(VendorItemCount(vItem->item,new_count));
2040        return new_count;
2041    }
2042
2043    VendorItemCount* vCount = &*itr;
2044
2045    time_t ptime = time(NULL);
2046
2047    if( vCount->lastIncrementTime + vItem->incrtime <= ptime )
2048    {
2049        ItemPrototype const* pProto = objmgr.GetItemPrototype(vItem->item);
2050
2051        uint32 diff = uint32((ptime - vCount->lastIncrementTime)/vItem->incrtime);
2052        if((vCount->count + diff * pProto->BuyCount) < vItem->maxcount )
2053            vCount->count += diff * pProto->BuyCount;
2054        else
2055            vCount->count = vItem->maxcount;
2056    }
2057
2058    vCount->count = vCount->count > used_count ? vCount->count-used_count : 0;
2059    vCount->lastIncrementTime = ptime;
2060    return vCount->count;
2061}
2062
2063TrainerSpellData const* Creature::GetTrainerSpells() const
2064{
2065    return objmgr.GetNpcTrainerSpells(GetEntry());
2066}
Note: See TracBrowser for help on using the browser.