PageRenderTime 70ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 3ms

/src/server/game/Entities/Unit/Unit.cpp

https://bitbucket.org/vitasic/gsv23.7
C++ | 12650 lines | 9953 code | 1410 blank | 1287 comment | 3557 complexity | 7ec1cff59d0e2ad064991a504da14235 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

  1. /*
  2. * Copyright (C) 2008-2012 TrinityCore <http://www.trinitycore.org/>
  3. *
  4. * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
  5. *
  6. * This program is free software; you can redistribute it and/or modify it
  7. * under the terms of the GNU General Public License as published by the
  8. * Free Software Foundation; either version 2 of the License, or (at your
  9. * option) any later version.
  10. */
  11. #include "Common.h"
  12. #include "CreatureAIImpl.h"
  13. #include "Log.h"
  14. #include "Opcodes.h"
  15. #include "WorldPacket.h"
  16. #include "WorldSession.h"
  17. #include "World.h"
  18. #include "ObjectMgr.h"
  19. #include "SpellMgr.h"
  20. #include "Unit.h"
  21. #include "QuestDef.h"
  22. #include "Player.h"
  23. #include "Creature.h"
  24. #include "Spell.h"
  25. #include "Group.h"
  26. #include "SpellAuras.h"
  27. #include "SpellAuraEffects.h"
  28. #include "MapManager.h"
  29. #include "ObjectAccessor.h"
  30. #include "CreatureAI.h"
  31. #include "Formulas.h"
  32. #include "Pet.h"
  33. #include "Util.h"
  34. #include "Totem.h"
  35. #include "Battleground.h"
  36. #include "OutdoorPvP.h"
  37. #include "InstanceSaveMgr.h"
  38. #include "GridNotifiersImpl.h"
  39. #include "CellImpl.h"
  40. #include "CreatureGroups.h"
  41. #include "PetAI.h"
  42. #include "PassiveAI.h"
  43. #include "TemporarySummon.h"
  44. #include "Vehicle.h"
  45. #include "Transport.h"
  46. #include "InstanceScript.h"
  47. #include "MoveSplineInit.h"
  48. #include "MoveSpline.h"
  49. #include "PathFinder.h"
  50. #include <math.h>
  51. float baseMoveSpeed[MAX_MOVE_TYPE] =
  52. {
  53. 2.5f, // MOVE_WALK
  54. 7.0f, // MOVE_RUN
  55. 4.5f, // MOVE_RUN_BACK
  56. 4.722222f, // MOVE_SWIM
  57. 2.5f, // MOVE_SWIM_BACK
  58. 3.141594f, // MOVE_TURN_RATE
  59. 7.0f, // MOVE_FLIGHT
  60. 4.5f, // MOVE_FLIGHT_BACK
  61. 3.14f // MOVE_PITCH_RATE
  62. };
  63. float playerBaseMoveSpeed[MAX_MOVE_TYPE] = {
  64. 2.5f, // MOVE_WALK
  65. 7.0f, // MOVE_RUN
  66. 4.5f, // MOVE_RUN_BACK
  67. 4.722222f, // MOVE_SWIM
  68. 2.5f, // MOVE_SWIM_BACK
  69. 3.141594f, // MOVE_TURN_RATE
  70. 7.0f, // MOVE_FLIGHT
  71. 4.5f, // MOVE_FLIGHT_BACK
  72. 3.14f // MOVE_PITCH_RATE
  73. };
  74. // Used for prepare can/can`t triggr aura
  75. static bool InitTriggerAuraData();
  76. // Define can trigger auras
  77. static bool isTriggerAura[TOTAL_AURAS];
  78. // Define can`t trigger auras (need for disable second trigger)
  79. static bool isNonTriggerAura[TOTAL_AURAS];
  80. // Triggered always, even from triggered spells
  81. static bool isAlwaysTriggeredAura[TOTAL_AURAS];
  82. // Prepare lists
  83. static bool procPrepared = InitTriggerAuraData();
  84. DamageInfo::DamageInfo(Unit* _attacker, Unit* _victim, uint32 _damage, SpellEntry const* _spellInfo, SpellSchoolMask _schoolMask, DamageEffectType _damageType)
  85. : m_attacker(_attacker), m_victim(_victim), m_damage(_damage), m_spellInfo(_spellInfo), m_schoolMask(_schoolMask),
  86. m_damageType(_damageType), m_attackType(BASE_ATTACK)
  87. {
  88. m_absorb = 0;
  89. m_resist = 0;
  90. m_block = 0;
  91. }
  92. DamageInfo::DamageInfo(CalcDamageInfo& dmgInfo)
  93. : m_attacker(dmgInfo.attacker), m_victim(dmgInfo.target), m_damage(dmgInfo.damage), m_spellInfo(NULL), m_schoolMask(SpellSchoolMask(dmgInfo.damageSchoolMask)),
  94. m_damageType(DIRECT_DAMAGE), m_attackType(dmgInfo.attackType)
  95. {
  96. m_absorb = 0;
  97. m_resist = 0;
  98. m_block = 0;
  99. }
  100. void DamageInfo::ModifyDamage(int32 amount)
  101. {
  102. amount = std::min(amount, int32(GetDamage()));
  103. m_damage += amount;
  104. }
  105. void DamageInfo::AbsorbDamage(uint32 amount)
  106. {
  107. amount = std::min(amount, GetDamage());
  108. m_absorb += amount;
  109. m_damage -= amount;
  110. }
  111. void DamageInfo::ResistDamage(uint32 amount)
  112. {
  113. amount = std::min(amount, GetDamage());
  114. m_resist += amount;
  115. m_damage -= amount;
  116. }
  117. void DamageInfo::BlockDamage(uint32 amount)
  118. {
  119. amount = std::min(amount, GetDamage());
  120. m_block += amount;
  121. m_damage -= amount;
  122. }
  123. static uint32 SpellFamilyNameByClass[] = {4, 10, 9, 8, 6, 15, 11, 3, 5, 18, 7};
  124. ProcEventInfo::ProcEventInfo(Unit* actor, Unit* actionTarget, Unit* procTarget, uint32 typeMask, uint32 spellTypeMask, uint32 spellPhaseMask, uint32 hitMask, Spell* spell, DamageInfo* damageInfo, HealInfo* healInfo)
  125. :_actor(actor), _actionTarget(actionTarget), _procTarget(procTarget), _typeMask(typeMask), _spellTypeMask(spellTypeMask), _spellPhaseMask(spellPhaseMask),
  126. _hitMask(hitMask), _spell(spell), _damageInfo(damageInfo), _healInfo(healInfo) { }
  127. // we can disable this warning for this since it only
  128. // causes undefined behavior when passed to the base class constructor
  129. #ifdef _MSC_VER
  130. #pragma warning(disable:4355)
  131. #endif
  132. Unit::Unit(bool isWorldObject): WorldObject(isWorldObject),
  133. m_movedPlayer(NULL), m_lastSanctuaryTime(0), IsAIEnabled(false), NeedChangeAI(false),
  134. m_ControlledByPlayer(false), i_AI(NULL), i_disabledAI(NULL), m_procDeep(0),
  135. m_removedAurasCount(0), i_motionMaster(this), m_ThreatManager(this), m_vehicle(NULL),
  136. m_vehicleKit(NULL), m_unitTypeMask(UNIT_MASK_NONE), m_HostileRefManager(this), movespline(new Movement::MoveSpline())
  137. {
  138. #ifdef _MSC_VER
  139. #pragma warning(default:4355)
  140. #endif
  141. m_objectType |= TYPEMASK_UNIT;
  142. m_objectTypeId = TYPEID_UNIT;
  143. m_updateFlag = (UPDATEFLAG_LIVING | UPDATEFLAG_HAS_POSITION);
  144. m_attackTimer[BASE_ATTACK] = 0;
  145. m_attackTimer[OFF_ATTACK] = 0;
  146. m_attackTimer[RANGED_ATTACK] = 0;
  147. m_modAttackSpeedPct[BASE_ATTACK] = 1.0f;
  148. m_modAttackSpeedPct[OFF_ATTACK] = 1.0f;
  149. m_modAttackSpeedPct[RANGED_ATTACK] = 1.0f;
  150. m_extraAttacks = 0;
  151. m_canDualWield = false;
  152. m_rootTimes = 0;
  153. m_state = 0;
  154. m_deathState = ALIVE;
  155. for(uint8 i = 0; i < CURRENT_MAX_SPELL; ++i)
  156. m_currentSpells[i] = NULL;
  157. m_addDmgOnce = 0;
  158. for(uint8 i = 0; i < MAX_SUMMON_SLOT; ++i)
  159. m_SummonSlot[i] = 0;
  160. m_ObjectSlot[0] = m_ObjectSlot[1] = m_ObjectSlot[2] = m_ObjectSlot[3] = 0;
  161. m_auraUpdateIterator = m_ownedAuras.end();
  162. m_interruptMask = 0;
  163. m_transform = 0;
  164. m_canModifyStats = false;
  165. for(uint8 i = 0; i < MAX_SPELL_IMMUNITY; ++i)
  166. m_spellImmune[i].clear();
  167. for(uint8 i = 0; i < UNIT_MOD_END; ++i)
  168. {
  169. m_auraModifiersGroup[i][BASE_VALUE] = 0.0f;
  170. m_auraModifiersGroup[i][BASE_PCT] = 1.0f;
  171. m_auraModifiersGroup[i][TOTAL_VALUE] = 0.0f;
  172. m_auraModifiersGroup[i][TOTAL_PCT] = 1.0f;
  173. }
  174. // implement 50% base damage from offhand
  175. m_auraModifiersGroup[UNIT_MOD_DAMAGE_OFFHAND][TOTAL_PCT] = 0.5f;
  176. for(uint8 i = 0; i < MAX_ATTACK; ++i)
  177. {
  178. m_weaponDamage[i][MINDAMAGE] = BASE_MINDAMAGE;
  179. m_weaponDamage[i][MAXDAMAGE] = BASE_MAXDAMAGE;
  180. }
  181. for(uint8 i = 0; i < MAX_STATS; ++i)
  182. m_createStats[i] = 0.0f;
  183. m_attacking = NULL;
  184. m_modMeleeHitChance = 0.0f;
  185. m_modRangedHitChance = 0.0f;
  186. m_modSpellHitChance = 0.0f;
  187. m_baseSpellCritChance = 5;
  188. m_CombatTimer = 0;
  189. m_lastManaUse = 0;
  190. for(uint8 i = 0; i < MAX_SPELL_SCHOOL; ++i)
  191. m_threatModifier[i] = 1.0f;
  192. m_isSorted = true;
  193. for(uint8 i = 0; i < MAX_MOVE_TYPE; ++i)
  194. m_speed_rate[i] = 1.0f;
  195. m_charmInfo = NULL;
  196. m_reducedThreatPercent = 0;
  197. m_misdirectionTargetGUID = 0;
  198. // remove aurastates allowing special moves
  199. for(uint8 i = 0; i < MAX_REACTIVE; ++i)
  200. m_reactiveTimer[i] = 0;
  201. m_cleanupDone = false;
  202. m_duringRemoveFromWorld = false;
  203. m_serverSideVisibility.SetValue(SERVERSIDE_VISIBILITY_GHOST, GHOST_VISIBILITY_ALIVE);
  204. _focusSpell = NULL;
  205. _targetLocked = false;
  206. m_lastLiquid = NULL;
  207. }
  208. bool GlobalCooldownMgr::HasGlobalCooldown(SpellEntry const* spellInfo) const
  209. {
  210. GlobalCooldownList::const_iterator itr = m_GlobalCooldowns.find(spellInfo->StartRecoveryCategory);
  211. return itr != m_GlobalCooldowns.end() && itr->second.duration && getMSTimeDiff(itr->second.cast_time, getMSTime()) < itr->second.duration;
  212. }
  213. void GlobalCooldownMgr::AddGlobalCooldown(SpellEntry const* spellInfo, uint32 gcd)
  214. {
  215. m_GlobalCooldowns[spellInfo->StartRecoveryCategory] = GlobalCooldown(gcd, getMSTime());
  216. }
  217. void GlobalCooldownMgr::CancelGlobalCooldown(SpellEntry const* spellInfo)
  218. {
  219. m_GlobalCooldowns[spellInfo->StartRecoveryCategory].duration = 0;
  220. }
  221. Unit::~Unit()
  222. {
  223. // set current spells as deletable
  224. for(uint8 i = 0; i < CURRENT_MAX_SPELL; ++i)
  225. {
  226. if(m_currentSpells[i])
  227. {
  228. m_currentSpells[i]->SetReferencedFromCurrent(false);
  229. m_currentSpells[i] = NULL;
  230. }
  231. }
  232. _DeleteRemovedAuras();
  233. // remove veiw point for spectator
  234. if (!m_sharedVision.empty())
  235. {
  236. for (SharedVisionList::iterator itr = m_sharedVision.begin(); itr != m_sharedVision.end(); ++itr)
  237. if ((*itr)->isSpectator() && (*itr)->getSpectateFrom())
  238. {
  239. (*itr)->SetViewpoint((*itr)->getSpectateFrom(), false);
  240. if (m_sharedVision.empty())
  241. break;
  242. --itr;
  243. }
  244. }
  245. delete m_charmInfo;
  246. delete m_vehicleKit;
  247. delete movespline;
  248. ASSERT(!m_duringRemoveFromWorld);
  249. ASSERT(!m_attacking);
  250. ASSERT(m_attackers.empty());
  251. ASSERT(m_sharedVision.empty());
  252. ASSERT(m_Controlled.empty());
  253. ASSERT(m_appliedAuras.empty());
  254. ASSERT(m_ownedAuras.empty());
  255. ASSERT(m_removedAuras.empty());
  256. ASSERT(m_gameObj.empty());
  257. ASSERT(m_dynObj.empty());
  258. }
  259. void Unit::Update(uint32 p_time)
  260. {
  261. m_Events.Update(p_time);
  262. if(!IsInWorld())
  263. return;
  264. _UpdateSpells(p_time);
  265. // If this is set during update SetCantProc(false) call is missing somewhere in the code
  266. // Having this would prevent spells from being proced, so let's crash
  267. ASSERT(!m_procDeep);
  268. if(CanHaveThreatList() && getThreatManager().isNeedUpdateToClient(p_time))
  269. SendThreatListUpdate();
  270. // update combat timer only for players and pets (only pets with PetAI)
  271. if(isInCombat() && (GetTypeId() == TYPEID_PLAYER || (ToCreature()->isPet() && IsControlledByPlayer())))
  272. {
  273. // Check UNIT_STAT_MELEE_ATTACKING or UNIT_STAT_CHASE (without UNIT_STAT_FOLLOW in this case) so pets can reach far away
  274. // targets without stopping half way there and running off.
  275. // These flags are reset after target dies or another command is given.
  276. if(m_HostileRefManager.isEmpty())
  277. {
  278. // m_CombatTimer set at aura start and it will be freeze until aura removing
  279. if(m_CombatTimer <= p_time)
  280. ClearInCombat();
  281. else
  282. m_CombatTimer -= p_time;
  283. }
  284. }
  285. // not implemented before 3.0.2
  286. //if(!HasUnitState(UNIT_STAT_CASTING))
  287. {
  288. if(uint32 base_att = getAttackTimer(BASE_ATTACK))
  289. setAttackTimer(BASE_ATTACK, (p_time >= base_att ? 0 : base_att - p_time));
  290. if(uint32 ranged_att = getAttackTimer(RANGED_ATTACK))
  291. setAttackTimer(RANGED_ATTACK, (p_time >= ranged_att ? 0 : ranged_att - p_time));
  292. if(uint32 off_att = getAttackTimer(OFF_ATTACK))
  293. setAttackTimer(OFF_ATTACK, (p_time >= off_att ? 0 : off_att - p_time));
  294. }
  295. // update abilities available only for fraction of time
  296. UpdateReactives(p_time);
  297. if(isAlive())
  298. {
  299. ModifyAuraState(AURA_STATE_HEALTHLESS_20_PERCENT, HealthBelowPct(20));
  300. ModifyAuraState(AURA_STATE_HEALTHLESS_35_PERCENT, HealthBelowPct(35));
  301. ModifyAuraState(AURA_STATE_HEALTH_ABOVE_75_PERCENT, HealthAbovePct(75));
  302. }
  303. UpdateSplineMovement(p_time);
  304. i_motionMaster.UpdateMotion(p_time);
  305. }
  306. bool Unit::haveOffhandWeapon() const
  307. {
  308. if(GetTypeId() == TYPEID_PLAYER)
  309. return ToPlayer()->GetWeaponForAttack(OFF_ATTACK, true);
  310. else
  311. return m_canDualWield;
  312. }
  313. void Unit::MonsterMoveWithSpeed(float x, float y, float z, float speed, bool generatePath, bool forceDestination)
  314. {
  315. Movement::MoveSplineInit init(*this);
  316. init.MoveTo(x, y, z, generatePath, forceDestination);
  317. init.SetVelocity(speed);
  318. init.Launch();
  319. }
  320. void Unit::UpdateSplineMovement(uint32 t_diff)
  321. {
  322. if(movespline->Finalized())
  323. return;
  324. movespline->updateState(t_diff);
  325. bool arrived = movespline->Finalized();
  326. if(arrived)
  327. DisableSpline();
  328. m_movesplineTimer.Update(t_diff);
  329. if(m_movesplineTimer.Passed() || arrived)
  330. {
  331. m_movesplineTimer.Reset(400 /*POSITION_UPDATE_DELAY*/);
  332. Movement::Location loc = movespline->ComputePosition();
  333. if(GetTypeId() == TYPEID_PLAYER)
  334. ((Player*)this)->SetPosition(loc.x,loc.y,loc.z,loc.orientation);
  335. else
  336. GetMap()->CreatureRelocation((Creature*)this,loc.x,loc.y,loc.z,loc.orientation);
  337. }
  338. }
  339. void Unit::DisableSpline()
  340. {
  341. m_movementInfo.RemoveMovementFlag(MovementFlags(MOVEMENTFLAG_SPLINE_ENABLED|MOVEMENTFLAG_FORWARD));
  342. movespline->_Interrupt();
  343. }
  344. void Unit::SendMonsterMoveExitVehicle(Position const* newPos)
  345. {
  346. WorldPacket data(SMSG_MONSTER_MOVE, 1+12+4+1+4+4+4+12+GetPackGUID().size());
  347. data.append(GetPackGUID());
  348. data << uint8(GetTypeId() == TYPEID_PLAYER ? 1 : 0); // new in 3.1, bool
  349. data << GetPositionX() << GetPositionY() << GetPositionZ();
  350. data << getMSTime();
  351. data << uint8(SPLINETYPE_FACING_ANGLE);
  352. data << float(GetOrientation()); // guess
  353. data << uint32(SPLINEFLAG_EXIT_VEHICLE);
  354. data << uint32(0); // Time in between points
  355. data << uint32(1); // 1 single waypoint
  356. data << newPos->GetPositionX();
  357. data << newPos->GetPositionY();
  358. data << newPos->GetPositionZ();
  359. SendMessageToSet(&data, true);
  360. }
  361. void Unit::SendMonsterMoveTransport(Unit* vehicleOwner)
  362. {
  363. // TODO: Turn into BuildMonsterMoveTransport packet and allow certain variables (for npc movement aboard vehicles)
  364. WorldPacket data(SMSG_MONSTER_MOVE_TRANSPORT, GetPackGUID().size()+vehicleOwner->GetPackGUID().size() + 47);
  365. data.append(GetPackGUID());
  366. data.append(vehicleOwner->GetPackGUID());
  367. data << int8(GetTransSeat());
  368. data << uint8(GetTypeId() == TYPEID_PLAYER ? 1 : 0); // boolean
  369. data << GetPositionX() - vehicleOwner->GetPositionX();
  370. data << GetPositionY() - vehicleOwner->GetPositionY();
  371. data << GetPositionZ() - vehicleOwner->GetPositionZ();
  372. data << uint32(getMSTime()); // should be an increasing constant that indicates movement packet count
  373. data << uint8(SPLINETYPE_FACING_ANGLE);
  374. data << GetTransOffsetO(); // facing angle?
  375. data << uint32(SPLINEFLAG_TRANSPORT);
  376. data << uint32(GetTransTime()); // move time
  377. data << uint32(1); // amount of waypoints
  378. data << uint32(0); // waypoint X
  379. data << uint32(0); // waypoint Y
  380. data << uint32(0); // waypoint Z
  381. SendMessageToSet(&data, true);
  382. }
  383. void Unit::resetAttackTimer(WeaponAttackType type)
  384. {
  385. m_attackTimer[type] = uint32(GetAttackTime(type) * m_modAttackSpeedPct[type]);
  386. }
  387. bool Unit::IsWithinCombatRange(const Unit* obj, float dist2compare) const
  388. {
  389. if(!obj || !IsInMap(obj)) return false;
  390. float dx = GetPositionX() - obj->GetPositionX();
  391. float dy = GetPositionY() - obj->GetPositionY();
  392. float dz = GetPositionZ() - obj->GetPositionZ();
  393. float distsq = dx*dx + dy*dy + dz*dz;
  394. float sizefactor = GetCombatReach() + obj->GetCombatReach();
  395. float maxdist = dist2compare + sizefactor;
  396. return distsq < maxdist * maxdist;
  397. }
  398. bool Unit::IsWithinMeleeRange(const Unit* obj, float dist) const
  399. {
  400. if(!obj || !IsInMap(obj)) return false;
  401. float dx = GetPositionX() - obj->GetPositionX();
  402. float dy = GetPositionY() - obj->GetPositionY();
  403. float dz = GetPositionZ() - obj->GetPositionZ();
  404. float distsq = dx*dx + dy*dy + dz*dz;
  405. float sizefactor = GetMeleeReach() + obj->GetMeleeReach();
  406. float maxdist = dist + sizefactor;
  407. return distsq < maxdist * maxdist;
  408. }
  409. void Unit::GetRandomContactPoint(const Unit* obj, float &x, float &y, float &z, float distance2dMin, float distance2dMax) const
  410. {
  411. float combat_reach = GetCombatReach();
  412. if(combat_reach < 0.1f) // sometimes bugged for players
  413. combat_reach = DEFAULT_COMBAT_REACH;
  414. uint32 attacker_number = getAttackers().size();
  415. if(attacker_number > 0)
  416. --attacker_number;
  417. GetNearPoint(obj, x, y, z, obj->GetCombatReach(), distance2dMin+(distance2dMax-distance2dMin) * (float)rand_norm()
  418. , GetAngle(obj) + (attacker_number ? (static_cast<float>(M_PI/2) - static_cast<float>(M_PI) * (float)rand_norm()) * float(attacker_number) / combat_reach * 0.3f : 0));
  419. }
  420. void Unit::SetVisibleAura(uint8 slot, AuraApplication * aur)
  421. {
  422. if (Aura* aura = aur->GetBase())
  423. if (Player *player = ToPlayer())
  424. if (player->HaveSpectators() && slot < MAX_AURAS)
  425. {
  426. SpectatorAddonMsg msg;
  427. uint64 casterID = 0;
  428. if (aura->GetCaster())
  429. casterID = (aura->GetCaster()->ToPlayer()) ? aura->GetCaster()->GetGUID() : 0;
  430. msg.SetPlayer(player->GetName());
  431. msg.CreateAura(casterID, aura->GetSpellProto()->Id,
  432. aur->IsPositive(), aura->GetSpellProto()->Dispel,
  433. aura->GetDuration(), aura->GetMaxDuration(),
  434. aura->GetStackAmount(), false);
  435. player->SendSpectatorAddonMsgToBG(msg);
  436. }
  437. m_visibleAuras[slot] = aur;
  438. UpdateAuraForGroup(slot);
  439. }
  440. void Unit::RemoveVisibleAura(uint8 slot)
  441. {
  442. AuraApplication *aurApp = GetVisibleAura(slot);
  443. if (aurApp && slot < MAX_AURAS)
  444. {
  445. if (Aura* aura = aurApp->GetBase())
  446. if (Player *player = ToPlayer())
  447. if (player->HaveSpectators())
  448. {
  449. SpectatorAddonMsg msg;
  450. uint64 casterID = 0;
  451. if (aura->GetCaster())
  452. casterID = (aura->GetCaster()->ToPlayer()) ? aura->GetCaster()->GetGUID() : 0;
  453. msg.SetPlayer(player->GetName());
  454. msg.CreateAura(casterID, aura->GetSpellProto()->Id,
  455. aurApp->IsPositive(), aura->GetSpellProto()->Dispel,
  456. aura->GetDuration(), aura->GetMaxDuration(),
  457. aura->GetStackAmount(), true);
  458. player->SendSpectatorAddonMsgToBG(msg);
  459. }
  460. }
  461. m_visibleAuras.erase(slot);
  462. UpdateAuraForGroup(slot);
  463. }
  464. void Unit::UpdateInterruptMask()
  465. {
  466. m_interruptMask = 0;
  467. for(AuraApplicationList::const_iterator i = m_interruptableAuras.begin(); i != m_interruptableAuras.end(); ++i)
  468. m_interruptMask |= (*i)->GetBase()->GetSpellProto()->AuraInterruptFlags;
  469. if(Spell* spell = m_currentSpells[CURRENT_CHANNELED_SPELL])
  470. if(spell->getState() == SPELL_STATE_CASTING)
  471. m_interruptMask |= spell->m_spellInfo->ChannelInterruptFlags;
  472. }
  473. bool Unit::HasAuraTypeWithFamilyFlags(AuraType auraType, uint32 familyName, uint32 familyFlags) const
  474. {
  475. if(!HasAuraType(auraType))
  476. return false;
  477. AuraEffectList const& auras = GetAuraEffectsByType(auraType);
  478. for(AuraEffectList::const_iterator itr = auras.begin(); itr != auras.end(); ++itr)
  479. if(SpellEntry const* iterSpellProto = (*itr)->GetSpellProto())
  480. if(iterSpellProto->SpellFamilyName == familyName && iterSpellProto->SpellFamilyFlags[0] & familyFlags)
  481. return true;
  482. return false;
  483. }
  484. void Unit::DealDamageMods(Unit* victim, uint32 &damage, uint32* absorb)
  485. {
  486. if(!victim || !victim->isAlive() || victim->HasUnitState(UNIT_STAT_IN_FLIGHT) || (victim->GetTypeId() == TYPEID_UNIT && victim->ToCreature()->IsInEvadeMode()))
  487. {
  488. if(absorb)
  489. *absorb += damage;
  490. damage = 0;
  491. return;
  492. }
  493. uint32 originalDamage = damage;
  494. if(absorb && originalDamage > damage)
  495. *absorb += (originalDamage - damage);
  496. }
  497. uint32 Unit::DealDamage(Unit* pVictim, uint32 damage, CleanDamage const* pCleanDMG, DamageEffectType damagetype, SpellSchoolMask damageSchoolMask, SpellEntry const* pSpell, bool durabilityLoss)
  498. {
  499. if(!pVictim)
  500. return 0;
  501. if(pVictim->IsAIEnabled)
  502. pVictim->GetAI()->DamageTaken(this, damage);
  503. if(IsAIEnabled)
  504. GetAI()->DamageDealt(pVictim, damage, damagetype);
  505. if(damage >= pVictim->GetHealth() && this->GetTypeId() == TYPEID_PLAYER)
  506. ((Player*)this)->UpdateKillingTimedAchievementProgress(pVictim->GetEntry(),pVictim->GetTypeId());
  507. if(damagetype != NODAMAGE)
  508. {
  509. // interrupting auras with AURA_INTERRUPT_FLAG_DAMAGE before checking !damage (absorbed damage breaks that type of auras)
  510. if(pSpell)
  511. {
  512. if(!(pSpell->AttributesEx4 & SPELL_ATTR4_DAMAGE_DOESNT_BREAK_AURAS))
  513. pVictim->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TAKE_DAMAGE, pSpell->Id);
  514. } else pVictim->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TAKE_DAMAGE, 0);
  515. // We're going to call functions which can modify content of the list during iteration over it's elements
  516. // Let's copy the list so we can prevent iterator invalidation
  517. AuraEffectList vCopyDamageCopy(pVictim->GetAuraEffectsByType(SPELL_AURA_SHARE_DAMAGE_PCT));
  518. // copy damage to casters of this aura
  519. for(AuraEffectList::iterator i = vCopyDamageCopy.begin(); i != vCopyDamageCopy.end(); ++i)
  520. {
  521. // Check if aura was removed during iteration - we don't need to work on such auras
  522. if(!((*i)->GetBase()->IsAppliedOnTarget(pVictim->GetGUID())))
  523. continue;
  524. // check damage school mask
  525. if(((*i)->GetMiscValue() & damageSchoolMask) == 0)
  526. continue;
  527. Unit* shareDamageTarget = (*i)->GetCaster();
  528. if(!shareDamageTarget)
  529. continue;
  530. SpellEntry const* spell = (*i)->GetSpellProto();
  531. uint32 share = CalculatePctN(damage, (*i)->GetAmount());
  532. // TODO: check packets if damage is done by pVictim, or by attacker of pVictim
  533. DealDamageMods(shareDamageTarget, share, NULL);
  534. DealDamage(shareDamageTarget, share, NULL, NODAMAGE, GetSpellSchoolMask(spell), spell, false);
  535. }
  536. }
  537. // Rage from Damage made (only from direct weapon damage)
  538. if(pCleanDMG && damagetype == DIRECT_DAMAGE && this != pVictim && getPowerType() == POWER_RAGE)
  539. {
  540. uint32 weaponSpeedHitFactor;
  541. uint32 rage_damage = damage + pCleanDMG->absorbed_damage;
  542. switch(pCleanDMG->attackType)
  543. {
  544. case BASE_ATTACK:
  545. {
  546. weaponSpeedHitFactor = uint32(GetAttackTime(pCleanDMG->attackType) / 1000.0f * 3.5f);
  547. if(pCleanDMG->hitOutCome == MELEE_HIT_CRIT)
  548. weaponSpeedHitFactor *= 2;
  549. RewardRage(rage_damage, weaponSpeedHitFactor, true);
  550. break;
  551. }
  552. case OFF_ATTACK:
  553. {
  554. weaponSpeedHitFactor = uint32(GetAttackTime(pCleanDMG->attackType) / 1000.0f * 1.75f);
  555. if(pCleanDMG->hitOutCome == MELEE_HIT_CRIT)
  556. weaponSpeedHitFactor *= 2;
  557. RewardRage(rage_damage, weaponSpeedHitFactor, true);
  558. break;
  559. }
  560. default:
  561. break;
  562. }
  563. }
  564. if(!damage)
  565. {
  566. // Rage from absorbed damage
  567. if(pCleanDMG && pCleanDMG->absorbed_damage && pVictim->getPowerType() == POWER_RAGE)
  568. pVictim->RewardRage(pCleanDMG->absorbed_damage, 0, false);
  569. return 0;
  570. }
  571. uint32 health = pVictim->GetHealth();
  572. // duel ends when player has 1 or less hp
  573. bool duel_hasEnded = false;
  574. bool duel_wasMounted = false;
  575. if(pVictim->GetTypeId() == TYPEID_PLAYER && pVictim->ToPlayer()->duel && damage >= (health-1))
  576. {
  577. // prevent kill only if killed in duel and killed by opponent or opponent controlled creature
  578. if(pVictim->ToPlayer()->duel->opponent == this || pVictim->ToPlayer()->duel->opponent->GetGUID() == GetOwnerGUID())
  579. damage = health - 1;
  580. duel_hasEnded = true;
  581. } else if(pVictim->IsVehicle() && damage >= (health-1) && pVictim->GetCharmer() && pVictim->GetCharmer()->GetTypeId() == TYPEID_PLAYER) {
  582. Player* pPlayer = pVictim->GetCharmer()->ToPlayer();
  583. if(pPlayer && pPlayer->duel && pPlayer->duel->isMounted)
  584. {
  585. // prevent kill only if killed in duel and killed by opponent or opponent controlled creature
  586. if(pPlayer->duel->opponent == this || pPlayer->duel->opponent->GetGUID() == GetCharmerGUID())
  587. damage = health - 1;
  588. duel_wasMounted = true;
  589. duel_hasEnded = true;
  590. }
  591. }
  592. if(GetTypeId() == TYPEID_PLAYER && this != pVictim)
  593. {
  594. Player* pKiller = ToPlayer();
  595. // in bg, count dmg if pVictim is also a player
  596. if(pVictim->GetTypeId() == TYPEID_PLAYER)
  597. if(Battleground* pBG = pKiller->GetBattleground())
  598. pBG->UpdatePlayerScore(pKiller, SCORE_DAMAGE_DONE, damage);
  599. pKiller->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_DAMAGE_DONE, damage, 0, pVictim);
  600. pKiller->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HIT_DEALT, damage);
  601. }
  602. if(pVictim->GetTypeId() == TYPEID_PLAYER)
  603. pVictim->ToPlayer()->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HIT_RECEIVED, damage);
  604. else if(!pVictim->IsControlledByPlayer() || pVictim->IsVehicle()) {
  605. if(!pVictim->ToCreature()->hasLootRecipient())
  606. pVictim->ToCreature()->SetLootRecipient(this);
  607. if(IsControlledByPlayer())
  608. pVictim->ToCreature()->LowerPlayerDamageReq(health < damage ? health : damage);
  609. }
  610. if(health <= damage)
  611. {
  612. if(pVictim->GetTypeId() == TYPEID_PLAYER && pVictim != this)
  613. {
  614. pVictim->ToPlayer()->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_TOTAL_DAMAGE_RECEIVED, health);
  615. // call before auras are removed
  616. if(Player* pPlayer = GetCharmerOrOwnerPlayerOrPlayerItself())
  617. pPlayer->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_SPECIAL_PVP_KILL, 1, 0, pVictim);
  618. }
  619. // Heirloom trinkets on "kill a target that yields experience or honor" effect
  620. if(this->ToPlayer() && this->isAlive()) {
  621. if(this->ToPlayer()->isHonorOrXPTarget(pVictim)) {
  622. AuraEffectList const& heirloom = GetAuraEffectsByType(SPELL_AURA_DUMMY);
  623. for(AuraEffectList::const_iterator j = heirloom.begin(); j != heirloom.end(); ++j)
  624. {
  625. if((*j)->GetId() == 59915 && this->getPowerType() == POWER_MANA)
  626. this->CastSpell(this, 59914, true);
  627. if((*j)->GetId() == 59906)
  628. {
  629. int32 bonushealth = this->GetMaxHealth() * this->GetAura(59906)->GetEffect(0)->GetAmount() / 100;
  630. this->CastCustomSpell(this, 59913, &bonushealth, 0, 0, true);
  631. }
  632. }
  633. }
  634. }
  635. Kill(pVictim, durabilityLoss);
  636. } else {
  637. if(pVictim->GetTypeId() == TYPEID_PLAYER)
  638. pVictim->ToPlayer()->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_TOTAL_DAMAGE_RECEIVED, damage);
  639. pVictim->ModifyHealth(- (int32)damage);
  640. if(damagetype == DIRECT_DAMAGE || damagetype == SPELL_DIRECT_DAMAGE)
  641. pVictim->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_DIRECT_DAMAGE, pSpell ? pSpell->Id : 0);
  642. if(pVictim->GetTypeId() != TYPEID_PLAYER)
  643. pVictim->AddThreat(this, (float)damage, damageSchoolMask, pSpell);
  644. else { // pVictim is a player
  645. // random durability for items (HIT TAKEN)
  646. if(roll_chance_f(sWorld->getRate(RATE_DURABILITY_LOSS_DAMAGE))) {
  647. EquipmentSlots slot = EquipmentSlots(urand(0, EQUIPMENT_SLOT_END-1));
  648. pVictim->ToPlayer()->DurabilityPointLossForEquipSlot(slot);
  649. }
  650. }
  651. // Rage from damage received
  652. if(this != pVictim && pVictim->getPowerType() == POWER_RAGE)
  653. {
  654. uint32 rage_damage = damage + (pCleanDMG ? pCleanDMG->absorbed_damage : 0);
  655. pVictim->RewardRage(rage_damage, 0, false);
  656. }
  657. if(GetTypeId() == TYPEID_PLAYER)
  658. {
  659. // random durability for items (HIT DONE)
  660. if(roll_chance_f(sWorld->getRate(RATE_DURABILITY_LOSS_DAMAGE)))
  661. {
  662. EquipmentSlots slot = EquipmentSlots(urand(0, EQUIPMENT_SLOT_END-1));
  663. ToPlayer()->DurabilityPointLossForEquipSlot(slot);
  664. }
  665. }
  666. if(damagetype != NODAMAGE && damage)
  667. {
  668. if(pVictim != this && pVictim->GetTypeId() == TYPEID_PLAYER) // does not support creature push_back
  669. {
  670. if(damagetype != DOT)
  671. {
  672. if(Spell* pSpell = pVictim->m_currentSpells[CURRENT_GENERIC_SPELL])
  673. {
  674. if(pSpell->getState() == SPELL_STATE_PREPARING)
  675. {
  676. uint32 interruptFlags = pSpell->m_spellInfo->InterruptFlags;
  677. if(interruptFlags & SPELL_INTERRUPT_FLAG_ABORT_ON_DMG)
  678. pVictim->InterruptNonMeleeSpells(false);
  679. else if(interruptFlags & SPELL_INTERRUPT_FLAG_PUSH_BACK)
  680. pSpell->Delayed();
  681. }
  682. }
  683. }
  684. if(Spell* pSpell = pVictim->m_currentSpells[CURRENT_CHANNELED_SPELL])
  685. {
  686. if(pSpell->getState() == SPELL_STATE_CASTING)
  687. {
  688. uint32 channelInterruptFlags = pSpell->m_spellInfo->ChannelInterruptFlags;
  689. if(((channelInterruptFlags & CHANNEL_FLAG_DELAY) != 0) && (damagetype != DOT))
  690. pSpell->DelayedChannel();
  691. }
  692. }
  693. }
  694. }
  695. // last damage from duel opponent
  696. if(duel_hasEnded)
  697. {
  698. Player* pPlayer;
  699. duel_wasMounted ? pPlayer = pVictim->GetCharmer()->ToPlayer() : pPlayer = pVictim->ToPlayer();
  700. if(!pPlayer->isAlive())
  701. pPlayer->ResurrectPlayer(1.0f);
  702. pPlayer->RemoveAurasByType(SPELL_AURA_PERIODIC_DAMAGE);
  703. pPlayer->RemoveAurasByType(SPELL_AURA_PERIODIC_DAMAGE_PERCENT);
  704. pPlayer->RemoveAurasByType(SPELL_AURA_PERIODIC_LEECH);
  705. pPlayer->SetHealth(1);
  706. pPlayer->duel->opponent->CombatStopWithPets(true);
  707. pPlayer->CombatStopWithPets(true);
  708. pPlayer->CastSpell(pPlayer, 7267, true);
  709. pPlayer->DuelComplete(DUEL_WON);
  710. }
  711. }
  712. return damage;
  713. }
  714. void Unit::CastStop(uint32 except_spellid)
  715. {
  716. for(uint32 i = CURRENT_FIRST_NON_MELEE_SPELL; i < CURRENT_MAX_SPELL; i++)
  717. if(m_currentSpells[i] && m_currentSpells[i]->m_spellInfo->Id != except_spellid)
  718. InterruptSpell(CurrentSpellTypes(i), false);
  719. }
  720. void Unit::CastSpell(Unit* Victim, uint32 spellId, bool triggered, Item* castItem, AuraEffect const* triggeredByAura, uint64 originalCaster)
  721. {
  722. SpellEntry const* spellInfo = sSpellStore.LookupEntry(spellId);
  723. if(!spellInfo)
  724. {
  725. sLog->outError("CastSpell: unknown spell id %i by caster: %s %u)", spellId, (GetTypeId() == TYPEID_PLAYER ? "player (GUID:" : "creature (Entry:"), (GetTypeId() == TYPEID_PLAYER ? GetGUIDLow() : GetEntry()));
  726. return;
  727. }
  728. CastSpell(Victim, spellInfo, triggered, castItem, triggeredByAura, originalCaster);
  729. }
  730. void Unit::CastSpell(Unit* Victim, SpellEntry const* spellInfo, bool triggered, Item* castItem, AuraEffect const* triggeredByAura, uint64 originalCaster)
  731. {
  732. if(!spellInfo)
  733. {
  734. sLog->outError("CastSpell: unknown spell by caster: %s %u)", (GetTypeId() == TYPEID_PLAYER ? "player (GUID:" : "creature (Entry:"), (GetTypeId() == TYPEID_PLAYER ? GetGUIDLow() : GetEntry()));
  735. return;
  736. }
  737. if(!originalCaster && GetTypeId() == TYPEID_UNIT && ToCreature()->isTotem() && IsControlledByPlayer())
  738. if(Unit* owner = GetOwner())
  739. originalCaster=owner->GetGUID();
  740. SpellCastTargets targets;
  741. targets.SetUnitTarget(Victim);
  742. if(castItem)
  743. sLog->outDebug(DebugLogFilters(LOG_FILTER_SPELLS_AURAS | LOG_FILTER_PLAYER_ITEMS), "WORLD: cast Item spellId - %i", spellInfo->Id);
  744. if(!originalCaster && triggeredByAura)
  745. originalCaster = triggeredByAura->GetCasterGUID();
  746. Spell* pSpell = new Spell(this, spellInfo, triggered, originalCaster);
  747. pSpell->m_CastItem = castItem;
  748. pSpell->prepare(&targets, triggeredByAura);
  749. }
  750. void Unit::CastCustomSpell(Unit* target, uint32 spellId, int32 const* bp0, int32 const* bp1, int32 const* bp2, bool triggered, Item* castItem, AuraEffect const* triggeredByAura, uint64 originalCaster)
  751. {
  752. CustomSpellValues values;
  753. if(bp0)
  754. values.AddSpellMod(SPELLVALUE_BASE_POINT0, *bp0);
  755. if(bp1)
  756. values.AddSpellMod(SPELLVALUE_BASE_POINT1, *bp1);
  757. if(bp2)
  758. values.AddSpellMod(SPELLVALUE_BASE_POINT2, *bp2);
  759. CastCustomSpell(spellId, values, target, triggered, castItem, triggeredByAura, originalCaster);
  760. }
  761. void Unit::CastCustomSpell(uint32 spellId, SpellValueMod mod, int32 value, Unit* target, bool triggered, Item* castItem, AuraEffect const* triggeredByAura, uint64 originalCaster)
  762. {
  763. CustomSpellValues values;
  764. values.AddSpellMod(mod, value);
  765. CastCustomSpell(spellId, values, target, triggered, castItem, triggeredByAura, originalCaster);
  766. }
  767. void Unit::CastCustomSpell(uint32 spellId, CustomSpellValues const& value, Unit* Victim, bool triggered, Item* castItem, AuraEffect const* triggeredByAura, uint64 originalCaster)
  768. {
  769. SpellEntry const* pSpellInfo = sSpellStore.LookupEntry(spellId);
  770. if(!pSpellInfo)
  771. {
  772. sLog->outError("CastSpell: unknown spell id %i by caster: %s %u)", spellId, (GetTypeId() == TYPEID_PLAYER ? "player (GUID:" : "creature (Entry:"), (GetTypeId() == TYPEID_PLAYER ? GetGUIDLow() : GetEntry()));
  773. return;
  774. }
  775. SpellCastTargets targets;
  776. targets.SetUnitTarget(Victim);
  777. if(!originalCaster && triggeredByAura)
  778. originalCaster = triggeredByAura->GetCasterGUID();
  779. Spell* pSpell = new Spell(this, pSpellInfo, triggered, originalCaster);
  780. if(castItem)
  781. {
  782. sLog->outDebug(DebugLogFilters(LOG_FILTER_SPELLS_AURAS | LOG_FILTER_PLAYER_ITEMS), "WORLD: cast Item spellId - %i", pSpellInfo->Id);
  783. pSpell->m_CastItem = castItem;
  784. }
  785. for(CustomSpellValues::const_iterator itr = value.begin(); itr != value.end(); ++itr)
  786. pSpell->SetSpellValue(itr->first, itr->second);
  787. pSpell->prepare(&targets, triggeredByAura);
  788. }
  789. // used for scripting
  790. void Unit::CastSpell(float x, float y, float z, uint32 spellId, bool triggered, Item* castItem, AuraEffect const* triggeredByAura, uint64 originalCaster, Unit* OriginalVictim)
  791. {
  792. SpellEntry const* pSpellInfo = sSpellStore.LookupEntry(spellId);
  793. if(!pSpellInfo)
  794. {
  795. sLog->outError("CastSpell(x, y, z): unknown spell id %i by caster: %s %u)", spellId, (GetTypeId() == TYPEID_PLAYER ? "player (GUID:" : "creature (Entry:"), (GetTypeId() == TYPEID_PLAYER ? GetGUIDLow() : GetEntry()));
  796. return;
  797. }
  798. if(castItem)
  799. sLog->outDebug(DebugLogFilters(LOG_FILTER_SPELLS_AURAS | LOG_FILTER_PLAYER_ITEMS), "WORLD: cast Item spellId - %i", pSpellInfo->Id);
  800. if(!originalCaster && triggeredByAura)
  801. originalCaster = triggeredByAura->GetCasterGUID();
  802. Spell* pSpell = new Spell(this, pSpellInfo, triggered, originalCaster);
  803. SpellCastTargets targets;
  804. targets.SetDst(x, y, z, GetOrientation());
  805. if(OriginalVictim)
  806. targets.SetUnitTarget(OriginalVictim);
  807. pSpell->m_CastItem = castItem;
  808. pSpell->prepare(&targets, triggeredByAura);
  809. }
  810. void Unit::CastSpell(GameObject* pGO, uint32 spellId, bool triggered, Item* castItem, AuraEffect* triggeredByAura, uint64 originalCaster)
  811. {
  812. if(!pGO)
  813. return;
  814. SpellEntry const* pSpellInfo = sSpellStore.LookupEntry(spellId);
  815. if(!pSpellInfo)
  816. {
  817. sLog->outError("CastSpell(x, y, z): unknown spell id %i by caster: %s %u)", spellId, (GetTypeId() == TYPEID_PLAYER ? "player (GUID:" : "creature (Entry:"), (GetTypeId() == TYPEID_PLAYER ? GetGUIDLow() : GetEntry()));
  818. return;
  819. }
  820. if(castItem)
  821. sLog->outDebug(DebugLogFilters(LOG_FILTER_SPELLS_AURAS | LOG_FILTER_PLAYER_ITEMS), "WORLD: cast Item spellId - %i", pSpellInfo->Id);
  822. if(!originalCaster && triggeredByAura)
  823. originalCaster = triggeredByAura->GetCasterGUID();
  824. Spell* pSpell = new Spell(this, pSpellInfo, triggered, originalCaster);
  825. SpellCastTargets targets;
  826. targets.SetGOTarget(pGO);
  827. pSpell->m_CastItem = castItem;
  828. pSpell->prepare(&targets, triggeredByAura);
  829. }
  830. // Obsolete func need remove, here only for comotability vs another patches
  831. uint32 Unit::SpellNonMeleeDamageLog(Unit* victim, uint32 spellID, uint32 damage)
  832. {
  833. SpellEntry const* pSpellInfo = sSpellStore.LookupEntry(spellID);
  834. SpellNonMeleeDamage damageInfo(this, victim, pSpellInfo->Id, pSpellInfo->SchoolMask);
  835. damage = SpellDamageBonus(victim, pSpellInfo, damage, SPELL_DIRECT_DAMAGE);
  836. CalculateSpellDamageTaken(&damageInfo, damage, pSpellInfo);
  837. DealDamageMods(damageInfo.target, damageInfo.damage, &damageInfo.absorb);
  838. SendSpellNonMeleeDamageLog(&damageInfo);
  839. DealSpellDamage(&damageInfo, true);
  840. return damageInfo.damage;
  841. }
  842. void Unit::CalculateSpellDamageTaken(SpellNonMeleeDamage *damageInfo, int32 damage, SpellEntry const* spellInfo, WeaponAttackType attackType, bool crit)
  843. {
  844. if(damage < 0)
  845. return;
  846. Unit* victim = damageInfo->target;
  847. if(!victim || !victim->isAlive())
  848. return;
  849. SpellSchoolMask damageSchoolMask = SpellSchoolMask(damageInfo->schoolMask);
  850. uint32 crTypeMask = victim->GetCreatureTypeMask();
  851. if(IsDamageReducedByArmor(damageSchoolMask, spellInfo))
  852. damage = CalcArmorReducedDamage(victim, damage, spellInfo, attackType);
  853. bool blocked = false;
  854. // Per-school calc
  855. switch(spellInfo->DmgClass)
  856. {
  857. // Melee and Ranged Spells
  858. case SPELL_DAMAGE_CLASS_RANGED:
  859. case SPELL_DAMAGE_CLASS_MELEE:
  860. {
  861. // Physical Damage
  862. if(damageSchoolMask & SPELL_SCHOOL_MASK_NORMAL)
  863. {
  864. // Get blocked status
  865. blocked = isSpellBlocked(victim, spellInfo, attackType);
  866. }
  867. if(crit)
  868. {
  869. damageInfo->HitInfo |= SPELL_HIT_TYPE_CRIT;
  870. // Calculate crit bonus
  871. uint32 crit_bonus = damage;
  872. // Apply crit_damage bonus for melee spells
  873. if(Player* modOwner = GetSpellModOwner())
  874. modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_CRIT_DAMAGE_BONUS, crit_bonus);
  875. damage += crit_bonus;
  876. // Apply SPELL_AURA_MOD_ATTACKER_RANGED_CRIT_DAMAGE or SPELL_AURA_MOD_ATTACKER_MELEE_CRIT_DAMAGE
  877. float critPctDamageMod = 0.0f;
  878. if(attackType == RANGED_ATTACK)
  879. critPctDamageMod += victim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_RANGED_CRIT_DAMAGE);
  880. else
  881. critPctDamageMod += victim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_MELEE_CRIT_DAMAGE);
  882. // Increase crit damage from SPELL_AURA_MOD_CRIT_DAMAGE_BONUS
  883. critPctDamageMod += (GetTotalAuraMultiplierByMiscMask(SPELL_AURA_MOD_CRIT_DAMAGE_BONUS, GetSpellSchoolMask(spellInfo)) - 1.0f) * 100;
  884. // Increase crit damage from SPELL_AURA_MOD_CRIT_PERCENT_VERSUS
  885. critPctDamageMod += GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_CRIT_PERCENT_VERSUS, crTypeMask);
  886. if(critPctDamageMod != 0)
  887. AddPctF(damage, critPctDamageMod);
  888. }
  889. // Spell weapon based damage CAN BE crit & blocked at same time
  890. if(blocked)
  891. {
  892. damageInfo->blocked = victim->GetShieldBlockValue();
  893. // double blocked amount if block is critical
  894. if(victim->isBlockCritical())
  895. damageInfo->blocked += damageInfo->blocked;
  896. if(damage < int32(damageInfo->blocked))
  897. damageInfo->blocked = uint32(damage);
  898. damage -= damageInfo->blocked;
  899. }
  900. if(attackType != RANGED_ATTACK)
  901. ApplyResilience(victim, NULL, &damage, crit, CR_CRIT_TAKEN_MELEE);
  902. else
  903. ApplyResilience(victim, NULL, &damage, crit, CR_CRIT_TAKEN_RANGED);
  904. break;
  905. }
  906. // Magical Attacks
  907. case SPELL_DAMAGE_CLASS_NONE:
  908. case SPELL_DAMAGE_CLASS_MAGIC:
  909. {
  910. // If crit add critical bonus
  911. if(crit)
  912. {
  913. damageInfo->HitInfo |= SPELL_HIT_TYPE_CRIT;
  914. damage = SpellCriticalDamageBonus(spellInfo, damage, victim);
  915. }
  916. ApplyResilience(victim, NULL, &damage, crit, CR_CRIT_TAKEN_SPELL);
  917. break;
  918. }
  919. default:
  920. break;
  921. }
  922. // Calculate absorb resist
  923. if(damage > 0)
  924. {
  925. CalcAbsorbResist(victim, damageSchoolMask, SPELL_DIRECT_DAMAGE, damage, &damageInfo->absorb, &damageInfo->resist, spellInfo);
  926. damage -= damageInfo->absorb + damageInfo->resist;
  927. }
  928. else
  929. damage = 0;
  930. damageInfo->damage = damage;
  931. }
  932. void Unit::DealSpellDamage(SpellNonMeleeDamage* pDamageInfo, bool durabilityLoss)
  933. {
  934. if(pDamageInfo == 0)
  935. return;
  936. Unit* victim = pDamageInfo->target;
  937. if(!victim)
  938. return;
  939. if(!victim->isAlive() || victim->HasUnitState(UNIT_STAT_IN_FLIGHT) || (victim->HasUnitState(UNIT_STAT_ONVEHICLE) && victim->GetVehicleBase() != this) || (victim->GetTypeId() == TYPEID_UNIT && victim->ToCreature()->IsInEvadeMode()))
  940. return;
  941. SpellEntry const* pSpellEntry = sSpellStore.LookupEntry(pDamageInfo->SpellID);
  942. if(pSpellEntry == NULL)
  943. {
  944. sLog->outDebug(LOG_FILTER_UNITS, "Unit::DealSpellDamage have wrong damageInfo->SpellID: %u", pDamageInfo->SpellID);
  945. return;
  946. }
  947. // Call default DealDamage
  948. CleanDamage cleanDamage(pDamageInfo->cleanDamage, pDamageInfo->absorb, BASE_ATTACK, MELEE_HIT_NORMAL);
  949. DealDamage(victim, pDamageInfo->damage, &cleanDamage, SPELL_DIRECT_DAMAGE, SpellSchoolMask(pDamageInfo->schoolMask), pSpellEntry, durabilityLoss);
  950. }
  951. // TODO for melee need create structure as in
  952. void Unit::CalculateMeleeDamage(Unit* pVictim, uint32 damage, CalcDamageInfo* pDamageInfo, WeaponAttackType attackType)
  953. {
  954. pDamageInfo->attacker = this;
  955. pDamageInfo->target = pVictim;
  956. pDamageInfo->damageSchoolMask = GetMeleeDamageSchoolMask();
  957. pDamageInfo->attackType = attackType;
  958. pDamageInfo->damage = 0;
  959. pDamageInfo->cleanDamage = 0;
  960. pDamageInfo->absorb = 0;
  961. pDamageInfo->resist = 0;
  962. pDamageInfo->blocked_amount = 0;
  963. pDamageInfo->TargetState = 0;
  964. pDamageInfo->HitInfo = 0;
  965. pDamageInfo->procAttacker = PROC_FLAG_NONE;
  966. pDamageInfo->procVictim = PROC_FLAG_NONE;
  967. pDamageInfo->procEx = PROC_EX_NONE;
  968. pDamageInfo->hitOutCome = MELEE_HIT_EVADE;
  969. if(!pVictim)
  970. return;
  971. if(!isAlive() || !pVictim->isAlive())
  972. return;
  973. // Select HitInfo/procAttacker/procVictim flag based on attack type
  974. switch(attackType)
  975. {
  976. case BASE_ATTACK:
  977. {
  978. pDamageInfo->procAttacker = PROC_FLAG_DONE_MELEE_AUTO_ATTACK | PROC_FLAG_DONE_MAINHAND_ATTACK;
  979. pDamageInfo->procVictim = PROC_FLAG_TAKEN_MELEE_AUTO_ATTACK;
  980. pDamageInfo->HitInfo = HITINFO_NORMALSWING2;
  981. break;
  982. }
  983. case OFF_ATTACK:
  984. {
  985. pDamageInfo->procAttacker = PROC_FLAG_DONE_MELEE_AUTO_ATTACK | PROC_FLAG_DONE_OFFHAND_ATTACK;
  986. pDamageInfo->procVictim = PROC_FLAG_TAKEN_MELEE_AUTO_ATTACK;
  987. pDamageInfo->HitInfo = HITINFO_LEFTSWING;
  988. break;
  989. }
  990. default:
  991. return;
  992. }
  993. // Physical Immune check
  994. if(pDamageInfo->target->IsImmunedToDamage(SpellSchoolMask(pDamageInfo->damageSchoolMask)))
  995. {
  996. pDamageInfo->HitInfo |= HITINFO_NORMALSWING;
  997. pDamageInfo->TargetState = VICTIMSTATE_IS_IMMUNE;
  998. pDamageInfo->procEx |= PROC_EX_IMMUNE;
  999. pDamageInfo->damage = 0;
  1000. pDamageInfo->cleanDamage = 0;
  1001. return;
  1002. }
  1003. damage += CalculateDamage(pDamageInfo->attackType, false, true);
  1004. // Add melee damage bonus
  1005. MeleeDamageBonus(pDamageInfo->target, &damage, pDamageInfo->attackType);
  1006. // Calculate armor reduction
  1007. if(IsDamageReducedByArmor((SpellSchoolMask)(pDamageInfo->damageSchoolMask)))
  1008. {
  1009. pDamageInfo->damage = CalcArmorReducedDamage(pDamageInfo->target, damage, NULL, pDamageInfo->attackType);
  1010. pDamageInfo->cleanDamage += damage - pDamageInfo->damage;
  1011. } else pDamageInfo->damage = damage;
  1012. pDamageInfo->hitOutCome = RollMeleeOutcomeAgainst(pDamageInfo->target, pDamageInfo->attackType);
  1013. switch(pDamageInfo->hitOutCome)
  1014. {
  1015. case MELEE_HIT_EVADE:
  1016. {
  1017. pDamageInfo->HitInfo |= HITINFO_MISS|HITINFO_SWINGNOHITSOUND;
  1018. pDamageInfo->TargetState = VICTIMSTATE_EVADES;
  1019. pDamageInfo->procEx|=PROC_EX_EVADE;
  1020. pDamageInfo->damage = 0;
  1021. pDamageInfo->cleanDamage = 0;
  1022. return;
  1023. }
  1024. case MELEE_HIT_MISS:
  1025. {
  1026. pDamageInfo->HitInfo |= HITINFO_MISS;
  1027. pDamageInfo->TargetState = VICTIMSTATE_INTACT;
  1028. pDamageInfo->procEx |= PROC_EX_MISS;
  1029. pDamageInfo->damage = 0;
  1030. pDamageInfo->cleanDamage = 0;
  1031. break;
  1032. }
  1033. case MELEE_HIT_NORMAL:
  1034. {
  1035. pDamageInfo->TargetState = VICTIMSTATE_HIT;
  1036. pDamageInfo->procEx|=PROC_EX_NORMAL_HIT;
  1037. break;
  1038. }
  1039. case MELEE_HIT_CRIT:
  1040. {
  1041. pDamageInfo->HitInfo |= HITINFO_CRITICALHIT;
  1042. pDamageInfo->TargetState = VICTIMSTATE_HIT;
  1043. pDamageInfo->procEx |= PROC_EX_CRITICAL_HIT;
  1044. // Crit bonus calc
  1045. pDamageInfo->damage += pDamageInfo->damage;
  1046. float mod = 0.0f;
  1047. // Apply SPELL_AURA_MOD_ATTACKER_RANGED_CRIT_DAMAGE or SPELL_AURA_MOD_ATTACKER_MELEE_CRIT_DAMAGE
  1048. if(pDamageInfo->attackType == RANGED_ATTACK)
  1049. mod += pDamageInfo->target->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_RANGED_CRIT_DAMAGE);
  1050. else
  1051. mod += pDamageInfo->target->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_MELEE_CRIT_DAMAGE);
  1052. // Increase crit damage from SPELL_AURA_MOD_CRIT_DAMAGE_BONUS
  1053. mod += (GetTotalAuraMultiplierByMiscMask(SPELL_AURA_MOD_CRIT_DAMAGE_BONUS, pDamageInfo->damageSchoolMask) - 1.0f) * 100;
  1054. uint32 crTypeMask = pDamageInfo->target->GetCreatureTypeMask();
  1055. // Increase crit damage from SPELL_AURA_MOD_CRIT_PERCENT_VERSUS
  1056. mod += GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_CRIT_PERCENT_VERSUS, crTypeMask);
  1057. if(mod != 0)
  1058. AddPctF(pDamageInfo->damage, mod);
  1059. break;
  1060. }
  1061. case MELEE_HIT_PARRY:
  1062. {
  1063. pDamageInfo->TargetState = VICTIMSTATE_PARRY;
  1064. pDamageInfo->procEx |= PROC_EX_PARRY;
  1065. pDamageInfo->cleanDamage += pDamageInfo->damage;
  1066. pDamageInfo->damage = 0;
  1067. break;
  1068. }
  1069. case MELEE_HIT_DODGE:
  1070. {
  1071. pDamageInfo->TargetState = VICTIMSTATE_DODGE;
  1072. pDamageInfo->procEx |= PROC_EX_DODGE;
  1073. pDamageInfo->cleanDamage += pDamageInfo->damage;
  1074. pDamageInfo->damage = 0;
  1075. break;
  1076. }
  1077. case MELEE_HIT_BLOCK:
  1078. {
  1079. pDamageInfo->TargetState = VICTIMSTATE_HIT;
  1080. pDamageInfo->HitInfo |= HITINFO_BLOCK;
  1081. pDamageInfo->procEx |= PROC_EX_BLOCK;
  1082. pDamageInfo->blocked_amount = pDamageInfo->target->GetShieldBlockValue();
  1083. if(pDamageInfo->target->isBlockCritical())
  1084. pDamageInfo->blocked_amount+=pDamageInfo->blocked_amount;
  1085. if(pDamageInfo->blocked_amount >= pDamageInfo->damage)
  1086. {
  1087. pDamageInfo->TargetState = VICTIMSTATE_BLOCKS;
  1088. pDamageInfo->blocked_amount = pDamageInfo->damage;
  1089. pDamageInfo->procEx |= PROC_EX_FULL_BLOCK;
  1090. } else pDamageInfo->procEx |= PROC_EX_NORMAL_HIT;
  1091. pDamageInfo->damage -= pDamageInfo->blocked_amount;
  1092. pDamageInfo->cleanDamage += pDamageInfo->blocked_amount;
  1093. break;
  1094. }
  1095. case MELEE_HIT_GLANCING:
  1096. {
  1097. pDamageInfo->HitInfo |= HITINFO_GLANCING;
  1098. pDamageInfo->TargetState = VICTIMSTATE_HIT;
  1099. pDamageInfo->procEx |= PROC_EX_NORMAL_HIT;
  1100. int32 leveldif = int32(pVictim->getLevel()) - int32(getLevel());
  1101. if(leveldif > 3)
  1102. leveldif = 3;
  1103. float reducePercent = 1 - leveldif * 0.1f;
  1104. pDamageInfo->cleanDamage += pDamageInfo->damage-uint32(reducePercent * pDamageInfo->damage);
  1105. pDamageInfo->damage = uint32(reducePercent * pDamageInfo->damage);
  1106. break;
  1107. }
  1108. case MELEE_HIT_CRUSHING:
  1109. {
  1110. pDamageInfo->HitInfo |= HITINFO_CRUSHING;
  1111. pDamageInfo->TargetState = VICTIMSTATE_HIT;
  1112. pDamageInfo->procEx |= PROC_EX_NORMAL_HIT;
  1113. // 150% normal damage
  1114. pDamageInfo->damage += (pDamag

Large files files are truncated, but you can click here to view the full file