2 * Copyright (C) 2005,2006,2007 MaNGOS <http://www.mangosproject.org/>
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.
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.
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
22 #include "WorldPacket.h"
23 #include "WorldSession.h"
25 #include "ObjectMgr.h"
32 #include "SpellAuras.h"
33 #include "MapManager.h"
34 #include "ObjectAccessor.h"
35 #include "CreatureAI.h"
40 #include "TemporarySummon.h"
44 float baseMoveSpeed
[MAX_MOVE_TYPE
] =
48 1.25f
, // MOVE_WALKBACK
49 4.722222f
, // MOVE_SWIM
50 4.5f
, // MOVE_SWIMBACK
51 3.141594f
, // MOVE_TURN
56 // auraTypes contains auras capable of proc'ing for attacker
57 static std::set
<uint32
> GenerateAttakerProcAuraTypes()
59 static std::set
<uint32
> auraTypes
;
60 auraTypes
.insert(SPELL_AURA_DUMMY
);
61 auraTypes
.insert(SPELL_AURA_PROC_TRIGGER_SPELL
);
62 auraTypes
.insert(SPELL_AURA_PROC_TRIGGER_DAMAGE
);
66 // auraTypes contains auras capable of proc'ing for attacker
67 static std::set
<uint32
> GenerateVictimProcAuraTypes()
69 static std::set
<uint32
> auraTypes
;
70 auraTypes
.insert(SPELL_AURA_PROC_TRIGGER_SPELL
);
71 auraTypes
.insert(SPELL_AURA_PROC_TRIGGER_DAMAGE
);
72 auraTypes
.insert(SPELL_AURA_DUMMY
);
73 auraTypes
.insert(SPELL_AURA_MOD_PARRY_PERCENT
);
77 static std::set
<uint32
> attackerProcAuraTypes
= GenerateAttakerProcAuraTypes();
78 static std::set
<uint32
> victimProcAuraTypes
= GenerateVictimProcAuraTypes();
80 // auraTypes contains auras capable of proc'ing for attacker and victim
81 static std::set
<uint32
> GenerateProcAuraTypes()
83 static std::set
<uint32
> auraTypes
= victimProcAuraTypes
;
84 auraTypes
.insert(attackerProcAuraTypes
.begin(),attackerProcAuraTypes
.end());
88 static std::set
<uint32
> procAuraTypes
= GenerateProcAuraTypes();
90 bool IsPassiveStackableSpell( uint32 spellId
)
92 if(!IsPassiveSpell(spellId
))
95 SpellEntry
const* spellProto
= sSpellStore
.LookupEntry(spellId
);
99 for(int j
= 0; j
< 3; ++j
)
101 if(std::find(procAuraTypes
.begin(),procAuraTypes
.end(),spellProto
->EffectApplyAuraName
[j
])!=procAuraTypes
.end())
108 Unit::Unit( WorldObject
*instantiator
)
109 : WorldObject( instantiator
), m_ThreatManager(this), m_HostilRefManager(this)
111 m_objectType
|= TYPE_UNIT
;
112 m_objectTypeId
= TYPEID_UNIT
;
114 m_updateFlag
= (UPDATEFLAG_ALL
| UPDATEFLAG_LIVING
| UPDATEFLAG_HASPOSITION
);
116 m_attackTimer
[BASE_ATTACK
] = 0;
117 m_attackTimer
[OFF_ATTACK
] = 0;
118 m_attackTimer
[RANGED_ATTACK
] = 0;
119 m_modAttackSpeedPct
[BASE_ATTACK
] = 1.0f
;
120 m_modAttackSpeedPct
[OFF_ATTACK
] = 1.0f
;
121 m_modAttackSpeedPct
[RANGED_ATTACK
] = 1.0f
;
125 m_deathState
= ALIVE
;
127 for (uint32 i
= 0; i
< CURRENT_MAX_SPELL
; i
++)
128 m_currentSpells
[i
] = NULL
;
130 m_TotemSlot
[0] = m_TotemSlot
[1] = m_TotemSlot
[2] = m_TotemSlot
[3] = 0;
131 m_ObjectSlot
[0] = m_ObjectSlot
[1] = m_ObjectSlot
[2] = m_ObjectSlot
[3] = 0;
133 //m_AurasCheck = 2000;
134 //m_removeAuraTimer = 4;
139 m_Visibility
= VISIBILITY_ON
;
142 m_detectInvisibility
= 0;
144 m_invisibilityvalue
= 0;
146 m_ShapeShiftForm
= 0;
147 m_canModifyStats
= false;
149 for (int i
= 0; i
< TOTAL_AURAS
; i
++)
150 m_AuraModifiers
[i
] = 0;
151 for (int i
= 0; i
< IMMUNITY_MECHANIC
; i
++)
152 m_spellImmune
[i
].clear();
153 for (int i
= 0; i
< UNIT_MOD_END
; i
++)
155 m_auraModifiersGroup
[i
][BASE_VALUE
] = 0.0f
;
156 m_auraModifiersGroup
[i
][BASE_PCT
] = 1.0f
;
157 m_auraModifiersGroup
[i
][TOTAL_VALUE
] = 0.0f
;
158 m_auraModifiersGroup
[i
][TOTAL_PCT
] = 1.0f
;
160 // implement 50% base damage from offhand
161 m_auraModifiersGroup
[UNIT_MOD_DAMAGE_OFFHAND
][TOTAL_PCT
] = 0.5f
;
163 for (int i
= 0; i
< 3; i
++)
165 m_weaponDamage
[i
][MINDAMAGE
] = BASE_MINDAMAGE
;
166 m_weaponDamage
[i
][MAXDAMAGE
] = BASE_MAXDAMAGE
;
168 for (int i
= 0; i
< MAX_STATS
; i
++)
169 m_createStats
[i
] = 0.0f
;
173 m_modSpellHitChance
= 0;
174 m_baseSpellCritChance
= 5;
175 m_modResilience
= 0.0;
177 //m_victimThreat = 0.0f;
178 for (int i
= 0; i
< MAX_SPELL_SCHOOL
; ++i
)
179 m_threatModifier
[i
] = 1.0f
;
181 for (int i
= 0; i
< MAX_MOVE_TYPE
; ++i
)
182 m_speed_rate
[i
] = 1.0f
;
190 // set current spells as deletable
191 for (uint32 i
= 0; i
< CURRENT_MAX_SPELL
; i
++)
193 // spell may be safely deleted now
194 if (m_currentSpells
[i
]) m_currentSpells
[i
]->SetDeletable(true);
195 m_currentSpells
[i
] = NULL
;
198 // remove references to unit
199 for(std::list
<GameObject
*>::iterator i
= m_gameObj
.begin(); i
!= m_gameObj
.end();)
201 (*i
)->SetOwnerGUID(0);
202 (*i
)->SetRespawnTime(0);
204 i
= m_gameObj
.erase(i
);
207 RemoveAllDynObjects();
209 if(m_charmInfo
) delete m_charmInfo
;
212 void Unit::RemoveAllDynObjects()
214 while(!m_dynObjGUIDs
.empty())
216 DynamicObject
* dynObj
= ObjectAccessor::Instance().GetDynamicObject(*this,*m_dynObjGUIDs
.begin());
219 m_dynObjGUIDs
.erase(m_dynObjGUIDs
.begin());
223 void Unit::Update( uint32 p_time
)
225 /*if(p_time > m_AurasCheck)
230 m_AurasCheck -= p_time;*/
232 // WARNING! Order of execution here is important, do not change.
233 // Spells must be processed with event system BEFORE they go to _UpdateSpells.
234 // Or else we may have some SPELL_STATE_FINISHED spells stalled in pointers, that is bad.
235 m_Events
.Update( p_time
);
236 _UpdateSpells( p_time
);
238 //update combat timer only for players and pets
239 if (isInCombat() && (GetTypeId() == TYPEID_PLAYER
|| ((Creature
*)this)->isPet() || ((Creature
*)this)->isCharmed()))
241 if(m_HostilRefManager
.isEmpty())
243 // m_CombatTimer set at aura start and it will be freeze until aura removing
244 if(!HasAuraType(SPELL_AURA_INTERRUPT_REGEN
))
246 if ( m_CombatTimer
<= p_time
)
249 m_CombatTimer
-= p_time
;
254 if(uint32 base_att
= getAttackTimer(BASE_ATTACK
))
256 setAttackTimer(BASE_ATTACK
, (p_time
>= base_att
? 0 : base_att
- p_time
) );
258 if(GetHealth() < GetMaxHealth()*0.2)
259 ModifyAuraState(AURA_STATE_HEALTHLESS
, true);
260 else ModifyAuraState(AURA_STATE_HEALTHLESS
, false);
263 bool Unit::haveOffhandWeapon() const
265 if(GetTypeId() == TYPEID_PLAYER
)
267 Item
*tmpitem
= ((Player
*)this)->GetItemByPos(INVENTORY_SLOT_BAG_0
, EQUIPMENT_SLOT_OFFHAND
);
269 return tmpitem
&& !tmpitem
->IsBroken() && (tmpitem
->GetProto()->InventoryType
== INVTYPE_WEAPON
|| tmpitem
->GetProto()->InventoryType
== INVTYPE_WEAPONOFFHAND
);
275 void Unit::SendMoveToPacket(float x
, float y
, float z
, bool run
, uint32 transitTime
)
277 float dx
= x
- GetPositionX();
278 float dy
= y
- GetPositionY();
279 float dz
= z
- GetPositionZ();
282 float dist
= ((dx
*dx
) + (dy
*dy
) + (dz
*dz
));
287 double speed
= GetSpeed(run
? MOVE_RUN
: MOVE_WALK
);
291 transitTime
= static_cast<uint32
>(dist
/ speed
+ 0.5);
293 //float orientation = (float)atan2((double)dy, (double)dx);
294 SendMonsterMove(x
,y
,z
,0,run
,transitTime
);
297 void Unit::SendMonsterMove(float NewPosX
, float NewPosY
, float NewPosZ
, uint8 type
, bool Run
, uint32 Time
)
299 WorldPacket
data( SMSG_MONSTER_MOVE
, (41 + GetPackGUID().size()) );
300 data
.append(GetPackGUID());
302 // Point A, starting location
303 data
<< GetPositionX() << GetPositionY() << GetPositionZ();
304 // unknown field - unrelated to orientation
305 // seems to increment about 1000 for every 1.7 seconds
306 // for now, we'll just use mstime
309 data
<< uint8(type
); // unknown
312 case 0: // normal packet
314 case 1: // stop packet
315 SendMessageToSet( &data
, true );
317 case 3: // not used currently
318 data
<< uint64(0); // probably target guid
320 case 4: // not used currently
321 data
<< float(0); // probably orientation
325 data
<< uint32(Run
? 0x00000100 : 0x00000000); // flags (0x100 - running, 0x200 - taxi)
327 512: Floating, moving without walking/running
329 data
<< Time
; // Time in between points
330 data
<< uint32(1); // 1 single waypoint
331 data
<< NewPosX
<< NewPosY
<< NewPosZ
; // the single waypoint Point B
333 SendMessageToSet( &data
, true );
336 void Unit::resetAttackTimer(WeaponAttackType type
)
338 if (GetTypeId() == TYPEID_PLAYER
)
339 m_attackTimer
[type
] = uint32(GetAttackTime(type
) * m_modAttackSpeedPct
[type
]);
341 m_attackTimer
[type
] = uint32(BASE_ATTACK_TIME
* m_modAttackSpeedPct
[type
]);
344 bool Unit::canReachWithAttack(Unit
*pVictim
) const
347 float reach
= GetFloatValue(UNIT_FIELD_COMBATREACH
);
350 return IsWithinDistInMap(pVictim
, reach
);
353 void Unit::RemoveSpellsCausingAura(uint32 auraType
)
355 if (auraType
>= TOTAL_AURAS
) return;
356 AuraList::iterator iter
, next
;
357 for (iter
= m_modAuras
[auraType
].begin(); iter
!= m_modAuras
[auraType
].end(); iter
= next
)
364 RemoveAurasDueToSpell((*iter
)->GetId());
365 if (!m_modAuras
[auraType
].empty())
366 next
= m_modAuras
[auraType
].begin();
373 bool Unit::HasAuraType(uint32 auraType
) const
375 return (!m_modAuras
[auraType
].empty());
378 /* Called by DealDamage for auras that have a chance to be dispelled on damage taken. */
379 void Unit::RemoveSpellbyDamageTaken(uint32 auraType
, uint32 damage
)
381 if(!HasAuraType(auraType
))
384 // The chance to dispell an aura depends on the damage taken with respect to the casters level.
385 uint32 max_dmg
= getLevel() > 8 ? 25 * getLevel() - 150 : 50;
386 float chance
= float(damage
) / max_dmg
* 100.0;
387 if (roll_chance_f(chance
))
388 RemoveSpellsCausingAura(auraType
);
391 void Unit::DealDamage(Unit
*pVictim
, uint32 damage
, CleanDamage
const* cleanDamage
, DamageEffectType damagetype
, uint8 damageSchool
, SpellEntry
const *spellProto
, uint32 procFlag
, bool durabilityLoss
)
393 if (!pVictim
->isAlive() || pVictim
->isInFlight()) return;
395 //You don't lose health from damage taken from another player while in a sanctuary
396 //You still see it in the combat log though
397 if(pVictim
!= this && GetTypeId() == TYPEID_PLAYER
&& pVictim
->GetTypeId() == TYPEID_PLAYER
)
399 const AreaTableEntry
*area
= GetAreaEntryByAreaID(pVictim
->GetAreaId());
400 if(area
&& area
->flags
& 0x800) //sanctuary
404 // remove affects at any damage (including 0 damage)
406 RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH
);
407 if(HasInvisibilityAura())
408 RemoveSpellsCausingAura(SPELL_AURA_MOD_INVISIBILITY
);
409 // remove death simulation at damage
410 if(hasUnitState(UNIT_STAT_DIED
))
411 RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH
);
413 //Script Event damage Deal
414 if( GetTypeId()== TYPEID_UNIT
&& ((Creature
*)this)->AI())
415 ((Creature
*)this)->AI()->DamageDeal(pVictim
, damage
);
416 //Script Event damage taken
417 if( pVictim
->GetTypeId()== TYPEID_UNIT
&& ((Creature
*)pVictim
)->AI() )
418 ((Creature
*)pVictim
)->AI()->DamageTaken(this, damage
);
422 // Rage from damage received.
423 if(cleanDamage
&& cleanDamage
->damage
&& pVictim
->GetTypeId() == TYPEID_PLAYER
&& (pVictim
->getPowerType() == POWER_RAGE
))
424 ((Player
*)pVictim
)->RewardRage(cleanDamage
->damage
, 0, false);
429 pVictim
->RemoveSpellbyDamageTaken(SPELL_AURA_MOD_FEAR
, damage
);
430 // root type spells do not dispell the root effect
431 if(!spellProto
|| spellProto
->Mechanic
!= MECHANIC_ROOT
)
432 pVictim
->RemoveSpellbyDamageTaken(SPELL_AURA_MOD_ROOT
, damage
);
434 if(pVictim
->GetTypeId() != TYPEID_PLAYER
)
436 //pVictim->SetInFront(this);
437 // no loot,xp,health if type 8 /critters/
438 if ( pVictim
->GetCreatureType() == CREATURE_TYPE_CRITTER
)
440 pVictim
->setDeathState(JUST_DIED
);
441 pVictim
->SetHealth(0);
442 pVictim
->CombatStop(true);
443 pVictim
->DeleteThreatList();
446 if(!pVictim
->isInCombat() && ((Creature
*)pVictim
)->AI())
447 ((Creature
*)pVictim
)->AI()->AttackStart(this);
450 DEBUG_LOG("DealDamageStart");
452 uint32 health
= pVictim
->GetHealth();
453 sLog
.outDetail("deal dmg:%d to health:%d ",damage
,health
);
455 // duel ends when player has 1 or less hp
456 bool duel_hasEnded
= false;
457 if(pVictim
->GetTypeId() == TYPEID_PLAYER
&& ((Player
*)pVictim
)->duel
&& damage
>= (health
-1))
459 // prevent kill only if killed in duel and killed by opponent or opponent controlled creature
460 if(((Player
*)pVictim
)->duel
->opponent
==this || ((Player
*)pVictim
)->duel
->opponent
->GetGUID() == GetOwnerGUID())
463 duel_hasEnded
= true;
466 if(pVictim
!= this && damagetype
!= DOT
)
469 pVictim
->SetInCombat();
472 // Rage from Damage made
473 if( cleanDamage
&& this != pVictim
&& GetTypeId() == TYPEID_PLAYER
&& (getPowerType() == POWER_RAGE
))
475 uint32 weaponSpeedHitFactor
;
477 switch(cleanDamage
->attackType
)
481 if(cleanDamage
->hitOutCome
== MELEE_HIT_CRIT
)
482 weaponSpeedHitFactor
= uint32(GetAttackTime(cleanDamage
->attackType
)/1000.0f
* 7);
484 weaponSpeedHitFactor
= uint32(GetAttackTime(cleanDamage
->attackType
)/1000.0f
* 3.5f
);
486 ((Player
*)this)->RewardRage(damage
, weaponSpeedHitFactor
, true);
492 if(cleanDamage
->hitOutCome
== MELEE_HIT_CRIT
)
493 weaponSpeedHitFactor
= uint32(GetAttackTime(cleanDamage
->attackType
)/1000.0f
* 3.5f
);
495 weaponSpeedHitFactor
= uint32(GetAttackTime(cleanDamage
->attackType
)/1000.0f
* 1.75f
);
497 ((Player
*)this)->RewardRage(damage
, weaponSpeedHitFactor
, true);
506 if (health
<= damage
)
509 DEBUG_LOG("DealDamage: victim just died");
511 DEBUG_LOG("DealDamageAttackStop");
513 pVictim
->CombatStop(true);
515 DEBUG_LOG("SET JUST_DIED");
516 pVictim
->setDeathState(JUST_DIED
);
518 DEBUG_LOG("DealDamageHealth1");
519 pVictim
->SetHealth(0);
521 // Call KilledUnit for creatures
522 if (GetTypeId() == TYPEID_UNIT
&& ((Creature
*)this)->AI())
523 ((Creature
*)this)->AI()->KilledUnit(pVictim
);
525 // 10% durability loss on death
526 // clean InHateListOf
527 if (pVictim
->GetTypeId() == TYPEID_PLAYER
)
529 if (GetTypeId() != TYPEID_PLAYER
&& durabilityLoss
)
531 DEBUG_LOG("We are dead, loosing 10 percents durability");
532 ((Player
*)pVictim
)->DurabilityLossAll(0.10);
533 // durability lost message
534 WorldPacket
data(SMSG_DURABILITY_DAMAGE_DEATH
, 0);
535 ((Player
*)pVictim
)->GetSession()->SendPacket(&data
);
537 pVictim
->getHostilRefManager().deleteReferences();
539 Pet
*pet
= pVictim
->GetPet();
540 if(pet
&& pVictim
->GetTypeId() != TYPEID_PLAYER
)
542 pet
->setDeathState(JUST_DIED
);
543 pet
->CombatStop(true);
545 pet
->addUnitState(UNIT_STAT_DIED
);
546 pet
->getHostilRefManager().deleteReferences();
549 else // creature died
551 DEBUG_LOG("DealDamageNotPlayer");
553 if(((Creature
*)pVictim
)->isPet())
554 pVictim
->getHostilRefManager().deleteReferences();
557 pVictim
->DeleteThreatList();
558 pVictim
->SetUInt32Value(UNIT_DYNAMIC_FLAGS
, UNIT_DYNFLAG_LOOTABLE
);
560 // Call creature just died function
561 if (((Creature
*)pVictim
)->AI())
562 ((Creature
*)pVictim
)->AI()->JustDied(this);
565 //judge if GainXP, Pet kill like player kill,kill pet not like PvP
567 Player
*player
= NULL
;
569 if(GetTypeId() == TYPEID_PLAYER
)
571 player
= (Player
*)this;
572 if(pVictim
->GetTypeId() == TYPEID_PLAYER
)
577 if(player
->GetPetGUID() && (pet
= player
->GetPet()))
578 pet
->ClearInCombat();
580 if(player
->GetCharmGUID() && (pet
= player
->GetCharm()))
581 pet
->ClearInCombat();
584 // FIXME: or charmed (can be player). Maybe must be check before GetTypeId() == TYPEID_PLAYER
585 else if(GetCharmerOrOwnerGUID()) // Pet or timed creature, etc
588 Unit
* owner
= pet
->GetCharmerOrOwner();
590 if(owner
&& owner
->GetTypeId() == TYPEID_PLAYER
)
592 player
= (Player
*)owner
;
593 player
->ClearInCombat();
594 if(pVictim
->GetTypeId() == TYPEID_PLAYER
)
598 if(pet
->GetTypeId()==TYPEID_UNIT
&& ((Creature
*)pet
)->isPet())
600 uint32 petxp
= MaNGOS::XP::BaseGain(getLevel(), pVictim
->getLevel());
601 ((Pet
*)pet
)->GivePetXP(petxp
);
605 // self or owner of pet
610 // prepare data for near group iteration (PvP and !PvP cases
611 uint32 xp
= PvP
|| IsNoDamageXPArea(player
->GetAreaId()) ? 0 : MaNGOS::XP::Gain(player
, pVictim
);
612 bool honored_kill
= false;
614 Group
*pGroup
= player
->GetGroup();
617 uint32 count
= pGroup
->GetMemberCountForXPAtKill(pVictim
);
620 // skip in check PvP case (for speed, not used)
621 bool is_raid
= PvP
? false : MapManager::Instance().GetBaseMap(player
->GetMapId())->IsRaid() && pGroup
->isRaidGroup();
623 for(GroupReference
*itr
= pGroup
->GetFirstMember(); itr
!= NULL
; itr
= itr
->next())
625 Player
*pGroupGuy
= pGroup
->GetMemberForXPAtKill(itr
->getSource(),pVictim
);
629 // honor can be in PvP and !PvP (racial leader) cases
630 if(pGroupGuy
->RewardHonor(pVictim
,count
) && player
==pGroupGuy
)
633 // xp and reputation only in !PvP case
636 // FIXME: xp/count for all in group at this moment, must be level dependent
637 float rate
= 1.0f
/count
;
639 // if with raid in raid dungeon then all receive full reputation at kill
640 pGroupGuy
->RewardReputation(pVictim
,is_raid
? 1.0f
: rate
);
642 uint32 itr_xp
= uint32(xp
*rate
);
644 pGroupGuy
->GiveXP(itr_xp
, pVictim
);
645 if(Pet
* pet
= player
->GetPet())
647 pet
->GivePetXP(itr_xp
/2);
650 // normal creature (not pet/etc) can be only in !PvP case
651 if(pVictim
->GetTypeId()==TYPEID_UNIT
)
652 pGroupGuy
->KilledMonster(pVictim
->GetEntry(), pVictim
->GetGUID());
659 // honor can be in PvP and !PvP (racial leader) cases
660 if(player
->RewardHonor(pVictim
,1))
663 // xp and reputation only in !PvP case
666 player
->RewardReputation(pVictim
,1);
667 player
->GiveXP(xp
, pVictim
);
668 if(Pet
* pet
= player
->GetPet())
673 // normal creature (not pet/etc) can be only in !PvP case
674 if(pVictim
->GetTypeId()==TYPEID_UNIT
)
675 player
->KilledMonster(pVictim
->GetEntry(),pVictim
->GetGUID());
679 if(xp
|| honored_kill
)
680 player
->ProcDamageAndSpell(pVictim
,PROC_FLAG_KILL_XP_GIVER
,PROC_FLAG_NONE
);
685 DEBUG_LOG("Monster kill Monster");
688 // last damage from duel opponent or opponent controlled creature?
691 assert(pVictim
->GetTypeId()==TYPEID_PLAYER
);
692 Player
*he
= (Player
*)pVictim
;
696 CombatStop(); // for case killed by pet
697 if (IsNonMeleeSpellCasted(true))
698 InterruptNonMeleeSpells(true);
699 if(he
->duel
->opponent
!=this)
701 he
->duel
->opponent
->CombatStop();
702 if(he
->duel
->opponent
->IsNonMeleeSpellCasted(true))
703 he
->duel
->opponent
->InterruptNonMeleeSpells(true);
706 if(he
->IsNonMeleeSpellCasted(true))
707 he
->InterruptNonMeleeSpells(true);
712 else // if (health <= damage)
714 DEBUG_LOG("DealDamageAlive");
716 pVictim
->ModifyHealth(- (int32
)damage
);
718 // Check if health is below 20% (apply damage before to prevent case when after ProcDamageAndSpell health < damage
719 if(pVictim
->GetHealth()*5 < pVictim
->GetMaxHealth())
721 uint32 procVictim
= PROC_FLAG_NONE
;
723 // if just dropped below 20% (for CheatDeath)
724 if((pVictim
->GetHealth()+damage
)*5 > pVictim
->GetMaxHealth())
725 procVictim
= PROC_FLAG_LOW_HEALTH
;
727 ProcDamageAndSpell(pVictim
,PROC_FLAG_TARGET_LOW_HEALTH
,procVictim
);
730 if(damagetype
!= DOT
)
732 //start melee attacks only after melee hit
733 Attack(pVictim
,(damagetype
== DIRECT_DAMAGE
));
736 if(pVictim
->getTransForm() && pVictim
->hasUnitState(UNIT_STAT_CONFUSED
))
738 pVictim
->RemoveAurasDueToSpell(pVictim
->getTransForm());
739 pVictim
->setTransForm(0);
742 if (pVictim
->GetTypeId() != TYPEID_PLAYER
)
744 if(spellProto
&& IsDamageToThreatSpell(spellProto
))
746 pVictim
->AddThreat(this, damage
, damageSchool
, spellProto
);
748 else // victim is a player
750 // Rage from damage received
751 if(this != pVictim
&& pVictim
->GetTypeId() == TYPEID_PLAYER
&& pVictim
->getPowerType() == POWER_RAGE
)
753 uint32 rage_damage
= damage
+ (cleanDamage
? cleanDamage
->damage
: 0);
754 ((Player
*)pVictim
)->RewardRage(rage_damage
, 0, false);
757 // random durability for items (HIT)
758 if (urand(0,300) == 10)
760 DEBUG_LOG("HIT: We decrease durability with 5 percent");
761 ((Player
*)pVictim
)->DurabilityLossAll(0.05);
765 // TODO: Store auras by interrupt flag to speed this up.
766 AuraMap
& vAuras
= pVictim
->GetAuras();
767 for (AuraMap::iterator i
= vAuras
.begin(), next
; i
!= vAuras
.end(); i
= next
)
769 const SpellEntry
*se
= i
->second
->GetSpellProto();
771 if( se
->AuraInterruptFlags
& AURA_INTERRUPT_FLAG_DAMAGE
)
774 if (se
->procFlags
& (1<<3))
776 if (!roll_chance_i(se
->procChance
))
781 pVictim
->RemoveAurasDueToSpell(i
->second
->GetId());
782 // FIXME: this may cause the auras with proc chance to be rerolled several times
783 next
= vAuras
.begin();
788 if (damagetype
!= NODAMAGE
)
790 if(pVictim
->m_currentSpells
[CURRENT_CHANNELED_SPELL
] && pVictim
->GetTypeId() == TYPEID_PLAYER
&& damage
)
792 if (pVictim
->m_currentSpells
[CURRENT_CHANNELED_SPELL
]->getState() == SPELL_STATE_CASTING
)
794 uint32 channelInterruptFlags
= pVictim
->m_currentSpells
[CURRENT_CHANNELED_SPELL
]->m_spellInfo
->ChannelInterruptFlags
;
795 if( channelInterruptFlags
& CHANNEL_FLAG_DELAY
)
797 sLog
.outDetail("Spell %u delayed (%d) at damage!",pVictim
->m_currentSpells
[CURRENT_CHANNELED_SPELL
]->m_spellInfo
->Id
,(int32
)(0.25f
* GetDuration(pVictim
->m_currentSpells
[CURRENT_CHANNELED_SPELL
]->m_spellInfo
)));
798 pVictim
->m_currentSpells
[CURRENT_CHANNELED_SPELL
]->DelayedChannel((int32
)(0.25f
* GetDuration(pVictim
->m_currentSpells
[CURRENT_CHANNELED_SPELL
]->m_spellInfo
)));
800 else if( (channelInterruptFlags
& (CHANNEL_FLAG_DAMAGE
| CHANNEL_FLAG_DAMAGE2
)) )
802 sLog
.outDetail("Spell %u canceled at damage!",pVictim
->m_currentSpells
[CURRENT_CHANNELED_SPELL
]->m_spellInfo
->Id
);
803 pVictim
->InterruptSpell(CURRENT_CHANNELED_SPELL
);
806 else if (pVictim
->m_currentSpells
[CURRENT_CHANNELED_SPELL
]->getState() == SPELL_STATE_DELAYED
)
807 // break channeled spell in delayed state on damage
809 sLog
.outDetail("Spell %u canceled at damage!",pVictim
->m_currentSpells
[CURRENT_CHANNELED_SPELL
]->m_spellInfo
->Id
);
810 pVictim
->InterruptSpell(CURRENT_CHANNELED_SPELL
);
815 // last damage from duel opponent
818 assert(pVictim
->GetTypeId()==TYPEID_PLAYER
);
819 Player
*he
= (Player
*)pVictim
;
824 CombatStop(); // for case killed by pet
825 if(he
->duel
->opponent
!=this)
826 he
->duel
->opponent
->CombatStop();
829 he
->CastSpell(he
, 7267, true); // beg
834 DEBUG_LOG("DealDamageEnd");
837 void Unit::CastSpell(Unit
* Victim
, uint32 spellId
, bool triggered
, Item
*castItem
, Aura
* triggredByAura
, uint64 originalCaster
)
839 SpellEntry
const *spellInfo
= sSpellStore
.LookupEntry(spellId
);
843 sLog
.outError("WORLD: unknown spell id %i\n", spellId
);
847 CastSpell(Victim
,spellInfo
,triggered
,castItem
,triggredByAura
, originalCaster
);
850 void Unit::CastSpell(Unit
* Victim
,SpellEntry
const *spellInfo
, bool triggered
, Item
*castItem
, Aura
* triggredByAura
, uint64 originalCaster
)
854 sLog
.outError("WORLD: unknown spell ");
859 DEBUG_LOG("WORLD: cast Item spellId - %i", spellInfo
->Id
);
861 Spell
*spell
= new Spell(this, spellInfo
, triggered
, triggredByAura
,originalCaster
);
863 SpellCastTargets targets
;
864 targets
.setUnitTarget( Victim
);
865 spell
->m_CastItem
= castItem
;
866 spell
->prepare(&targets
);
869 void Unit::CastCustomSpell(Unit
* Victim
,uint32 spellId
, int32
const* bp0
, int32
const* bp1
, int32
const* bp2
, bool triggered
, Item
*castItem
, Aura
* triggredByAura
, uint64 originalCaster
)
871 SpellEntry
const *spellInfo
= sSpellStore
.LookupEntry(spellId
);
875 sLog
.outError("WORLD: unknown spell id %i\n", spellId
);
879 CastCustomSpell(Victim
,spellInfo
,bp0
,bp1
,bp2
,triggered
,castItem
,triggredByAura
, originalCaster
);
882 void Unit::CastCustomSpell(Unit
* Victim
,SpellEntry
const *spellInfo
, int32
const* bp0
, int32
const* bp1
, int32
const* bp2
, bool triggered
, Item
*castItem
, Aura
* triggredByAura
, uint64 originalCaster
)
886 sLog
.outError("WORLD: unknown spell ");
891 DEBUG_LOG("WORLD: cast Item spellId - %i", spellInfo
->Id
);
893 Spell
*spell
= new Spell(this, spellInfo
, triggered
, triggredByAura
,originalCaster
);
896 spell
->m_currentBasePoints
[0] = *bp0
;
899 spell
->m_currentBasePoints
[1] = *bp1
;
902 spell
->m_currentBasePoints
[2] = *bp2
;
904 SpellCastTargets targets
;
905 targets
.setUnitTarget( Victim
);
906 spell
->m_CastItem
= castItem
;
907 spell
->prepare(&targets
);
910 void Unit::DealDamageBySchool(Unit
*pVictim
, SpellEntry
const *spellInfo
, uint32
*damage
, CleanDamage
*cleanDamage
, bool *crit
, bool isTriggeredSpell
)
913 // TODO this in only generic way, check for exceptions
914 DEBUG_LOG("DealDamageBySchool (BEFORE) SCHOOL %u >> DMG:%u", spellInfo
->School
, *damage
);
917 switch (spellInfo
->School
)
919 // Physical damage school
920 case SPELL_SCHOOL_NORMAL
:
922 // Calculate physical outcome
923 MeleeHitOutcome outcome
;
924 outcome
= RollPhysicalOutcomeAgainst(pVictim
, BASE_ATTACK
, spellInfo
);
926 //Used to store the Hit Outcome
927 cleanDamage
->hitOutCome
= outcome
;
929 // Return miss first (sends miss message)
930 if(outcome
== MELEE_HIT_MISS
)
932 SendAttackStateUpdate(HITINFO_MISS
, pVictim
, 1, spellInfo
->School
, 0, 0,0,1,0);
935 if(GetTypeId()== TYPEID_PLAYER
)
936 ((Player
*)this)->UpdateWeaponSkill(BASE_ATTACK
);
938 CastMeleeProcDamageAndSpell(pVictim
,0,BASE_ATTACK
,MELEE_HIT_MISS
,spellInfo
,isTriggeredSpell
);
942 // Hitinfo, Victimstate
943 uint32 hitInfo
, victimState
;
944 hitInfo
= HITINFO_NORMALSWING
;
946 //Calculate armor mitigation
947 uint32 damageAfterArmor
;
948 damageAfterArmor
= CalcArmorReducedDamage(pVictim
, *damage
);
949 cleanDamage
->damage
+= *damage
- damageAfterArmor
;
950 *damage
= damageAfterArmor
;
958 // Resilience - reduce crit damage by 2x%
959 uint32 resilienceReduction
= uint32(pVictim
->m_modResilience
* 2/100 * (*damage
));
960 cleanDamage
->damage
+= resilienceReduction
;
961 *damage
-= resilienceReduction
;
963 hitInfo
|= HITINFO_CRITICALHIT
;
966 case MELEE_HIT_PARRY
:
968 cleanDamage
->damage
+= *damage
; // To Help Calculate Rage
970 victimState
= VICTIMSTATE_PARRY
;
972 // Counter-attack ( explained in Unit::DoAttackDamage() )
975 float offtime
= float(pVictim
->getAttackTimer(OFF_ATTACK
));
976 float basetime
= float(pVictim
->getAttackTimer(BASE_ATTACK
));
978 // Reduce attack time
979 if (pVictim
->haveOffhandWeapon() && offtime
< basetime
)
981 float percent20
= pVictim
->GetAttackTime(OFF_ATTACK
) * 0.20;
982 float percent60
= 3 * percent20
;
983 if(offtime
> percent20
&& offtime
<= percent60
)
985 pVictim
->setAttackTimer(OFF_ATTACK
, uint32(percent20
));
987 else if(offtime
> percent60
)
989 offtime
-= 2 * percent20
;
990 pVictim
->setAttackTimer(OFF_ATTACK
, uint32(offtime
));
995 float percent20
= pVictim
->GetAttackTime(BASE_ATTACK
) * 0.20;
996 float percent60
= 3 * percent20
;
997 if(basetime
> percent20
&& basetime
<= percent60
)
999 pVictim
->setAttackTimer(BASE_ATTACK
, uint32(percent20
));
1001 else if(basetime
> percent60
)
1003 basetime
-= 2 * percent20
;
1004 pVictim
->setAttackTimer(BASE_ATTACK
, uint32(basetime
));
1009 // Update victim defense ?
1010 if(pVictim
->GetTypeId() == TYPEID_PLAYER
)
1011 ((Player
*)pVictim
)->UpdateDefense();
1014 pVictim
->HandleEmoteCommand(EMOTE_ONESHOT_PARRYUNARMED
);
1015 pVictim
->ModifyAuraState(AURA_STATE_PARRY
, true);
1018 case MELEE_HIT_DODGE
:
1020 if(pVictim
->GetTypeId() == TYPEID_PLAYER
)
1021 ((Player
*)pVictim
)->UpdateDefense();
1023 cleanDamage
->damage
+= *damage
; // To Help Calculate Rage
1025 hitInfo
|= HITINFO_SWINGNOHITSOUND
;
1026 victimState
= VICTIMSTATE_DODGE
;
1029 case MELEE_HIT_BLOCK
:
1031 uint32 blocked_amount
;
1032 blocked_amount
= uint32(pVictim
->GetShieldBlockValue());
1033 if (blocked_amount
>= *damage
)
1035 hitInfo
|= HITINFO_SWINGNOHITSOUND
;
1036 victimState
= VICTIMSTATE_BLOCKS
;
1037 cleanDamage
->damage
+= *damage
; // To Help Calculate Rage
1042 // To Help Calculate Rage
1043 cleanDamage
->damage
+= blocked_amount
;
1044 *damage
= *damage
- blocked_amount
;
1049 case MELEE_HIT_MISS
:
1050 case MELEE_HIT_GLANCING
:
1051 case MELEE_HIT_CRUSHING
:
1052 case MELEE_HIT_NORMAL
:
1056 // Update attack state
1057 SendAttackStateUpdate(victimState
? hitInfo
|victimState
: hitInfo
, pVictim
, 1, spellInfo
->School
, 0, 0,0,1,0);
1059 // do all damage=0 cases here
1061 CastMeleeProcDamageAndSpell(pVictim
,0,BASE_ATTACK
,outcome
,spellInfo
,isTriggeredSpell
);
1066 case SPELL_SCHOOL_HOLY
:
1067 case SPELL_SCHOOL_FIRE
:
1068 case SPELL_SCHOOL_NATURE
:
1069 case SPELL_SCHOOL_FROST
:
1070 case SPELL_SCHOOL_SHADOW
:
1071 case SPELL_SCHOOL_ARCANE
:
1073 //Spell miss (sends resist message)
1074 if(SpellMissChanceCalc(pVictim
) > urand(0,10000))
1076 cleanDamage
->damage
= 0;
1078 ProcDamageAndSpell(pVictim
, PROC_FLAG_TARGET_RESISTS
, PROC_FLAG_RESIST_SPELL
, 0, spellInfo
,isTriggeredSpell
);
1079 SendAttackStateUpdate(HITINFO_RESIST
|HITINFO_SWINGNOHITSOUND
, pVictim
, 1, spellInfo
->School
, 0, 0,0,1,0);
1083 // Calculate damage bonus
1084 *damage
= SpellDamageBonus(pVictim
, spellInfo
, *damage
, SPELL_DIRECT_DAMAGE
);
1086 // Calculate critical bonus
1087 *crit
= SpellCriticalBonus(spellInfo
, (int32
*)damage
, pVictim
);
1088 cleanDamage
->hitOutCome
= MELEE_HIT_CRIT
;
1090 // spell proc all magic damage==0 case in this function
1094 uint32 procAttacker
= PROC_FLAG_HIT_SPELL
;
1095 uint32 procVictim
= (PROC_FLAG_STRUCK_SPELL
|PROC_FLAG_TAKE_DAMAGE
);
1097 ProcDamageAndSpell(pVictim
, procAttacker
, procVictim
, 0, spellInfo
, isTriggeredSpell
);
1103 // TODO this in only generic way, check for exceptions
1104 DEBUG_LOG("DealDamageBySchool (AFTER) SCHOOL %u >> DMG:%u", spellInfo
->School
, *damage
);
1107 void Unit::SpellNonMeleeDamageLog(Unit
*pVictim
, uint32 spellID
, uint32 damage
, bool isTriggeredSpell
, bool useSpellDamage
)
1109 if(!this || !pVictim
)
1111 if(!this->isAlive() || !pVictim
->isAlive())
1114 SpellEntry
const *spellInfo
= sSpellStore
.LookupEntry(spellID
);
1118 CleanDamage cleanDamage
= CleanDamage(0, BASE_ATTACK
, MELEE_HIT_NORMAL
);
1122 DealDamageBySchool(pVictim
, spellInfo
, &damage
, &cleanDamage
, &crit
, isTriggeredSpell
);
1124 // If we actually dealt some damage (spell proc's for 0 damage (normal and magic) called in DealDamageBySchool)
1127 // Calculate absorb & resists
1131 CalcAbsorbResist(pVictim
,spellInfo
->School
, damage
, &absorb
, &resist
);
1133 // Only send absorbed message if we actually absorbed some damage
1136 // Handle absorb &Â resists
1137 if(damage
<= absorb
+ resist
&& absorb
)
1139 SendAttackStateUpdate(HITINFO_ABSORB
|HITINFO_SWINGNOHITSOUND
, pVictim
, 1, spellInfo
->School
,damage
, absorb
,resist
,1,0);
1142 else if(damage
<= resist
) // If we didn't fully absorb check if we fully resisted
1144 ProcDamageAndSpell(pVictim
, PROC_FLAG_TARGET_RESISTS
, PROC_FLAG_RESIST_SPELL
, 0, spellInfo
,isTriggeredSpell
);
1145 SendAttackStateUpdate(HITINFO_RESIST
|HITINFO_SWINGNOHITSOUND
, pVictim
, 1, spellInfo
->School
, damage
, absorb
,resist
,1,0);
1151 sLog
.outDetail("SpellNonMeleeDamageLog: %u (TypeId: %u) attacked %u (TypeId: %u) for %u dmg inflicted by %u,absorb is %u,resist is %u",
1152 GetGUIDLow(), GetTypeId(), pVictim
->GetGUIDLow(), pVictim
->GetTypeId(), damage
, spellID
, absorb
,resist
);
1153 SendSpellNonMeleeDamageLog(pVictim
, spellID
, damage
, spellInfo
->School
, absorb
, resist
, false, 0, crit
);
1156 DealDamage(pVictim
, (damage
-absorb
-resist
), &cleanDamage
, SPELL_DIRECT_DAMAGE
, spellInfo
->School
, spellInfo
, 0, true);
1159 uint32 procAttacker
= PROC_FLAG_HIT_SPELL
;
1160 uint32 procVictim
= (PROC_FLAG_STRUCK_SPELL
|PROC_FLAG_TAKE_DAMAGE
);
1164 procAttacker
|= PROC_FLAG_CRIT_SPELL
;
1165 procVictim
|= PROC_FLAG_STRUCK_CRIT_SPELL
;
1168 ProcDamageAndSpell(pVictim
, procAttacker
, procVictim
, (damage
-absorb
-resist
), spellInfo
, isTriggeredSpell
);
1172 // all spell proc for 0 normal and magic damage called in DealDamageBySchool
1175 if(cleanDamage
.damage
)
1176 // Rage from damage received.
1177 if(pVictim
->GetTypeId() == TYPEID_PLAYER
&& (pVictim
->getPowerType() == POWER_RAGE
))
1178 ((Player
*)pVictim
)->RewardRage(cleanDamage
.damage
, 0, false);
1182 void Unit::PeriodicAuraLog(Unit
*pVictim
, SpellEntry
const *spellProto
, Modifier
*mod
, uint8 effect_idx
)
1184 uint32 procFlag
= 0;
1185 if(!this || !pVictim
|| !isAlive() || !pVictim
->isAlive())
1191 CleanDamage cleanDamage
= CleanDamage(0, BASE_ATTACK
, MELEE_HIT_NORMAL
);
1192 uint32 pdamage
= mod
->m_amount
;
1194 if(mod
->m_auraname
!= SPELL_AURA_PERIODIC_HEAL
&& mod
->m_auraname
!= SPELL_AURA_OBS_MOD_HEALTH
)
1196 //Calculate armor mitigation if it is a physical spell
1197 if (spellProto
->School
== 0)
1199 uint32 pdamageReductedArmor
= CalcArmorReducedDamage(pVictim
, pdamage
);
1200 cleanDamage
.damage
+= pdamage
- pdamageReductedArmor
;
1201 pdamage
= pdamageReductedArmor
;
1204 CalcAbsorbResist(pVictim
, spellProto
->School
, pdamage
, &absorb
, &resist
);
1207 sLog
.outDetail("PeriodicAuraLog: %u (TypeId: %u) attacked %u (TypeId: %u) for %u dmg inflicted by %u abs is %u",
1208 GetGUIDLow(), GetTypeId(), pVictim
->GetGUIDLow(), pVictim
->GetTypeId(), pdamage
, spellProto
->Id
,absorb
);
1210 switch(mod
->m_auraname
)
1212 case SPELL_AURA_PERIODIC_DAMAGE
:
1213 case SPELL_AURA_PERIODIC_DAMAGE_PERCENT
:
1215 pdamage
= SpellDamageBonus(pVictim
,spellProto
,pdamage
,DOT
);
1217 if(mod
->m_auraname
== SPELL_AURA_PERIODIC_DAMAGE_PERCENT
)
1218 pdamage
= GetHealth()*(100+mod
->m_amount
)/100;
1220 WorldPacket
data(SMSG_PERIODICAURALOG
, (21+16));// we guess size
1221 data
.append(pVictim
->GetPackGUID());
1222 data
.append(GetPackGUID());
1223 data
<< uint32(spellProto
->Id
);
1225 data
<< uint32(mod
->m_auraname
);
1226 data
<< (uint32
)pdamage
;
1227 data
<< (uint32
)spellProto
->School
;
1228 data
<< (uint32
)absorb
;
1229 data
<< (uint32
)resist
;
1230 SendMessageToSet(&data
,true);
1232 DealDamage(pVictim
, (pdamage
<= absorb
+resist
) ? 0 : (pdamage
-absorb
-resist
), &cleanDamage
, DOT
, spellProto
->School
, spellProto
, procFlag
, true);
1233 ProcDamageAndSpell(pVictim
, PROC_FLAG_HIT_SPELL
, PROC_FLAG_TAKE_DAMAGE
, (pdamage
<= absorb
+resist
) ? 0 : (pdamage
-absorb
-resist
), spellProto
);
1236 case SPELL_AURA_PERIODIC_HEAL
:
1237 case SPELL_AURA_OBS_MOD_HEALTH
:
1239 pdamage
= SpellHealingBonus(spellProto
, pdamage
, DOT
, pVictim
);
1241 WorldPacket
data(SMSG_PERIODICAURALOG
, (21+16));// we guess size
1242 data
.append(pVictim
->GetPackGUID());
1243 data
.append(GetPackGUID());
1244 data
<< uint32(spellProto
->Id
);
1246 data
<< uint32(mod
->m_auraname
);
1247 data
<< (uint32
)pdamage
;
1248 SendMessageToSet(&data
,true);
1250 int32 gain
= pVictim
->ModifyHealth(pdamage
);
1251 pVictim
->getHostilRefManager().threatAssist(this, float(gain
) * 0.5f
, spellProto
->School
, spellProto
);
1253 // heal for caster damage
1254 if(pVictim
!=this && spellProto
->SpellVisual
==163)
1256 uint32 dmg
= spellProto
->manaPerSecond
;
1257 if(GetHealth() <= dmg
&& GetTypeId()==TYPEID_PLAYER
)
1259 RemoveAurasDueToSpell(spellProto
->Id
);
1261 // finish current generic/channeling spells, don't affect autorepeat
1262 if(m_currentSpells
[CURRENT_GENERIC_SPELL
])
1264 m_currentSpells
[CURRENT_GENERIC_SPELL
]->finish();
1266 if(m_currentSpells
[CURRENT_CHANNELED_SPELL
])
1268 m_currentSpells
[CURRENT_CHANNELED_SPELL
]->SendChannelUpdate(0);
1269 m_currentSpells
[CURRENT_CHANNELED_SPELL
]->finish();
1274 SendSpellNonMeleeDamageLog(this, spellProto
->Id
, gain
, spellProto
->School
, 0, 0, false, 0, false);
1275 DealDamage(this, gain
, &cleanDamage
, NODAMAGE
, spellProto
->School
, spellProto
, PROC_FLAG_HEAL
, true);
1279 if(mod
->m_auraname
== SPELL_AURA_PERIODIC_HEAL
&& pVictim
!= this)
1280 ProcDamageAndSpell(pVictim
, PROC_FLAG_HEAL
, PROC_FLAG_HEALED
, pdamage
, spellProto
);
1283 case SPELL_AURA_PERIODIC_LEECH
:
1285 float multiplier
= spellProto
->EffectMultipleValue
[effect_idx
] > 0 ? spellProto
->EffectMultipleValue
[effect_idx
] : 1;
1286 uint32 pdamage
= mod
->m_amount
;
1288 pdamage
= SpellDamageBonus(pVictim
,spellProto
,pdamage
,DOT
);
1290 if(pVictim
->GetHealth() < pdamage
)
1291 pdamage
= uint32(pVictim
->GetHealth());
1293 SendSpellNonMeleeDamageLog(pVictim
, spellProto
->Id
, pdamage
, spellProto
->School
, absorb
, resist
, false, 0);
1294 DealDamage(pVictim
, (pdamage
<= absorb
+resist
) ? 0 : (pdamage
-absorb
-resist
), &cleanDamage
, DOT
, spellProto
->School
, spellProto
, procFlag
, false);
1295 ProcDamageAndSpell(pVictim
, PROC_FLAG_HIT_SPELL
, PROC_FLAG_TAKE_DAMAGE
, (pdamage
<= absorb
+resist
) ? 0 : (pdamage
-absorb
-resist
), spellProto
);
1296 if (!pVictim
->isAlive() && IsNonMeleeSpellCasted(false))
1298 for (uint32 i
= CURRENT_FIRST_NON_MELEE_SPELL
; i
< CURRENT_MAX_SPELL
; i
++)
1300 if (m_currentSpells
[i
] && m_currentSpells
[i
]->m_spellInfo
->Id
== spellProto
->Id
)
1301 m_currentSpells
[i
]->cancel();
1305 int32 gain
= ModifyHealth(int32(pdamage
* multiplier
));
1306 getHostilRefManager().threatAssist(this, float(gain
) * 0.5f
, spellProto
->School
, spellProto
);
1308 if(GetTypeId() == TYPEID_PLAYER
)
1309 SendHealSpellOnPlayer(this, spellProto
->Id
, uint32(pdamage
* multiplier
));
1312 case SPELL_AURA_PERIODIC_MANA_LEECH
:
1314 if(mod
->m_miscvalue
< 0 || mod
->m_miscvalue
> 4)
1317 Powers power
= Powers(mod
->m_miscvalue
);
1319 int32 drain_amount
= pVictim
->GetPower(power
) > pdamage
? pdamage
: pVictim
->GetPower(power
);
1321 pVictim
->ModifyPower(power
, -drain_amount
);
1323 float gain_multiplier
= GetMaxPower(power
) > 0 ? spellProto
->EffectMultipleValue
[effect_idx
] : 0;
1325 WorldPacket
data(SMSG_PERIODICAURALOG
, (21+16));// we guess size
1326 data
.append(pVictim
->GetPackGUID());
1327 data
.append(GetPackGUID());
1328 data
<< uint32(spellProto
->Id
);
1330 data
<< uint32(mod
->m_auraname
);
1331 data
<< (uint32
)power
; // power type
1332 data
<< (uint32
)drain_amount
;
1333 data
<< (float)gain_multiplier
;
1334 SendMessageToSet(&data
,true);
1336 int32 gain_amount
= int32(drain_amount
*gain_multiplier
);
1340 int32 gain
= ModifyPower(power
,gain_amount
);
1341 pVictim
->AddThreat(this, float(gain
) * 0.5f
, spellProto
->School
, spellProto
);
1345 case SPELL_AURA_PERIODIC_ENERGIZE
:
1347 if(mod
->m_miscvalue
< 0 || mod
->m_miscvalue
> 4)
1350 Powers power
= Powers(mod
->m_miscvalue
);
1352 if(pVictim
->GetMaxPower(power
) == 0)
1355 WorldPacket
data(SMSG_PERIODICAURALOG
, (21+16));// we guess size
1356 data
.append(pVictim
->GetPackGUID());
1357 data
.append(GetPackGUID());
1358 data
<< uint32(spellProto
->Id
);
1360 data
<< uint32(mod
->m_auraname
);
1361 data
<< (uint32
)power
; // power type
1362 data
<< (uint32
)mod
->m_amount
;
1363 SendMessageToSet(&data
,true);
1365 int32 gain
= pVictim
->ModifyPower(power
,mod
->m_amount
);
1366 pVictim
->getHostilRefManager().threatAssist(this, float(gain
) * 0.5f
, spellProto
->School
, spellProto
);
1369 case SPELL_AURA_OBS_MOD_MANA
:
1371 if(GetMaxPower(POWER_MANA
) == 0)
1374 WorldPacket
data(SMSG_PERIODICAURALOG
, (21+16));// we guess size
1375 data
.append(pVictim
->GetPackGUID());
1376 data
.append(GetPackGUID());
1377 data
<< uint32(spellProto
->Id
);
1379 data
<< uint32(mod
->m_auraname
);
1380 data
<< (uint32
)mod
->m_amount
;
1381 data
<< (uint32
)0; // ?
1382 SendMessageToSet(&data
,true);
1384 int32 gain
= ModifyPower(POWER_MANA
, mod
->m_amount
);
1385 getHostilRefManager().threatAssist(this, float(gain
) * 0.5f
, spellProto
->School
, spellProto
);
1391 void Unit::HandleEmoteCommand(uint32 anim_id
)
1393 WorldPacket
data( SMSG_EMOTE
, 12 );
1394 data
<< anim_id
<< GetGUID();
1395 WPAssert(data
.size() == 12);
1397 SendMessageToSet(&data
, true);
1400 uint32
Unit::CalcArmorReducedDamage(Unit
* pVictim
, const uint32 damage
)
1402 uint32 newdamage
= 0;
1403 float armor
= pVictim
->GetArmor();
1405 float tmpvalue
= 0.0;
1406 if(getLevel() <= 59) //Level 1-59
1407 tmpvalue
= armor
/ (armor
+ 400.0 + 85.0 * getLevel());
1408 else if(getLevel() < 70) //Level 60-69
1409 tmpvalue
= armor
/ (armor
- 22167.5 + 467.5 * getLevel());
1411 tmpvalue
= armor
/ (armor
+ 10557.5);
1417 newdamage
= uint32(damage
- (damage
* tmpvalue
));
1419 return (newdamage
> 1) ? newdamage
: 1;
1422 void Unit::CalcAbsorbResist(Unit
*pVictim
,uint32 School
, const uint32 damage
, uint32
*absorb
, uint32
*resist
)
1424 if(!pVictim
|| !pVictim
->isAlive() || !damage
)
1427 // Magic damage, check for resists
1428 if (School
!= SPELL_SCHOOL_NORMAL
)
1430 int32 tmpvalue2
= pVictim
->GetResistance(SpellSchools(School
));
1431 AuraList
const& mModTargetRes
= GetAurasByType(SPELL_AURA_MOD_TARGET_RESISTANCE
);
1432 for(AuraList::const_iterator i
= mModTargetRes
.begin(); i
!= mModTargetRes
.end(); ++i
)
1433 if ((*i
)->GetModifier()->m_miscvalue
& (1 << School
))
1434 tmpvalue2
+= (*i
)->GetModifier()->m_amount
;
1435 if (tmpvalue2
< 0) tmpvalue2
= 0;
1436 *resist
+= uint32(damage
*tmpvalue2
*0.0025*pVictim
->getLevel()/getLevel());
1437 if(*resist
> damage
)
1443 int32 RemainingDamage
= damage
- *resist
;
1444 int32 currentAbsorb
, manaReduction
, maxAbsorb
;
1445 float manaMultiplier
;
1447 if (School
== SPELL_SCHOOL_NORMAL
)
1449 AuraList
const& vManaShield
= pVictim
->GetAurasByType(SPELL_AURA_MANA_SHIELD
);
1450 for(AuraList::const_iterator i
= vManaShield
.begin(), next
; i
!= vManaShield
.end() && RemainingDamage
>= 0; i
= next
)
1453 if (RemainingDamage
- (*i
)->m_absorbDmg
>= 0)
1454 currentAbsorb
= (*i
)->m_absorbDmg
;
1456 currentAbsorb
= RemainingDamage
;
1458 manaMultiplier
= (*i
)->GetSpellProto()->EffectMultipleValue
[(*i
)->GetEffIndex()];
1459 maxAbsorb
= int32(pVictim
->GetPower(POWER_MANA
) / manaMultiplier
);
1460 if (currentAbsorb
> maxAbsorb
)
1461 currentAbsorb
= maxAbsorb
;
1463 (*i
)->m_absorbDmg
-= currentAbsorb
;
1464 if((*i
)->m_absorbDmg
<= 0)
1466 pVictim
->RemoveAurasDueToSpell((*i
)->GetId());
1467 next
= vManaShield
.begin();
1470 manaReduction
= int32(currentAbsorb
* manaMultiplier
);
1471 pVictim
->ApplyPowerMod(POWER_MANA
, manaReduction
, false);
1473 RemainingDamage
-= currentAbsorb
;
1477 AuraList
const& vSchoolAbsorb
= pVictim
->GetAurasByType(SPELL_AURA_SCHOOL_ABSORB
);
1478 for(AuraList::const_iterator i
= vSchoolAbsorb
.begin(), next
; i
!= vSchoolAbsorb
.end() && RemainingDamage
>= 0; i
= next
)
1481 if ((*i
)->GetModifier()->m_miscvalue
& int32(1<<School
))
1483 if (RemainingDamage
- (*i
)->m_absorbDmg
>= 0)
1485 currentAbsorb
= (*i
)->m_absorbDmg
;
1486 pVictim
->RemoveAurasDueToSpell((*i
)->GetId());
1487 next
= vSchoolAbsorb
.begin();
1491 currentAbsorb
= RemainingDamage
;
1492 (*i
)->m_absorbDmg
-= RemainingDamage
;
1495 RemainingDamage
-= currentAbsorb
;
1499 *absorb
= damage
- RemainingDamage
- *resist
;
1502 void Unit::DoAttackDamage (Unit
*pVictim
, uint32
*damage
, CleanDamage
*cleanDamage
, uint32
*blocked_amount
, uint32
*damageType
, uint32
*hitInfo
, uint32
*victimState
, uint32
*absorbDamage
, uint32
*resistDamage
, WeaponAttackType attType
, SpellEntry
const *spellCasted
, bool isTriggeredSpell
)
1504 pVictim
->ModifyAuraState(AURA_STATE_PARRY
, false);
1505 pVictim
->ModifyAuraState(AURA_STATE_DEFENSE
, false);
1506 ModifyAuraState(AURA_STATE_CRIT
, false);
1508 MeleeHitOutcome outcome
;
1510 // If is casted Melee spell, calculate like physical
1512 outcome
= RollMeleeOutcomeAgainst (pVictim
, attType
);
1514 outcome
= RollPhysicalOutcomeAgainst (pVictim
, attType
, spellCasted
);
1516 if (outcome
== MELEE_HIT_MISS
)
1518 *hitInfo
|= HITINFO_MISS
;
1520 cleanDamage
->damage
= 0;
1521 if(GetTypeId()== TYPEID_PLAYER
)
1522 ((Player
*)this)->UpdateWeaponSkill(attType
);
1526 /// If this is a creature and it attacks from behind it has a probability to daze it's victim
1527 if( (outcome
==MELEE_HIT_CRIT
|| outcome
==MELEE_HIT_CRUSHING
|| outcome
==MELEE_HIT_NORMAL
|| outcome
==MELEE_HIT_GLANCING
) &&
1528 GetTypeId() != TYPEID_PLAYER
&& !((Creature
*)this)->GetCharmerOrOwnerGUID() && !pVictim
->HasInArc(M_PI
, this) )
1530 // -probability is between 0% and 40%
1532 float Probability
= 20;
1534 //there is a newbie protection, at level 10 just 7% base chance; assuming linear function
1535 if( pVictim
->getLevel() < 30 )
1536 Probability
= 0.65f
*pVictim
->getLevel()+0.5;
1538 uint32 VictimDefense
=pVictim
->GetDefenseSkillValue();
1539 uint32 AttackerMeleeSkill
=GetUnitMeleeSkill();
1541 Probability
*= AttackerMeleeSkill
/(float)VictimDefense
;
1543 if(Probability
> 40)
1546 if(roll_chance_f(Probability
))
1547 CastSpell(pVictim
, 1604, true);
1550 *damage
+= CalculateDamage (attType
);
1552 //Calculate the damage after armor mitigation if SPELL_SCHOOL_NORMAL
1553 if (*damageType
== SPELL_SCHOOL_NORMAL
)
1555 uint32 damageAfterArmor
= CalcArmorReducedDamage(pVictim
, *damage
);
1556 cleanDamage
->damage
+= *damage
- damageAfterArmor
;
1557 *damage
= damageAfterArmor
;
1559 // Instant Attacks (Spellmods)
1560 // TODO: AP bonus related to mainhand weapon
1562 if(GetTypeId()== TYPEID_PLAYER
)
1563 ((Player
*)this)->ApplySpellMod(spellCasted
->Id
, SPELLMOD_DAMAGE
, *damage
);
1565 if(GetTypeId() == TYPEID_PLAYER
&& pVictim
->GetTypeId() != TYPEID_PLAYER
&& pVictim
->GetCreatureType() != CREATURE_TYPE_CRITTER
)
1566 ((Player
*)this)->UpdateCombatSkills(pVictim
, attType
, outcome
, false);
1568 if(GetTypeId() != TYPEID_PLAYER
&& pVictim
->GetTypeId() == TYPEID_PLAYER
)
1569 ((Player
*)pVictim
)->UpdateCombatSkills(this, attType
, outcome
, true);
1573 case MELEE_HIT_CRIT
:
1576 *hitInfo
= HITINFO_CRITICALHIT
| HITINFO_NORMALSWING2
| 0x8;
1580 crit_bonus
= *damage
;
1582 // Apply crit_damage bonus for melee spells
1583 if (GetTypeId() == TYPEID_PLAYER
&& spellCasted
)
1585 ((Player
*)this)->ApplySpellMod(spellCasted
->Id
, SPELLMOD_CRIT_DAMAGE_BONUS
, crit_bonus
);
1588 *damage
+= crit_bonus
;
1590 // Resilience - reduce crit damage by 2x%
1591 uint32 resilienceReduction
;
1592 resilienceReduction
= uint32(pVictim
->m_modResilience
* 2/100 * (*damage
));
1593 *damage
-= resilienceReduction
;
1594 cleanDamage
->damage
+= resilienceReduction
;
1596 if(GetTypeId() == TYPEID_PLAYER
&& pVictim
->GetTypeId() != TYPEID_PLAYER
&& pVictim
->GetCreatureType() != CREATURE_TYPE_CRITTER
)
1597 ((Player
*)this)->UpdateWeaponSkill(attType
);
1599 ModifyAuraState(AURA_STATE_CRIT
, true);
1601 pVictim
->HandleEmoteCommand(EMOTE_ONESHOT_WOUNDCRITICAL
);
1604 case MELEE_HIT_PARRY
:
1605 if(attType
== RANGED_ATTACK
) //range attack - no parry
1608 cleanDamage
->damage
+= *damage
;
1610 *victimState
= VICTIMSTATE_PARRY
;
1612 // instant (maybe with small delay) counter attack
1614 float offtime
= float(pVictim
->getAttackTimer(OFF_ATTACK
));
1615 float basetime
= float(pVictim
->getAttackTimer(BASE_ATTACK
));
1617 // after parry nearest next attack time will reduced at %40 from full attack time.
1618 // The delay cannot be reduced to less than 20% of your weapon’s base swing delay.
1619 if (pVictim
->haveOffhandWeapon() && offtime
< basetime
)
1621 float percent20
= pVictim
->GetAttackTime(OFF_ATTACK
)*0.20;
1622 float percent60
= 3*percent20
;
1623 // set to 20% if in range 20%...20+40% of full time
1624 if(offtime
> percent20
&& offtime
<= percent60
)
1626 pVictim
->setAttackTimer(OFF_ATTACK
,uint32(percent20
));
1628 // decrease at %40 from full time
1629 else if(offtime
> percent60
)
1631 offtime
-= 2*percent20
;
1632 pVictim
->setAttackTimer(OFF_ATTACK
,uint32(offtime
));
1638 float percent20
= pVictim
->GetAttackTime(BASE_ATTACK
)*0.20;
1639 float percent60
= 3*percent20
;
1640 // set to 20% if in range 20%...20+40% of full time
1641 if(basetime
> percent20
&& basetime
<= percent60
)
1643 pVictim
->setAttackTimer(BASE_ATTACK
,uint32(percent20
));
1645 // decrease at %40 from full time
1646 else if(basetime
> percent60
)
1648 basetime
-= 2*percent20
;
1649 pVictim
->setAttackTimer(BASE_ATTACK
,uint32(basetime
));
1655 if(pVictim
->GetTypeId() == TYPEID_PLAYER
)
1656 ((Player
*)pVictim
)->UpdateDefense();
1658 pVictim
->HandleEmoteCommand(EMOTE_ONESHOT_PARRYUNARMED
);
1659 pVictim
->ModifyAuraState(AURA_STATE_PARRY
,true);
1660 if (pVictim
->getClass() != CLASS_HUNTER
) // Mongoose Bite
1661 pVictim
->ModifyAuraState(AURA_STATE_DEFENSE
, true);
1663 CastMeleeProcDamageAndSpell(pVictim
, 0, attType
, outcome
, spellCasted
, isTriggeredSpell
);
1666 case MELEE_HIT_DODGE
:
1667 if(attType
== RANGED_ATTACK
) //range attack - no dodge
1669 cleanDamage
->damage
+= *damage
;
1671 *victimState
= VICTIMSTATE_DODGE
;
1673 if(pVictim
->GetTypeId() == TYPEID_PLAYER
)
1674 ((Player
*)pVictim
)->UpdateDefense();
1676 pVictim
->HandleEmoteCommand(EMOTE_ONESHOT_PARRYUNARMED
);
1678 if (pVictim
->getClass() != CLASS_ROGUE
) // Riposte
1679 pVictim
->ModifyAuraState(AURA_STATE_DEFENSE
, true);
1681 CastMeleeProcDamageAndSpell(pVictim
, 0, attType
, outcome
, spellCasted
, isTriggeredSpell
);
1684 case MELEE_HIT_BLOCK
:
1685 *blocked_amount
= uint32(pVictim
->GetShieldBlockValue() + (pVictim
->GetStat(STAT_STRENGTH
) / 20.0f
) -1);
1687 if (pVictim
->GetUnitBlockChance())
1688 pVictim
->HandleEmoteCommand(EMOTE_ONESHOT_PARRYSHIELD
);
1690 pVictim
->HandleEmoteCommand(EMOTE_ONESHOT_PARRYUNARMED
);
1692 //Only set VICTIMSTATE_BLOCK on a full block
1693 if (*blocked_amount
>= *damage
)
1695 *victimState
= VICTIMSTATE_BLOCKS
;
1696 *blocked_amount
= *damage
;
1699 if(pVictim
->GetTypeId() == TYPEID_PLAYER
)
1700 ((Player
*)pVictim
)->UpdateDefense();
1701 pVictim
->ModifyAuraState(AURA_STATE_DEFENSE
,true);
1704 case MELEE_HIT_GLANCING
:
1706 float reducePercent
= 1.0f
; //damage factor
1708 // calculate base values and mods
1709 float baseLowEnd
= 1.3;
1710 float baseHighEnd
= 1.2;
1711 switch(getClass()) // lowering base values for casters
1723 float maxLowEnd
= 0.6;
1724 switch(getClass()) // upper for melee classes
1728 maxLowEnd
= 0.91; //If the attacker is a melee class then instead the lower value of 0.91
1732 int32 diff
= pVictim
->GetDefenseSkillValue() - GetWeaponSkillValue(attType
);
1733 float lowEnd
= baseLowEnd
- ( 0.05f
* diff
);
1734 float highEnd
= baseHighEnd
- ( 0.03f
* diff
);
1736 // apply max/min bounds
1737 if ( lowEnd
< 0.01f
) //the low end must not go bellow 0.01f
1739 else if ( lowEnd
> maxLowEnd
) //the smaller value of this and 0.6 is kept as the low end
1742 if ( highEnd
< 0.2f
) //high end limits
1744 if ( highEnd
> 0.99f
)
1747 if(lowEnd
> highEnd
) // prevent negative range size
1750 reducePercent
= lowEnd
+ rand_norm() * ( highEnd
- lowEnd
);
1752 *damage
= uint32(reducePercent
* *damage
);
1753 cleanDamage
->damage
+= *damage
;
1754 *hitInfo
|= HITINFO_GLANCING
;
1757 case MELEE_HIT_CRUSHING
:
1759 // 150% normal damage
1760 *damage
+= (*damage
/ 2);
1761 cleanDamage
->damage
= *damage
;
1762 *hitInfo
|= HITINFO_CRUSHING
;
1763 // TODO: victimState, victim animation?
1770 // apply melee damage bonus and absorb only if base damage not fully blocked to prevent negative damage or damage with full block
1771 if(*victimState
!= VICTIMSTATE_BLOCKS
)
1773 MeleeDamageBonus(pVictim
, damage
,attType
);
1774 CalcAbsorbResist(pVictim
, *damageType
, *damage
-*blocked_amount
, absorbDamage
, resistDamage
);
1777 if (*absorbDamage
) *hitInfo
|= HITINFO_ABSORB
;
1778 if (*resistDamage
) *hitInfo
|= HITINFO_RESIST
;
1779 cleanDamage
+= *blocked_amount
;
1781 if (*damage
<= *absorbDamage
+ *resistDamage
+ *blocked_amount
)
1783 //*hitInfo = 0x00010020;
1784 //*hitInfo |= HITINFO_SWINGNOHITSOUND;
1786 CastMeleeProcDamageAndSpell(pVictim
, 0, attType
, outcome
, spellCasted
, isTriggeredSpell
);
1790 CastMeleeProcDamageAndSpell(pVictim
, (*damage
- *absorbDamage
- *resistDamage
- *blocked_amount
), attType
, outcome
, spellCasted
, isTriggeredSpell
);
1792 // victim's damage shield
1793 // yet another hack to fix crashes related to the aura getting removed during iteration
1794 std::set
<Aura
*> alreadyDone
;
1795 uint32 removedAuras
= pVictim
->m_removedAuras
;
1796 AuraList
const& vDamageShields
= pVictim
->GetAurasByType(SPELL_AURA_DAMAGE_SHIELD
);
1797 for(AuraList::const_iterator i
= vDamageShields
.begin(), next
= vDamageShields
.begin(); i
!= vDamageShields
.end(); i
= next
)
1800 if (alreadyDone
.find(*i
) == alreadyDone
.end())
1802 alreadyDone
.insert(*i
);
1803 pVictim
->SpellNonMeleeDamageLog(this, (*i
)->GetId(), (*i
)->GetModifier()->m_amount
, false, false);
1804 if (pVictim
->m_removedAuras
> removedAuras
)
1806 removedAuras
= pVictim
->m_removedAuras
;
1807 next
= vDamageShields
.begin();
1812 if (pVictim
->GetTypeId() == TYPEID_PLAYER
&& *damage
)
1814 for (uint32 i
= CURRENT_FIRST_NON_MELEE_SPELL
; i
< CURRENT_MAX_SPELL
; i
++)
1816 // skip channeled spell (processed differently below)
1817 if (i
== CURRENT_CHANNELED_SPELL
)
1820 if(pVictim
->m_currentSpells
[i
])
1822 sLog
.outDetail("Spell Delayed!%d",(int32
)(0.25f
* pVictim
->m_currentSpells
[i
]->casttime
));
1823 pVictim
->m_currentSpells
[i
]->Delayed((int32
)(0.25f
* pVictim
->m_currentSpells
[i
]->casttime
));
1827 // process channeled spell separately
1828 if (pVictim
->m_currentSpells
[CURRENT_CHANNELED_SPELL
])
1830 if (pVictim
->m_currentSpells
[CURRENT_CHANNELED_SPELL
]->getState() == SPELL_STATE_CASTING
)
1832 uint32 channelInterruptFlags
= pVictim
->m_currentSpells
[CURRENT_CHANNELED_SPELL
]->m_spellInfo
->ChannelInterruptFlags
;
1833 if( channelInterruptFlags
& CHANNEL_FLAG_DELAY
)
1835 sLog
.outDetail("Spell Delayed!%d",(int32
)(0.25f
* GetDuration(pVictim
->m_currentSpells
[CURRENT_CHANNELED_SPELL
]->m_spellInfo
)));
1836 pVictim
->m_currentSpells
[CURRENT_CHANNELED_SPELL
]->DelayedChannel((int32
)(0.25f
* GetDuration(pVictim
->m_currentSpells
[CURRENT_CHANNELED_SPELL
]->m_spellInfo
)));
1839 else if( !(channelInterruptFlags
& (CHANNEL_FLAG_DAMAGE
| CHANNEL_FLAG_DAMAGE2
)) )
1842 sLog
.outDetail("Spell Canceled!");
1843 pVictim
->m_currentSpells
[CURRENT_CHANNELED_SPELL
]->cancel();
1845 else if (pVictim
->m_currentSpells
[CURRENT_CHANNELED_SPELL
]->getState() == SPELL_STATE_DELAYED
)
1847 // break channeled spell in delayed state on damage
1848 sLog
.outDetail("Spell Canceled!");
1849 pVictim
->m_currentSpells
[CURRENT_CHANNELED_SPELL
]->cancel();
1855 void Unit::AttackerStateUpdate (Unit
*pVictim
, WeaponAttackType attType
, bool isTriggered
)
1857 if(hasUnitState(UNIT_STAT_CONFUSED
| UNIT_STAT_STUNDED
| UNIT_STAT_FLEEING
) || HasFlag(UNIT_FIELD_FLAGS
, UNIT_FLAG_PACIFIED
) )
1860 if (!pVictim
->isAlive())
1865 if(IsNonMeleeSpellCasted(false))
1868 // melee attack spell casted at main hand attack only
1869 if (m_currentSpells
[CURRENT_MELEE_SPELL
] && attType
== BASE_ATTACK
)
1871 m_currentSpells
[CURRENT_MELEE_SPELL
]->cast();
1877 if (attType
== BASE_ATTACK
)
1878 hitInfo
= HITINFO_NORMALSWING2
;
1879 else if (attType
== OFF_ATTACK
)
1880 hitInfo
= HITINFO_LEFTSWING
;
1884 uint32 damageType
= NORMAL_DAMAGE
;
1885 uint32 victimState
= VICTIMSTATE_NORMAL
;
1888 CleanDamage cleanDamage
= CleanDamage(0, BASE_ATTACK
, MELEE_HIT_NORMAL
);
1889 uint32 blocked_dmg
= 0;
1890 uint32 absorbed_dmg
= 0;
1891 uint32 resisted_dmg
= 0;
1893 if( pVictim
->IsImmunedToPhysicalDamage() )
1895 SendAttackStateUpdate (HITINFO_MISS
, pVictim
, 1, NORMAL_DAMAGE
, 0, 0, 0, VICTIMSTATE_IS_IMMUNE
, 0);
1899 DoAttackDamage (pVictim
, &damage
, &cleanDamage
, &blocked_dmg
, &damageType
, &hitInfo
, &victimState
, &absorbed_dmg
, &resisted_dmg
, attType
);
1901 cleanDamage
.damage
+= blocked_dmg
;
1903 if (hitInfo
& HITINFO_MISS
)
1905 SendAttackStateUpdate (hitInfo
, pVictim
, 1, damageType
, damage
, absorbed_dmg
, resisted_dmg
, victimState
, blocked_dmg
);
1909 SendAttackStateUpdate (hitInfo
, pVictim
, 1, damageType
, damage
, absorbed_dmg
, resisted_dmg
, victimState
, blocked_dmg
);
1911 if (damage
> (absorbed_dmg
+ resisted_dmg
+ blocked_dmg
))
1912 damage
-= (absorbed_dmg
+ resisted_dmg
+ blocked_dmg
);
1916 DealDamage (pVictim
, damage
, &cleanDamage
, DIRECT_DAMAGE
, SPELL_SCHOOL_NORMAL
, NULL
, 0, true);
1918 if(GetTypeId() == TYPEID_PLAYER
&& pVictim
->isAlive())
1920 for(int i
= EQUIPMENT_SLOT_START
; i
< EQUIPMENT_SLOT_END
; i
++)
1921 ((Player
*)this)->CastItemCombatSpell(((Player
*)this)->GetItemByPos(INVENTORY_SLOT_BAG_0
,i
),pVictim
);
1925 if (GetTypeId() == TYPEID_PLAYER
)
1926 DEBUG_LOG("AttackerStateUpdate: (Player) %u attacked %u (TypeId: %u) for %u dmg, absorbed %u, blocked %u, resisted %u.",
1927 GetGUIDLow(), pVictim
->GetGUIDLow(), pVictim
->GetTypeId(), damage
, absorbed_dmg
, blocked_dmg
, resisted_dmg
);
1929 DEBUG_LOG("AttackerStateUpdate: (NPC) %u attacked %u (TypeId: %u) for %u dmg, absorbed %u, blocked %u, resisted %u.",
1930 GetGUIDLow(), pVictim
->GetGUIDLow(), pVictim
->GetTypeId(), damage
, absorbed_dmg
, blocked_dmg
, resisted_dmg
);
1933 MeleeHitOutcome
Unit::RollPhysicalOutcomeAgainst (const Unit
*pVictim
, WeaponAttackType attType
, SpellEntry
const *spellInfo
)
1935 // Miss chance based on melee
1936 int32 miss_chance
= (int32
)(MeleeMissChanceCalc(pVictim
));
1938 // Critical hit chance
1939 float crit_chance
= GetUnitCriticalChance(attType
);
1941 // Only players can have Talent&Spell bonuses
1942 if (GetTypeId() == TYPEID_PLAYER
)
1945 AuraList
const& mSpellCritSchool
= GetAurasByType(SPELL_AURA_MOD_SPELL_CRIT_CHANCE_SCHOOL
);
1946 for(AuraList::const_iterator i
= mSpellCritSchool
.begin(); i
!= mSpellCritSchool
.end(); ++i
)
1947 if((*i
)->GetModifier()->m_miscvalue
== -2 || ((*i
)->GetModifier()->m_miscvalue
& (int32
)(1<<spellInfo
->School
)) != 0)
1948 crit_chance
+= (*i
)->GetModifier()->m_amount
;
1951 AuraList
const& mAttackerSWCrit
= pVictim
->GetAurasByType(SPELL_AURA_MOD_ATTACKER_SPELL_AND_WEAPON_CRIT_CHANCE
);
1952 for(AuraList::const_iterator i
= mAttackerSWCrit
.begin(); i
!= mAttackerSWCrit
.end(); ++i
)
1953 crit_chance
+= (*i
)->GetModifier()->m_amount
;
1956 ((Player
*)this)->ApplySpellMod(spellInfo
->Id
, SPELLMOD_CRITICAL_CHANCE
, crit_chance
);
1959 DEBUG_LOG("PHYSICAL OUTCOME: hit %u crit %f miss %u",m_modHitChance
,crit_chance
,miss_chance
);
1961 return RollMeleeOutcomeAgainst(pVictim
, attType
, int32(crit_chance
* 100 ), miss_chance
, m_modHitChance
);
1964 MeleeHitOutcome
Unit::RollMeleeOutcomeAgainst (const Unit
*pVictim
, WeaponAttackType attType
) const
1966 // This is only wrapper
1968 // Miss chance based on melee
1969 int32 miss_chance
= (int32
)(MeleeMissChanceCalc(pVictim
));
1971 // Critical hit chance
1972 float crit_chance
= GetUnitCriticalChance(attType
);
1975 AuraList
const& mAttackerSWCrit
= pVictim
->GetAurasByType(SPELL_AURA_MOD_ATTACKER_SPELL_AND_WEAPON_CRIT_CHANCE
);
1976 for(AuraList::const_iterator i
= mAttackerSWCrit
.begin(); i
!= mAttackerSWCrit
.end(); ++i
)
1977 crit_chance
+= (*i
)->GetModifier()->m_amount
;
1979 // Useful if want to specify crit & miss chances for melee, else it could be removed
1980 DEBUG_LOG("MELEE OUTCOME: hit %u crit %u miss %u", m_modHitChance
,crit_chance
,miss_chance
);
1981 return RollMeleeOutcomeAgainst(pVictim
, attType
, int32(crit_chance
* 100 ), miss_chance
, m_modHitChance
);
1984 MeleeHitOutcome
Unit::RollMeleeOutcomeAgainst (const Unit
*pVictim
, WeaponAttackType attType
, int32 crit_chance
, int32 miss_chance
, int32 hit_chance
) const
1986 int32 skillDiff
= GetWeaponSkillValue(attType
) - pVictim
->GetDefenseSkillValue();
1987 // bonus from skills is 0.04%
1988 int32 skillBonus
= skillDiff
* 4;
1989 int32 skillBonus2
= 4 * ( GetWeaponSkillValue(attType
) - pVictim
->GetPureDefenseSkillValue() );
1990 int32 sum
= 0, tmp
= 0;
1991 int32 roll
= urand (0, 10000);
1993 DEBUG_LOG ("RollMeleeOutcomeAgainst: skill bonus of %d for attacker", skillBonus
);
1994 DEBUG_LOG ("RollMeleeOutcomeAgainst: rolled %d, +hit %d, dodge %u, parry %u, block %u, crit %u",
1995 roll
, hit_chance
, (uint32
)(pVictim
->GetUnitDodgeChance()*100), (uint32
)(pVictim
->GetUnitParryChance()*100),
1996 (uint32
)(pVictim
->GetUnitBlockChance()*100), crit_chance
);
1998 // dual wield has 24% base chance to miss instead of 5%, also
1999 // base miss rate is 5% and can't get higher than 60%
2001 // Inherit if passed
2002 tmp
= miss_chance
- skillBonus
;
2007 if (tmp
> 0 && roll
< (sum
+= tmp
))
2009 DEBUG_LOG ("RollMeleeOutcomeAgainst: MISS");
2010 return MELEE_HIT_MISS
;
2013 // always crit against a sitting target (except 0 crit chance)
2014 if( (pVictim
->GetTypeId() == TYPEID_PLAYER
) && crit_chance
> 0 &&
2015 (((Player
*)pVictim
)->getStandState() & (PLAYER_STATE_SLEEP
| PLAYER_STATE_SIT
2016 | PLAYER_STATE_SIT_CHAIR
2017 | PLAYER_STATE_SIT_LOW_CHAIR
2018 | PLAYER_STATE_SIT_MEDIUM_CHAIR
2019 | PLAYER_STATE_SIT_HIGH_CHAIR
)))
2021 DEBUG_LOG ("RollMeleeOutcomeAgainst: CRIT (sitting victim)");
2022 return MELEE_HIT_CRIT
;
2025 // stunned target cannot dodge and this is check in GetUnitDodgeChance()
2026 tmp
= (int32
)(pVictim
->GetUnitDodgeChance()*100) - skillBonus2
;
2027 if (tmp
> 0 && roll
< (sum
+= tmp
))
2029 DEBUG_LOG ("RollMeleeOutcomeAgainst: DODGE <%d, %d)", sum
-tmp
, sum
);
2030 return MELEE_HIT_DODGE
;
2035 // check if attack comes from behind
2036 if (!pVictim
->HasInArc(M_PI
,this))
2038 // ASSUME +10% crit from behind
2039 DEBUG_LOG ("RollMeleeOutcomeAgainst: attack came from behind.");
2044 // cannot parry or block attacks from behind, but can from forward
2045 tmp
= (int32
)(pVictim
->GetUnitParryChance()*100);
2046 if ( (tmp
> 0) // check if unit _can_ parry
2047 && ((tmp
-= skillBonus2
) > 0)
2048 && (roll
< (sum
+= tmp
)))
2050 DEBUG_LOG ("RollMeleeOutcomeAgainst: PARRY <%d, %d)", sum
-tmp
, sum
);
2051 return MELEE_HIT_PARRY
;
2054 tmp
= (int32
)(pVictim
->GetUnitBlockChance()*100);
2055 if ( (tmp
> 0) // check if unit _can_ block
2056 && ((tmp
-= skillBonus2
) > 0)
2057 && (roll
< (sum
+= tmp
)))
2059 DEBUG_LOG ("RollMeleeOutcomeAgainst: BLOCK <%d, %d)", sum
-tmp
, sum
);
2060 return MELEE_HIT_BLOCK
;
2064 // Resilience - reduce crit chance by x%
2065 modCrit
-= int32(pVictim
->m_modResilience
*100);
2068 tmp
= crit_chance
+ skillBonus
+ modCrit
;
2070 if (tmp
> 0 && roll
< (sum
+= tmp
))
2072 DEBUG_LOG ("RollMeleeOutcomeAgainst: CRIT <%d, %d)", sum
-tmp
, sum
);
2073 return MELEE_HIT_CRIT
;
2076 // Max 40% chance to score a glancing blow against mobs that are higher level
2077 if( GetTypeId() == TYPEID_PLAYER
&& pVictim
->GetTypeId() != TYPEID_PLAYER
&& getLevel() < pVictim
->getLevel() )
2079 // cap possible value (with bonuses > max skill)
2080 int32 skill
= GetWeaponSkillValue(attType
);
2081 int32 maxskill
= GetMaxSkillValueForLevel();
2082 skill
= (skill
> maxskill
) ? maxskill
: skill
;
2084 tmp
= (10 + (pVictim
->GetDefenseSkillValue() - skill
)) * 100;
2085 tmp
= tmp
> 4000 ? 4000 : tmp
;
2086 if (roll
< (sum
+= tmp
))
2088 DEBUG_LOG ("RollMeleeOutcomeAgainst: GLANCING <%d, %d)", sum
-4000, sum
);
2089 return MELEE_HIT_GLANCING
;
2093 // mobs can score crushing blows if they're 3 or more levels above victim
2094 // or when their weapon skill is 15 or more above victim's defense skill
2095 tmp
= pVictim
->GetDefenseSkillValue();
2096 int32 tmpmax
= pVictim
->GetMaxSkillValueForLevel();
2097 // having defense above your maximum (from items, talents etc.) has no effect
2098 tmp
= tmp
> tmpmax
? tmpmax
: tmp
;
2099 // tmp = mob's level * 5 - player's current defense skill
2100 tmp
= GetMaxSkillValueForLevel() - tmp
;
2101 if (GetTypeId() != TYPEID_PLAYER
&& (tmp
>= 15))
2103 // add 2% chance per lacking skill point, min. is 15%
2104 tmp
= tmp
* 200 - 1500;
2105 if (roll
< (sum
+= tmp
))
2107 DEBUG_LOG ("RollMeleeOutcomeAgainst: CRUSHING <%d, %d)", sum
-tmp
, sum
);
2108 return MELEE_HIT_CRUSHING
;
2112 DEBUG_LOG ("RollMeleeOutcomeAgainst: NORMAL");
2113 return MELEE_HIT_NORMAL
;
2116 uint32
Unit::CalculateDamage (WeaponAttackType attType
)
2118 float min_damage
, max_damage
;
2123 min_damage
= GetFloatValue(UNIT_FIELD_MINRANGEDDAMAGE
);
2124 max_damage
= GetFloatValue(UNIT_FIELD_MAXRANGEDDAMAGE
);
2127 min_damage
= GetFloatValue(UNIT_FIELD_MINDAMAGE
);
2128 max_damage
= GetFloatValue(UNIT_FIELD_MAXDAMAGE
);
2131 min_damage
= GetFloatValue(UNIT_FIELD_MINOFFHANDDAMAGE
);
2132 max_damage
= GetFloatValue(UNIT_FIELD_MAXOFFHANDDAMAGE
);
2136 if (min_damage
> max_damage
)
2138 std::swap(min_damage
,max_damage
);
2141 if(max_damage
== 0.0)
2144 return rand32((uint32
)min_damage
, (uint32
)max_damage
);
2147 void Unit::SendAttackStart(Unit
* pVictim
)
2149 if(GetTypeId()!=TYPEID_PLAYER
|| !pVictim
)
2152 WorldPacket
data( SMSG_ATTACKSTART
, 16 );
2154 data
<< pVictim
->GetGUID();
2156 ((Player
*)this)->SendMessageToSet(&data
, true);
2157 DEBUG_LOG( "WORLD: Sent SMSG_ATTACKSTART" );
2160 void Unit::SendAttackStop(Unit
* victim
)
2165 WorldPacket
data( SMSG_ATTACKSTOP
, (4+16) ); // we guess size
2166 data
.append(GetPackGUID());
2167 data
.append(victim
->GetPackGUID()); // can be 0x00...
2168 data
<< uint32(0); // can be 0x1
2169 SendMessageToSet(&data
, true);
2170 sLog
.outDetail("%s %u stopped attacking %s %u", (GetTypeId()==TYPEID_PLAYER
? "player" : "creature"), GetGUIDLow(), (victim
->GetTypeId()==TYPEID_PLAYER
? "player" : "creature"),victim
->GetGUIDLow());
2172 /*if(victim->GetTypeId() == TYPEID_UNIT)
2173 ((Creature*)victim)->AI().EnterEvadeMode(this);*/
2176 uint32
Unit::SpellMissChanceCalc(Unit
*pVictim
) const
2181 // PvP : PvE spell misschances per leveldif > 2
2182 int32 chance
= pVictim
->GetTypeId() == TYPEID_PLAYER
? 700 : 1100;
2184 int32 leveldif
= pVictim
->getLevel() - getLevel();
2188 int32 misschance
= 400 - m_modSpellHitChance
*100;
2190 misschance
+= leveldif
* 100;
2192 misschance
+= (leveldif
- 2) * chance
;
2194 return misschance
< 100 ? 100 : misschance
;
2197 int32
Unit::MeleeMissChanceCalc(const Unit
*pVictim
) const
2202 // Base misschance 5%
2203 int32 misschance
= 500;
2205 // DualWield - Melee spells and physical dmg spells - 5% , white damage 24%
2206 if (haveOffhandWeapon())
2208 bool isNormal
= false;
2209 for (uint32 i
= CURRENT_FIRST_NON_MELEE_SPELL
; i
< CURRENT_MAX_SPELL
; i
++)
2211 if (m_currentSpells
[i
] && m_currentSpells
[i
]->m_spellInfo
->School
== SPELL_SCHOOL_NORMAL
)
2217 if (isNormal
|| m_currentSpells
[CURRENT_MELEE_SPELL
])
2227 // PvP : PvE melee misschances per leveldif > 2
2228 int32 chance
= pVictim
->GetTypeId() == TYPEID_PLAYER
? 500 : 700;
2230 int32 leveldif
= pVictim
->getLevel() - getLevel();
2235 misschance
+= leveldif
* 100 - m_modHitChance
*100;
2237 misschance
+= (leveldif
- 2) * chance
- m_modHitChance
*100;
2239 return misschance
> 6000 ? 6000 : misschance
;
2242 uint16
Unit::GetDefenseSkillValue() const
2244 if(GetTypeId() == TYPEID_PLAYER
)
2245 return ((Player
*)this)->GetSkillValue (SKILL_DEFENSE
);
2247 return GetUnitMeleeSkill();
2250 uint16
Unit::GetPureDefenseSkillValue() const
2252 if(GetTypeId() == TYPEID_PLAYER
)
2253 return ((Player
*)this)->GetPureSkillValue(SKILL_DEFENSE
);
2255 return GetUnitMeleeSkill();
2258 float Unit::GetUnitDodgeChance() const
2260 if(hasUnitState(UNIT_STAT_STUNDED
))
2262 return GetTypeId() == TYPEID_PLAYER
? GetFloatValue(PLAYER_DODGE_PERCENTAGE
) : 5;
2265 float Unit::GetUnitParryChance() const
2268 if(GetTypeId() == TYPEID_PLAYER
)
2270 Player
const* player
= (Player
const*)this;
2271 if(player
->CanParry() && player
->IsUseEquipedWeapon() )
2273 Item
*tmpitem
= ((Player
*)this)->GetItemByPos(INVENTORY_SLOT_BAG_0
, EQUIPMENT_SLOT_MAINHAND
);
2274 if(!tmpitem
|| tmpitem
->IsBroken())
2275 tmpitem
= ((Player
*)this)->GetItemByPos(INVENTORY_SLOT_BAG_0
, EQUIPMENT_SLOT_OFFHAND
);
2277 if(tmpitem
&& !tmpitem
->IsBroken() && (
2278 tmpitem
->GetProto()->InventoryType
== INVTYPE_WEAPON
||
2279 tmpitem
->GetProto()->InventoryType
== INVTYPE_WEAPONOFFHAND
||
2280 tmpitem
->GetProto()->InventoryType
== INVTYPE_WEAPONMAINHAND
||
2281 tmpitem
->GetProto()->InventoryType
== INVTYPE_2HWEAPON
))
2282 chance
= GetFloatValue(PLAYER_PARRY_PERCENTAGE
);
2285 else if(GetTypeId() == TYPEID_UNIT
)
2287 if(GetCreatureType() == CREATURE_TYPE_HUMANOID
)
2294 float Unit::GetUnitBlockChance() const
2297 if(GetTypeId() == TYPEID_PLAYER
)
2299 Item
*tmpitem
= ((Player
const*)this)->GetItemByPos(INVENTORY_SLOT_BAG_0
, EQUIPMENT_SLOT_OFFHAND
);
2300 if(tmpitem
&& !tmpitem
->IsBroken() && tmpitem
->GetProto()->Block
)
2301 chance
= GetFloatValue(PLAYER_BLOCK_PERCENTAGE
);
2309 uint16
Unit::GetWeaponSkillValue (WeaponAttackType attType
) const
2311 if(GetTypeId() == TYPEID_PLAYER
)
2316 case BASE_ATTACK
: slot
= EQUIPMENT_SLOT_MAINHAND
; break;
2317 case OFF_ATTACK
: slot
= EQUIPMENT_SLOT_OFFHAND
; break;
2318 case RANGED_ATTACK
: slot
= EQUIPMENT_SLOT_RANGED
; break;
2322 Item
*item
= ((Player
*)this)->GetItemByPos (INVENTORY_SLOT_BAG_0
, slot
);
2324 if(slot
!= EQUIPMENT_SLOT_MAINHAND
&& (!item
|| item
->IsBroken() ||
2325 item
->GetProto()->Class
!= ITEM_CLASS_WEAPON
|| !((Player
*)this)->IsUseEquipedWeapon() ))
2329 uint32 skill
= item
&& !item
->IsBroken() && ((Player
*)this)->IsUseEquipedWeapon()
2330 ? item
->GetSkill() : SKILL_UNARMED
;
2331 return ((Player
*)this)->GetSkillValue (skill
);
2334 return GetUnitMeleeSkill();
2337 uint16
Unit::GetPureWeaponSkillValue (WeaponAttackType attType
) const
2339 if(GetTypeId() == TYPEID_PLAYER
)
2344 case BASE_ATTACK
: slot
= EQUIPMENT_SLOT_MAINHAND
; break;
2345 case OFF_ATTACK
: slot
= EQUIPMENT_SLOT_OFFHAND
; break;
2346 case RANGED_ATTACK
: slot
= EQUIPMENT_SLOT_RANGED
; break;
2350 Item
*item
= ((Player
*)this)->GetItemByPos (INVENTORY_SLOT_BAG_0
, slot
);
2352 if(slot
!= EQUIPMENT_SLOT_MAINHAND
&& (!item
|| item
->IsBroken() ||
2353 item
->GetProto()->Class
!= ITEM_CLASS_WEAPON
|| !((Player
*)this)->IsUseEquipedWeapon() ))
2357 uint32 skill
= item
&& !item
->IsBroken() && ((Player
*)this)->IsUseEquipedWeapon()
2358 ? item
->GetSkill() : SKILL_UNARMED
;
2359 return ((Player
*)this)->GetPureSkillValue (skill
);
2362 return GetUnitMeleeSkill();
2365 void Unit::_UpdateSpells( uint32 time
)
2367 if(m_currentSpells
[CURRENT_AUTOREPEAT_SPELL
])
2368 _UpdateAutoRepeatSpell( time
);
2370 // remove finished spells from current pointers
2371 for (uint32 i
= 0; i
< CURRENT_MAX_SPELL
; i
++)
2373 if (m_currentSpells
[i
] && m_currentSpells
[i
]->getState() == SPELL_STATE_FINISHED
)
2375 m_currentSpells
[i
]->SetDeletable(true); // spell may be safely deleted now
2376 m_currentSpells
[i
] = NULL
; // remove pointer
2380 // TODO: Find a better way to prevent crash when multiple auras are removed.
2382 for (AuraMap::iterator i
= m_Auras
.begin(); i
!= m_Auras
.end(); ++i
)
2384 (*i
).second
->SetUpdated(false);
2386 for (AuraMap::iterator i
= m_Auras
.begin(), next
; i
!= m_Auras
.end(); i
= next
)
2392 // prevent double update
2393 if ((*i
).second
->IsUpdated())
2395 (*i
).second
->SetUpdated(true);
2396 (*i
).second
->Update( time
);
2397 // several auras can be deleted due to update
2400 if (m_Auras
.empty()) break;
2401 next
= m_Auras
.begin();
2407 for (AuraMap::iterator i
= m_Auras
.begin(); i
!= m_Auras
.end();)
2411 if ( !(*i
).second
->GetAuraDuration() && !((*i
).second
->IsPermanent() || ((*i
).second
->IsPassive())) )
2426 if(!m_gameObj
.empty())
2428 std::list
<GameObject
*>::iterator ite1
, dnext1
;
2429 for (ite1
= m_gameObj
.begin(); ite1
!= m_gameObj
.end(); ite1
= dnext1
)
2432 //(*i)->Update( difftime );
2433 if( !(*ite1
)->isSpawned() )
2435 (*ite1
)->SetOwnerGUID(0);
2436 (*ite1
)->SetRespawnTime(0);
2438 dnext1
= m_gameObj
.erase(ite1
);
2446 void Unit::_UpdateAutoRepeatSpell( uint32 time
)
2448 if(m_currentSpells
[CURRENT_AUTOREPEAT_SPELL
]->getState() == SPELL_STATE_FINISHED
)
2451 if( m_currentSpells
[CURRENT_AUTOREPEAT_SPELL
]->m_spellInfo
->AttributesEx2
== 0x000020 && GetTypeId() == TYPEID_PLAYER
)
2453 // Auto Shot don't require ranged weapon cooldown at first cast, wand shoot does, so the 'FINISHED' state
2454 if(m_currentSpells
[CURRENT_AUTOREPEAT_SPELL
]->m_spellInfo
->Category
== 351)
2457 resetAttackTimer( RANGED_ATTACK
);
2462 if (m_AutoRepeatFirstCast
)
2464 // first cast only with recovery time (not less)
2465 if (getAttackTimer( RANGED_ATTACK
) < m_currentSpells
[CURRENT_AUTOREPEAT_SPELL
]->m_spellInfo
->RecoveryTime
)
2466 setAttackTimer( RANGED_ATTACK
, m_currentSpells
[CURRENT_AUTOREPEAT_SPELL
]->m_spellInfo
->RecoveryTime
);
2467 m_AutoRepeatFirstCast
= false;
2471 // second or further casts
2472 resetAttackTimer( RANGED_ATTACK
);
2478 setAttackTimer( RANGED_ATTACK
, m_currentSpells
[CURRENT_AUTOREPEAT_SPELL
]->m_spellInfo
->RecoveryTime
);
2481 m_currentSpells
[CURRENT_AUTOREPEAT_SPELL
]->setState(SPELL_STATE_IDLE
);
2483 else if(m_currentSpells
[CURRENT_AUTOREPEAT_SPELL
]->getState() == SPELL_STATE_IDLE
&& isAttackReady(RANGED_ATTACK
) )
2485 // check if we can cast
2486 if (m_currentSpells
[CURRENT_AUTOREPEAT_SPELL
]->CanCast() == 0)
2488 // check movement in player case
2489 if(GetTypeId() == TYPEID_PLAYER
&& ((Player
*)this)->isMoving())
2491 // cancel wand shooting
2492 if(m_currentSpells
[CURRENT_AUTOREPEAT_SPELL
]->m_spellInfo
->Category
== 351)
2493 InterruptSpell(CURRENT_AUTOREPEAT_SPELL
);
2494 // ELSE delay auto-repeat ranged weapon until player movement stop
2497 // recheck range and req. items (ammo and gun, etc)
2498 if(m_currentSpells
[CURRENT_AUTOREPEAT_SPELL
]->CheckRange() == 0 && m_currentSpells
[CURRENT_AUTOREPEAT_SPELL
]->CheckItems() == 0 )
2500 // check, if we are casting melee spell (it blocks autorepeat)
2501 if ( ! (m_currentSpells
[CURRENT_MELEE_SPELL
] &&
2502 (m_currentSpells
[CURRENT_MELEE_SPELL
]->getState() != SPELL_STATE_FINISHED
) &&
2503 (m_currentSpells
[CURRENT_MELEE_SPELL
]->getState() != SPELL_STATE_DELAYED
)) )
2505 // check, if we are casting something else, if no then run autorepeat spell
2506 if (!IsNonMeleeSpellCasted(false, false, true))
2508 m_currentSpells
[CURRENT_AUTOREPEAT_SPELL
]->setState(SPELL_STATE_PREPARING
);
2509 m_currentSpells
[CURRENT_AUTOREPEAT_SPELL
]->ReSetTimer();
2515 InterruptSpell(CURRENT_AUTOREPEAT_SPELL
);
2520 InterruptSpell(CURRENT_AUTOREPEAT_SPELL
);
2523 else if (m_currentSpells
[CURRENT_AUTOREPEAT_SPELL
]->getState() == SPELL_STATE_PREPARING
)
2525 // check, if some other incomplete spell exists (including melee) or ranged attack is not ready
2526 if ( m_currentSpells
[CURRENT_MELEE_SPELL
] ||
2527 m_currentSpells
[CURRENT_GENERIC_SPELL
] ||
2528 m_currentSpells
[CURRENT_CHANNELED_SPELL
] ||
2529 !isAttackReady(RANGED_ATTACK
) )
2531 // some other spell is here or ranged attack is not ready, break us to idle state
2532 m_currentSpells
[CURRENT_AUTOREPEAT_SPELL
]->finish(false);
2533 m_currentSpells
[CURRENT_AUTOREPEAT_SPELL
]->setState(SPELL_STATE_IDLE
);
2538 void Unit::SetCurrentCastedSpell( Spell
* pSpell
)
2540 assert(pSpell
); // NULL may be never passed here, use InterruptSpell or InterruptNonMeleeSpells
2542 uint32 CSpellType
= pSpell
->GetCurrentContainer();
2544 pSpell
->SetDeletable(false); // spell will not be deleted until gone from current pointers
2545 if (pSpell
== m_currentSpells
[CSpellType
]) return; // avoid breaking self
2547 // break same type spell if it is not delayed
2548 if ( m_currentSpells
[CSpellType
] &&
2549 m_currentSpells
[CSpellType
]->getState() != SPELL_STATE_DELAYED
)
2551 InterruptSpell(CSpellType
);
2554 // special breakage effects:
2557 case CURRENT_GENERIC_SPELL
:
2559 // generic spells always break channeled not delayed spells
2560 if ( m_currentSpells
[CURRENT_CHANNELED_SPELL
] &&
2561 m_currentSpells
[CURRENT_CHANNELED_SPELL
]->getState() != SPELL_STATE_DELAYED
)
2563 InterruptSpell(CURRENT_CHANNELED_SPELL
);
2566 // autorepeat breaking
2567 if ( m_currentSpells
[CURRENT_AUTOREPEAT_SPELL
] )
2569 // break autorepeat if not Auto Shot
2570 if (m_currentSpells
[CURRENT_AUTOREPEAT_SPELL
]->m_spellInfo
->Category
== 351)
2571 InterruptSpell(CURRENT_AUTOREPEAT_SPELL
);
2575 case CURRENT_CHANNELED_SPELL
:
2577 // channel spells always break generic and channeled spells
2578 InterruptSpell(CURRENT_GENERIC_SPELL
);
2579 InterruptSpell(CURRENT_CHANNELED_SPELL
);
2581 // it also does break autorepeat if not Auto Shot
2582 if ( m_currentSpells
[CURRENT_AUTOREPEAT_SPELL
] &&
2583 m_currentSpells
[CURRENT_AUTOREPEAT_SPELL
]->m_spellInfo
->Category
== 351 )
2584 InterruptSpell(CURRENT_AUTOREPEAT_SPELL
);
2587 case CURRENT_AUTOREPEAT_SPELL
:
2589 // only Auto Shoot does not break anything
2590 if (pSpell
->m_spellInfo
->Category
== 351)
2592 // generic autorepeats break generic and channeled spells
2593 InterruptSpell(CURRENT_GENERIC_SPELL
);
2594 InterruptSpell(CURRENT_CHANNELED_SPELL
);
2598 // special action: set first cast flag for Auto Shoot
2599 m_AutoRepeatFirstCast
= true;
2605 // other spell types don't break anything now
2609 // current spell (if it is still here) may be safely deleted now
2610 if (m_currentSpells
[CSpellType
])
2611 m_currentSpells
[CSpellType
]->SetDeletable(true);
2613 // set new current spell
2614 m_currentSpells
[CSpellType
] = pSpell
;
2617 void Unit::InterruptSpell(uint32 spellType
)
2619 assert(spellType
< CURRENT_MAX_SPELL
);
2621 if(m_currentSpells
[spellType
])
2623 // send autorepeat cancel message for autorepeat spells
2624 if (spellType
== CURRENT_AUTOREPEAT_SPELL
)
2626 if(GetTypeId()==TYPEID_PLAYER
)
2627 ((Player
*)this)->SendAutoRepeatCancel();
2630 if (m_currentSpells
[spellType
]->getState() != SPELL_STATE_FINISHED
)
2631 m_currentSpells
[spellType
]->cancel();
2632 m_currentSpells
[spellType
]->SetDeletable(true);
2633 m_currentSpells
[spellType
] = NULL
;
2637 bool Unit::IsNonMeleeSpellCasted(bool withDelayed
, bool skipChanneled
, bool skipAutorepeat
)
2639 // We don't do loop here to explicitly show that melee spell is excluded.
2640 // Maybe later some special spells will be excluded too.
2642 // generic spells are casted when they are not finished and not delayed
2643 if ( m_currentSpells
[CURRENT_GENERIC_SPELL
] &&
2644 (m_currentSpells
[CURRENT_GENERIC_SPELL
]->getState() != SPELL_STATE_FINISHED
) &&
2645 (withDelayed
|| m_currentSpells
[CURRENT_GENERIC_SPELL
]->getState() != SPELL_STATE_DELAYED
) )
2648 // channeled spells may be delayed, but they are still considered casted
2649 else if ( !skipChanneled
&& m_currentSpells
[CURRENT_CHANNELED_SPELL
] &&
2650 (m_currentSpells
[CURRENT_CHANNELED_SPELL
]->getState() != SPELL_STATE_FINISHED
) )
2653 // autorepeat spells may be finished or delayed, but they are still considered casted
2654 else if ( !skipAutorepeat
&& m_currentSpells
[CURRENT_AUTOREPEAT_SPELL
] )
2660 void Unit::InterruptNonMeleeSpells(bool withDelayed
)
2662 // generic spells are interrupted if they are not finished or delayed
2663 if (m_currentSpells
[CURRENT_GENERIC_SPELL
])
2665 if ( (m_currentSpells
[CURRENT_GENERIC_SPELL
]->getState() != SPELL_STATE_FINISHED
) &&
2666 (withDelayed
|| m_currentSpells
[CURRENT_GENERIC_SPELL
]->getState() != SPELL_STATE_DELAYED
) )
2667 m_currentSpells
[CURRENT_GENERIC_SPELL
]->cancel();
2668 m_currentSpells
[CURRENT_GENERIC_SPELL
]->SetDeletable(true);
2669 m_currentSpells
[CURRENT_GENERIC_SPELL
] = NULL
;
2672 // autorepeat spells are interrupted if they are not finished or delayed
2673 if (m_currentSpells
[CURRENT_AUTOREPEAT_SPELL
])
2675 // send disable autorepeat packet in any case
2676 if(GetTypeId()==TYPEID_PLAYER
)
2677 ((Player
*)this)->SendAutoRepeatCancel();
2679 if ( (m_currentSpells
[CURRENT_AUTOREPEAT_SPELL
]->getState() != SPELL_STATE_FINISHED
) &&
2680 (withDelayed
|| m_currentSpells
[CURRENT_AUTOREPEAT_SPELL
]->getState() != SPELL_STATE_DELAYED
) )
2681 m_currentSpells
[CURRENT_AUTOREPEAT_SPELL
]->cancel();
2682 m_currentSpells
[CURRENT_AUTOREPEAT_SPELL
]->SetDeletable(true);
2683 m_currentSpells
[CURRENT_AUTOREPEAT_SPELL
] = NULL
;
2686 // channeled spells are interrupted if they are not finished, even if they are delayed
2687 if (m_currentSpells
[CURRENT_CHANNELED_SPELL
])
2689 if (m_currentSpells
[CURRENT_CHANNELED_SPELL
]->getState() != SPELL_STATE_FINISHED
)
2690 m_currentSpells
[CURRENT_CHANNELED_SPELL
]->cancel();
2691 m_currentSpells
[CURRENT_CHANNELED_SPELL
]->SetDeletable(true);
2692 m_currentSpells
[CURRENT_CHANNELED_SPELL
] = NULL
;
2696 bool Unit::isInFront(Unit
const* target
, float radius
) const
2698 return IsWithinDistInMap(target
, radius
) && HasInArc( M_PI
, target
);
2701 void Unit::SetInFront(Unit
const* target
)
2703 SetOrientation(GetAngle(target
));
2706 bool Unit::isInAccessablePlaceFor(Creature
* c
) const
2709 return c
->isCanSwimOrFly();
2711 return c
->isCanWalkOrFly();
2714 bool Unit::IsInWater() const
2716 return MapManager::Instance().GetMap(GetMapId(), this)->IsInWater(GetPositionX(),GetPositionY(), GetPositionZ());
2719 bool Unit::IsUnderWater() const
2721 return MapManager::Instance().GetMap(GetMapId(), this)->IsUnderWater(GetPositionX(),GetPositionY(),GetPositionZ());
2724 void Unit::DeMorph()
2726 SetUInt32Value(UNIT_FIELD_DISPLAYID
, GetUInt32Value(UNIT_FIELD_NATIVEDISPLAYID
));
2729 long Unit::GetTotalAuraModifier(uint32 ModifierID
) const
2731 uint32 modifier
= 0;
2733 AuraList
const& mTotalAuraList
= GetAurasByType(ModifierID
);
2734 for(AuraList::const_iterator i
= mTotalAuraList
.begin();i
!= mTotalAuraList
.end(); ++i
)
2735 modifier
+= (*i
)->GetModifier()->m_amount
;
2740 bool Unit::AddAura(Aura
*Aur
, bool uniq
)
2742 // ghost spell check
2743 if (!isAlive() && !(Aur
->GetSpellProto()->Id
== 20584 || Aur
->GetSpellProto()->Id
== 8326))
2749 if(Aur
->GetTarget() != this)
2751 sLog
.outError("Aura (spell %u eff %u) add to aura list of %s (lowguid: %u) but Aura target is %s (lowguid: %u)",
2752 Aur
->GetId(),Aur
->GetEffIndex(),(GetTypeId()==TYPEID_PLAYER
?"player":"creature"),GetGUIDLow(),
2753 (Aur
->GetTarget()->GetTypeId()==TYPEID_PLAYER
?"player":"creature"),Aur
->GetTarget()->GetGUIDLow());
2758 AuraMap::iterator i
= m_Auras
.find( spellEffectPair(Aur
->GetId(), Aur
->GetEffIndex()) );
2760 // take out same spell
2761 if (i
!= m_Auras
.end())
2763 /*(*i).second->SetAuraDuration(Aur->GetAuraDuration());
2764 if ((*i).second->GetTarget())
2765 if ((*i).second->GetTarget()->GetTypeId() == TYPEID_PLAYER )
2766 (*i).second->UpdateAuraDuration();
2769 // passive and persistent auras can stack with themselves any number of times
2770 if (!Aur
->IsPassive() && !Aur
->IsPersistent() && m_Auras
.count(spellEffectPair(Aur
->GetId(), Aur
->GetEffIndex())) >= Aur
->GetSpellProto()->StackAmount
)
2774 // passive auras stack with all (except passive spell proc auras)
2775 if ((!Aur
->IsPassive() || !IsPassiveStackableSpell(Aur
->GetId())) &&
2776 !(Aur
->GetSpellProto()->Id
== 20584 || Aur
->GetSpellProto()->Id
== 8326))
2778 if (!RemoveNoStackAurasDueToAura(Aur
))
2781 return false; // couldnt remove conflicting aura with higher rank
2785 // adding linked auras
2786 // add the shapeshift aura's boosts
2787 if(Aur
->GetModifier()->m_auraname
== SPELL_AURA_MOD_SHAPESHIFT
)
2788 Aur
->HandleShapeshiftBoosts(true);
2791 m_Auras
.insert(AuraMap::value_type(spellEffectPair(Aur
->GetId(), Aur
->GetEffIndex()), Aur
));
2792 if (Aur
->GetModifier()->m_auraname
< TOTAL_AURAS
)
2794 m_modAuras
[Aur
->GetModifier()->m_auraname
].push_back(Aur
);
2795 m_AuraModifiers
[Aur
->GetModifier()->m_auraname
] += (Aur
->GetModifier()->m_amount
);
2798 if (IsSingleTarget(Aur
->GetId()) && Aur
->GetTarget() && Aur
->GetSpellProto())
2800 if(Unit
* caster
= Aur
->GetCaster())
2802 AuraList
& scAuras
= caster
->GetSingleCastAuras();
2803 AuraList::iterator itr
, next
;
2804 for (itr
= scAuras
.begin(); itr
!= scAuras
.end(); itr
= next
)
2808 if ((*itr
)->GetTarget() != Aur
->GetTarget() &&
2809 (*itr
)->GetSpellProto()->Category
== Aur
->GetSpellProto()->Category
&&
2810 (*itr
)->GetSpellProto()->SpellIconID
== Aur
->GetSpellProto()->SpellIconID
&&
2811 (*itr
)->GetSpellProto()->SpellVisual
== Aur
->GetSpellProto()->SpellVisual
&&
2812 (*itr
)->GetSpellProto()->Attributes
== Aur
->GetSpellProto()->Attributes
&&
2813 (*itr
)->GetSpellProto()->AttributesEx
== Aur
->GetSpellProto()->AttributesEx
&&
2814 (*itr
)->GetSpellProto()->AttributesExEx
== Aur
->GetSpellProto()->AttributesExEx
)
2816 (*itr
)->GetTarget()->RemoveAura((*itr
)->GetId(), (*itr
)->GetEffIndex());
2820 next
= scAuras
.begin();
2823 scAuras
.push_back(Aur
);
2829 void Unit::RemoveRankAurasDueToSpell(uint32 spellId
)
2831 SpellEntry
const *spellInfo
= sSpellStore
.LookupEntry(spellId
);
2834 AuraMap::iterator i
,next
;
2835 for (i
= m_Auras
.begin(); i
!= m_Auras
.end(); i
= next
)
2839 uint32 i_spellId
= (*i
).second
->GetId();
2840 if((*i
).second
&& i_spellId
&& i_spellId
!= spellId
)
2842 if(objmgr
.IsRankSpellDueToSpell(spellInfo
,i_spellId
))
2844 RemoveAurasDueToSpell(i_spellId
);
2846 if( m_Auras
.empty() )
2849 next
= m_Auras
.begin();
2855 bool Unit::RemoveNoStackAurasDueToAura(Aura
*Aur
)
2860 SpellEntry
const* spellProto
= Aur
->GetSpellProto();
2864 uint32 spellId
= Aur
->GetId();
2865 uint32 effIndex
= Aur
->GetEffIndex();
2866 bool is_sec
= IsSpellSingleEffectPerCaster(spellId
);
2867 AuraMap::iterator i
,next
;
2868 for (i
= m_Auras
.begin(); i
!= m_Auras
.end(); i
= next
)
2872 if (!(*i
).second
) continue;
2874 if (!(*i
).second
->GetSpellProto())
2877 uint32 i_spellId
= (*i
).second
->GetId();
2879 if(IsPassiveSpell(i_spellId
))
2881 if(IsPassiveStackableSpell(i_spellId
))
2884 // passive non-stackable spells not stackable only with another rank of same spell
2885 if (!objmgr
.IsRankSpellDueToSpell(Aur
->GetSpellProto(), i_spellId
))
2889 uint32 i_effIndex
= (*i
).second
->GetEffIndex();
2891 if(i_spellId
== spellId
) continue;
2893 bool is_triggered_by_spell
= false;
2894 // prevent triggered aura of removing aura that triggered it
2895 for(int j
= 0; j
< 3; ++j
)
2896 if ((*i
).second
->GetSpellProto()->EffectTriggerSpell
[j
] == spellProto
->Id
)
2897 is_triggered_by_spell
= true;
2898 if (is_triggered_by_spell
) continue;
2900 // prevent remove dummy triggered spells at next effect aura add
2901 for(int j
= 0; j
< 3; ++j
)
2903 switch(spellProto
->Effect
[j
])
2905 case SPELL_EFFECT_DUMMY
:
2908 case 5420: if(i_spellId
==34123) is_triggered_by_spell
= true; break;
2912 if(is_triggered_by_spell
)
2915 switch(spellProto
->EffectApplyAuraName
[j
])
2917 case SPELL_AURA_MOD_SHAPESHIFT
:
2920 case 33891: if(i_spellId
==5420 || i_spellId
==34123) is_triggered_by_spell
= true; break;
2926 if(!is_triggered_by_spell
)
2928 bool sec_match
= false;
2929 bool is_i_sec
= IsSpellSingleEffectPerCaster(i_spellId
);
2930 if( is_sec
&& is_i_sec
)
2931 if (Aur
->GetCasterGUID() == (*i
).second
->GetCasterGUID())
2932 if (GetSpellSpecific(spellId
) == GetSpellSpecific(i_spellId
))
2934 if( sec_match
|| objmgr
.IsNoStackSpellDueToSpell(spellId
, i_spellId
) && !is_sec
&& !is_i_sec
)
2936 // if sec_match this isn't always true, needs to be rechecked
2937 if (objmgr
.IsRankSpellDueToSpell(Aur
->GetSpellProto(), i_spellId
))
2938 if(CompareAuraRanks(spellId
, effIndex
, i_spellId
, i_effIndex
) < 0)
2939 return false; // cannot remove higher rank
2941 RemoveAurasDueToSpell(i_spellId
);
2943 if( m_Auras
.empty() )
2946 next
= m_Auras
.begin();
2948 else // Potions stack aura by aura
2949 if (Aur
->GetSpellProto()->SpellFamilyName
== SPELLFAMILY_POTION
&&
2950 (*i
).second
->GetSpellProto()->SpellFamilyName
== SPELLFAMILY_POTION
)
2952 if (IsNoStackAuraDueToAura(spellId
, effIndex
, i_spellId
, i_effIndex
))
2954 if(CompareAuraRanks(spellId
, effIndex
, i_spellId
, i_effIndex
) < 0)
2955 return false; // cannot remove higher rank
2966 void Unit::RemoveFirstAuraByDispel(uint32 dispel_type
, Unit
*pCaster
)
2968 AuraMap::iterator i
;
2969 for (i
= m_Auras
.begin(); i
!= m_Auras
.end();)
2971 if ((*i
).second
&& (*i
).second
->GetSpellProto()->Dispel
== dispel_type
)
2973 if(dispel_type
== 1)
2975 bool positive
= true;
2976 switch((*i
).second
->GetSpellProto()->EffectImplicitTargetA
[(*i
).second
->GetEffIndex()])
2978 case TARGET_CHAIN_DAMAGE
:
2979 case TARGET_ALL_ENEMY_IN_AREA
:
2980 case TARGET_ALL_ENEMY_IN_AREA_INSTANT
:
2981 case TARGET_ALL_ENEMIES_AROUND_CASTER
:
2982 case TARGET_IN_FRONT_OF_CASTER
:
2983 case TARGET_DUELVSPLAYER
:
2984 case TARGET_ALL_ENEMY_IN_AREA_CHANNELED
:
2985 case TARGET_CURRENT_SELECTED_ENEMY
:
2990 positive
= ((*i
).second
->GetSpellProto()->AttributesEx
& (1<<7)) ? false : true;
2992 if(positive
&& IsFriendlyTo(pCaster
)) // PBW
3006 void Unit::RemoveAreaAurasByOthers(uint64 guid
)
3009 for (AuraMap::iterator i
= m_Auras
.begin(); i
!= m_Auras
.end();)
3011 if (i
->second
&& i
->second
->IsAreaAura())
3013 uint64 casterGuid
= i
->second
->GetCasterGUID();
3014 uint64 targetGuid
= i
->second
->GetTarget()->GetGUID();
3015 // if area aura cast by someone else or by the specified caster
3016 if (casterGuid
== guid
|| (guid
== 0 && casterGuid
!= targetGuid
))
3018 for (j
= 0; j
< 4; j
++)
3019 if (m_TotemSlot
[j
] == casterGuid
)
3021 // and not by one of my totems
3035 void Unit::RemoveAura(uint32 spellId
, uint32 effindex
)
3037 AuraMap::iterator iter
;
3038 while((iter
= m_Auras
.find(spellEffectPair(spellId
, effindex
))) != m_Auras
.end())
3042 void Unit::RemoveAurasDueToSpell(uint32 spellId
)
3044 for (int i
= 0; i
< 3; ++i
)
3045 RemoveAura(spellId
,i
);
3048 void Unit::RemoveAurasDueToItem(Item
* castItem
)
3050 for (AuraMap::iterator iter
= m_Auras
.begin(); iter
!= m_Auras
.end(); )
3052 if (iter
->second
->GetCastItemGUID() == castItem
->GetGUID())
3059 void Unit::RemoveAura(AuraMap::iterator
&i
, bool onDeath
)
3061 if (IsSingleTarget((*i
).second
->GetId()))
3063 if(Unit
* caster
= (*i
).second
->GetCaster())
3065 AuraList
& scAuras
= caster
->GetSingleCastAuras();
3066 scAuras
.remove((*i
).second
);
3069 sLog
.outError("Unit::RemoveAura: cannot remove the single cast aura from the caster, potential crash!");
3071 // remove aura from party members when the caster turns off the aura
3072 if((*i
).second
->IsAreaAura())
3074 Unit
*i_target
= (*i
).second
->GetTarget();
3075 if((*i
).second
->GetCasterGUID() == i_target
->GetGUID())
3077 Unit
* i_caster
= i_target
;
3080 Group
*pGroup
= NULL
;
3081 Player
*pGroupOf
= NULL
;
3082 if (i_caster
->GetTypeId() == TYPEID_PLAYER
)
3084 pGroupOf
= (Player
*)i_caster
;
3085 pGroup
= pGroupOf
->GetGroup();
3087 else if(((Creature
*)i_caster
)->isTotem() || ((Creature
*)i_caster
)->isPet() || i_caster
->isCharmed())
3089 owner
= i_caster
->GetCharmerOrOwner();
3090 if (owner
&& owner
->GetTypeId() == TYPEID_PLAYER
)
3092 pGroupOf
= (Player
*)owner
;
3093 pGroup
= pGroupOf
->GetGroup();
3097 //float radius = GetRadius(sSpellRadiusStore.LookupEntry((*i).second->GetSpellProto()->EffectRadiusIndex[(*i).second->GetEffIndex()]));
3098 if(pGroup
&& pGroupOf
)
3100 for(GroupReference
*itr
= pGroup
->GetFirstMember(); itr
!= NULL
; itr
= itr
->next())
3102 Player
* Target
= itr
->getSource();
3103 if(!Target
|| !pGroup
->SameSubGroup(pGroupOf
, Target
))
3106 if(Target
->GetGUID() == i_caster
->GetGUID())
3108 Aura
*t_aura
= Target
->GetAura((*i
).second
->GetId(), (*i
).second
->GetEffIndex());
3110 if (t_aura
->GetCasterGUID() == i_caster
->GetGUID())
3111 Target
->RemoveAura((*i
).second
->GetId(), (*i
).second
->GetEffIndex());
3116 Aura
*t_aura
= owner
->GetAura((*i
).second
->GetId(), (*i
).second
->GetEffIndex());
3118 if (t_aura
->GetCasterGUID() == i_caster
->GetGUID())
3119 owner
->RemoveAura((*i
).second
->GetId(), (*i
).second
->GetEffIndex());
3123 if ((*i
).second
->GetModifier()->m_auraname
< TOTAL_AURAS
)
3125 m_AuraModifiers
[(*i
).second
->GetModifier()->m_auraname
] -= ((*i
).second
->GetModifier()->m_amount
);
3126 m_modAuras
[(*i
).second
->GetModifier()->m_auraname
].remove((*i
).second
);
3128 (*i
).second
->SetRemoveOnDeath(onDeath
);
3130 // remove from list before mods removing (prevent cyclic calls, mods added before including to aura list - use reverse order)
3131 Aura
* Aur
= i
->second
;
3133 DiminishingMechanics mech
= DIMINISHING_NONE
;
3134 if(Aur
->GetSpellProto()->Mechanic
)
3136 mech
= Unit::Mechanic2DiminishingMechanics(Aur
->GetSpellProto()->Mechanic
);
3137 if(mech
== DIMINISHING_MECHANIC_STUN
|| GetTypeId() == TYPEID_PLAYER
&& mech
!= DIMINISHING_NONE
)
3138 UpdateDiminishingTime(mech
);
3141 // must remove before removing from list (its remove dependent auras and _i_ is only safe iterator value
3142 // remove the shapeshift aura's boosts
3143 if(Aur
->GetModifier()->m_auraname
== SPELL_AURA_MOD_SHAPESHIFT
)
3144 Aur
->HandleShapeshiftBoosts(false);
3147 m_removedAuras
++; // internal count used by unit update
3152 // only way correctly remove all auras from list
3153 if( m_Auras
.empty() )
3156 i
= m_Auras
.begin();
3159 bool Unit::SetAurDuration(uint32 spellId
, uint32 effindex
,uint32 duration
)
3161 AuraMap::iterator iter
= m_Auras
.find(spellEffectPair(spellId
, effindex
));
3162 if (iter
!= m_Auras
.end())
3164 (*iter
).second
->SetAuraDuration(duration
);
3165 (*iter
).second
->UpdateAuraDuration();
3171 uint32
Unit::GetAurDuration(uint32 spellId
, uint32 effindex
)
3173 AuraMap::iterator iter
= m_Auras
.find(spellEffectPair(spellId
, effindex
));
3174 if (iter
!= m_Auras
.end())
3176 return (*iter
).second
->GetAuraDuration();
3181 void Unit::RemoveAllAuras()
3183 while (!m_Auras
.empty())
3185 AuraMap::iterator iter
= m_Auras
.begin();
3190 void Unit::RemoveAllAurasOnDeath()
3192 // used just after dieing to remove all visible auras
3193 // and disable the mods for the passive ones
3194 for(AuraMap::iterator iter
= m_Auras
.begin(); iter
!= m_Auras
.end();)
3196 if (!iter
->second
->IsPassive())
3197 RemoveAura(iter
, true);
3203 void Unit::DelayAura(uint32 spellId
, uint32 effindex
, int32 delaytime
)
3205 AuraMap::iterator iter
= m_Auras
.find(spellEffectPair(spellId
, effindex
));
3206 if (iter
!= m_Auras
.end())
3208 if (iter
->second
->GetAuraDuration() < delaytime
)
3209 iter
->second
->SetAuraDuration(0);
3211 iter
->second
->SetAuraDuration(iter
->second
->GetAuraDuration() - delaytime
);
3212 iter
->second
->UpdateAuraDuration();
3213 sLog
.outDebug("Aura %u partially interrupted on unit %u, new duration: %u ms",iter
->second
->GetModifier()->m_auraname
, GetGUIDLow(), iter
->second
->GetAuraDuration());
3217 void Unit::_RemoveAllAuraMods()
3219 for (AuraMap::iterator i
= m_Auras
.begin(); i
!= m_Auras
.end(); ++i
)
3221 (*i
).second
->ApplyModifier(false);
3225 void Unit::_ApplyAllAuraMods()
3227 for (AuraMap::iterator i
= m_Auras
.begin(); i
!= m_Auras
.end(); ++i
)
3229 (*i
).second
->ApplyModifier(true);
3234 /*void Unit::_UpdateAura()
3236 if(GetTypeId() != TYPEID_PLAYER || !m_Auras)
3239 Player* pThis = (Player*)this;
3244 pGroup = objmgr.GetGroupByLeader(pThis->GetGroupLeader());
3246 if(!SetAffDuration(m_Auras->GetId(),this,6000))
3253 for(uint32 i=0;i<pGroup->GetMembersCount();i++)
3255 pGroupGuy = ObjectAccessor::Instance().FindPlayer(pGroup->GetMemberGUID(i));
3259 if(pGroupGuy->GetGUID() == GetGUID())
3262 (GetPositionX()-pGroupGuy->GetPositionX())*(GetPositionX()-pGroupGuy->GetPositionX())
3263 +(GetPositionY()-pGroupGuy->GetPositionY())*(GetPositionY()-pGroupGuy->GetPositionY())
3264 +(GetPositionZ()-pGroupGuy->GetPositionZ())*(GetPositionZ()-pGroupGuy->GetPositionZ())
3267 if(!pGroupGuy->SetAffDuration(m_Auras->GetId(),this,6000))
3268 pGroupGuy->AddAura(m_Auras);
3272 if(m_removeAuraTimer == 0)
3274 printf("remove aura from %u\n", pGroupGuy->GetGUID());
3275 pGroupGuy->RemoveAura(m_Auras->GetId());
3280 if(m_removeAuraTimer > 0)
3281 m_removeAuraTimer -= 1;
3283 m_removeAuraTimer = 4;
3286 Aura
* Unit::GetAura(uint32 spellId
, uint32 effindex
)
3288 AuraMap::iterator iter
= m_Auras
.find(spellEffectPair(spellId
, effindex
));
3289 if (iter
!= m_Auras
.end())
3290 return iter
->second
;
3294 void Unit::AddDynObject(DynamicObject
* dynObj
)
3296 m_dynObjGUIDs
.push_back(dynObj
->GetGUID());
3299 void Unit::RemoveDynObject(uint32 spellid
)
3301 if(m_dynObjGUIDs
.empty())
3303 for (DynObjectGUIDs::iterator i
= m_dynObjGUIDs
.begin(); i
!= m_dynObjGUIDs
.end();)
3305 DynamicObject
* dynObj
= ObjectAccessor::Instance().GetDynamicObject(*this,*m_dynObjGUIDs
.begin());
3308 i
= m_dynObjGUIDs
.erase(i
);
3310 else if(spellid
== 0 || dynObj
->GetSpellId() == spellid
)
3313 i
= m_dynObjGUIDs
.erase(i
);
3320 DynamicObject
* Unit::GetDynObject(uint32 spellId
, uint32 effIndex
)
3322 for (DynObjectGUIDs::iterator i
= m_dynObjGUIDs
.begin(); i
!= m_dynObjGUIDs
.end();)
3324 DynamicObject
* dynObj
= ObjectAccessor::Instance().GetDynamicObject(*this,*m_dynObjGUIDs
.begin());
3327 i
= m_dynObjGUIDs
.erase(i
);
3331 if (dynObj
->GetSpellId() == spellId
&& dynObj
->GetEffIndex() == effIndex
)
3338 void Unit::AddGameObject(GameObject
* gameObj
)
3340 assert(gameObj
&& gameObj
->GetOwnerGUID()==0);
3341 m_gameObj
.push_back(gameObj
);
3342 gameObj
->SetOwnerGUID(GetGUID());
3345 void Unit::RemoveGameObject(GameObject
* gameObj
, bool del
)
3347 assert(gameObj
&& gameObj
->GetOwnerGUID()==GetGUID());
3348 gameObj
->SetOwnerGUID(0);
3349 m_gameObj
.remove(gameObj
);
3352 gameObj
->SetRespawnTime(0);
3357 void Unit::RemoveGameObject(uint32 spellid
, bool del
)
3359 if(m_gameObj
.empty())
3361 std::list
<GameObject
*>::iterator i
, next
;
3362 for (i
= m_gameObj
.begin(); i
!= m_gameObj
.end(); i
= next
)
3365 if(spellid
== 0 || (*i
)->GetSpellId() == spellid
)
3367 (*i
)->SetOwnerGUID(0);
3370 (*i
)->SetRespawnTime(0);
3374 next
= m_gameObj
.erase(i
);
3381 void Unit::SendSpellNonMeleeDamageLog(Unit
*target
,uint32 SpellID
,uint32 Damage
, uint8 DamageType
,uint32 AbsorbedDamage
, uint32 Resist
,bool PhysicalDamage
, uint32 Blocked
, bool CriticalHit
)
3383 WorldPacket
data(SMSG_SPELLNONMELEEDAMAGELOG
, (16+31)); // we guess size
3384 data
.append(target
->GetPackGUID());
3385 data
.append(GetPackGUID());
3386 data
<< uint32(SpellID
);
3387 data
<< uint32(Damage
-AbsorbedDamage
-Resist
-Blocked
);
3388 data
<< uint8(DamageType
); //damagetype
3389 data
<< uint32(AbsorbedDamage
); //AbsorbedDamage
3390 data
<< uint32(Resist
); //resist
3391 data
<< (uint8
)PhysicalDamage
;
3393 data
<< uint32(Blocked
); //blocked
3394 data
<< uint8(CriticalHit
? 2 : 0); //seen 0x05 also...
3396 SendMessageToSet( &data
, true );
3399 void Unit::SendAttackStateUpdate(uint32 HitInfo
, Unit
*target
, uint8 SwingType
, uint32 DamageType
, uint32 Damage
, uint32 AbsorbDamage
, uint32 Resist
, uint32 TargetState
, uint32 BlockedAmount
)
3401 sLog
.outDebug("WORLD: Sending SMSG_ATTACKERSTATEUPDATE");
3403 WorldPacket
data(SMSG_ATTACKERSTATEUPDATE
, (16+45)); // we guess size
3404 data
<< (uint32
)HitInfo
;
3405 data
.append(GetPackGUID());
3406 data
.append(target
->GetPackGUID());
3407 data
<< (uint32
)(Damage
-AbsorbDamage
-Resist
-BlockedAmount
);
3409 data
<< (uint8
)SwingType
;
3410 data
<< (uint32
)DamageType
;
3413 data
<< (float)(Damage
-AbsorbDamage
-Resist
-BlockedAmount
);
3414 // still need to double check damaga
3415 data
<< (uint32
)(Damage
-AbsorbDamage
-Resist
-BlockedAmount
);
3416 data
<< (uint32
)AbsorbDamage
;
3417 data
<< (uint32
)Resist
;
3418 data
<< (uint32
)TargetState
;
3420 if( AbsorbDamage
== 0 ) //also 0x3E8 = 0x3E8, check when that happens
3426 data
<< (uint32
)BlockedAmount
;
3428 SendMessageToSet( &data
, true );
3431 struct ProcTriggeredData
3433 ProcTriggeredData(SpellEntry
const * _spellInfo
, uint32 _spellParam
, Aura
* _triggeredByAura
)
3434 : spellInfo(_spellInfo
), spellParam(_spellParam
), triggeredByAura(_triggeredByAura
) {}
3436 SpellEntry
const * spellInfo
;
3438 Aura
* triggeredByAura
;
3441 typedef std::list
< ProcTriggeredData
> ProcTriggeredList
;
3443 // used to prevent spam in log about same non-handled spells
3444 static std::set
<uint32
> nonHandledSpellProcSet
;
3446 void Unit::ProcDamageAndSpell(Unit
*pVictim
, uint32 procAttacker
, uint32 procVictim
, uint32 damage
, SpellEntry
const *procSpell
, bool isTriggeredSpell
, WeaponAttackType attType
)
3448 sLog
.outDebug("ProcDamageAndSpell: attacker flags are 0x%x, victim flags 0x%x", procAttacker
, procVictim
);
3450 sLog
.outDebug("ProcDamageAndSpell: invoked due to spell id %u %s", procSpell
->Id
, (isTriggeredSpell
?"(triggered)":""));
3452 // Assign melee/ranged proc flags for magic attacks, that are actually melee/ranged abilities
3453 // not assign for spell proc triggered spell to prevent infinity (or unexpacted 2-3 times) melee damage spell proc call with melee damage effect
3454 // That is the question though if it's fully correct
3455 if(procSpell
&& !isTriggeredSpell
)
3457 if(procSpell
->DmgClass
== SPELL_DAMAGE_CLASS_MELEE
)
3459 if(procAttacker
& PROC_FLAG_HIT_SPELL
) procAttacker
|= PROC_FLAG_HIT_MELEE
;
3460 if(procAttacker
& PROC_FLAG_CRIT_SPELL
) procAttacker
|= PROC_FLAG_CRIT_MELEE
;
3461 if(procVictim
& PROC_FLAG_STRUCK_SPELL
) procVictim
|= PROC_FLAG_STRUCK_MELEE
;
3462 if(procVictim
& PROC_FLAG_STRUCK_CRIT_SPELL
) procVictim
|= PROC_FLAG_STRUCK_CRIT_MELEE
;
3463 attType
= BASE_ATTACK
; // Melee abilities are assumed to be dealt with mainhand weapon
3465 else if (procSpell
->DmgClass
== SPELL_DAMAGE_CLASS_RANGED
)
3467 if(procAttacker
& PROC_FLAG_HIT_SPELL
) procAttacker
|= PROC_FLAG_HIT_RANGED
;
3468 if(procAttacker
& PROC_FLAG_CRIT_SPELL
) procAttacker
|= PROC_FLAG_CRIT_RANGED
;
3469 if(procVictim
& PROC_FLAG_STRUCK_SPELL
) procVictim
|= PROC_FLAG_STRUCK_RANGED
;
3470 if(procVictim
& PROC_FLAG_STRUCK_CRIT_SPELL
) procVictim
|= PROC_FLAG_STRUCK_CRIT_RANGED
;
3471 attType
= RANGED_ATTACK
;
3474 if(damage
&& (procVictim
& (PROC_FLAG_STRUCK_MELEE
|PROC_FLAG_STRUCK_RANGED
|PROC_FLAG_STRUCK_SPELL
)))
3475 procVictim
|= (PROC_FLAG_TAKE_DAMAGE
|PROC_FLAG_TOUCH
);
3477 // Not much to do if no flags are set.
3480 for(std::set
<uint32
>::iterator aur
= attackerProcAuraTypes
.begin(); aur
!= attackerProcAuraTypes
.end(); ++aur
)
3482 // List of spells (effects) that proced. Spell prototype and aura-specific value (damage for TRIGGER_DAMAGE)
3483 ProcTriggeredList procTriggered
;
3485 AuraList
const& attackerAuras
= GetAurasByType(*aur
);
3486 for(AuraList::const_iterator i
= attackerAuras
.begin(), next
; i
!= attackerAuras
.end(); i
= next
)
3489 uint32 procFlag
= procAttacker
;
3491 SpellEntry
const *spellProto
= (*i
)->GetSpellProto();
3492 if(!spellProto
) continue;
3493 SpellProcEventEntry
const *spellProcEvent
= objmgr
.GetSpellProcEvent(spellProto
->Id
);
3495 if(!spellProcEvent
&& spellProto
->procFlags
!= 0 && nonHandledSpellProcSet
.find(spellProto
->Id
)==nonHandledSpellProcSet
.end())
3497 sLog
.outError("ProcDamageAndSpell: spell %u (attacker's aura source) not have record in `spell_proc_event`)",spellProto
->Id
);
3498 nonHandledSpellProcSet
.insert(spellProto
->Id
);
3501 uint32 procFlags
= spellProcEvent
? spellProcEvent
->procFlags
: spellProto
->procFlags
;
3502 // Check if current equipment allows aura to proc
3503 if(GetTypeId() == TYPEID_PLAYER
&& ((Player
*)this)->IsUseEquipedWeapon())
3505 if(spellProto
->EquippedItemClass
== ITEM_CLASS_WEAPON
)
3508 if(attType
== BASE_ATTACK
)
3509 item
= ((Player
*)this)->GetItemByPos(INVENTORY_SLOT_BAG_0
, EQUIPMENT_SLOT_MAINHAND
);
3510 else if (attType
== OFF_ATTACK
)
3511 item
= ((Player
*)this)->GetItemByPos(INVENTORY_SLOT_BAG_0
, EQUIPMENT_SLOT_OFFHAND
);
3513 item
= ((Player
*)this)->GetItemByPos(INVENTORY_SLOT_BAG_0
, EQUIPMENT_SLOT_RANGED
);
3515 if(!item
|| item
->IsBroken() || item
->GetProto()->Class
!= ITEM_CLASS_WEAPON
|| !((1<<item
->GetProto()->SubClass
) & spellProto
->EquippedItemSubClassMask
))
3518 else if(spellProto
->EquippedItemClass
== ITEM_CLASS_ARMOR
)
3520 // Check if player is wearing shield
3521 Item
*item
= ((Player
*)this)->GetItemByPos(INVENTORY_SLOT_BAG_0
, EQUIPMENT_SLOT_OFFHAND
);
3522 if(!item
|| item
->IsBroken() || item
->GetProto()->Class
!= ITEM_CLASS_ARMOR
|| !((1<<item
->GetProto()->SubClass
) & spellProto
->EquippedItemSubClassMask
))
3526 if((procFlag
& procFlags
) == 0)
3529 // Additional checks in case spell cast/hit/crit is the event
3530 // Check (if set) school, category, skill line, spell talent mask
3533 if(spellProcEvent
->schoolMask
&& (!procSpell
|| !procSpell
->School
|| ((1<<procSpell
->School
) & spellProcEvent
->schoolMask
) == 0))
3535 if(spellProcEvent
->category
&& (!procSpell
|| procSpell
->Category
!= spellProcEvent
->category
))
3537 if(spellProcEvent
->skillId
)
3539 if (!procSpell
) continue;
3540 SkillLineAbilityEntry
const *skillLineEntry
= sSkillLineAbilityStore
.LookupEntry(procSpell
->Id
);
3541 if(!skillLineEntry
|| skillLineEntry
->skillId
!= spellProcEvent
->skillId
)
3544 if(spellProcEvent
->spellFamilyName
&& (!procSpell
|| spellProcEvent
->spellFamilyName
!= procSpell
->SpellFamilyName
))
3546 if(spellProcEvent
->spellFamilyMask
&& (!procSpell
|| (spellProcEvent
->spellFamilyMask
& procSpell
->SpellFamilyFlags
) == 0))
3550 // Need to use floats here, cuz calculated PPM chance often is about 1-2%
3551 float chance
= (float)spellProto
->procChance
;
3552 if(GetTypeId() == TYPEID_PLAYER
)
3553 ((Player
*)this)->ApplySpellMod(spellProto
->Id
,SPELLMOD_CHANCE_OF_SUCCESS
,chance
);
3554 uint32 WeaponSpeed
= GetAttackTime(attType
);
3555 if(spellProcEvent
&& spellProcEvent
->ppmRate
!= 0)
3556 chance
= GetPPMProcChance(WeaponSpeed
, spellProcEvent
->ppmRate
);
3558 if(roll_chance_f(chance
))
3560 if((*i
)->m_procCharges
> 0)
3561 (*i
)->m_procCharges
-= 1;
3563 uint32 i_spell_eff
= (*i
)->GetEffIndex();
3565 int32 i_spell_param
;
3568 case SPELL_AURA_PROC_TRIGGER_SPELL
: i_spell_param
= procFlag
; break;
3569 case SPELL_AURA_DUMMY
: i_spell_param
= i_spell_eff
; break;
3570 default: i_spell_param
= (*i
)->GetModifier()->m_amount
; break;
3573 procTriggered
.push_back( ProcTriggeredData(spellProto
,i_spell_param
,*i
) );
3577 // Handle effects proceed this time
3578 for(ProcTriggeredList::iterator i
= procTriggered
.begin(); i
!= procTriggered
.end(); i
++)
3580 if(*aur
== SPELL_AURA_PROC_TRIGGER_SPELL
)
3582 sLog
.outDebug("ProcDamageAndSpell: casting spell %u (triggered by an attacker's aura of spell %u)", i
->spellInfo
->Id
,i
->triggeredByAura
->GetId());
3583 HandleProcTriggerSpell(pVictim
, damage
, i
->triggeredByAura
, procSpell
,i
->spellParam
);
3585 else if(*aur
== SPELL_AURA_PROC_TRIGGER_DAMAGE
)
3587 sLog
.outDebug("ProcDamageAndSpell: doing %u damage from spell id %u (triggered by an attacker's aura of spell %u)", i
->spellParam
, i
->spellInfo
->Id
,i
->triggeredByAura
->GetId());
3588 uint32 damage
= i
->spellParam
;
3589 // TODO: remove hack for Seal of Righteousness. That should not be there
3590 if(i
->spellInfo
->SpellVisual
== 7986)
3591 damage
= (damage
* GetAttackTime(BASE_ATTACK
))/60/1000;
3592 if(pVictim
&& pVictim
->isAlive())
3593 SpellNonMeleeDamageLog(pVictim
, i
->spellInfo
->Id
, damage
, true, true);
3595 else if(*aur
== SPELL_AURA_DUMMY
)
3597 // TODO: write a DUMMY aura handle code
3598 if (pVictim
&& pVictim
->isAlive())
3600 sLog
.outDebug("ProcDamageAndSpell: casting spell id %u (triggered by an attacker dummy aura of spell %u)", i
->spellInfo
->Id
,i
->triggeredByAura
->GetId());
3601 HandleDummyAuraProc(pVictim
, i
->spellInfo
, i
->spellParam
, damage
, i
->triggeredByAura
, procSpell
, procAttacker
);
3606 // Safely remove attacker auras with zero charges
3607 for(AuraList::const_iterator i
= attackerAuras
.begin(), next
; i
!= attackerAuras
.end(); i
= next
)
3610 if((*i
)->m_procCharges
== 0)
3612 RemoveAurasDueToSpell((*i
)->GetId());
3613 next
= attackerAuras
.begin();
3619 // Now go on with a victim's events'n'auras
3620 // Not much to do if no flags are set or there is no victim
3621 if(pVictim
&& pVictim
->isAlive() && procVictim
)
3623 for(std::set
<uint32
>::iterator aur
= victimProcAuraTypes
.begin(); aur
!= victimProcAuraTypes
.end(); aur
++)
3625 // List of spells (effects) that proceed. Spell prototype and aura-specific value (damage for TRIGGER_DAMAGE)
3626 ProcTriggeredList procTriggered
;
3628 AuraList
const& victimAuras
= pVictim
->GetAurasByType(*aur
);
3629 for(AuraList::const_iterator i
= victimAuras
.begin(), next
; i
!= victimAuras
.end(); i
= next
)
3632 uint32 procFlag
= procVictim
;
3634 SpellEntry
const *spellProto
= (*i
)->GetSpellProto();
3635 if(!spellProto
) continue;
3636 SpellProcEventEntry
const *spellProcEvent
= objmgr
.GetSpellProcEvent(spellProto
->Id
);
3638 if(!spellProcEvent
&& spellProto
->procFlags
!= 0 && nonHandledSpellProcSet
.find(spellProto
->Id
)==nonHandledSpellProcSet
.end())
3640 sLog
.outError("ProcDamageAndSpell: spell %u (victim's aura source) not have record in `spell_proc_event`)",spellProto
->Id
);
3641 nonHandledSpellProcSet
.insert(spellProto
->Id
);
3644 uint32 procFlags
= spellProcEvent
? spellProcEvent
->procFlags
: spellProto
->procFlags
;
3645 if((procFlag
& procFlags
) == 0)
3648 // Additional checks in case spell cast/hit/crit is the event
3649 // Check (if set) school, category, skill line, spell talent mask
3652 if(spellProcEvent
->schoolMask
&& (!procSpell
|| !procSpell
->School
|| ((1<<procSpell
->School
) & spellProcEvent
->schoolMask
) == 0))
3654 if(spellProcEvent
->category
&& (!procSpell
|| procSpell
->Category
!= spellProcEvent
->category
))
3656 if(spellProcEvent
->skillId
)
3658 if (!procSpell
) continue;
3659 SkillLineAbilityEntry
const *skillLineEntry
= sSkillLineAbilityStore
.LookupEntry(procSpell
->Id
);
3660 if(!skillLineEntry
|| skillLineEntry
->skillId
!= spellProcEvent
->skillId
)
3663 if(spellProcEvent
->spellFamilyName
&& (!procSpell
|| spellProcEvent
->spellFamilyName
!= procSpell
->SpellFamilyName
))
3665 if(spellProcEvent
->spellFamilyMask
&& (!procSpell
|| (spellProcEvent
->spellFamilyMask
& procSpell
->SpellFamilyFlags
) == 0))
3669 // procChance is exact number in percents anyway
3670 uint32 chance
= spellProto
->procChance
;
3671 if(pVictim
->GetTypeId() == TYPEID_PLAYER
)
3672 ((Player
*)pVictim
)->ApplySpellMod(spellProto
->Id
,SPELLMOD_CHANCE_OF_SUCCESS
,chance
);
3673 if(roll_chance_i(chance
))
3675 if((*i
)->m_procCharges
> 0)
3676 (*i
)->m_procCharges
-= 1;
3678 uint32 i_spell_eff
= (*i
)->GetEffIndex();
3679 int32 i_spell_param
;
3682 case SPELL_AURA_PROC_TRIGGER_SPELL
: i_spell_param
= procFlag
; break;
3683 case SPELL_AURA_DUMMY
: i_spell_param
= i_spell_eff
; break;
3684 default: i_spell_param
= (*i
)->GetModifier()->m_amount
; break;
3687 procTriggered
.push_back( ProcTriggeredData(spellProto
,i_spell_param
,*i
) );
3691 // Handle effects proced this time
3692 for(ProcTriggeredList::iterator i
= procTriggered
.begin(); i
!= procTriggered
.end(); i
++)
3694 if(*aur
== SPELL_AURA_PROC_TRIGGER_SPELL
)
3696 sLog
.outDebug("ProcDamageAndSpell: casting spell %u (triggered by a victim's aura of spell %u))",i
->spellInfo
->Id
, i
->triggeredByAura
);
3697 pVictim
->HandleProcTriggerSpell(this, damage
, i
->triggeredByAura
, procSpell
,i
->spellParam
);
3699 else if(*aur
== SPELL_AURA_PROC_TRIGGER_DAMAGE
)
3701 sLog
.outDebug("ProcDamageAndSpell: doing %u damage from spell id %u (triggered by a victim's aura of spell %u))", i
->spellParam
, i
->spellInfo
->Id
, i
->triggeredByAura
);
3702 pVictim
->SpellNonMeleeDamageLog(this, i
->spellInfo
->Id
, i
->spellParam
, true, true);
3704 else if(*aur
== SPELL_AURA_DUMMY
)
3706 // TODO: write a DUMMY aura handle code
3707 sLog
.outDebug("ProcDamageAndSpell: casting spell %u (triggered by a victim's dummy aura of spell %u))",i
->spellInfo
->Id
, i
->triggeredByAura
);
3708 pVictim
->HandleDummyAuraProc(this, i
->spellInfo
, i
->spellParam
, damage
, i
->triggeredByAura
, procSpell
, procVictim
);
3712 // Safely remove auras with zero charges
3713 for(AuraList::const_iterator i
= victimAuras
.begin(), next
; i
!= victimAuras
.end(); i
= next
)
3716 if((*i
)->m_procCharges
== 0)
3718 pVictim
->RemoveAurasDueToSpell((*i
)->GetId());
3719 next
= victimAuras
.begin();
3726 void Unit::CastMeleeProcDamageAndSpell(Unit
* pVictim
, uint32 damage
, WeaponAttackType attType
, MeleeHitOutcome outcome
, SpellEntry
const *spellCasted
, bool isTriggeredSpell
)
3731 uint32 procAttacker
= PROC_FLAG_NONE
;
3732 uint32 procVictim
= PROC_FLAG_NONE
;
3736 case MELEE_HIT_MISS
:
3737 if(attType
== BASE_ATTACK
|| attType
== OFF_ATTACK
)
3739 procAttacker
= PROC_FLAG_MISS
;
3742 case MELEE_HIT_CRIT
:
3743 if(spellCasted
&& attType
== BASE_ATTACK
)
3745 procAttacker
|= PROC_FLAG_CRIT_SPELL
;
3746 procVictim
|= PROC_FLAG_STRUCK_CRIT_SPELL
;
3748 else if(attType
== BASE_ATTACK
|| attType
== OFF_ATTACK
)
3750 procAttacker
= PROC_FLAG_HIT_MELEE
| PROC_FLAG_CRIT_MELEE
;
3751 procVictim
= PROC_FLAG_STRUCK_MELEE
| PROC_FLAG_STRUCK_CRIT_MELEE
;
3755 procAttacker
= PROC_FLAG_HIT_RANGED
| PROC_FLAG_CRIT_RANGED
;
3756 procVictim
= PROC_FLAG_STRUCK_RANGED
| PROC_FLAG_STRUCK_CRIT_RANGED
;
3759 case MELEE_HIT_PARRY
:
3760 procAttacker
= PROC_FLAG_TARGET_DODGE_OR_PARRY
;
3761 procVictim
= PROC_FLAG_PARRY
;
3763 case MELEE_HIT_BLOCK
:
3764 procAttacker
= PROC_FLAG_TARGET_BLOCK
;
3765 procVictim
= PROC_FLAG_BLOCK
;
3767 case MELEE_HIT_DODGE
:
3768 procAttacker
= PROC_FLAG_TARGET_DODGE_OR_PARRY
;
3769 procVictim
= PROC_FLAG_DODGE
;
3771 case MELEE_HIT_CRUSHING
:
3772 if(attType
== BASE_ATTACK
|| attType
== OFF_ATTACK
)
3774 procAttacker
= PROC_FLAG_HIT_MELEE
| PROC_FLAG_CRIT_MELEE
;
3775 procVictim
= PROC_FLAG_STRUCK_MELEE
| PROC_FLAG_STRUCK_CRIT_MELEE
;
3779 procAttacker
= PROC_FLAG_HIT_RANGED
| PROC_FLAG_CRIT_RANGED
;
3780 procVictim
= PROC_FLAG_STRUCK_RANGED
| PROC_FLAG_STRUCK_CRIT_RANGED
;
3784 if(attType
== BASE_ATTACK
|| attType
== OFF_ATTACK
)
3786 procAttacker
= PROC_FLAG_HIT_MELEE
;
3787 procVictim
= PROC_FLAG_STRUCK_MELEE
;
3791 procAttacker
= PROC_FLAG_HIT_RANGED
;
3792 procVictim
= PROC_FLAG_STRUCK_RANGED
;
3798 procVictim
|= PROC_FLAG_TAKE_DAMAGE
;
3800 if(procAttacker
!= PROC_FLAG_NONE
|| procVictim
!= PROC_FLAG_NONE
)
3801 ProcDamageAndSpell(pVictim
, procAttacker
, procVictim
, damage
, spellCasted
, isTriggeredSpell
, attType
);
3804 void Unit::HandleDummyAuraProc(Unit
*pVictim
, SpellEntry
const *dummySpell
, uint32 effIndex
, uint32 damage
, Aura
* triggredByAura
, SpellEntry
const * procSpell
, uint32 procFlag
)
3806 switch(dummySpell
->Id
)
3818 int32 igniteDotBasePoints0
;
3820 switch (dummySpell
->Id
)
3822 case 11119: igniteDotBasePoints0
=int32(0.04f
*damage
)-1; break;
3823 case 11120: igniteDotBasePoints0
=int32(0.08f
*damage
)-1; break;
3824 case 12846: igniteDotBasePoints0
=int32(0.12f
*damage
)-1; break;
3825 case 12847: igniteDotBasePoints0
=int32(0.16f
*damage
)-1; break;
3826 case 12848: igniteDotBasePoints0
=int32(0.20f
*damage
)-1; break;
3828 sLog
.outError("Unit::HandleDummyAuraProc: non handled spell id: %u (IG)",dummySpell
->Id
);
3831 CastCustomSpell(pVictim
, 12654, &igniteDotBasePoints0
, NULL
, NULL
, true, NULL
, triggredByAura
);
3838 CastSpell(this, 28682, true, NULL
, triggredByAura
);
3839 if (!(procFlag
& PROC_FLAG_CRIT_SPELL
)) //no crit
3840 triggredByAura
->m_procCharges
+= 1; //-> reincrease procCharge count since it was decreased before
3841 else if (triggredByAura
->m_procCharges
== 0) //no more charges left and crit
3842 RemoveAurasDueToSpell(28682); //-> remove Combustion auras
3849 CastSpell(this, 17941, true, NULL
, triggredByAura
);
3858 if(triggredByAura
->GetCasterGUID() == pVictim
->GetGUID())
3860 //VEHeal has a BaseDice of 0, so no decrement needed
3861 int32 VEHealBasePoints0
= triggredByAura
->GetModifier()->m_amount
*damage
/100;
3862 pVictim
->CastCustomSpell(pVictim
, 15290, &VEHealBasePoints0
, NULL
, NULL
, true, NULL
, triggredByAura
);
3873 // return damage % to attacker but < 50% own total health
3874 uint32 backDamage
= triggredByAura
->GetModifier()->m_amount
*damage
/100;
3875 if(backDamage
> GetMaxHealth()/2)
3876 backDamage
= GetMaxHealth()/2;
3878 int32 YYDamageBasePoints0
= backDamage
-1;
3879 CastCustomSpell(pVictim
, 25997, &YYDamageBasePoints0
, NULL
, NULL
, true, NULL
, triggredByAura
);
3894 // we assume lightning bolt and chain lightning are generic (not channeled/autorepeat) spells
3895 if(!pVictim
|| !m_currentSpells
[CURRENT_GENERIC_SPELL
])
3898 // remove cooldown from first cast
3899 if(GetTypeId()==TYPEID_PLAYER
)
3900 ((Player
*)this)->RemoveSpellCooldown(procSpell
->Id
);
3901 // prepare cast as triggered spell (this need for correct targets selection after not finished currently cast)
3902 m_currentSpells
[CURRENT_GENERIC_SPELL
]->AddTriggeredSpell(procSpell
);
3912 // if healed by another unit (pVictim)
3915 int32 SAHealBasePoints0
= triggredByAura
->GetModifier()->m_amount
*damage
/100-1;
3916 CastCustomSpell(this, 31786, &SAHealBasePoints0
, NULL
, NULL
, true, NULL
, triggredByAura
);
3922 // Shadowflame (item set effect)
3925 if(GetTypeId() != TYPEID_PLAYER
)
3928 Item
* castItem
= ((Player
*)this)->GetItemByGuid(triggredByAura
->GetCastItemGUID());
3932 CastSpell(pVictim
,37379,true,castItem
,triggredByAura
);
3935 // Shadowflame Hellfire (item set effect)
3938 if(GetTypeId() != TYPEID_PLAYER
)
3941 Item
* castItem
= ((Player
*)this)->GetItemByGuid(triggredByAura
->GetCastItemGUID());
3945 CastSpell(pVictim
,37378,true,castItem
,triggredByAura
);
3952 switch(dummySpell
->SpellFamilyName
)
3954 case SPELLFAMILY_SHAMAN
:
3955 if(dummySpell
->SpellFamilyFlags
==0x40000000000LL
)
3957 int32 HealBasePoints0
= dummySpell
->EffectBasePoints
[0];
3958 CastCustomSpell(this,379,&HealBasePoints0
,NULL
,NULL
,true,NULL
,triggredByAura
);
3966 // Non SpellID checks
3967 switch(dummySpell
->SpellIconID
)
3969 // Master of Elements
3975 if(dummySpell
->SpellFamilyName
!=SPELLFAMILY_MAGE
)
3978 int32 MEManaCostSave
= procSpell
->manaCost
* triggredByAura
->GetModifier()->m_amount
/100;
3979 if(MEManaCostSave
<= 0)
3981 int32 MEManaRestoreBasePoints0
= MEManaCostSave
-1;
3982 CastCustomSpell(this,29077,&MEManaRestoreBasePoints0
,NULL
,NULL
,true,NULL
, triggredByAura
);
3992 if(triggredByAura
->GetCasterGUID() == pVictim
->GetGUID())
3994 int32 VTEnergizeBasePoints0
= triggredByAura
->GetModifier()->m_amount
*damage
/100 - 1;
3995 pVictim
->CastCustomSpell(pVictim
,34919,&VTEnergizeBasePoints0
,NULL
,NULL
,true,NULL
, triggredByAura
);
4005 if(dummySpell
->SpellFamilyName
!=SPELLFAMILY_ROGUE
)
4008 // only rogue's finishing moves (maybe need additional checks)
4009 if( procSpell
->SpellFamilyName
!=SPELLFAMILY_ROGUE
||
4010 (procSpell
->SpellFamilyFlags
& (0x40000 | 0x80000 | 0x100000 | 0x200000 | 0x800000000LL
| 0x20000)) == 0)
4013 int32 QREnegyCostSave
= procSpell
->manaCost
* triggredByAura
->GetModifier()->m_amount
/100;
4014 if(QREnegyCostSave
<= 0)
4016 int32 QREnergizeBasePoints0
= QREnegyCostSave
-1;
4017 CastCustomSpell(this,31663,&QREnergizeBasePoints0
,NULL
,NULL
,true,NULL
, triggredByAura
);
4021 // Thrill of the Hunt
4027 if(dummySpell
->SpellFamilyName
!=SPELLFAMILY_HUNTER
)
4030 int32 THManaCostSave
= procSpell
->manaCost
* 40/100;
4031 if(THManaCostSave
<= 0)
4033 int32 THEnergizeBasePoints0
= THManaCostSave
-1;
4034 CastCustomSpell(this,34720,&THEnergizeBasePoints0
,NULL
,NULL
,true,NULL
, triggredByAura
);
4041 void Unit::HandleProcTriggerSpell(Unit
*pVictim
, uint32 damage
, Aura
* triggeredByAura
, SpellEntry
const *procSpell
, uint32 procFlags
)
4043 SpellEntry
const* auraSpellInfo
= triggeredByAura
->GetSpellProto();
4045 switch(auraSpellInfo
->SpellIconID
)
4049 switch(auraSpellInfo
->SpellFamilyName
)
4051 case SPELLFAMILY_SHAMAN
:
4053 //Lightning Shield (overwrite non existing triggered spell call in spell.dbc
4054 if(auraSpellInfo
->SpellFamilyFlags
==0x00000400)
4060 switch(triggeredByAura
->GetSpellProto()->Id
)
4063 case 324: spell
= 26364; break;
4065 case 325: spell
= 26365; break;
4067 case 905: spell
= 26366; break;
4069 case 945: spell
= 26367; break;
4071 case 8134: spell
= 26369; break;
4073 case 10431: spell
= 26370; break;
4075 case 10432: spell
= 26363; break;
4077 case 25469: spell
= 26371; break;
4079 case 25472: spell
= 26372; break;
4081 sLog
.outError("Unit::HandleProcTriggerSpell: Spell %u not handled in LShield",triggeredByAura
->GetSpellProto()->Id
);
4084 CastSpell(pVictim
, spell
, true, NULL
, triggeredByAura
);
4089 case SPELLFAMILY_PRIEST
:
4091 // Priest's "Shadowguard"
4092 if(auraSpellInfo
->SpellFamilyFlags
==0x100080000000LL
)
4098 switch(triggeredByAura
->GetSpellProto()->Id
)
4101 case 18137: spell
= 28377; break;
4103 case 19308: spell
= 28378; break;
4105 case 19309: spell
= 28379; break;
4107 case 19310: spell
= 28380; break;
4109 case 19311: spell
= 28381; break;
4111 case 19312: spell
= 28382; break;
4113 case 25477: spell
= 28385; break;
4115 sLog
.outError("Unit::HandleProcTriggerSpell: Spell %u not handled in SG",triggeredByAura
->GetSpellProto()->Id
);
4118 CastSpell(pVictim
, spell
, true, NULL
, triggeredByAura
);
4128 //Mana Surge (Shaman T1 bonus)
4133 int32 manaSurgeSpellBasePoints0
= procSpell
->manaCost
* 35/100;
4134 CastCustomSpell(this, 23571, &manaSurgeSpellBasePoints0
, NULL
, NULL
, true, NULL
, triggeredByAura
);
4139 //Improved Drain Soul
4141 Unit::AuraList
const& mAddFlatModifier
= GetAurasByType(SPELL_AURA_ADD_FLAT_MODIFIER
);
4142 for(Unit::AuraList::const_iterator i
= mAddFlatModifier
.begin(); i
!= mAddFlatModifier
.end(); ++i
)
4144 if ((*i
)->GetModifier()->m_miscvalue
== SPELLMOD_CHANCE_OF_SUCCESS
&& (*i
)->GetSpellProto()->SpellIconID
== 113)
4146 int32 impDrainSoulBasePoints0
= (*i
)->GetSpellProto()->EffectBasePoints
[2] * GetMaxPower(POWER_MANA
) / 100;
4147 CastCustomSpell(this, 18371, &impDrainSoulBasePoints0
, NULL
, NULL
, true, NULL
, triggeredByAura
);
4154 switch(auraSpellInfo
->EffectTriggerSpell
[0])
4162 SpellEntry
const *originalSpell
= procSpell
;
4164 // in case HShock procspell is triggered spell but we need mana cost of original casted spell
4165 if(procSpell
->SpellFamilyName
== SPELLFAMILY_PALADIN
&& procSpell
->SpellFamilyFlags
== 0x00200000)
4167 uint32 originalSpellId
= 0;
4168 switch(procSpell
->Id
)
4170 case 25914: originalSpellId
= 20473; break;
4171 case 25913: originalSpellId
= 20929; break;
4172 case 25903: originalSpellId
= 20930; break;
4173 case 27175: originalSpellId
= 27174; break;
4174 case 33074: originalSpellId
= 33072; break;
4176 sLog
.outError("Unit::HandleProcTriggerSpell: Spell %u not handled in HShock",procSpell
->Id
);
4179 SpellEntry
const *HSSpell
= sSpellStore
.LookupEntry(originalSpellId
);
4182 sLog
.outError("Unit::HandleProcTriggerSpell: Spell %u unknown but used in HShock",originalSpellId
);
4185 originalSpell
= HSSpell
;
4188 // percent stored in effect 1 (class scripts) base points
4189 int32 percent
= auraSpellInfo
->EffectBasePoints
[1]+1;
4191 // BasePoints = val -1 not required (EffectBaseDice==0)
4192 int32 ILManaSpellBasePoints0
= originalSpell
->manaCost
*percent
/100;
4193 CastCustomSpell(this, 20272, &ILManaSpellBasePoints0
, NULL
, NULL
, true, NULL
, triggeredByAura
);
4201 //Improved Leader of the Pack
4204 if (triggeredByAura
->GetModifier()->m_amount
== 0)
4206 int32 improvedLotPBasePoints0
= triggeredByAura
->GetModifier()->m_amount
* GetMaxHealth() / 100 - 1;
4207 CastCustomSpell(this, 34299, &improvedLotPBasePoints0
, NULL
, NULL
, true, NULL
, triggeredByAura
);
4208 if (GetTypeId() == TYPEID_PLAYER
)
4209 ((Player
*)this)->AddSpellCooldown(34299,0,time(NULL
) + 6);
4217 switch (triggeredByAura
->GetSpellProto()->Id
)
4226 if (pVictim
&& pVictim
->isAlive() && roll_chance_f(chance
))
4227 CastSpell(pVictim
, 18093, true, NULL
, triggeredByAura
);
4233 uint32 EffectId
= 0;
4234 switch (triggeredByAura
->GetSpellProto()->Id
)
4236 case 27811: EffectId
= 27813; break;
4237 case 27815: EffectId
= 27817; break;
4238 case 27816: EffectId
= 27818; break;
4240 sLog
.outError("Unit::HandleProcTriggerSpell: Spell %u not handled in BR",triggeredByAura
->GetSpellProto()->Id
);
4244 int32 heal_amount
= damage
* triggeredByAura
->GetModifier()->m_amount
/ 100;
4245 int32 BRHealBasePoints0
= heal_amount
/3-1;
4246 CastCustomSpell(this, EffectId
, &BRHealBasePoints0
, NULL
, NULL
, true, NULL
, triggeredByAura
);
4251 //Effects: 30029(Rank 1), 30031(Rank 2), 30032(Rank 3)
4252 //Check EffectTriggerSpell[1] to determine correct effect id
4253 //CastSpell(this, triggredByAura->GetSpellProto()->EffectTriggerSpell[1], true, NULL, triggredByAura);
4258 //Effects: 31616, 39301
4260 /*float HealthRatio = GetHealth() / GetMaxHealth();
4261 float HealthRatioBefore = (GetHealth() + damage) / GetMaxHealth();
4262 if (HealthRatio < 0.3 && HealthRatioBefore >= 0.3)
4264 SpellEntry const *NGHealTemplate = sSpellStore.LookupEntry(31616);
4265 SpellEntry NGHeal = *NGHealTemplate;
4266 NGHeal.EffectBasePoints[0] = triggredByAura->GetModifier()->m_amount * GetMaxHealth() / 100;
4267 CastSpell(this, &NGHeal, true, NULL, triggredByAura);
4268 if (pVictim && pVictim->isAlive())
4269 CastSpell(pVictim, 39301, true, NULL, triggredByAura);
4270 if (GetTypeId() == TYPEID_PLAYER)
4272 ((Player*)this)->AddSpellCooldown(31616,0,time(NULL) + 5);
4273 ((Player*)this)->AddSpellCooldown(39301,0,time(NULL) + 5);
4281 CastSpell(this, 31643, true, NULL
, triggeredByAura
);
4285 // custom check for proc spell
4286 switch(auraSpellInfo
->Id
)
4288 // Lightning Capacitor
4295 CastSpell(this, 37658, true, NULL
, triggeredByAura
);
4299 AuraList
const& dummyAura
= GetAurasByType(SPELL_AURA_DUMMY
);
4300 for(AuraList::const_iterator itr
= dummyAura
.begin(); itr
!= dummyAura
.end(); ++itr
)
4301 if((*itr
)->GetId()==37658)
4304 // release at 3 aura in stack
4307 RemoveAurasDueToSpell(37658);
4308 CastSpell(pVictim
, 37661, true, NULL
, triggeredByAura
);
4314 // standard non-dummy case
4315 uint32 trigger_spell_id
= auraSpellInfo
->EffectTriggerSpell
[triggeredByAura
->GetEffIndex()];
4316 if(!trigger_spell_id
)
4318 sLog
.outError("Unit::HandleProcTriggerSpell: Spell %u have 0 in EffectTriggered[%d], not handled custom case?",auraSpellInfo
->Id
,triggeredByAura
->GetEffIndex());
4322 // but with dummy basepoints or other customs
4323 switch(trigger_spell_id
)
4325 // Shamanistic Rage triggered spell
4328 int32 SRBasePoints0
= int32(GetTotalAttackPowerValue(BASE_ATTACK
)*triggeredByAura
->GetModifier()->m_amount
/100) -1;
4329 CastCustomSpell(this, 30824, &SRBasePoints0
, NULL
, NULL
, true, NULL
, triggeredByAura
);
4332 // Backlash triggered spell
4335 // need set custom cooldown
4336 if(isAlive() && GetTypeId()==TYPEID_PLAYER
&& !((Player
*)this)->HasSpellCooldown(34936))
4338 CastSpell(this,trigger_spell_id
,true,NULL
,triggeredByAura
);
4339 ((Player
*)this)->AddSpellCooldown(34936,0,time(NULL
)+8);
4346 if(IsPositiveSpell(trigger_spell_id
) && !(procFlags
& PROC_FLAG_HEAL
))
4347 CastSpell(this,trigger_spell_id
,true,NULL
,triggeredByAura
);
4348 else if(pVictim
&& pVictim
->isAlive())
4349 CastSpell(pVictim
,trigger_spell_id
,true,NULL
,triggeredByAura
);
4352 void Unit::setPowerType(Powers new_powertype
)
4354 uint32 tem_bytes_0
= GetUInt32Value(UNIT_FIELD_BYTES_0
);
4355 SetUInt32Value(UNIT_FIELD_BYTES_0
,((tem_bytes_0
<<8)>>8) + (uint32(new_powertype
)<<24));
4357 if (GetTypeId() == TYPEID_PLAYER
)
4358 ((Player
*)this)->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_POWER_TYPE
);
4360 switch(new_powertype
)
4366 SetMaxPower(POWER_RAGE
,GetCreatePowers(POWER_RAGE
));
4367 SetPower( POWER_RAGE
,0);
4370 SetMaxPower(POWER_FOCUS
,GetCreatePowers(POWER_FOCUS
));
4371 SetPower( POWER_FOCUS
,GetCreatePowers(POWER_FOCUS
));
4374 SetMaxPower(POWER_ENERGY
,GetCreatePowers(POWER_ENERGY
));
4375 SetPower( POWER_ENERGY
,0);
4377 case POWER_HAPPINESS
:
4378 SetMaxPower(POWER_HAPPINESS
,GetCreatePowers(POWER_HAPPINESS
));
4379 SetPower(POWER_HAPPINESS
,GetCreatePowers(POWER_HAPPINESS
));
4384 FactionTemplateEntry
const* Unit::getFactionTemplateEntry() const
4386 FactionTemplateEntry
const* entry
= sFactionTemplateStore
.LookupEntry(getFaction());
4389 static uint64 guid
= 0; // prevent repeating spam same faction problem
4391 if(GetGUID() != guid
)
4393 if(GetTypeId() == TYPEID_PLAYER
)
4394 sLog
.outError("Player %s have invalid faction (faction template id) #%u", ((Player
*)this)->GetName(), getFaction());
4396 sLog
.outError("Creature (template id: %u) have invalid faction (faction template id) #%u", ((Creature
*)this)->GetCreatureInfo()->Entry
, getFaction());
4403 bool Unit::IsHostileTo(Unit
const* unit
) const
4405 // always non-hostile to self
4409 // always hostile to enemy
4410 if(getVictim()==unit
|| unit
->getVictim()==this)
4413 // test pet/charm masters instead pers/charmeds
4414 Unit
const* testerOwner
= GetCharmerOrOwner();
4415 Unit
const* targetOwner
= unit
->GetCharmerOrOwner();
4417 // always hostile to owner's enemy
4418 if(testerOwner
&& (testerOwner
->getVictim()==unit
|| unit
->getVictim()==testerOwner
))
4421 // always hostile to enemy owner
4422 if(targetOwner
&& (getVictim()==targetOwner
|| targetOwner
->getVictim()==this))
4425 // always hostile to owner of owner's enemy
4426 if(testerOwner
&& targetOwner
&& (testerOwner
->getVictim()==targetOwner
|| targetOwner
->getVictim()==testerOwner
))
4429 Unit
const* tester
= testerOwner
? testerOwner
: this;
4430 Unit
const* target
= targetOwner
? targetOwner
: unit
;
4432 // special cases (Duel, etc)
4433 if(tester
->GetTypeId()==TYPEID_PLAYER
&& target
->GetTypeId()==TYPEID_PLAYER
)
4436 if(((Player
const*)tester
)->duel
&& ((Player
const*)tester
)->duel
->opponent
== target
&& ((Player
const*)tester
)->duel
->startTime
!= 0)
4440 // Green/Blue (can't attack)
4441 if(((Player
*)tester
)->GetTeam()==((Player
*)target
)->GetTeam())
4444 // Red (can attack) if true, Blue/Yellow (can't attack) in another case
4445 return ((Player
*)tester
)->IsPvP() && ((Player
*)target
)->IsPvP();
4448 // faction base cases
4449 FactionTemplateEntry
const*tester_faction
= tester
->getFactionTemplateEntry();
4450 FactionTemplateEntry
const*target_faction
= target
->getFactionTemplateEntry();
4451 if(!tester_faction
|| !target_faction
)
4454 // PvC forced reaction and reputation case
4455 if(tester
->GetTypeId()==TYPEID_PLAYER
)
4458 ForcedReactions::const_iterator forceItr
= ((Player
*)tester
)->m_forcedReactions
.find(target_faction
->faction
);
4459 if(forceItr
!=((Player
*)tester
)->m_forcedReactions
.end())
4461 return forceItr
->second
<= REP_HOSTILE
;
4464 // apply reputation state
4465 FactionEntry
const* raw_target_faction
= sFactionStore
.LookupEntry(target_faction
->faction
);
4466 if(raw_target_faction
&& raw_target_faction
->reputationListID
>=0 )
4468 if(((Player
*)tester
)->IsFactionAtWar(raw_target_faction
))
4472 // CvP forced reaction and reputation case
4473 else if(target
->GetTypeId()==TYPEID_PLAYER
)
4476 ForcedReactions::const_iterator forceItr
= ((Player
*)target
)->m_forcedReactions
.find(tester_faction
->faction
);
4477 if(forceItr
!=((Player
*)target
)->m_forcedReactions
.end())
4479 return forceItr
->second
<= REP_HOSTILE
;
4482 // apply reputation state
4483 FactionEntry
const* raw_tester_faction
= sFactionStore
.LookupEntry(tester_faction
->faction
);
4484 if(raw_tester_faction
&& raw_tester_faction
->reputationListID
>=0 )
4486 return ((Player
*)target
)->GetReputationRank(raw_tester_faction
) <= REP_HOSTILE
;
4490 // common faction based case (CvC,PvC,CvP)
4491 return tester_faction
->IsHostileTo(*target_faction
);
4494 bool Unit::IsFriendlyTo(Unit
const* unit
) const
4496 // always friendly to self
4500 // always non-friendly to enemy
4501 if(getVictim()==unit
|| unit
->getVictim()==this)
4504 // test pet/charm masters instead pers/charmeds
4505 Unit
const* testerOwner
= GetCharmerOrOwner();
4506 Unit
const* targetOwner
= unit
->GetCharmerOrOwner();
4508 // always non-friendly to owner's enemy
4509 if(testerOwner
&& (testerOwner
->getVictim()==unit
|| unit
->getVictim()==testerOwner
))
4512 // always non-friendly to enemy owner
4513 if(targetOwner
&& (getVictim()==targetOwner
|| targetOwner
->getVictim()==this))
4516 // always non-friendly to owner of owner's enemy
4517 if(testerOwner
&& targetOwner
&& (testerOwner
->getVictim()==targetOwner
|| targetOwner
->getVictim()==testerOwner
))
4520 Unit
const* tester
= testerOwner
? testerOwner
: this;
4521 Unit
const* target
= targetOwner
? targetOwner
: unit
;
4523 // special cases (Duel)
4524 if(tester
->GetTypeId()==TYPEID_PLAYER
&& target
->GetTypeId()==TYPEID_PLAYER
)
4527 if(((Player
const*)tester
)->duel
&& ((Player
const*)tester
)->duel
->opponent
== target
&& ((Player
const*)tester
)->duel
->startTime
!= 0)
4531 // Green/Blue (non-attackable)
4532 if(((Player
*)tester
)->GetTeam()==((Player
*)target
)->GetTeam())
4535 // Blue (friendly/non-attackable) if not PVP, or Yellow/Red in another case (attackable)
4536 return !((Player
*)target
)->IsPvP();
4539 // faction base cases
4540 FactionTemplateEntry
const*tester_faction
= tester
->getFactionTemplateEntry();
4541 FactionTemplateEntry
const*target_faction
= target
->getFactionTemplateEntry();
4542 if(!tester_faction
|| !target_faction
)
4545 // PvC forced reaction and reputation case
4546 if(tester
->GetTypeId()==TYPEID_PLAYER
)
4549 ForcedReactions::const_iterator forceItr
= ((Player
*)tester
)->m_forcedReactions
.find(target_faction
->faction
);
4550 if(forceItr
!=((Player
*)tester
)->m_forcedReactions
.end())
4552 return forceItr
->second
>= REP_FRIENDLY
;
4555 // apply reputation state
4556 FactionEntry
const* raw_target_faction
= sFactionStore
.LookupEntry(target_faction
->faction
);
4557 if(raw_target_faction
&& raw_target_faction
->reputationListID
>=0 )
4559 if(((Player
*)tester
)->IsFactionAtWar(raw_target_faction
))
4563 // CvP forced reaction and reputation case
4564 else if(target
->GetTypeId()==TYPEID_PLAYER
)
4567 ForcedReactions::const_iterator forceItr
= ((Player
*)target
)->m_forcedReactions
.find(tester_faction
->faction
);
4568 if(forceItr
!=((Player
*)target
)->m_forcedReactions
.end())
4570 return forceItr
->second
>= REP_FRIENDLY
;
4573 // apply reputation state
4574 FactionEntry
const* raw_tester_faction
= sFactionStore
.LookupEntry(tester_faction
->faction
);
4575 if(raw_tester_faction
&& raw_tester_faction
->reputationListID
>=0 )
4577 return ((Player
*)target
)->GetReputationRank(raw_tester_faction
) >= REP_FRIENDLY
;
4581 // common faction based case (CvC,PvC,CvP)
4582 return tester_faction
->IsFriendlyTo(*target_faction
);
4585 bool Unit::IsHostileToPlayers() const
4587 FactionTemplateEntry
const* my_faction
= getFactionTemplateEntry();
4591 FactionEntry
const* raw_faction
= sFactionStore
.LookupEntry(my_faction
->faction
);
4592 if(raw_faction
&& raw_faction
->reputationListID
>=0 )
4595 return my_faction
->IsHostileToPlayers();
4598 bool Unit::IsNeutralToAll() const
4600 FactionTemplateEntry
const* my_faction
= getFactionTemplateEntry();
4604 FactionEntry
const* raw_faction
= sFactionStore
.LookupEntry(my_faction
->faction
);
4605 if(raw_faction
&& raw_faction
->reputationListID
>=0 )
4608 return my_faction
->IsNeutralToAll();
4611 bool Unit::Attack(Unit
*victim
, bool playerMeleeAttack
)
4613 if(!victim
|| victim
== this)
4616 // player don't must attack in mount state
4617 if(GetTypeId()==TYPEID_PLAYER
&& IsMounted())
4620 // anyone don't must attack GM in GM-mode
4621 if(victim
->GetTypeId()==TYPEID_PLAYER
&& ((Player
*)victim
)->isGameMaster())
4626 if (m_attacking
== victim
)
4632 SetUInt64Value(UNIT_FIELD_TARGET
, victim
->GetGUID());
4634 addUnitState(UNIT_STAT_ATTACKING
);
4636 m_attacking
= victim
;
4637 m_attacking
->_addAttacker(this);
4639 if(m_attacking
->GetTypeId()==TYPEID_UNIT
&& ((Creature
*)m_attacking
)->AI())
4640 ((Creature
*)m_attacking
)->AI()->AttackedBy(this);
4642 if( GetTypeId()==TYPEID_UNIT
&& !(((Creature
*)this)->isPet() || isCharmed()) )
4644 ((Creature
*)this)->CallAssistence();
4646 //if(!isAttackReady(BASE_ATTACK))
4647 //resetAttackTimer(BASE_ATTACK);
4649 // delay offhand weapon attack to next attack time
4650 if(haveOffhandWeapon())
4651 resetAttackTimer(OFF_ATTACK
);
4653 if(playerMeleeAttack
)
4654 SendAttackStart(victim
);
4659 bool Unit::AttackStop()
4664 Unit
* victim
= m_attacking
;
4666 m_attacking
->_removeAttacker(this);
4670 SetUInt64Value(UNIT_FIELD_TARGET
, 0);
4672 clearUnitState(UNIT_STAT_ATTACKING
);
4674 InterruptSpell(CURRENT_MELEE_SPELL
);
4676 if( GetTypeId()==TYPEID_UNIT
)
4678 // reset call assistance
4679 ((Creature
*)this)->SetNoCallAssistence(false);
4682 SendAttackStop(victim
);
4687 bool Unit::isAttackingPlayer() const
4691 if(getVictim()->GetTypeId() == TYPEID_PLAYER
)
4694 if(getVictim()->GetOwnerGUID() && GUID_HIPART(getVictim()->GetOwnerGUID())==HIGHGUID_PLAYER
)
4698 Pet
* pet
= GetPet();
4699 if(pet
&& pet
->isAttackingPlayer())
4702 Unit
* charmed
= GetCharm();
4703 if(charmed
&& charmed
->isAttackingPlayer())
4706 for (int8 i
= 0; i
< 4; i
++)
4710 Creature
*totem
= ObjectAccessor::Instance().GetCreature(*this, m_TotemSlot
[i
]);
4711 if(totem
&& totem
->isAttackingPlayer())
4719 void Unit::RemoveAllAttackers()
4721 while (m_attackers
.size() != 0)
4723 AttackerSet::iterator iter
= m_attackers
.begin();
4724 if(!(*iter
)->AttackStop())
4726 sLog
.outError("WORLD: Unit has an attacker that isnt attacking it!");
4727 m_attackers
.erase(iter
);
4732 void Unit::ModifyAuraState(uint32 flag
, bool apply
)
4736 if (!HasFlag(UNIT_FIELD_AURASTATE
, 1<<(flag
-1)))
4738 SetFlag(UNIT_FIELD_AURASTATE
, 1<<(flag
-1));
4739 if(GetTypeId() == TYPEID_PLAYER
)
4741 const PlayerSpellMap
& sp_list
= ((Player
*)this)->GetSpellMap();
4742 for (PlayerSpellMap::const_iterator itr
= sp_list
.begin(); itr
!= sp_list
.end(); ++itr
)
4744 if(itr
->second
->state
== PLAYERSPELL_REMOVED
) continue;
4745 SpellEntry
const *spellInfo
= sSpellStore
.LookupEntry(itr
->first
);
4746 if (!spellInfo
|| !IsPassiveSpell(itr
->first
)) continue;
4747 if (spellInfo
->CasterAuraState
== flag
)
4748 CastSpell(this, itr
->first
, true, NULL
);
4755 if (HasFlag(UNIT_FIELD_AURASTATE
,1<<(flag
-1)))
4757 RemoveFlag(UNIT_FIELD_AURASTATE
, 1<<(flag
-1));
4758 Unit::AuraMap
& tAuras
= GetAuras();
4759 for (Unit::AuraMap::iterator itr
= tAuras
.begin(); itr
!= tAuras
.end();)
4761 if ((*itr
).second
->GetSpellProto()->CasterAuraState
== flag
)
4770 Unit
*Unit::GetOwner() const
4772 uint64 ownerid
= GetOwnerGUID();
4775 return ObjectAccessor::Instance().GetUnit(*this, ownerid
);
4778 Unit
*Unit::GetCharmer() const
4780 uint64 charmerid
= GetCharmerGUID();
4783 return ObjectAccessor::Instance().GetUnit(*this, charmerid
);
4786 Pet
* Unit::GetPet() const
4788 uint64 pet_guid
= GetPetGUID();
4791 Pet
* pet
= ObjectAccessor::Instance().GetPet(pet_guid
);
4794 sLog
.outError("Unit::GetPet: Pet %u not exist.",GUID_LOPART(pet_guid
));
4795 const_cast<Unit
*>(this)->SetPet(0);
4804 Unit
* Unit::GetCharm() const
4806 uint64 charm_guid
= GetCharmGUID();
4809 Unit
* pet
= ObjectAccessor::Instance().GetUnit(*this, charm_guid
);
4812 sLog
.outError("Unit::GetCharm: Charmed creature %u not exist.",GUID_LOPART(charm_guid
));
4813 const_cast<Unit
*>(this)->SetCharm(0);
4821 void Unit::SetPet(Pet
* pet
)
4823 SetUInt64Value(UNIT_FIELD_SUMMON
,pet
? pet
->GetGUID() : 0);
4827 for(int i
= 0; i
< MAX_MOVE_TYPE
; ++i
)
4829 pet
->SetSpeed(UnitMoveType(i
),m_speed_rate
[i
],true);
4834 void Unit::SetCharm(Unit
* charmed
)
4836 SetUInt64Value(UNIT_FIELD_CHARM
,charmed
? charmed
->GetGUID() : 0);
4839 void Unit::UnsummonAllTotems()
4841 for (int8 i
= 0; i
< 4; ++i
)
4846 Creature
*OldTotem
= ObjectAccessor::Instance().GetCreature(*this, m_TotemSlot
[i
]);
4847 if (OldTotem
&& OldTotem
->isTotem())
4848 ((Totem
*)OldTotem
)->UnSummon();
4852 void Unit::SendHealSpellOnPlayer(Unit
*pVictim
, uint32 SpellID
, uint32 Damage
, bool critical
)
4855 WorldPacket
data(SMSG_HEALSPELL_ON_PLAYER_OBSOLETE
, (8+8+4+4+1));
4856 data
.append(pVictim
->GetPackGUID());
4857 data
.append(GetPackGUID());
4860 data
<< uint8(critical
? 1 : 0);
4861 SendMessageToSet(&data
, true);
4864 void Unit::SendHealSpellOnPlayerPet(Unit
*pVictim
, uint32 SpellID
, uint32 Damage
,Powers powertype
, bool critical
)
4866 WorldPacket
data(SMSG_HEALSPELL_ON_PLAYERS_PET_OBSOLETE
, (8+8+4+4+4+1));
4867 data
.append(pVictim
->GetPackGUID());
4868 data
.append(GetPackGUID());
4870 data
<< uint32(powertype
);
4872 data
<< uint8(critical
? 1 : 0);
4873 SendMessageToSet(&data
, true);
4876 uint32
Unit::SpellDamageBonus(Unit
*pVictim
, SpellEntry
const *spellProto
, uint32 pdamage
, DamageEffectType damagetype
)
4878 if(!spellProto
|| !pVictim
|| damagetype
==DIRECT_DAMAGE
)
4881 if(pVictim
->IsImmunedToSpellDamage(spellProto
))
4884 uint32 creatureTypeMask
= GetCreatureTypeMask();
4887 uint32 CastingTime
= GetCastTime(sCastTimesStore
.LookupEntry(spellProto
->CastingTimeIndex
));
4888 if (CastingTime
> 7000) CastingTime
= 7000; // Plus Damage efficient maximum 200% ( 7.0 seconds )
4889 if (CastingTime
< 1500) CastingTime
= 1500;
4891 // Taken/Done fixed damage bonus auras
4892 int32 DoneAdvertisedBenefit
= 0;
4893 int32 TakenAdvertisedBenefit
= 0;
4895 // ..done (for creature type by mask) in taken
4896 AuraList
const& mDamageDoneCreature
= GetAurasByType(SPELL_AURA_MOD_DAMAGE_DONE_CREATURE
);
4897 for(AuraList::const_iterator i
= mDamageDoneCreature
.begin();i
!= mDamageDoneCreature
.end(); ++i
)
4898 if(creatureTypeMask
& uint32((*i
)->GetModifier()->m_miscvalue
))
4899 TakenAdvertisedBenefit
+= (*i
)->GetModifier()->m_amount
;
4902 AuraList
const& mDamageDone
= this->GetAurasByType(SPELL_AURA_MOD_DAMAGE_DONE
);
4903 for(AuraList::const_iterator i
= mDamageDone
.begin();i
!= mDamageDone
.end(); ++i
)
4904 if(((*i
)->GetModifier()->m_miscvalue
& (int32
)(1<<spellProto
->School
)) != 0 &&
4905 (*i
)->GetSpellProto()->EquippedItemClass
== -1 &&
4906 // -1 == any item class (not wand then)
4907 (*i
)->GetSpellProto()->EquippedItemInventoryTypeMask
== 0 )
4908 // 0 == any inventory type (not wand then)
4909 DoneAdvertisedBenefit
+= (*i
)->GetModifier()->m_amount
;
4911 if (GetTypeId() == TYPEID_PLAYER
)
4913 // Damage bonus of spirit
4914 AuraList
const& mDamageDonebySpi
= GetAurasByType(SPELL_AURA_MOD_SPELL_DAMAGE_OF_SPIRIT
);
4915 for(AuraList::const_iterator i
= mDamageDonebySpi
.begin();i
!= mDamageDonebySpi
.end(); ++i
)
4916 if((*i
)->GetModifier()->m_miscvalue
& 1 << spellProto
->School
)
4917 DoneAdvertisedBenefit
+= int32(GetStat(STAT_SPIRIT
) * (*i
)->GetModifier()->m_amount
/ 100.0f
);
4919 // ... and intellect
4920 AuraList
const& mDamageDonebyInt
= GetAurasByType(SPELL_AURA_MOD_SPELL_DAMAGE_OF_INTELLECT
);
4921 for(AuraList::const_iterator i
= mDamageDonebyInt
.begin();i
!= mDamageDonebyInt
.end(); ++i
)
4922 if ((*i
)->GetModifier()->m_miscvalue
& 1 << spellProto
->School
)
4923 DoneAdvertisedBenefit
+= int32(GetStat(STAT_INTELLECT
) * (*i
)->GetModifier()->m_amount
/ 100.0f
);
4927 AuraList
const& mDamageTaken
= pVictim
->GetAurasByType(SPELL_AURA_MOD_DAMAGE_TAKEN
);
4928 for(AuraList::const_iterator i
= mDamageTaken
.begin();i
!= mDamageTaken
.end(); ++i
)
4929 if(((*i
)->GetModifier()->m_miscvalue
& (int32
)(1<<spellProto
->School
)) != 0)
4930 TakenAdvertisedBenefit
+= (*i
)->GetModifier()->m_amount
;
4932 // Damage over Time spells bonus calculation
4933 float DotFactor
= 1.0f
;
4934 if(damagetype
== DOT
)
4937 uint32 DotDuration
= GetDuration(spellProto
);
4941 if(DotDuration
> 30000) DotDuration
= 30000;
4942 DotFactor
= DotDuration
/ 15000.0f
;
4944 for(int j
= 0; j
< 3; j
++)
4945 if(spellProto
->Effect
[j
] == 6) x
= j
;
4947 if(spellProto
->EffectAmplitude
[x
] != 0)
4948 DotTicks
= DotDuration
/ spellProto
->EffectAmplitude
[x
];
4951 DoneAdvertisedBenefit
/= DotTicks
;
4952 TakenAdvertisedBenefit
/= DotTicks
;
4957 // Taken/Done total percent damage auras
4958 float DoneTotalMod
= 1.0f
;
4959 float TakenTotalMod
= 1.0f
;
4962 AuraList
const& mModDamagePercentDone
= this->GetAurasByType(SPELL_AURA_MOD_DAMAGE_PERCENT_DONE
);
4963 for(AuraList::const_iterator i
= mModDamagePercentDone
.begin(); i
!= mModDamagePercentDone
.end(); ++i
)
4964 if( spellProto
->School
!= 0 && ((*i
)->GetModifier()->m_miscvalue
& (int32
)(1<<spellProto
->School
)) != 0 &&
4965 (*i
)->GetSpellProto()->EquippedItemClass
== -1 &&
4966 // -1 == any item class (not wand then)
4967 (*i
)->GetSpellProto()->EquippedItemInventoryTypeMask
== 0 )
4968 // 0 == any inventory type (not wand then)
4969 DoneTotalMod
*= ((*i
)->GetModifier()->m_amount
+100.0f
)/100.0f
;
4972 AuraList
const& mModDamagePercentTaken
= pVictim
->GetAurasByType(SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN
);
4973 for(AuraList::const_iterator i
= mModDamagePercentTaken
.begin(); i
!= mModDamagePercentTaken
.end(); ++i
)
4974 if( spellProto
->School
!= 0 && ((*i
)->GetModifier()->m_miscvalue
& (int32
)(1<<spellProto
->School
)) != 0 )
4975 TakenTotalMod
*= ((*i
)->GetModifier()->m_amount
+100.0f
)/100.0f
;
4979 if(spellProto
->SpellVisual
== 1225 && spellProto
->SpellIconID
== 208)
4981 CastingTime
= 2800; // 80% from +shadow damage
4982 DoneTotalMod
= 1.0f
;
4983 TakenTotalMod
= 1.0f
;
4986 if(spellProto
->SpellVisual
== 827 && spellProto
->SpellIconID
== 154 && GetPet())
4988 CastingTime
= 3360; // 96% from +shadow damage
4989 DoneTotalMod
= 1.0f
;
4990 TakenTotalMod
= 1.0f
;
4993 if(spellProto
->Id
== 30455)
4995 CastingTime
/= 3.0f
; // applied 1/3 bonuses in case generic target
4996 if(pVictim
->isFrozen()) // and compensate this for frozen target.
4997 TakenTotalMod
*= 3.0f
;
5001 float LvlPenalty
= 0.0f
;
5002 if(spellProto
->spellLevel
< 20)
5003 LvlPenalty
= (20.0f
- (float)(spellProto
->spellLevel
)) * 3.75f
;
5004 float LvlFactor
= ((float)(spellProto
->spellLevel
) + 6.0f
) / (float)(getLevel());
5005 if(LvlFactor
> 1.0f
)
5008 // Spellmod SpellDamage
5009 float SpellModSpellDamage
= 100.0f
;
5010 if (GetTypeId() == TYPEID_PLAYER
)
5011 ((Player
*)this)->ApplySpellMod(spellProto
->Id
,SPELLMOD_SPELL_DAMAGE
,SpellModSpellDamage
);
5012 SpellModSpellDamage
/= 100.0f
;
5014 float DoneActualBenefit
= DoneAdvertisedBenefit
* (CastingTime
/ 3500.0f
) * (100.0f
- LvlPenalty
) * LvlFactor
* DotFactor
* SpellModSpellDamage
/ 100.0f
;
5015 float TakenActualBenefit
= TakenAdvertisedBenefit
* (CastingTime
/ 3500.0f
) * (100.0f
- LvlPenalty
) * LvlFactor
* DotFactor
/ 100.0f
;
5017 float tmpDamage
= (float(pdamage
)+DoneActualBenefit
)*DoneTotalMod
;
5018 tmpDamage
= (tmpDamage
+TakenActualBenefit
)*TakenTotalMod
;
5020 return tmpDamage
> 0 ? uint32(tmpDamage
) : 0;
5023 bool Unit::SpellCriticalBonus(SpellEntry
const *spellProto
, int32
*peffect
, Unit
*pVictim
)
5025 // Chance to crit is computed from INT and LEVEL as follows:
5026 // chance = base + INT / (rate0 + rate1 * LEVEL)
5027 // The formula keeps the crit chance at %5 on every level unless the player
5028 // increases his intelligence by other means (enchants, buffs, talents, ...)
5029 if(spellProto
->Id
== 15290 || spellProto
->Id
== 39373) return false;
5031 float crit_chance
= 0.0f
;
5034 if (GetTypeId() != TYPEID_PLAYER
)
5037 // TODO: can creatures have critical chance auras?
5038 crit_chance
= m_baseSpellCritChance
;
5039 AuraList
const& mSpellCritSchool
= GetAurasByType(SPELL_AURA_MOD_SPELL_CRIT_CHANCE_SCHOOL
);
5040 for(AuraList::const_iterator i
= mSpellCritSchool
.begin(); i
!= mSpellCritSchool
.end(); ++i
)
5041 if((*i
)->GetModifier()->m_miscvalue
== -2 || ((*i
)->GetModifier()->m_miscvalue
& (int32
)(1<<spellProto
->School
)) != 0)
5042 crit_chance
+= (*i
)->GetModifier()->m_amount
;
5045 crit_chance
= GetFloatValue( PLAYER_SPELL_CRIT_PERCENTAGE1
+ spellProto
->School
);
5048 // only players use intelligence for critical chance computations
5049 if (GetTypeId() == TYPEID_PLAYER
)
5051 ((Player
*)this)->ApplySpellMod(spellProto
->Id
, SPELLMOD_CRITICAL_CHANCE
, crit_chance
);
5058 AuraList
const& mAttackerSpellCrit
= pVictim
->GetAurasByType(SPELL_AURA_MOD_ATTACKER_SPELL_CRIT_CHANCE
);
5059 for(AuraList::const_iterator i
= mAttackerSpellCrit
.begin(); i
!= mAttackerSpellCrit
.end(); ++i
)
5060 if((*i
)->GetModifier()->m_miscvalue
== -2 || ((*i
)->GetModifier()->m_miscvalue
& (int32
)(1<<spellProto
->School
)) != 0)
5061 crit_chance
+= (*i
)->GetModifier()->m_amount
;
5063 // flat: Resilience - reduce crit chance by x%
5064 crit_chance
-= pVictim
->m_modResilience
;
5066 // flat: scripted (increase crit chance ... against ... target by x%
5067 AuraList
const& mOverrideClassScript
= GetAurasByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS
);
5068 for(AuraList::const_iterator i
= mOverrideClassScript
.begin(); i
!= mOverrideClassScript
.end(); ++i
)
5070 switch((*i
)->GetModifier()->m_miscvalue
)
5073 case 849: if(pVictim
->isFrozen()) crit_chance
+= 10; break;
5075 case 910: if(pVictim
->isFrozen()) crit_chance
+= 20; break;
5077 case 911: if(pVictim
->isFrozen()) crit_chance
+= 30; break;
5079 case 912: if(pVictim
->isFrozen()) crit_chance
+= 40; break;
5081 case 913: if(pVictim
->isFrozen()) crit_chance
+= 50; break;
5086 AuraList
const& mAttackerSWCrit
= pVictim
->GetAurasByType(SPELL_AURA_MOD_ATTACKER_SPELL_AND_WEAPON_CRIT_CHANCE
);
5087 for(AuraList::const_iterator i
= mAttackerSWCrit
.begin(); i
!= mAttackerSWCrit
.end(); ++i
)
5088 crit_chance
+= (*i
)->GetModifier()->m_amount
;
5091 crit_chance
= crit_chance
> 0.0 ? crit_chance
: 0.0;
5092 if (roll_chance_f(crit_chance
))
5094 int32 crit_bonus
= *peffect
/ 2;
5095 if (GetTypeId() == TYPEID_PLAYER
) // adds additional damage to crit_bonus (from talents)
5096 ((Player
*)this)->ApplySpellMod(spellProto
->Id
, SPELLMOD_CRIT_DAMAGE_BONUS
, crit_bonus
);
5098 *peffect
+= crit_bonus
;
5099 // Resilience - reduce crit damage by 2x%
5101 *peffect
-= int32(pVictim
->m_modResilience
* 2/100 * (*peffect
));
5108 uint32
Unit::SpellHealingBonus(SpellEntry
const *spellProto
, uint32 healamount
, DamageEffectType damagetype
, Unit
*pVictim
)
5112 // Vampiric Embrace, Shadowmend - cannot critically heal
5113 if(spellProto
->Id
== 15290 || spellProto
->Id
== 39373) return healamount
;
5115 int32 AdvertisedBenefit
= 0;
5116 uint32 CastingTime
= GetCastTime(sCastTimesStore
.LookupEntry(spellProto
->CastingTimeIndex
));
5117 if (CastingTime
> 7000) CastingTime
= 7000;
5118 if (CastingTime
< 1500) CastingTime
= 1500;
5119 if (spellProto
->Effect
[0] == SPELL_EFFECT_APPLY_AURA
) CastingTime
= 3500;
5121 AuraList
const& mHealingDone
= GetAurasByType(SPELL_AURA_MOD_HEALING_DONE
);
5122 for(AuraList::const_iterator i
= mHealingDone
.begin();i
!= mHealingDone
.end(); ++i
)
5123 if(((*i
)->GetModifier()->m_miscvalue
& (int32
)(1<<spellProto
->School
)) != 0)
5124 AdvertisedBenefit
+= (*i
)->GetModifier()->m_amount
;
5126 // Healing bonus of spirit, intellect and strength
5127 if (GetTypeId() == TYPEID_PLAYER
)
5129 AdvertisedBenefit
+= int32(GetStat(STAT_SPIRIT
) * GetTotalAuraModifier(SPELL_AURA_MOD_SPELL_HEALING_OF_SPIRIT
) / 100.0f
);
5130 AdvertisedBenefit
+= int32(GetStat(STAT_INTELLECT
) * GetTotalAuraModifier(SPELL_AURA_MOD_SPELL_HEALING_OF_INTELLECT
) / 100.0f
);
5131 AdvertisedBenefit
+= int32(GetStat(STAT_STRENGTH
) * GetTotalAuraModifier(SPELL_AURA_MOD_SPELL_HEALING_OF_STRENGTH
) / 100.0f
);
5135 AdvertisedBenefit
+= pVictim
->GetTotalAuraModifier(SPELL_AURA_MOD_HEALING
);
5138 if (spellProto
->SpellFamilyName
== SPELLFAMILY_PALADIN
&& (spellProto
->SpellFamilyFlags
& 0xC0000000))
5140 AuraList
const& mDummyAuras
= pVictim
->GetAurasByType(SPELL_AURA_DUMMY
);
5141 for(AuraList::const_iterator i
= mDummyAuras
.begin();i
!= mDummyAuras
.end(); ++i
)
5142 if((*i
)->GetSpellProto()->SpellVisual
== 9180)
5144 if ((spellProto
->SpellFamilyFlags
& 0x40000000) && (*i
)->GetEffIndex() == 1)
5145 AdvertisedBenefit
+= (*i
)->GetModifier()->m_amount
;
5147 else if ((spellProto
->SpellFamilyFlags
& 0x80000000) && (*i
)->GetEffIndex() == 0)
5148 AdvertisedBenefit
+= (*i
)->GetModifier()->m_amount
;
5151 // Healing over Time spells
5152 float DotFactor
= 1.0f
;
5153 if(damagetype
== DOT
)
5156 uint32 DotDuration
= GetDuration(spellProto
);
5158 if(DotDuration
> 30000) DotDuration
= 30000;
5159 DotFactor
= DotDuration
/ 15000.0f
;
5161 for(int j
= 0; j
< 3; j
++)
5162 if(spellProto
->Effect
[j
] == 6) x
= j
;
5164 if(spellProto
->EffectAmplitude
[x
] != 0)
5165 DotTicks
= DotDuration
/ spellProto
->EffectAmplitude
[x
];
5167 AdvertisedBenefit
/= DotTicks
;
5171 float LvlPenalty
= 0.0f
;
5172 if(spellProto
->spellLevel
< 20)
5173 LvlPenalty
= (20.0f
- (float)(spellProto
->spellLevel
)) * 3.75f
;
5174 float LvlFactor
= ((float)(spellProto
->spellLevel
) + 6.0f
) / (float)(getLevel());
5175 if(LvlFactor
> 1.0f
)
5178 float ActualBenefit
= (float)AdvertisedBenefit
* ((float)CastingTime
/ 3500.0f
) * (100.0f
- LvlPenalty
) * LvlFactor
* DotFactor
/ 100.0f
;
5180 // use float as more appropriate for negative values and percent applying
5181 float heal
= healamount
+ ActualBenefit
;
5183 // TODO: check for ALL/SPELLS type
5184 // Healing done percent
5185 AuraList
const& mHealingDonePct
= GetAurasByType(SPELL_AURA_MOD_HEALING_DONE_PERCENT
);
5186 for(AuraList::const_iterator i
= mHealingDonePct
.begin();i
!= mHealingDonePct
.end(); ++i
)
5187 heal
*= (100.0f
+ (*i
)->GetModifier()->m_amount
) / 100.0f
;
5189 // Healing taken percent
5190 AuraList
const& mHealingPct
= pVictim
->GetAurasByType(SPELL_AURA_MOD_HEALING_PCT
);
5191 for(AuraList::const_iterator i
= mHealingPct
.begin();i
!= mHealingPct
.end(); ++i
)
5192 heal
*= (100.0f
+ (*i
)->GetModifier()->m_amount
) / 100.0f
;
5194 if (heal
< 0) heal
= 0;
5196 return uint32(heal
);
5199 bool Unit::IsImmunedToPhysicalDamage() const
5201 //If m_immuneToDamage type contain magic, IMMUNE damage.
5202 SpellImmuneList
const& damageImmList
= m_spellImmune
[IMMUNITY_DAMAGE
];
5203 for (SpellImmuneList::const_iterator itr
= damageImmList
.begin(); itr
!= damageImmList
.end(); ++itr
)
5204 if(itr
->type
& IMMUNE_DAMAGE_PHYSICAL
)
5207 //If m_immuneToSchool type contain this school type, IMMUNE damage.
5208 SpellImmuneList
const& spellImmList
= m_spellImmune
[IMMUNITY_SCHOOL
];
5209 for (SpellImmuneList::const_iterator itr
= spellImmList
.begin(); itr
!= spellImmList
.end(); ++itr
)
5210 if(itr
->type
& IMMUNE_SCHOOL_PHYSICAL
)
5216 bool Unit::IsImmunedToSpellDamage(SpellEntry
const* spellInfo
) const
5218 //If m_immuneToDamage type contain magic, IMMUNE damage.
5219 SpellImmuneList
const& damageList
= m_spellImmune
[IMMUNITY_DAMAGE
];
5220 for (SpellImmuneList::const_iterator itr
= damageList
.begin(); itr
!= damageList
.end(); ++itr
)
5221 if(itr
->type
& uint32(1<<spellInfo
->School
))
5224 //If m_immuneToSchool type contain this school type, IMMUNE damage.
5225 SpellImmuneList
const& schoolList
= m_spellImmune
[IMMUNITY_SCHOOL
];
5226 for (SpellImmuneList::const_iterator itr
= schoolList
.begin(); itr
!= schoolList
.end(); ++itr
)
5227 if(itr
->type
& uint32(1<<spellInfo
->School
))
5233 bool Unit::IsImmunedToSpell(SpellEntry
const* spellInfo
) const
5238 SpellImmuneList
const& dispelList
= m_spellImmune
[IMMUNITY_DISPEL
];
5239 for(SpellImmuneList::const_iterator itr
= dispelList
.begin(); itr
!= dispelList
.end(); ++itr
)
5240 if(itr
->type
== spellInfo
->Dispel
)
5243 SpellImmuneList
const& mechanicList
= m_spellImmune
[IMMUNITY_MECHANIC
];
5244 for(SpellImmuneList::const_iterator itr
= mechanicList
.begin(); itr
!= mechanicList
.end(); ++itr
)
5245 if(itr
->type
== spellInfo
->Mechanic
)
5249 AuraList
const& mModMechanicRes
= GetAurasByType(SPELL_AURA_MOD_MECHANIC_RESISTANCE
);
5250 for(AuraList::const_iterator i
= mModMechanicRes
.begin();i
!= mModMechanicRes
.end(); ++i
)
5251 if((*i
)->GetModifier()->m_miscvalue
== int32(spellInfo
->Mechanic
))
5252 chance
+= (*i
)->GetModifier()->m_amount
;
5253 if(roll_chance_i(chance
))
5259 bool Unit::IsImmunedToSpellEffect(uint32 effect
) const
5261 //If m_immuneToEffect type contain this effect type, IMMUNE effect.
5262 SpellImmuneList
const& effectList
= m_spellImmune
[IMMUNITY_EFFECT
];
5263 for (SpellImmuneList::const_iterator itr
= effectList
.begin(); itr
!= effectList
.end(); ++itr
)
5264 if(itr
->type
== effect
)
5270 bool Unit::IsDamageToThreatSpell(SpellEntry
const * spellInfo
) const
5275 uint32 family
= spellInfo
->SpellFamilyName
;
5276 uint32 flags
= spellInfo
->SpellFamilyFlags
;
5278 if((family
== 5 && flags
== 256) || //Searing Pain
5279 (family
== 6 && flags
== 8192) || //Mind Blast
5280 (family
== 11 && flags
== 1048576)) //Earth Shock
5286 void Unit::MeleeDamageBonus(Unit
*pVictim
, uint32
*pdamage
,WeaponAttackType attType
)
5288 if(!pVictim
) return;
5293 uint32 creatureTypeMask
= GetCreatureTypeMask();
5295 if(GetTypeId() != TYPEID_PLAYER
&& ((Creature
*)this)->isPet())
5297 if(getPowerType() == POWER_FOCUS
)
5299 uint32 happiness
= GetPower(POWER_HAPPINESS
);
5300 if(happiness
>=666000)
5301 *pdamage
= uint32(*pdamage
* 1.25);
5302 else if(happiness
<333000)
5303 *pdamage
= uint32(*pdamage
* 0.75);
5304 else *pdamage
= uint32(*pdamage
* 1.0);
5308 // Taken/Done fixed damage bonus auras
5309 int32 DoneFlatBenefit
= 0;
5310 int32 TakenFlatBenefit
= 0;
5312 // ..done (for creature type by mask) in taken
5313 AuraList
const& mDamageDoneCreature
= this->GetAurasByType(SPELL_AURA_MOD_DAMAGE_DONE_CREATURE
);
5314 for(AuraList::const_iterator i
= mDamageDoneCreature
.begin();i
!= mDamageDoneCreature
.end(); ++i
)
5315 if(creatureTypeMask
& uint32((*i
)->GetModifier()->m_miscvalue
))
5316 DoneFlatBenefit
+= (*i
)->GetModifier()->m_amount
;
5319 // SPELL_AURA_MOD_DAMAGE_DONE included in weapon damage
5321 // ..done (base at attack power and creature type)
5322 AuraList
const& mCreatureAttackPower
= GetAurasByType(SPELL_AURA_MOD_CREATURE_ATTACK_POWER
);
5323 for(AuraList::const_iterator i
= mCreatureAttackPower
.begin();i
!= mCreatureAttackPower
.end(); ++i
)
5324 if(creatureTypeMask
& uint32((*i
)->GetModifier()->m_miscvalue
))
5325 DoneFlatBenefit
+= int32((*i
)->GetModifier()->m_amount
/14.0f
* GetAttackTime(attType
)/1000);
5327 // ..done (base at attack power for marked target)
5328 if(attType
== RANGED_ATTACK
)
5330 AuraList
const& mRangedAttackPowerAttackerBonus
= pVictim
->GetAurasByType(SPELL_AURA_RANGED_ATTACK_POWER_ATTACKER_BONUS
);
5331 for(AuraList::const_iterator i
= mRangedAttackPowerAttackerBonus
.begin();i
!= mRangedAttackPowerAttackerBonus
.end(); ++i
)
5332 DoneFlatBenefit
+= int32((*i
)->GetModifier()->m_amount
/14.0f
* GetAttackTime(RANGED_ATTACK
)/1000);
5336 AuraList
const& mDamageTaken
= pVictim
->GetAurasByType(SPELL_AURA_MOD_DAMAGE_TAKEN
);
5337 for(AuraList::const_iterator i
= mDamageTaken
.begin();i
!= mDamageTaken
.end(); ++i
)
5338 if((*i
)->GetModifier()->m_miscvalue
& IMMUNE_SCHOOL_PHYSICAL
)
5339 TakenFlatBenefit
+= (*i
)->GetModifier()->m_amount
;
5341 if(attType
!=RANGED_ATTACK
)
5343 AuraList
const& mModMeleeDamageTaken
= pVictim
->GetAurasByType(SPELL_AURA_MOD_MELEE_DAMAGE_TAKEN
);
5344 for(AuraList::const_iterator i
= mModMeleeDamageTaken
.begin(); i
!= mModMeleeDamageTaken
.end(); ++i
)
5345 TakenFlatBenefit
+= (*i
)->GetModifier()->m_amount
;
5349 AuraList
const& mModRangedDamageTaken
= pVictim
->GetAurasByType(SPELL_AURA_MOD_RANGED_DAMAGE_TAKEN
);
5350 for(AuraList::const_iterator i
= mModRangedDamageTaken
.begin(); i
!= mModRangedDamageTaken
.end(); ++i
)
5351 TakenFlatBenefit
+= (*i
)->GetModifier()->m_amount
;
5354 // Done/Taken total percent damage auras
5355 float TakenTotalMod
= 1;
5358 // SPELL_AURA_MOD_DAMAGE_PERCENT_DONE included in weapon damage
5359 // SPELL_AURA_MOD_OFFHAND_DAMAGE_PCT included in weapon damage
5362 AuraList
const& mModDamagePercentTaken
= pVictim
->GetAurasByType(SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN
);
5363 for(AuraList::const_iterator i
= mModDamagePercentTaken
.begin(); i
!= mModDamagePercentTaken
.end(); ++i
)
5364 if((*i
)->GetModifier()->m_miscvalue
& IMMUNE_SCHOOL_PHYSICAL
)
5365 TakenTotalMod
*= ((*i
)->GetModifier()->m_amount
+100.0f
)/100.0f
;
5367 if(attType
!= RANGED_ATTACK
)
5369 AuraList
const& mModMeleeDamageTakenPercent
= pVictim
->GetAurasByType(SPELL_AURA_MOD_MELEE_DAMAGE_TAKEN_PCT
);
5370 for(AuraList::const_iterator i
= mModMeleeDamageTakenPercent
.begin(); i
!= mModMeleeDamageTakenPercent
.end(); ++i
)
5371 TakenTotalMod
*= ((*i
)->GetModifier()->m_amount
+100.0f
)/100.0f
;
5375 AuraList
const& mModRangedDamageTakenPercent
= pVictim
->GetAurasByType(SPELL_AURA_MOD_RANGED_DAMAGE_TAKEN_PCT
);
5376 for(AuraList::const_iterator i
= mModRangedDamageTakenPercent
.begin(); i
!= mModRangedDamageTakenPercent
.end(); ++i
)
5377 TakenTotalMod
*= ((*i
)->GetModifier()->m_amount
+100.0f
)/100.0f
;
5380 float tmpDamage
= ((int32(*pdamage
) + DoneFlatBenefit
) + TakenFlatBenefit
)*TakenTotalMod
;
5382 // bonus result can be negative
5383 *pdamage
= tmpDamage
> 0 ? uint32(tmpDamage
) : 0;
5386 void Unit::ApplySpellImmune(uint32 spellId
, uint32 op
, uint32 type
, bool apply
)
5390 for (SpellImmuneList::iterator itr
= m_spellImmune
[op
].begin(), next
; itr
!= m_spellImmune
[op
].end(); itr
= next
)
5393 if(itr
->type
== type
)
5395 m_spellImmune
[op
].erase(itr
);
5396 next
= m_spellImmune
[op
].begin();
5400 Immune
.spellId
= spellId
;
5402 m_spellImmune
[op
].push_back(Immune
);
5406 for (SpellImmuneList::iterator itr
= m_spellImmune
[op
].begin(); itr
!= m_spellImmune
[op
].end(); ++itr
)
5408 if(itr
->spellId
== spellId
)
5410 m_spellImmune
[op
].erase(itr
);
5418 float Unit::GetWeaponProcChance() const
5420 // normalized proc chance for weapon attack speed
5421 // (odd formulae...)
5422 if(isAttackReady(BASE_ATTACK
))
5423 return (GetAttackTime(BASE_ATTACK
) * 1.8f
/ 1000.0f
);
5424 else if (haveOffhandWeapon() && isAttackReady(OFF_ATTACK
))
5425 return (GetAttackTime(OFF_ATTACK
) * 1.6f
/ 1000.0f
);
5429 float Unit::GetPPMProcChance(uint32 WeaponSpeed
, float PPM
) const
5431 // proc per minute chance calculation
5432 if (PPM
<= 0) return 0.0f
;
5433 uint32 result
= uint32((WeaponSpeed
* PPM
) / 600.0f
); // result is chance in percents (probability = Speed_in_sec * (PPM / 60))
5437 void Unit::Mount(uint32 mount
, bool taxi
)
5442 SetUInt32Value(UNIT_FIELD_MOUNTDISPLAYID
, mount
);
5444 uint32 flag
= UNIT_FLAG_MOUNT
;
5446 flag
|= UNIT_FLAG_DISABLE_MOVE
;
5448 SetFlag( UNIT_FIELD_FLAGS
, flag
);
5451 if(GetTypeId() == TYPEID_PLAYER
)
5453 Pet
* pet
= GetPet();
5456 if(pet
->getPetType() == SUMMON_PET
|| pet
->getPetType() == HUNTER_PET
)
5458 ((Player
*)this)->SetOldPetNumber(pet
->GetCharmInfo()->GetPetNumber());
5459 ((Player
*)this)->SetOldPetSpell(pet
->GetUInt32Value(UNIT_CREATED_BY_SPELL
));
5462 ((Player
*)this)->RemovePet(NULL
,PET_SAVE_NOT_IN_SLOT
);
5465 ((Player
*)this)->SetOldPetNumber(0);
5469 void Unit::Unmount()
5474 SetUInt32Value(UNIT_FIELD_MOUNTDISPLAYID
, 0);
5475 RemoveFlag( UNIT_FIELD_FLAGS
,UNIT_FLAG_DISABLE_MOVE
| UNIT_FLAG_MOUNT
);
5477 if(GetTypeId() == TYPEID_PLAYER
&& ((Player
*)this)->GetOldPetNumber() && isAlive())
5479 Pet
* NewPet
= new Pet(this);
5480 if(!NewPet
->LoadPetFromDB(this, 0, ((Player
*)this)->GetOldPetNumber(), true))
5483 ((Player
*)this)->SetOldPetNumber(0);
5487 void Unit::SetInCombat()
5489 m_CombatTimer
= 5000;
5490 SetFlag(UNIT_FIELD_FLAGS
, UNIT_FLAG_IN_COMBAT
);
5492 if(isCharmed() || (GetTypeId()!=TYPEID_PLAYER
&& ((Creature
*)this)->isPet()))
5493 SetFlag(UNIT_FIELD_FLAGS
, UNIT_FLAG_PET_IN_COMBAT
);
5496 void Unit::ClearInCombat(bool force
)
5498 // wait aura and combat timer expire
5499 if(!force
&& HasAuraType(SPELL_AURA_INTERRUPT_REGEN
))
5503 RemoveFlag(UNIT_FIELD_FLAGS
, UNIT_FLAG_IN_COMBAT
);
5505 if(isCharmed() || (GetTypeId()!=TYPEID_PLAYER
&& ((Creature
*)this)->isPet()))
5506 RemoveFlag(UNIT_FIELD_FLAGS
, UNIT_FLAG_PET_IN_COMBAT
);
5508 // remove combo points
5509 if(GetTypeId()==TYPEID_PLAYER
)
5510 ((Player
*)this)->ClearComboPoints();
5513 bool Unit::isTargetableForAttack()
5515 if (GetTypeId()==TYPEID_PLAYER
&& ((Player
*)this)->isGameMaster())
5517 return isAlive() && !hasUnitState(UNIT_STAT_DIED
)&& !isInFlight() /*&& !isStealth()*/;
5520 int32
Unit::ModifyHealth(int32 dVal
)
5527 int32 curHealth
= (int32
)GetHealth();
5529 int32 val
= dVal
+ curHealth
;
5536 int32 maxHealth
= (int32
)GetMaxHealth();
5541 gain
= val
- curHealth
;
5543 else if(curHealth
!= maxHealth
)
5545 SetHealth(maxHealth
);
5546 gain
= maxHealth
- curHealth
;
5552 int32
Unit::ModifyPower(Powers power
, int32 dVal
)
5559 int32 curPower
= (int32
)GetPower(power
);
5561 int32 val
= dVal
+ curPower
;
5568 int32 maxPower
= (int32
)GetMaxPower(power
);
5572 SetPower(power
,val
);
5573 gain
= val
- curPower
;
5575 else if(curPower
!= maxPower
)
5577 SetPower(power
,maxPower
);
5578 gain
= maxPower
- curPower
;
5584 bool Unit::isVisibleForOrDetect(Unit
const* u
, bool detect
, bool inVisibleList
) const
5589 // Always can see self
5593 // player visible for other player if not logout and at same transport
5594 // including case when player is out of world
5595 bool at_same_transport
=
5596 GetTypeId() == TYPEID_PLAYER
&& u
->GetTypeId()==TYPEID_PLAYER
&&
5597 !((Player
*)this)->GetSession()->PlayerLogout() && !((Player
*)u
)->GetSession()->PlayerLogout() &&
5598 !((Player
*)this)->GetSession()->PlayerLoading() && !((Player
*)u
)->GetSession()->PlayerLoading() &&
5599 ((Player
*)this)->GetTransport() && ((Player
*)this)->GetTransport() == ((Player
*)u
)->GetTransport();
5602 if(!at_same_transport
&& (!IsInWorld() || !u
->IsInWorld()))
5605 // always seen by owner
5606 if(GetCharmerOrOwnerGUID()==u
->GetGUID())
5609 // Grid dead/alive checks
5610 if( u
->GetTypeId()==TYPEID_PLAYER
)
5612 // non visible at grid for any stealth state
5613 if(!IsVisibleInGridForPlayer((Player
*)u
))
5616 // if player is dead then he can't detect anyone in anycases
5622 // all dead creatures/players not visible for any creatures
5623 if(!u
->isAlive() || !isAlive())
5627 // different visible distance checks
5628 if(u
->isInFlight()) // what see player in flight
5630 // use object grey distance for all (only see objects any way)
5631 if (!IsWithinDistInMap(u
,World::GetMaxVisibleDistanceInFlight()+(inVisibleList
? World::GetVisibleObjectGreyDistance() : 0.0f
)))
5634 else if(!isAlive()) // distance for show body
5636 if (!IsWithinDistInMap(u
,World::GetMaxVisibleDistanceForObject()+(inVisibleList
? World::GetVisibleObjectGreyDistance() : 0.0f
)))
5639 else if(GetTypeId()==TYPEID_PLAYER
) // distance for show player
5641 if(u
->GetTypeId()==TYPEID_PLAYER
)
5643 // Players far than max visible distance for player or not in our map are not visible too
5644 if (!at_same_transport
&& !IsWithinDistInMap(u
,World::GetMaxVisibleDistanceForPlayer()+(inVisibleList
? World::GetVisibleUnitGreyDistance() : 0.0f
)))
5649 // Units far than max visible distance for creature or not in our map are not visible too
5650 if (!IsWithinDistInMap(u
,World::GetMaxVisibleDistanceForCreature()+(inVisibleList
? World::GetVisibleUnitGreyDistance() : 0.0f
)))
5654 else if(GetCharmerOrOwnerGUID()) // distance for show pet/charmed
5656 // Pet/charmed far than max visible distance for player or not in our map are not visible too
5657 if (!IsWithinDistInMap(u
,World::GetMaxVisibleDistanceForPlayer()+(inVisibleList
? World::GetVisibleUnitGreyDistance() : 0.0f
)))
5660 else // distance for show creature
5662 // Units far than max visible distance for creature or not in our map are not visible too
5663 if (!IsWithinDistInMap(u
,World::GetMaxVisibleDistanceForCreature()+(inVisibleList
? World::GetVisibleUnitGreyDistance() : 0.0f
)))
5667 // Visible units, always are visible for all units, except for units under invisibility
5668 if (m_Visibility
== VISIBILITY_ON
&& u
->GetVisibility()!= VISIBILITY_GROUP_INVISIBILITY
)
5671 // GMs are visible for higher gms (or players are visible for gms)
5672 if (u
->GetTypeId() == TYPEID_PLAYER
&& ((Player
*)u
)->isGameMaster())
5673 return (GetTypeId() == TYPEID_PLAYER
&& ((Player
*)this)->GetSession()->GetSecurity() <= ((Player
*)u
)->GetSession()->GetSecurity());
5675 // non faction visibility non-breakable for non-GMs
5676 if (m_Visibility
== VISIBILITY_OFF
)
5679 // Invisible units, always are visible for units under invisibility or unit that can detect this invisibility
5680 if (m_Visibility
== VISIBILITY_GROUP_INVISIBILITY
)
5682 if(u
->GetVisibility()== VISIBILITY_GROUP_INVISIBILITY
|| m_invisibilityvalue
<= u
->m_detectInvisibility
)
5686 // Units that can detect invisibility always are visible for units that can be detected
5687 if (u
->GetVisibility()== VISIBILITY_GROUP_INVISIBILITY
)
5689 if(m_detectInvisibility
>= u
->m_invisibilityvalue
)
5693 // Stealth/invisible not hostile units, not visible (except Player-with-Player case)
5694 if (!u
->IsHostileTo(this))
5696 // player see other player with stealth/invisibility only if he in same group or raid or same team (raid/team case dependent from conf setting)
5697 if(GetTypeId()==TYPEID_PLAYER
&& u
->GetTypeId()==TYPEID_PLAYER
)
5699 if(((Player
*)this)->IsGroupVisibleFor(((Player
*)u
)))
5702 // else apply same rules as for hostile case (detecting check)
5707 // Hunter mark functionality
5708 AuraList
const& auras
= GetAurasByType(SPELL_AURA_MOD_STALKED
);
5709 for(AuraList::const_iterator iter
= auras
.begin(); iter
!= auras
.end(); ++iter
)
5710 if((*iter
)->GetCasterGUID()==u
->GetGUID())
5714 // unit got in stealth in this moment and must ignore old detected state
5715 // invisibility not have chance for detection
5716 if (m_Visibility
== VISIBILITY_ON
|| m_Visibility
== VISIBILITY_GROUP_NO_DETECT
|| m_Visibility
== VISIBILITY_GROUP_INVISIBILITY
)
5719 // NOW ONLY STEALTH CASE
5721 // stealth and detected
5722 if (u
->GetTypeId() == TYPEID_PLAYER
&& ((Player
*)u
)->HaveAtClient(this) )
5725 //if in non-detect mode then invisible for unit
5730 bool IsVisible
= true;
5731 bool isInFront
= u
->isInFront(this,World::GetMaxVisibleDistanceForObject());
5732 float distance
= sqrt(GetDistanceSq(u
));
5734 // If is attacked then stealth is lost, some creature can use stealth too
5735 if (this->isAttacked())
5738 // If there is collision rogue is seen regardless of level difference
5739 // TODO: check sizes in DB
5740 if (distance
< 0.24f
)
5743 //If a mob or player is stunned he will not be able to detect stealth
5744 if ((u
->hasUnitState(UNIT_STAT_STUNDED
)) && (u
!= this))
5750 // Cases based on level difference and position
5751 int32 levelDiff
= u
->getLevel() - this->getLevel();
5753 //If mob is 5 levels more than player he gets detected automaticly
5754 if (u
->GetTypeId()!=TYPEID_PLAYER
&& levelDiff
> 5)
5757 // If victim has more than 5 lvls above caster
5758 if ((this->GetTypeId() == TYPEID_UNIT
)&& ( levelDiff
> 5 ))
5761 // If caster has more than 5 levels above victim
5762 if ((this->GetTypeId() == TYPEID_UNIT
)&& ( levelDiff
< -5 ))
5768 float modifier
= 1; // 100 pct
5769 float pvpMultiplier
= 0;
5770 float distanceFormula
= 0;
5772 //This allows to check talent tree and will add addition stealth dependant on used points)
5773 uint32 stealthMod
= GetTotalAuraModifier(SPELL_AURA_MOD_STEALTH_LEVEL
);
5774 //****************************************************************************************
5775 // Stealth detection calculation
5776 int32 x
= (((u
->m_detectStealth
+1) / 5) - (((m_stealthvalue
+1) / 5) + (stealthMod
/5) + 59));
5779 // Check rank if mob is not a player otherwise rank = 1 and there is no modifier when in pvp
5780 if (u
->GetTypeId() != TYPEID_PLAYER
)
5781 rank
= ((Creature
const*)u
)->GetCreatureInfo()->rank
;
5783 // Probabilty of being seen
5785 distanceFormula
= ((sWorld
.getRate(RATE_CREATURE_AGGRO
) * 16) / (abs(levelDiff
) + 1.5)) * 2;
5787 distanceFormula
= (((sWorld
.getRate(RATE_CREATURE_AGGRO
) * 16)) / 2) * (levelDiff
+ 1);
5789 // Original probability values
5790 // removed level of mob in calculation as it should not affect the detection, it is mainly dependant on level difference
5791 //at this distance, the detector has to be a 15% prob of detect
5792 float averageDist
= 1 - 0.11016949 * x
+ 0.00301637 * x
* x
;
5793 if (averageDist
< 1) averageDist
= 1;
5796 if (distance
> averageDist
)
5797 //prob between 10% and 0%
5798 prob
= (averageDist
- 200 + 9 * distance
) / (averageDist
- 20);
5800 prob
= 75 - (60 / averageDist
) * distance
; //prob between 15% and 75% (75% max prob)
5802 // If is not in front, probability floor
5808 // Mob rank affects modifier
5809 modifier
= rank
<= 4 ? 1 + rank
* 0.2f
: 1;
5811 if (distance
< 0.24f
)
5816 // PVP distance assigned depending on level
5817 if (this->GetTypeId() == TYPEID_UNIT
)
5819 // Level diff floor/ceiling <-5,5>
5820 pvpMultiplier
= levelDiff
> 5 ? 12 : levelDiff
< 5 ? 2 : 7 + levelDiff
;
5821 pvpMultiplier
= pvpMultiplier
- (x
/ 100);
5824 // PVP stealth handler
5825 if (this->GetTypeId() == TYPEID_PLAYER
)
5827 // Do not loose stealth when coming from back
5834 // If comes in front
5835 if (isInFront
&& (distance
>= pvpMultiplier
))
5841 if (isInFront
&& (distance
< pvpMultiplier
))
5848 // PVE stealth handler
5849 // Distance of approch player stays stealth 100% is dependant of level. No probabiliy or detection is rolled
5850 // This establishes a buffer zone in between mob start to see you and mob start to roll probabilities or detect you
5851 if ((distance
< 100) && (distance
> ((distanceFormula
* 2) * modifier
)) && (distance
> 0.24f
))
5857 //If victim is level lower or more probability of detection drops
5858 if ((levelDiff
< 0) && (distance
> 0.24f
))
5860 if (abs(levelDiff
) > 4)
5864 if (rand_chance() > ( prob
* modifier
/ (30 + levelDiff
* 5)))
5869 // Level detection based on level, the higher the mob level the higher the chance of detection.
5870 if ((distance
> 0.24f
) && (levelDiff
< 5) && (levelDiff
>= 0))
5872 if (rand_chance() > ((prob
* modifier
) / (30 - levelDiff
* 5) ))
5881 // Didn't match any criteria ?
5882 DEBUG_LOG("Unit::isVisibleForFor unhandled result, dist %f levelDiff %i target_type %u prob %u modifier %u",distance
,levelDiff
,u
->GetTypeId(),prob
, modifier
);
5888 void Unit::SetVisibility(UnitVisibility x
)
5894 Map
*m
= MapManager::Instance().GetMap(GetMapId(), this);
5896 if(GetTypeId()==TYPEID_PLAYER
)
5897 m
->PlayerRelocation((Player
*)this,GetPositionX(),GetPositionY(),GetPositionZ(),GetOrientation());
5899 m
->CreatureRelocation((Creature
*)this,GetPositionX(),GetPositionY(),GetPositionZ(),GetOrientation());
5903 float Unit::GetSpeed( UnitMoveType mtype
) const
5905 return m_speed_rate
[mtype
]*baseMoveSpeed
[mtype
];
5908 void Unit::SetSpeed(UnitMoveType mtype
, float rate
, bool forced
)
5910 m_speed_rate
[mtype
] = 1.0f
;
5911 ApplySpeedMod(mtype
, rate
, forced
, true);
5914 void Unit::ApplySpeedMod(UnitMoveType mtype
, float rate
, bool forced
, bool apply
)
5919 m_speed_rate
[mtype
] *= rate
;
5921 m_speed_rate
[mtype
] /= rate
;
5926 if(forced
) { data
.Initialize(SMSG_FORCE_WALK_SPEED_CHANGE
, 16); }
5927 else { data
.Initialize(MSG_MOVE_SET_WALK_SPEED
, 16); }
5930 if(forced
) { data
.Initialize(SMSG_FORCE_RUN_SPEED_CHANGE
, 16); }
5931 else { data
.Initialize(MSG_MOVE_SET_RUN_SPEED
, 16); }
5934 if(forced
) { data
.Initialize(SMSG_FORCE_RUN_BACK_SPEED_CHANGE
, 16); }
5935 else { data
.Initialize(MSG_MOVE_SET_RUN_BACK_SPEED
, 16); }
5938 if(forced
) { data
.Initialize(SMSG_FORCE_SWIM_SPEED_CHANGE
, 16); }
5939 else { data
.Initialize(MSG_MOVE_SET_SWIM_SPEED
, 16); }
5942 if(forced
) { data
.Initialize(SMSG_FORCE_SWIM_BACK_SPEED_CHANGE
, 16); }
5943 else { data
.Initialize(MSG_MOVE_SET_SWIM_BACK_SPEED
, 16); }
5946 if(forced
) { data
.Initialize(SMSG_FORCE_TURN_RATE_CHANGE
, 16); }
5947 else { data
.Initialize(MSG_MOVE_SET_TURN_RATE
, 16); }
5950 if(forced
) { data
.Initialize(SMSG_FORCE_FLY_SPEED_CHANGE
, 16); }
5951 else { data
.Initialize(SMSG_MOVE_SET_FLY_SPEED
, 16); }
5956 sLog
.outError("Unit::SetSpeed: Unsupported move type (%d), data not sent to client.",mtype
);
5960 data
.append(GetPackGUID());
5962 if (mtype
== MOVE_RUN
) data
<< uint8(0); // new 2.1.0
5963 data
<< float(GetSpeed(mtype
));
5964 SendMessageToSet( &data
, true );
5966 if(Pet
* pet
= GetPet())
5967 pet
->SetSpeed(mtype
,m_speed_rate
[mtype
],forced
);
5970 void Unit::SetHover(bool on
)
5973 CastSpell(this,11010,true);
5975 RemoveAurasDueToSpell(11010);
5978 void Unit::setDeathState(DeathState s
)
5984 if(IsNonMeleeSpellCasted(false))
5985 InterruptNonMeleeSpells(false);
5990 RemoveAllAurasOnDeath();
5991 UnsummonAllTotems();
5993 if (m_deathState
!= ALIVE
&& s
== ALIVE
)
5995 //_ApplyAllAuraMods();
6000 /*########################################
6002 ######## AGGRO SYSTEM ########
6004 ########################################*/
6005 bool Unit::CanHaveThreatList() const
6007 if(GetTypeId() == TYPEID_UNIT
&& !((Creature
*)this)->isPet() && !((Creature
*)this)->isTotem() )
6013 //======================================================================
6015 float Unit::ApplyTotalThreatModifier(float threat
, uint8 school
)
6017 if(!HasAuraType(SPELL_AURA_MOD_THREAT
))
6020 if(school
>= MAX_SPELL_SCHOOL
)
6023 return threat
* m_threatModifier
[school
];
6026 //======================================================================
6028 void Unit::AddThreat(Unit
* pVictim
, float threat
, uint8 school
, SpellEntry
const *threatSpell
)
6030 // Only mobs can manage threat lists
6031 if(CanHaveThreatList())
6032 m_ThreatManager
.addThreat(pVictim
, threat
, school
, threatSpell
);
6035 //======================================================================
6037 void Unit::DeleteThreatList()
6039 m_ThreatManager
.clearReferences();
6042 //======================================================================
6044 void Unit::TauntApply(Unit
* taunter
)
6046 assert(GetTypeId()== TYPEID_UNIT
);
6048 if(!taunter
|| (taunter
->GetTypeId() == TYPEID_PLAYER
&& ((Player
*)taunter
)->isGameMaster()))
6051 if(!CanHaveThreatList())
6054 Unit
*target
= getVictim();
6055 if(target
&& target
== taunter
)
6058 SetInFront(taunter
);
6059 if (((Creature
*)this)->AI())
6060 ((Creature
*)this)->AI()->AttackStart(taunter
);
6062 m_ThreatManager
.tauntApply(taunter
);
6065 //======================================================================
6067 void Unit::TauntFadeOut(Unit
*taunter
)
6069 assert(GetTypeId()== TYPEID_UNIT
);
6071 if(!taunter
|| (taunter
->GetTypeId() == TYPEID_PLAYER
&& ((Player
*)taunter
)->isGameMaster()))
6074 if(!CanHaveThreatList())
6077 Unit
*target
= getVictim();
6078 if(!target
|| target
!= taunter
)
6081 if(m_ThreatManager
.isThreatListEmpty())
6083 if(((Creature
*)this)->AI())
6084 ((Creature
*)this)->AI()->EnterEvadeMode();
6088 m_ThreatManager
.tauntFadeOut(taunter
);
6089 target
= m_ThreatManager
.getHostilTarget();
6091 if (target
&& target
!= taunter
)
6094 if (((Creature
*)this)->AI())
6095 ((Creature
*)this)->AI()->AttackStart(target
);
6099 //======================================================================
6101 bool Unit::SelectHostilTarget()
6103 //function provides main threat functionality
6104 //next-victim-selection algorithm and evade mode are called
6105 //threat list sorting etc.
6107 assert(GetTypeId()== TYPEID_UNIT
);
6108 Unit
* target
= NULL
;
6110 //This function only useful once AI has been initilazied
6111 if (!((Creature
*)this)->AI())
6114 if(!m_ThreatManager
.isThreatListEmpty())
6116 if(!HasAuraType(SPELL_AURA_MOD_TAUNT
))
6118 target
= m_ThreatManager
.getHostilTarget();
6125 ((Creature
*)this)->AI()->AttackStart(target
);
6129 if(isInCombat() && !HasAuraType(SPELL_AURA_MOD_TAUNT
) && CanFreeMove() && m_attackers
.empty())
6130 ((Creature
*)this)->AI()->EnterEvadeMode();
6135 //======================================================================
6136 //======================================================================
6137 //======================================================================
6139 void Unit::CalculateSpellDamageAndDuration(int32
* damage
, int32
* duration
, SpellEntry
const* spellProto
, uint8 effect_index
, int32 effBasePoints
)
6141 Player
* unitPlayer
= (GetTypeId() == TYPEID_PLAYER
) ? (Player
*)this : NULL
;
6143 uint8 comboPoints
= unitPlayer
? unitPlayer
->GetComboPoints() : 0;
6144 bool needClearCombo
= false;
6151 level
= getLevel() - spellProto
->spellLevel
;
6152 if (level
> spellProto
->maxLevel
&& spellProto
->maxLevel
> 0)
6153 level
= spellProto
->maxLevel
;
6155 float basePointsPerLevel
= spellProto
->EffectRealPointsPerLevel
[effect_index
];
6156 float randomPointsPerLevel
= spellProto
->EffectDicePerLevel
[effect_index
];
6157 int32 basePoints
= int32(effBasePoints
+ level
* basePointsPerLevel
);
6158 int32 randomPoints
= int32(spellProto
->EffectDieSides
[effect_index
] + level
* randomPointsPerLevel
);
6159 float comboDamage
= spellProto
->EffectPointsPerComboPoint
[effect_index
];
6161 value
= basePoints
+ rand32(spellProto
->EffectBaseDice
[effect_index
], randomPoints
);
6163 if(int32(spellProto
->EffectBaseDice
[effect_index
]) != randomPoints
&& GetTypeId() == TYPEID_UNIT
&& ((Creature
*)this)->isPet())
6164 value
+= ((Pet
*)this)->GetBonusDamage(); //bonus damage only on spells without fixed basePoints?)
6166 if(comboDamage
!= 0 && unitPlayer
&& m_attacking
&& (m_attacking
->GetGUID() == unitPlayer
->GetComboTarget()))
6168 value
+= (int32
)(comboDamage
* comboPoints
);
6171 if( spellProto
->SpellIconID
== 514 && spellProto
->SpellFamilyName
== SPELLFAMILY_ROGUE
)
6172 value
+= (int32
)(GetTotalAttackPowerValue(BASE_ATTACK
) * comboPoints
* 0.03);
6174 needClearCombo
= true;
6177 if (GetTypeId() == TYPEID_PLAYER
)
6179 ((Player
*)this)->ApplySpellMod(spellProto
->Id
,SPELLMOD_ALL_EFFECTS
, value
);
6180 switch(effect_index
)
6183 ((Player
*)this)->ApplySpellMod(spellProto
->Id
,SPELLMOD_EFFECT1
, value
);
6186 ((Player
*)this)->ApplySpellMod(spellProto
->Id
,SPELLMOD_EFFECT2
, value
);
6189 ((Player
*)this)->ApplySpellMod(spellProto
->Id
,SPELLMOD_EFFECT3
, value
);
6193 ((Player
*)this)->ApplySpellMod(spellProto
->Id
, SPELLMOD_DAMAGE
, value
);
6202 int32 minduration
= GetDuration(spellProto
);
6203 int32 maxduration
= GetMaxDuration(spellProto
);
6205 if( minduration
!= -1 && minduration
!= maxduration
)
6207 *duration
= minduration
+ int32((maxduration
- minduration
) * comboPoints
/ 5);
6208 needClearCombo
= true;
6211 *duration
= minduration
;
6214 if(unitPlayer
&& needClearCombo
)
6215 unitPlayer
->SetComboPoints(unitPlayer
->GetComboTarget(), 0);
6218 void Unit::AddDiminishing(DiminishingMechanics mech
, uint32 hitTime
, uint32 hitCount
)
6220 m_Diminishing
.push_back(DiminishingReturn(mech
,hitTime
,hitCount
));
6223 DiminishingLevels
Unit::GetDiminishing(DiminishingMechanics mech
)
6225 for(Diminishing::iterator i
= m_Diminishing
.begin(); i
!= m_Diminishing
.end(); ++i
)
6227 if(i
->Mechanic
!= mech
) continue;
6228 if(!i
->hitCount
) return DIMINISHING_LEVEL_1
;
6229 if(!i
->hitTime
) return DIMINISHING_LEVEL_1
;
6230 // If last spell was casted more than 15 seconds ago - reset the count.
6231 if((getMSTime() - i
->hitTime
) > 15000)
6233 i
->hitCount
= DIMINISHING_LEVEL_1
;
6234 return DIMINISHING_LEVEL_1
;
6236 // or else increase the count.
6239 if(i
->hitCount
> DIMINISHING_LEVEL_2
)
6241 i
->hitCount
= DIMINISHING_LEVEL_IMMUNE
;
6242 return DIMINISHING_LEVEL_IMMUNE
;
6244 else return DiminishingLevels(i
->hitCount
);
6247 return DIMINISHING_LEVEL_1
;
6250 void Unit::IncrDiminishing(DiminishingMechanics mech
, uint32 duration
)
6252 // Checking for existing in the table
6253 bool IsExist
= false;
6254 for(Diminishing::iterator i
= m_Diminishing
.begin(); i
!= m_Diminishing
.end(); ++i
)
6256 if(i
->Mechanic
!= mech
)
6260 if(i
->hitCount
< DIMINISHING_LEVEL_IMMUNE
)
6265 case DIMINISHING_LEVEL_2
: i
->hitTime
= uint32(getMSTime() + duration
); break;
6266 case DIMINISHING_LEVEL_3
: i
->hitTime
= uint32(getMSTime() + duration
*0.5); break;
6267 case DIMINISHING_LEVEL_IMMUNE
: i
->hitTime
= uint32(getMSTime() + duration
*0.25); break;
6275 AddDiminishing(mech
,uint32(getMSTime() + duration
),DIMINISHING_LEVEL_2
);
6278 void Unit::UpdateDiminishingTime(DiminishingMechanics mech
)
6280 // Checking for existing in the table
6281 bool IsExist
= false;
6282 for(Diminishing::iterator i
= m_Diminishing
.begin(); i
!= m_Diminishing
.end(); ++i
)
6284 if(i
->Mechanic
!= mech
)
6288 i
->hitTime
= getMSTime();
6293 AddDiminishing(mech
,getMSTime(),DIMINISHING_LEVEL_1
);
6296 DiminishingMechanics
Unit::Mechanic2DiminishingMechanics(uint32 mech
)
6300 case MECHANIC_CHARM
: case MECHANIC_FEAR
: case MECHANIC_SLEEP
:
6301 return DIMINISHING_MECHANIC_CHARM
;
6302 case MECHANIC_CONFUSED
: case MECHANIC_KNOCKOUT
: case MECHANIC_POLYMORPH
:
6303 return DIMINISHING_MECHANIC_CONFUSE
;
6304 case MECHANIC_ROOT
: case MECHANIC_FREEZE
:
6305 return DIMINISHING_MECHANIC_ROOT
;
6306 case MECHANIC_STUNDED
:
6307 return DIMINISHING_MECHANIC_STUN
;
6308 case MECHANIC_CHASE
:
6309 return DIMINISHING_MECHANIC_SPEED
;
6313 return DIMINISHING_NONE
;
6316 float Unit::ApplyDiminishingToDuration(DiminishingMechanics mech
, int32 duration
,Unit
* caster
)
6318 if(duration
== -1 || mech
== DIMINISHING_NONE
)
6321 // Duration of crowd control abilities on pvp target is limited by 10 sec. (2.2.0)
6322 if(duration
> 10000)
6324 // test pet/charm masters instead pets/charmeds
6325 Unit
const* targetOwner
= GetCharmerOrOwner();
6326 Unit
const* casterOwner
= caster
->GetCharmerOrOwner();
6328 Unit
const* target
= targetOwner
? targetOwner
: this;
6329 Unit
const* source
= casterOwner
? casterOwner
: caster
;
6331 if(target
->GetTypeId() == TYPEID_PLAYER
&& source
->GetTypeId() == TYPEID_PLAYER
)
6337 // Stun diminishing is applies to mobs too
6338 if(mech
== DIMINISHING_MECHANIC_STUN
|| GetTypeId() == TYPEID_PLAYER
)
6340 DiminishingLevels diminish
= GetDiminishing(mech
);
6343 case DIMINISHING_LEVEL_1
: IncrDiminishing(mech
, duration
); break;
6344 case DIMINISHING_LEVEL_2
: IncrDiminishing(mech
, duration
); mod
= 0.5f
; break;
6345 case DIMINISHING_LEVEL_3
: IncrDiminishing(mech
, duration
); mod
= 0.25f
; break;
6346 case DIMINISHING_LEVEL_IMMUNE
: mod
= 0.0f
; break;
6354 Creature
* Unit::SummonCreature(uint32 id
, float x
, float y
, float z
, float ang
,TempSummonType spwtype
,uint32 despwtime
)
6356 TemporarySummon
* pCreature
= new TemporarySummon(this,this);
6358 pCreature
->SetInstanceId(GetInstanceId());
6360 if (!pCreature
->Create(objmgr
.GenerateLowGuid(HIGHGUID_UNIT
), GetMapId(), x
, y
, z
, ang
, id
))
6366 pCreature
->Summon(spwtype
, despwtime
);
6368 //return the creature therewith the summoner has access to it
6372 Unit
* Unit::GetUnit(WorldObject
& object
, uint64 guid
)
6374 return ObjectAccessor::Instance().GetUnit(object
,guid
);
6377 bool Unit::isVisibleForInState( Player
const* u
, bool inVisibleList
) const
6379 return isVisibleForOrDetect(u
,false,inVisibleList
);
6382 uint32
Unit::GetCreatureType() const
6384 if(GetTypeId() == TYPEID_PLAYER
)
6386 switch(((Player
const*)this)->m_form
)
6393 case FORM_GHOSTWOLF
:
6394 case FORM_SWIFT_FLIGHT
:
6396 return CREATURE_TYPE_BEAST
;
6398 case FORM_SPIRITOFREDEMPTION
:
6399 return CREATURE_TYPE_ELEMENTAL
;
6402 return CREATURE_TYPE_HUMANOID
;
6406 return ((Creature
*)this)->GetCreatureInfo()->type
;
6409 /*#######################################
6411 ######## STAT SYSTEM ########
6413 #######################################*/
6415 bool Unit::HandleStatModifier(UnitMods unitMod
, UnitModifierType modifierType
, float amount
, bool apply
)
6417 if(unitMod
>= UNIT_MOD_END
|| modifierType
>= MODIFIER_TYPE_END
)
6419 sLog
.outError("ERROR in HandleStatModifier(): nonexisted UnitMods or wrong UnitModifierType!");
6425 switch(modifierType
)
6429 m_auraModifiersGroup
[unitMod
][modifierType
] += apply
? amount
: -amount
;
6433 if(amount
<= -100.0f
) //small hack-fix for -100% modifiers
6436 val
= (100.0f
+ amount
) / 100.0f
;
6437 m_auraModifiersGroup
[unitMod
][modifierType
] *= apply
? val
: (1.0f
/val
);
6444 if(!CanModifyStats())
6449 case UNIT_MOD_STAT_STRENGTH
:
6450 case UNIT_MOD_STAT_AGILITY
:
6451 case UNIT_MOD_STAT_STAMINA
:
6452 case UNIT_MOD_STAT_INTELLECT
:
6453 case UNIT_MOD_STAT_SPIRIT
: UpdateStats(GetStatByAuraGroup(unitMod
)); break;
6455 case UNIT_MOD_ARMOR
: UpdateArmor(); break;
6456 case UNIT_MOD_HEALTH
: UpdateMaxHealth(); break;
6460 case UNIT_MOD_FOCUS
:
6461 case UNIT_MOD_ENERGY
:
6462 case UNIT_MOD_HAPPINESS
: UpdateMaxPower(GetPowerTypeByAuraGroup(unitMod
)); break;
6464 case UNIT_MOD_RESISTANCE_HOLY
:
6465 case UNIT_MOD_RESISTANCE_FIRE
:
6466 case UNIT_MOD_RESISTANCE_NATURE
:
6467 case UNIT_MOD_RESISTANCE_FROST
:
6468 case UNIT_MOD_RESISTANCE_SHADOW
:
6469 case UNIT_MOD_RESISTANCE_ARCANE
: UpdateResistances(GetSpellSchoolByAuraGroup(unitMod
)); break;
6471 case UNIT_MOD_ATTACK_POWER
: UpdateAttackPowerAndDamage(); break;
6472 case UNIT_MOD_ATTACK_POWER_RANGED
: UpdateAttackPowerAndDamage(true); break;
6474 case UNIT_MOD_DAMAGE_MAINHAND
: UpdateDamagePhysical(BASE_ATTACK
); break;
6475 case UNIT_MOD_DAMAGE_OFFHAND
: UpdateDamagePhysical(OFF_ATTACK
); break;
6476 case UNIT_MOD_DAMAGE_RANGED
: UpdateDamagePhysical(RANGED_ATTACK
); break;
6485 float Unit::GetModifierValue(UnitMods unitMod
, UnitModifierType modifierType
) const
6487 if( unitMod
>= UNIT_MOD_END
|| modifierType
>= MODIFIER_TYPE_END
)
6489 sLog
.outError("ERROR: trial to access nonexisted modifier value from UnitMods!");
6493 if(modifierType
== TOTAL_PCT
&& m_auraModifiersGroup
[unitMod
][modifierType
] <= 0.0f
)
6496 return m_auraModifiersGroup
[unitMod
][modifierType
];
6499 float Unit::GetTotalStatValue(Stats stat
) const
6501 UnitMods unitMod
= UnitMods(UNIT_MOD_STAT_START
+ stat
);
6503 if(m_auraModifiersGroup
[unitMod
][TOTAL_PCT
] <= 0.0f
)
6506 // value = ((base_value * base_pct) + total_value) * total_pct
6507 float value
= m_auraModifiersGroup
[unitMod
][BASE_VALUE
] + GetCreateStat(stat
);
6508 value
*= m_auraModifiersGroup
[unitMod
][BASE_PCT
];
6509 value
+= m_auraModifiersGroup
[unitMod
][TOTAL_VALUE
];
6510 value
*= m_auraModifiersGroup
[unitMod
][TOTAL_PCT
];
6515 float Unit::GetTotalAuraModValue(UnitMods unitMod
) const
6517 if(unitMod
>= UNIT_MOD_END
)
6519 sLog
.outError("ERROR: trial to access nonexisted UnitMods in GetTotalAuraModValue()!");
6523 if(m_auraModifiersGroup
[unitMod
][TOTAL_PCT
] <= 0.0f
)
6526 float value
= m_auraModifiersGroup
[unitMod
][BASE_VALUE
];
6527 value
*= m_auraModifiersGroup
[unitMod
][BASE_PCT
];
6528 value
+= m_auraModifiersGroup
[unitMod
][TOTAL_VALUE
];
6529 value
*= m_auraModifiersGroup
[unitMod
][TOTAL_PCT
];
6534 SpellSchools
Unit::GetSpellSchoolByAuraGroup(UnitMods unitMod
) const
6536 SpellSchools school
= SPELL_SCHOOL_NORMAL
;
6540 case UNIT_MOD_RESISTANCE_HOLY
: school
= SPELL_SCHOOL_HOLY
; break;
6541 case UNIT_MOD_RESISTANCE_FIRE
: school
= SPELL_SCHOOL_FIRE
; break;
6542 case UNIT_MOD_RESISTANCE_NATURE
: school
= SPELL_SCHOOL_NATURE
; break;
6543 case UNIT_MOD_RESISTANCE_FROST
: school
= SPELL_SCHOOL_FROST
; break;
6544 case UNIT_MOD_RESISTANCE_SHADOW
: school
= SPELL_SCHOOL_SHADOW
; break;
6545 case UNIT_MOD_RESISTANCE_ARCANE
: school
= SPELL_SCHOOL_ARCANE
; break;
6554 Stats
Unit::GetStatByAuraGroup(UnitMods unitMod
) const
6556 Stats stat
= STAT_STRENGTH
;
6560 case UNIT_MOD_STAT_STRENGTH
: stat
= STAT_STRENGTH
; break;
6561 case UNIT_MOD_STAT_AGILITY
: stat
= STAT_AGILITY
; break;
6562 case UNIT_MOD_STAT_STAMINA
: stat
= STAT_STAMINA
; break;
6563 case UNIT_MOD_STAT_INTELLECT
: stat
= STAT_INTELLECT
; break;
6564 case UNIT_MOD_STAT_SPIRIT
: stat
= STAT_SPIRIT
; break;
6573 Powers
Unit::GetPowerTypeByAuraGroup(UnitMods unitMod
) const
6575 Powers power
= POWER_MANA
;
6579 case UNIT_MOD_MANA
: power
= POWER_MANA
; break;
6580 case UNIT_MOD_RAGE
: power
= POWER_RAGE
; break;
6581 case UNIT_MOD_FOCUS
: power
= POWER_FOCUS
; break;
6582 case UNIT_MOD_ENERGY
: power
= POWER_ENERGY
; break;
6583 case UNIT_MOD_HAPPINESS
: power
= POWER_HAPPINESS
; break;
6592 float Unit::GetTotalAttackPowerValue(WeaponAttackType attType
) const
6594 UnitMods unitMod
= (attType
== RANGED_ATTACK
) ? UNIT_MOD_ATTACK_POWER_RANGED
: UNIT_MOD_ATTACK_POWER
;
6596 float val
= GetTotalAuraModValue(unitMod
);
6603 float Unit::GetWeaponDamageRange(WeaponAttackType attType
,WeaponDamageRange type
) const
6605 if (attType
== OFF_ATTACK
&& !haveOffhandWeapon())
6608 return m_weaponDamage
[attType
][type
];
6611 void Unit::SetLevel(uint32 lvl
)
6613 SetUInt32Value(UNIT_FIELD_LEVEL
,lvl
);
6616 if (GetTypeId() == TYPEID_PLAYER
)
6617 ((Player
*)this)->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_LEVEL
);
6620 void Unit::SetHealth(uint32 val
)
6622 uint32 maxHealth
= GetMaxHealth();
6626 SetUInt32Value(UNIT_FIELD_HEALTH
,val
);
6629 if (GetTypeId() == TYPEID_PLAYER
)
6630 ((Player
*)this)->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_CUR_HP
);
6633 void Unit::SetMaxHealth(uint32 val
)
6635 uint32 health
= GetHealth();
6636 SetUInt32Value(UNIT_FIELD_MAXHEALTH
,val
);
6639 if (GetTypeId() == TYPEID_PLAYER
)
6640 ((Player
*)this)->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_MAX_HP
);
6646 void Unit::SetPower(Powers power
, uint32 val
)
6648 uint32 maxPower
= GetMaxPower(power
);
6652 SetStatInt32Value(UNIT_FIELD_POWER1
+ power
,val
);
6655 if (GetTypeId() == TYPEID_PLAYER
)
6656 ((Player
*)this)->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_CUR_POWER
);
6659 void Unit::SetMaxPower(Powers power
, uint32 val
)
6661 uint32 cur_power
= GetPower(power
);
6662 SetStatInt32Value(UNIT_FIELD_MAXPOWER1
+ power
,val
);
6665 if (GetTypeId() == TYPEID_PLAYER
)
6666 ((Player
*)this)->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_MAX_POWER
);
6669 SetPower(power
, val
);
6672 void Unit::ApplyPowerMod(Powers power
, uint32 val
, bool apply
)
6674 ApplyModUInt32Value(UNIT_FIELD_POWER1
+power
, val
, apply
);
6677 if (GetTypeId() == TYPEID_PLAYER
)
6678 ((Player
*)this)->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_CUR_POWER
);
6681 void Unit::ApplyMaxPowerMod(Powers power
, uint32 val
, bool apply
)
6683 ApplyModUInt32Value(UNIT_FIELD_MAXPOWER1
+power
, val
, apply
);
6686 if (GetTypeId() == TYPEID_PLAYER
)
6687 ((Player
*)this)->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_MAX_POWER
);
6690 void Unit::ApplyAuraProcTriggerDamage( Aura
* aura
, bool apply
)
6692 AuraList
& tAuraProcTriggerDamage
= m_modAuras
[SPELL_AURA_PROC_TRIGGER_DAMAGE
];
6694 tAuraProcTriggerDamage
.push_back(aura
);
6696 tAuraProcTriggerDamage
.remove(aura
);
6699 uint32
Unit::GetCreatePowers( Powers power
) const
6701 // POWER_FOCUS and POWER_HAPPINESS only have hunter pet
6704 case POWER_MANA
: return GetCreateMana();
6705 case POWER_RAGE
: return 1000;
6706 case POWER_FOCUS
: return (GetTypeId()==TYPEID_PLAYER
|| !((Creature
const*)this)->isPet() || ((Pet
const*)this)->getPetType()!=HUNTER_PET
? 0 : 100);
6707 case POWER_ENERGY
: return 100;
6708 case POWER_HAPPINESS
: return (GetTypeId()==TYPEID_PLAYER
|| !((Creature
const*)this)->isPet() || ((Pet
const*)this)->getPetType()!=HUNTER_PET
? 0 : 1050000);
6714 void Unit::CleanupsBeforeDelete()
6716 if(m_uint32Values
) // only for fully created object
6718 m_Events
.KillAllEvents();
6721 getHostilRefManager().setOnlineOfflineState(false);
6727 CharmInfo
* Unit::InitCharmInfo(Unit
*charm
)
6730 m_charmInfo
= new CharmInfo(charm
);
6734 CharmInfo::CharmInfo(Unit
* unit
)
6735 : m_unit(unit
), m_CommandState(COMMAND_STAY
), m_ReactSate(REACT_PASSIVE
), m_petnumber(0)
6737 for(int i
=0; i
<4; ++i
)
6739 m_charmspells
[i
].spellId
= 0;
6740 m_charmspells
[i
].active
= ACT_DISABLED
;
6744 void CharmInfo::InitPetActionBar()
6746 // the first 3 SpellOrActions are attack, follow and stay
6747 for(uint32 i
= 0; i
< 3; i
++)
6749 PetActionBar
[i
].Type
= ACT_COMMAND
;
6750 PetActionBar
[i
].SpellOrAction
= COMMAND_ATTACK
- i
;
6752 PetActionBar
[i
+ 7].Type
= ACT_REACTION
;
6753 PetActionBar
[i
+ 7].SpellOrAction
= COMMAND_ATTACK
- i
;
6755 for(uint32 i
=0; i
< 4; i
++)
6757 PetActionBar
[i
+ 3].Type
= ACT_DISABLED
;
6758 PetActionBar
[i
+ 3].SpellOrAction
= 0;
6762 void CharmInfo::InitEmptyActionBar()
6764 for(uint32 x
= 1; x
< 10; ++x
)
6766 PetActionBar
[x
].Type
= ACT_CAST
;
6767 PetActionBar
[x
].SpellOrAction
= 0;
6769 PetActionBar
[0].Type
= ACT_COMMAND
;
6770 PetActionBar
[0].SpellOrAction
= COMMAND_ATTACK
;
6773 void CharmInfo::InitPossessCreateSpells()
6775 if(m_unit
->GetTypeId() == TYPEID_PLAYER
)
6778 InitEmptyActionBar(); //charm action bar
6780 for(uint32 x
= 0; x
< CREATURE_MAX_SPELLS
; ++x
)
6782 if (IsPassiveSpell(((Creature
*)m_unit
)->m_spells
[x
]))
6783 m_unit
->CastSpell(m_unit
, ((Creature
*)m_unit
)->m_spells
[x
], true);
6785 AddSpellToAB(0, ((Creature
*)m_unit
)->m_spells
[x
], ACT_CAST
);
6789 void CharmInfo::InitCharmCreateSpells()
6791 if(m_unit
->GetTypeId() == TYPEID_PLAYER
) //charmed players don't have spells
6793 InitEmptyActionBar();
6799 for(uint32 x
= 0; x
< CREATURE_MAX_SPELLS
; ++x
)
6801 uint32 spellId
= ((Creature
*)m_unit
)->m_spells
[x
];
6802 m_charmspells
[x
].spellId
= spellId
;
6807 if (IsPassiveSpell(spellId
))
6809 m_unit
->CastSpell(m_unit
, spellId
, true);
6810 m_charmspells
[x
].active
= ACT_PASSIVE
;
6814 ActiveStates newstate
;
6815 bool onlyselfcast
= true;
6816 SpellEntry
const *spellInfo
= sSpellStore
.LookupEntry(spellId
);
6818 if(!spellInfo
) onlyselfcast
= false;
6819 for(uint32 i
= 0;i
<3 && onlyselfcast
;++i
) //non existent spell will not make any problems as onlyselfcast would be false -> break right away
6821 if(spellInfo
->EffectImplicitTargetA
[i
] != TARGET_SELF
&& spellInfo
->EffectImplicitTargetA
[i
] != 0)
6822 onlyselfcast
= false;
6825 if(onlyselfcast
|| !IsPositiveSpell(spellId
)) //only self cast and spells versus enemies are autocastable
6826 newstate
= ACT_DISABLED
;
6828 newstate
= ACT_CAST
;
6830 AddSpellToAB(0, spellId
, newstate
);
6835 bool CharmInfo::AddSpellToAB(uint32 oldid
, uint32 newid
, ActiveStates newstate
)
6837 for(uint8 i
= 0; i
< 10; i
++)
6839 if((PetActionBar
[i
].Type
== ACT_DISABLED
|| PetActionBar
[i
].Type
== ACT_ENABLED
|| PetActionBar
[i
].Type
== ACT_CAST
) && PetActionBar
[i
].SpellOrAction
== oldid
)
6841 PetActionBar
[i
].SpellOrAction
= newid
;
6844 if(newstate
== ACT_DECIDE
)
6845 PetActionBar
[i
].Type
= ACT_DISABLED
;
6847 PetActionBar
[i
].Type
= newstate
;
6856 void CharmInfo::ToggleCreatureAutocast(uint32 spellid
, bool apply
)
6858 if(IsPassiveSpell(spellid
))
6861 for(uint32 x
= 0; x
< CREATURE_MAX_SPELLS
; ++x
)
6863 if(spellid
== m_charmspells
[x
].spellId
)
6865 m_charmspells
[x
].active
= apply
? ACT_ENABLED
: ACT_DISABLED
;
6870 void CharmInfo::SetPetNumber(uint32 petnumber
, bool statwindow
)
6872 m_petnumber
= petnumber
;
6874 m_unit
->SetUInt32Value(UNIT_FIELD_PETNUMBER
, m_petnumber
);
6876 m_unit
->SetUInt32Value(UNIT_FIELD_PETNUMBER
, 0);
6879 bool Unit::isFrozen() const
6881 AuraList
const& mRoot
= GetAurasByType(SPELL_AURA_MOD_ROOT
);
6882 for(AuraList::const_iterator i
= mRoot
.begin(); i
!= mRoot
.end(); ++i
)
6883 if( (*i
)->GetSpellProto()->School
== SPELL_SCHOOL_FROST
)