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

Revision 6, 66.0 kB (checked in by yumileroy, 17 years ago)

[svn] * Added ACE for Linux and Windows (Thanks Derex for Linux part and partial Windows part)
* Updated to 6721 and 676
* Fixed TrinityScript? logo
* Version updated to 0.2.6721.676

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