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

Revision 18, 70.0 kB (checked in by yumileroy, 17 years ago)

[svn] * Little fix in RandomMovementGenerator?
* Updated to 6731 and 680

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