/*
* Ascent MMORPG Server
* Copyright (C) 2005-2007 Ascent Team
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
*/
#include "StdAfx.h"
Unit::Unit()
{
m_attackTimer = 0;
m_attackTimer_1 = 0;
m_duelWield = false;
m_state = 0;
m_deathState = ALIVE;
m_currentSpell = NULL;
m_meleespell = 0;
m_addDmgOnce = 0;
m_TotemSlots[0] = NULL;
m_TotemSlots[1] = NULL;
m_TotemSlots[2] = NULL;
m_TotemSlots[3] = NULL;
m_ObjectSlots[0] = 0;
m_ObjectSlots[1] = 0;
m_ObjectSlots[2] = 0;
m_ObjectSlots[3] = 0;
m_silenced = false;
disarmed = false;
// Pet
m_isPet = false;
//DK:modifiers
PctRegenModifier = 0;
for (uint32 x=0;x<4;x++)
{
PctPowerRegenModifier[x] = 1;
}
m_speedModifier = 0;
m_slowdown = 0;
m_mountedspeedModifier=0;
VampTchCaster=0;
for(uint32 x=0;x<27;x++)
{
MechanicsDispels[x]=0;
MechanicsResistancesPCT[x]=0;
}
//SM
SM_CriticalChance=0;
SM_FDur=0;//flat
SM_PDur=0;//pct
SM_FRadius=0;
SM_FRange=0;
SM_PCastTime=0;
SM_FCastTime=0;
SM_PCriticalDamage=0;
SM_FDOT=0;
SM_PDOT=0;
SM_FEffectBonus=0;
SM_PEffectBonus=0;
SM_FDamageBonus=0;
SM_PDamageBonus=0;
SM_PDummy=0;
SM_FDummy=0;
SM_FResist=0;
SM_PRange=0;//pct
SM_PRadius=0;
SM_PAPBonus=0;
SM_PCost=0;
SM_FCost=0;
SM_FAdditionalTargets=0;
SM_PJumpReduce=0;
SM_FSpeedMod=0;
SM_PNonInterrupt=0;
SM_FPenalty=0;
SM_PPenalty=0;
SM_FCooldownTime = 0;
SM_PCooldownTime = 0;
SM_FChanceOfSuccess = 0;
m_pacified = 0;
m_interruptRegen = 0;
m_resistChance = 0;
m_powerRegenPCT = 0;
RAPvModifier=0;
APvModifier=0;
stalkedby=0;
m_invisible = false;
m_extraattacks = 0;
m_stunned = 0;
m_manashieldamt=0;
m_rooted = 0;
m_triggerSpell = 0;
m_triggerDamage = 0;
m_canMove = 0;
m_noInterrupt = 0;
m_modlanguage = -1;
critterPet = NULL;
summonPet = NULL;
m_useAI = false;
for(uint32 x=0;x<10;x++)
{
dispels[x]=0;
CreatureAttackPowerMod[x] = 0;
CreatureRangedAttackPowerMod[x] = 0;
}
//REMIND:Update these if you make any changes
CreatureAttackPowerMod[UNIT_TYPE_MISC] = 0;
CreatureRangedAttackPowerMod[UNIT_TYPE_MISC] = 0;
CreatureAttackPowerMod[11] = 0;
CreatureRangedAttackPowerMod[11] = 0;
m_stealthLevel = 0;
m_stealthDetectBonus = 0;
m_stealth = 0;
m_sleep = 0;
for(uint32 x=0;x<5;x++)
BaseStats[x]=0;
m_attackTimer = 0;
m_H_regenTimer = 2000;
m_P_regenTimer = 2000;
m_P_I_regenTimer = 2000;
// if(GetTypeId() == TYPEID_PLAYER) //only player for now
// CalculateActualArmor();
m_aiInterface = new AIInterface();
m_aiInterface->Init(this, AITYPE_AGRO, MOVEMENTTYPE_NONE);
m_emoteState = 0;
m_oldEmote = 0;
BaseDamage[0]=0;
BaseOffhandDamage[0]=0;
BaseRangedDamage[0]=0;
BaseDamage[1]=0;
BaseOffhandDamage[1]=0;
BaseRangedDamage[1]=0;
m_attackTarget = 0;
m_CombatUpdateTimer = 0;
for(uint32 x=0;x<7;x++)
{
SchoolImmunityList[x] = 0;
BaseResistance[x] = 0;
HealDoneMod[x] = 0;
HealDonePctMod[x] = 0;
HealTakenMod[x] = 0;
HealTakenPctMod[x] = 0;
DamageTakenMod[x] = 0;
DamageDoneModPCT[x]= 0;
SchoolCastPrevent[x]=0;
DamageTakenPctMod[x] = 1;
SpellCritChanceSchool[x] = 0;
PowerCostMod[x] = 0;
PowerCostPctMod[x] = 0;
AttackerSpellCritChanceMod[x]=0;
}
DamageTakenPctModOnHP = 1;
RangedDamageTaken = 0;
for(int i = 0; i < 5; i++)
{
m_detectRangeGUID[i] = 0;
m_detectRangeMOD[i] = 0;
InvisibilityDetectBonus[i] = 0;
}
//REMIND:Update these if you make any changes
InvisibilityDetectBonus[INVISIBILTY_FLAG_TRAP] = 180;//MaxLevel*3
InvisibilityDetectBonus[INVISIBILTY_FLAG_GHOSTS] = 0;
trackStealth = false;
modAttackTimeIncreasePCT = 0;
m_threatModifyer = 0;
m_generatedThreatModifyer = 0;
memset(m_auras, 0, (MAX_AURAS+MAX_PASSIVE_AURAS)*sizeof(Aura*));
// diminishing return stuff
memset(m_diminishAuraCount, 0, 23);
memset(m_diminishCount, 0, 23*2);
memset(m_diminishTimer, 0, 23*2);
memset(m_auraStackCount, 0, MAX_AURAS);
m_diminishActive = false;
dynObj = 0;
pLastSpell = 0;
m_flyspeedModifier = 0;
bInvincible = false;
m_redirectSpellPackets = 0;
can_parry = false;
bProcInUse = false;
spellcritperc = 0;
polySpell = 0;
RangedDamageTaken = 0;
m_procCounter = 0;
m_invisibityFlag = 0;
m_extrastriketargets = 0;
// fearSpell = 0;
}
Unit::~Unit()
{
RemoveAllAuras();
if(SM_CriticalChance != 0) delete [] SM_CriticalChance ;
if(SM_FDur != 0) delete [] SM_FDur ;//flat
if(SM_PDur != 0) delete [] SM_PDur ;//pct
if(SM_FRadius != 0) delete [] SM_FRadius ;
if(SM_FRange != 0) delete [] SM_FRange ;
if(SM_PCastTime != 0) delete [] SM_PCastTime ;
if(SM_FCastTime != 0) delete [] SM_FCastTime ;
if(SM_PCriticalDamage != 0) delete [] SM_PCriticalDamage ;
if(SM_FDOT != 0) delete [] SM_FDOT ;
if(SM_PDOT != 0) delete [] SM_PDOT ;
if(SM_PEffectBonus != 0) delete [] SM_PEffectBonus ;
if(SM_FEffectBonus != 0) delete [] SM_FEffectBonus ;
if(SM_FDamageBonus != 0) delete [] SM_FDamageBonus ;
if(SM_PDamageBonus != 0) delete [] SM_PDamageBonus ;
if(SM_PDummy != 0) delete [] SM_PDummy ;
if(SM_FDummy != 0) delete [] SM_FDummy ;
if(SM_FResist != 0) delete [] SM_FResist ;
if(SM_PRange != 0) delete [] SM_PRange ;//pct
if(SM_PRadius != 0) delete [] SM_PRadius ;
if(SM_PAPBonus != 0) delete [] SM_PAPBonus ;
if(SM_PCost != 0) delete [] SM_PCost ;
if(SM_FCost != 0) delete [] SM_FCost ;
if(SM_FAdditionalTargets != 0) delete [] SM_FAdditionalTargets ;
if(SM_PJumpReduce != 0) delete [] SM_PJumpReduce ;
if(SM_FSpeedMod != 0) delete [] SM_FSpeedMod ;
if(SM_PNonInterrupt != 0) delete [] SM_PNonInterrupt ;
if(SM_FPenalty != 0) delete [] SM_FPenalty ;
if(SM_PPenalty != 0) delete [] SM_PPenalty ;
if(SM_FCooldownTime != 0) delete [] SM_FCooldownTime ;
if(SM_PCooldownTime != 0) delete [] SM_PCooldownTime ;
if(SM_FChanceOfSuccess != 0) delete [] SM_FChanceOfSuccess ;
delete m_aiInterface;
/*for(int i = 0; i < 4; i++)
if (m_ObjectSlots[i])
delete m_ObjectSlots[i];*/
if(m_currentSpell)
m_currentSpell->cancel();
}
void Unit::Update( uint32 p_time )
{
_UpdateSpells( p_time );
if(m_attackers.size() == 0 && m_attackTarget == 0)
{
RemoveFlag(UNIT_FIELD_FLAGS, U_FIELD_FLAG_ATTACK_ANIMATION);
if(hasStateFlag(UF_ATTACKING)) clearStateFlag(UF_ATTACKING);
}
else
{
SetFlag(UNIT_FIELD_FLAGS, U_FIELD_FLAG_ATTACK_ANIMATION);
if(!hasStateFlag(UF_ATTACKING)) addStateFlag(UF_ATTACKING);
}
if(!isDead())
{
if(p_time >= m_H_regenTimer)
RegenerateHealth();
else
m_H_regenTimer -= p_time;
//most of the times the 2 timers will be the same (except on spell casts)
if(p_time >= m_P_regenTimer)
RegeneratePower(false);
else
{
if (p_time >= m_P_I_regenTimer&&this->IsPlayer())
RegeneratePower(true);
else
m_P_I_regenTimer -= p_time;
m_P_regenTimer -= p_time;
}
if(m_aiInterface != NULL && m_useAI)
m_aiInterface->Update(p_time);
if(m_diminishActive)
{
uint32 count = 0;
for(uint32 x = 0; x < 16; ++x)
{
// diminishing return stuff
if(m_diminishTimer[x] && !m_diminishAuraCount[x])
{
if(p_time >= m_diminishTimer[x])
{
// resetting after 15 sec
m_diminishTimer[x] = 0;
m_diminishCount[x] = 0;
}
else
{
// reducing, still.
m_diminishTimer[x] -= p_time;
++count;
}
}
}
if(!count)
m_diminishActive = false;
}
/* //if health changed since last time. Would be perfect if it would work for creatures too :)
if(m_updateMask.GetBit(UNIT_FIELD_HEALTH))
EventHealthChangeSinceLastUpdate();*/
}
}
bool Unit::canReachWithAttack(Unit *pVictim)
{
// float targetreach = pVictim->GetFloatValue(UNIT_FIELD_COMBATREACH);
float selfreach = GetFloatValue(UNIT_FIELD_COMBATREACH);
float targetradius = pVictim->GetFloatValue(UNIT_FIELD_BOUNDINGRADIUS);
float selfradius = GetFloatValue(UNIT_FIELD_BOUNDINGRADIUS);
float targetscale = pVictim->GetFloatValue(OBJECT_FIELD_SCALE_X);
float selfscale = GetFloatValue(OBJECT_FIELD_SCALE_X);
if( GetMapId() != pVictim->GetMapId() )
return false;
float distance = sqrt(GetDistanceSq(pVictim));
float attackreach = (((targetradius*targetscale) + selfreach) + ((pow(selfradius,2)*selfscale)+1.50));
//formula adjustment for player side.
if(this->IsPlayer())
{
if (attackreach <= 8 && attackreach >= 5 && targetradius >= 1.80f) attackreach = 11; //giant type units
if (attackreach > 11) attackreach = 11; //distance limited to max 11 yards attack range //max attack distance
if (attackreach < 5 ) attackreach = 5; //normal units with too small reach.
//range can not be less than 5 yards - this is normal combat range, SCALE IS NOT SIZE
}
return (distance <= attackreach);
}
void Unit::GiveGroupXP(Unit *pVictim, Player *PlayerInGroup)
{
if(!PlayerInGroup)
return;
if(!pVictim)
return;
if(!PlayerInGroup->InGroup())
return;
Group *pGroup = PlayerInGroup->GetGroup();
uint32 xp;
if(!pGroup)
return;
//Get Highest Level Player, Calc Xp and give it to each group member
Player *pHighLvlPlayer = NULL;
Player *pGroupGuy = NULL;
int active_player_count=0;
Player *active_player_list[MAX_GROUP_SIZE_RAID];//since group is small we can afford to do this ratehr then recheck again the whole active player set
int total_level=0;
float xp_mod = 1.0f;
/* if(pGroup->GetGroupType() == GROUP_TYPE_RAID)
{ //needs to change
//Calc XP
xp = CalculateXpToGive(pVictim, PlayerInGroup);
xp /= pGroup->MemberCount();
GroupMembersSet::iterator itr;
for(uint32 i = 0; i < pGroup->GetSubGroupCount(); i++)
{
for(itr = pGroup->GetSubGroup(i)->GetGroupMembersBegin(); itr != pGroup->GetSubGroup(i)->GetGroupMembersEnd(); ++itr)
{
if((*itr)->getLevel() < sWorld.LevelCap)
(*itr)->GiveXP(xp, pVictim->GetGUID(), true);
}
}
}
else if(pGroup->GetGroupType() == GROUP_TYPE_PARTY) */
//change on 2007 04 22 by Zack
//we only take into count players that are near us, on same map
GroupMembersSet::iterator itr;
pGroup->Lock();
for(uint32 i = 0; i < pGroup->GetSubGroupCount(); i++) {
for(itr = pGroup->GetSubGroup(i)->GetGroupMembersBegin(); itr != pGroup->GetSubGroup(i)->GetGroupMembersEnd(); ++itr)
{
pGroupGuy = itr->player;
if( pGroupGuy &&
pGroupGuy->isAlive() &&
// PlayerInGroup->GetInstanceID()==pGroupGuy->GetInstanceID() &&
pVictim->GetMapMgr() == pGroupGuy->GetMapMgr() &&
pGroupGuy->GetDistanceSq(pVictim)<100*100
)
{
active_player_list[active_player_count]=pGroupGuy;
active_player_count++;
total_level += pGroupGuy->getLevel();
if(pHighLvlPlayer)
{
if(pGroupGuy->getLevel() > pHighLvlPlayer->getLevel())
pHighLvlPlayer = pGroupGuy;
}
else
pHighLvlPlayer = pGroupGuy;
}
}
}
pGroup->Unlock();
if(active_player_count<1) //killer is always close to the victim. This should never execute
{
if(PlayerInGroup == 0) PlayerInGroup = pGroup->GetLeader();
xp = CalculateXpToGive(pVictim, PlayerInGroup);
PlayerInGroup->GiveXP(xp, pVictim->GetGUID(), true);
}
else
{
if( pGroup->GetGroupType() == GROUP_TYPE_PARTY)
{
if(active_player_count==3)
xp_mod=1.1666f;
else if(active_player_count==4)
xp_mod=1.3;
else if(active_player_count==5)
xp_mod=1.4;
else xp_mod=1;//in case we have only 2 members ;)
}
else if(pGroup->GetGroupType() == GROUP_TYPE_RAID)
xp_mod=0.5f;
if(pHighLvlPlayer == 0) pHighLvlPlayer = pGroup->GetLeader();
xp = CalculateXpToGive(pVictim, pHighLvlPlayer);
//i'm not sure about this formula is correct or not. Maybe some brackets are wrong placed ?
for(int i=0;iGiveXP( float2int32(((xp*active_player_list[i]->getLevel()) / total_level)*xp_mod), pVictim->GetGUID(), true );
}
/* old code start before 2007 04 22
GroupMembersSet::iterator itr;
for(uint32 i = 0; i < pGroup->GetSubGroupCount(); i++)
{
for(itr = pGroup->GetSubGroup(i)->GetGroupMembersBegin(); itr != pGroup->GetSubGroup(i)->GetGroupMembersEnd(); ++itr)
{
pGroupGuy = (*itr);
if(pGroupGuy)
{
if(pHighLvlPlayer)
{
if(pGroupGuy->getLevel() > pHighLvlPlayer->getLevel())
{
pHighLvlPlayer = pGroupGuy;
}
}
else
pHighLvlPlayer = pGroupGuy;
}
}
}
//Calc XP
xp = CalculateXpToGive(pVictim, pHighLvlPlayer);
uint32 giveXP = 0;
for(uint32 i = 0; i < pGroup->GetSubGroupCount(); i++)
{
for(itr = pGroup->GetSubGroup(i)->GetGroupMembersBegin(); itr != pGroup->GetSubGroup(i)->GetGroupMembersEnd(); ++itr)
{
pGroupGuy = (*itr);
giveXP = xp * pGroupGuy->getLevel() / (pHighLvlPlayer->getLevel() + pGroupGuy->getLevel());
if(pGroupGuy->getLevel() < sWorld.LevelCap)
pGroupGuy->GiveXP(giveXP, pVictim->GetGUID(), true);
}
}
}*/
}
void Unit::HandleProc(uint32 flag, Unit* victim, SpellEntry* CastingSpell,uint32 dmg)
{
++m_procCounter;
bool can_delete = !bProcInUse;
bProcInUse = true;
std::list remove;
std::list::iterator itr,itr2;
for( itr = m_procSpells.begin();itr != m_procSpells.end();) // Proc Trigger Spells for Victim
{
itr2= itr;
++itr;
if(itr2->deleted)
{
if(can_delete) m_procSpells.erase(itr2);
continue;
}
uint32 origId = itr2->origId;
if(CastingSpell)
{
//this is to avoid spell proc on spellcast loop. We use dummy that is same for both spells
if(CastingSpell->Id == itr2->origId || CastingSpell->Id == itr2->spellId)
{
//printf("WOULD CRASH HERE ON PROC: CastingId: %u, OrigId: %u, SpellId: %u\n", CastingSpell->Id, itr2->origId, itr2->spellId);
continue;
}
}
SpellEntry *ospinfo = sSpellStore.LookupEntry(origId );//no need to check if exists or not since we were not able to register this trigger if it would not exist :P
//this requires some specific spell check,not yet implemented
if(itr2->procFlags & flag)
{
uint32 spellId = itr2->spellId;
if(itr2->procFlags & PROC_ON_CAST_SPECIFIC_SPELL)
{
if(!CastingSpell)
continue;
//this is wrong, dummy is too common to be based on this, we should use spellgroup or something
SpellEntry *sp=sSpellStore.LookupEntry(spellId);
if(sp->dummy != CastingSpell->dummy)
{
if(!ospinfo->School)
continue;
if(ospinfo->School != CastingSpell->School)
continue;
if(CastingSpell->EffectImplicitTargetA[0] == 1 ||
CastingSpell->EffectImplicitTargetA[1] == 1 ||
CastingSpell->EffectImplicitTargetA[2] == 1) //Prevents school based procs affecting caster when self buffing
continue;
}
else
if(sp->dummy == 1)
continue;
}
uint32 proc_Chance = itr2->procChance;
SM_FIValue(SM_FChanceOfSuccess, (int32*)&proc_Chance, ospinfo->SpellGroupType);
if(spellId && Rand(proc_Chance))
{
/* hmm whats a reasonable value here */
if(m_procCounter > 40)
{
/* something has proceed over 10 times in a loop :/ dump the spellids to the crashlog, as the crashdump will most likely be useless. */
OutputCrashLogLine("HandleProc %u SpellId %u (%s) %u", flag, spellId, sSpellStore.LookupString(sSpellStore.LookupEntry(spellId)->Name), m_procCounter);
return;
}
//check if we can trigger due to time limitation
if(ospinfo->proc_interval)
{
uint32 now_in_ms=getMSTime();
if(itr2->LastTrigger+ospinfo->proc_interval>now_in_ms)
continue; //we can't trigger it yet.
itr2->LastTrigger = now_in_ms; // consider it triggered
}
//since we did not allow to remove auras like these with interrupt flag we have to remove them manually.
if(itr2->procFlags & PROC_REMOVEONUSE)
RemoveAura(origId);
//these are player talents. Fuckem they pull the emu speed down
if(IsPlayer())
{
uint32 talentlevel=0;
switch(origId)
{
//mace specialization
case 12284: {talentlevel=1;}break;
case 12701: {talentlevel=2;}break;
case 12702: {talentlevel=3;}break;
case 12703: {talentlevel=4;}break;
case 12704: {talentlevel=5;}break;
//Unbridled Wrath
case 12999: {talentlevel=1;}break;
case 13000: {talentlevel=2;}break;
case 13001: {talentlevel=3;}break;
case 13002: {talentlevel=4;}break;
}
switch(spellId)
{
case 31616:
{
//yep, another special case: Nature's grace
if(GetHealthPct()>30)
continue;
}break;
case 5530:
{
//warrior mace specialization can trigger only when using maces
Item *it;
if(static_cast(this)->GetItemInterface())
{
it = static_cast(this)->GetItemInterface()->GetInventoryItem(EQUIPMENT_SLOT_MAINHAND);
if(it && it->GetProto())
{
uint32 reqskill=GetSkillByProto(it->GetProto()->Class,it->GetProto()->SubClass);
if(reqskill!=SKILL_MACES && reqskill!=SKILL_2H_MACES)
continue;
}
else continue; //no weapon no joy
}
else continue; //no weapon no joy
//let's recalc chance to cast since we have a full 100 all time on this one
//how lame to get talentpointlevel for this spell :(
// float chance=it->GetProto()->Delay*100*talentlevel/60000;
float chance=it->GetProto()->Delay*talentlevel/600;
if(!Rand(chance))
continue;
}break;
case 4350:
{
//sword specialization
if(static_cast(this)->GetItemInterface())
{
Item *it;
it = static_cast(this)->GetItemInterface()->GetInventoryItem(EQUIPMENT_SLOT_MAINHAND);
if(it && it->GetProto())
{
uint32 reqskill=GetSkillByProto(it->GetProto()->Class,it->GetProto()->SubClass);
if(reqskill!=SKILL_SWORDS && reqskill!=SKILL_2H_SWORDS)
continue;
}
else continue; //no weapon no joy
}
else continue; //no weapon no joy
}break;
case 12721:
{
//deep wound requires a melee weapon
if(static_cast(this)->GetItemInterface())
{
Item *it;
it = static_cast(this)->GetItemInterface()->GetInventoryItem(EQUIPMENT_SLOT_MAINHAND);
if(it && it->GetProto())
{
//class 2 means weapons ;)
if(it->GetProto()->Class!=2)
continue;
}
else continue; //no weapon no joy
}
else continue; //no weapon no joy
}break;
//Unbridled Wrath
case 12964:
{
//let's recalc chance to cast since we have a full 100 all time on this one
Item *it;
if(static_cast(this)->GetItemInterface())
{
it = static_cast(this)->GetItemInterface()->GetInventoryItem(EQUIPMENT_SLOT_MAINHAND);
if(!(it && it->GetProto()))
continue; //no weapon no joy
}
else continue; //no weapon no joy
// float chance=it->GetProto()->Delay*100*talentlevel/60000;
float chance=it->GetProto()->Delay*talentlevel/600;
if(!Rand(chance))
continue;
}break;
// Mage ignite talent only for fire dmg
case 12654:
{
if(!CastingSpell)
continue;
if(CastingSpell->School!=SCHOOL_FIRE)
continue;
}break;
//rogue - blade twisting
case 31125:
{
if(!CastingSpell)
continue;//this should not ocur unless we made a fuckup somewhere
//only trigger effect for specified spells
if( CastingSpell->NameHash!=0xD3D32C05 && //backstab
CastingSpell->NameHash!=0xE876878A && //sinister strike
CastingSpell->NameHash!=0x92253E33 && //shiv
CastingSpell->NameHash!=0xCCC8A100 ) //gouge
continue;
}break;
//warlock - Improved Shadow Bolt
case 17794:
case 17798:
case 17797:
case 17799:
case 17800:
{
if(!CastingSpell)
continue;//this should not ocur unless we made a fuckup somewhere
//only trigger effect for specified spells
if( CastingSpell->NameHash!=0x7A7B6753)//shadow bolt
continue;
}break;
//warlock - Shadow Embrace
case 32386:
case 32388:
case 32389:
case 32390:
case 32391:
{
if (!CastingSpell)
continue;
else
{
if (CastingSpell->NameHash!=0x2ADC25D7 &&// Corruption
CastingSpell->NameHash!=0x34C208A2 &&//CoA
CastingSpell->NameHash!=0x2979DFBA &&//Siphon Life
CastingSpell->NameHash!=0xFD712ED2) //SoC
continue;
}
}break;
//warlock - Aftermath
case 18118:
{
if(!CastingSpell)
continue;//this should not ocur unless we made a fuckup somewhere
//only trigger effect for specified spells
skilllinespell* skillability = objmgr.GetSpellSkill(CastingSpell->Id);
if (!skillability)
continue;
if(skillability->skilline!=SKILL_DESTRUCTION)
continue;
}break;
//warlock - Nether Protection
case 30300:
{
if(!CastingSpell)
continue;//this should not ocur unless we made a fuckup somewhere
//only trigger effect for specified spells
if( CastingSpell->School!=SCHOOL_FIRE &&
CastingSpell->School!=SCHOOL_SHADOW)
continue;
}break;
//warlock - Soul Leech
//this whole spell should get rewriten. Uses bad formulas, bad trigger method, spell is rewriten ...
case 30294:
{
if(!CastingSpell)
continue;//this should not ocur unless we made a fuckup somewhere
//only trigger effect for specified spells
uint32 amount;
switch(CastingSpell->NameHash)
{
case 0x7A7B6753: //Shadow Bolt
case 0x9B1BA9E0: //Soul Fire
case 0x2BC0AE00: //Incinerate
case 0xD6269851: //Searing Pain
case 0xB767620F: //Conflagrate
{
amount = CastingSpell->EffectBasePoints[0]+1;
}break;
case 0x1066956C: //Shadowburn
{
amount = CastingSpell->EffectBasePoints[1]+1;
}break;
default:
amount=0;
}
if(!amount)
continue;
SpellEntry *spellInfo = sSpellStore.LookupEntry(spellId );
if(!spellInfo)
continue;
Spell *spell = new Spell(this, spellInfo ,true, NULL);
spell->SetUnitTarget(this);
spell->Heal(amount*(ospinfo->EffectBasePoints[0]+1)/100);
delete spell;
continue;
}break;
//warlock - pyroclasm
case 18093:
{
if(!CastingSpell)
continue;//this should not ocur unless we made a fuckup somewhere
//only trigger effect for specified spells
if( CastingSpell->NameHash!=0xBA051C13 && //Rain of Fire
CastingSpell->NameHash!=0x799839A4 && //Hellfire
CastingSpell->NameHash!=0x9B1BA9E0 ) //Soul Fire
continue;
}break;
//mage - Improved Scorch
case 22959:
{
if(!CastingSpell)
continue;//this should not ocur unless we made a fuckup somewhere
//only trigger effect for specified spells
if( CastingSpell->NameHash!=1828847009) //Rain of Fire
continue;
}break;
//priest - Misery
case 33200:
case 33199:
case 33198:
case 33197:
case 33196:
{
if (!CastingSpell)
continue;
else
{
if (CastingSpell->NameHash!=0xC3D9ACEB &&// Mind Flay
CastingSpell->NameHash!=0x7335D7AF &&//SW:P
CastingSpell->NameHash!=0x34D76539) //SoC
continue;
}
}break;
//priest - Shadow Weaving
case 15258:
{
if(!CastingSpell)
continue;//this should not ocur unless we made a fuckup somewhere
if(CastingSpell->School!=SCHOOL_SHADOW || !IsDamagingSpell(CastingSpell)) //we need damaging spells for this, so we suppose all shadow spells casted on target are dmging spells = Wrong
continue;
}break;
//shaman - windfurry weapon
case 8232:
case 8235:
case 10486:
case 16362:
case 25505:
{
if(!IsPlayer())
continue;
//!! The wierd thing is that we need the spell thet trigegred this enchant spell in order to output logs ..we are using oldspell info too
//we have to recalc the value of this spell
SpellEntry *spellInfo = sSpellStore.LookupEntry(origId);
uint32 AP_owerride=GetAP() + spellInfo->EffectBasePoints[0]+1;
float dmg = static_cast(this)->GetMainMeleeDamage(AP_owerride);
SpellEntry *sp_for_the_logs = sSpellStore.LookupEntry(spellId);
Strike(victim,MELEE,sp_for_the_logs,dmg,0,0,true);
Strike(victim,MELEE,sp_for_the_logs,dmg,0,0,true);
//nothing else to be done for this trigger
continue;
}break;
//rogue - Ruthlessness
case 14157:
{
//we need a finishing move for this
if(CastingSpell->buffType!=SPELL_TYPE_FINISHING_MOVE || victim==this)
continue;
}break;
//rogue - Relentless Strikes
case 14181:
{
int32 proc_Chance;
//chance is based actually on combopoint count and not 100% always
if(CastingSpell->buffType==SPELL_TYPE_FINISHING_MOVE && IsPlayer())
proc_Chance = static_cast(this)->m_comboPoints*ospinfo->EffectBasePoints[1];
else continue;
if(!Rand(proc_Chance))
continue;
}break;
//rogue - Initiative
case 13977:
{
//we need a Ambush, Garrote, or Cheap Shot
if (CastingSpell->NameHash!=3345919181 && //Cheap Shot
CastingSpell->NameHash!=566770777 && //Ambush
CastingSpell->NameHash!=891349384) //Garrote
continue;
}break;
//Blackout
case 15269:
{
if(!CastingSpell)
continue;//this should not ocur unless we made a fuckup somewhere
if(CastingSpell->School!=SCHOOL_SHADOW && !IsDamagingSpell(CastingSpell))
continue;
}break;
/* //paladin - illumination
case 18350:
{
continue; //disabled until finished
if(!CastingSpell)
continue;//this should not ocur unless we made a fuckup somewhere
//only trigger effect for specified spells
if( CastingSpell->NameHash!=666 && //Holy light
CastingSpell->NameHash!=666 && //Flash of light
CastingSpell->NameHash!=666 ) //Holy shock
continue;
}break;*/
}
}
if(spellId==22858 && isInBack(victim)) //retatliation needs target to be not in front. Can be casted by creatures too
continue;
SpellEntry *spellInfo = sSpellStore.LookupEntry(spellId );
if(!spellInfo)
{
continue;
}
Spell *spell = new Spell(this, spellInfo ,true, NULL);
//Spell *spell = new Spell(this,spellInfo,false,0,true,false);
if(spellId==974||spellId==32593||spellId==32594) // Earth Shield handler
{
spell->pSpellId=itr2->spellId;
spell->SpellEffectDummy(0);
spell->ProcedOnSpell = CastingSpell;
delete spell;
continue;
}
SpellCastTargets targets;
if(itr2->procFlags & PROC_TAGRGET_SELF)
targets.m_unitTarget = GetGUID();
else
targets.m_unitTarget = victim->GetGUID();
spell->pSpellId=origId;
spell->prepare(&targets);
}//not always we have a spell to cast
}
}
HandleProcDmgShield(flag,victim);
std::map::iterator iter,iter2;
iter=m_chargeSpells.begin();
while(iter!= m_chargeSpells.end())
{
iter2=iter++;
if(iter2->second.count)
{
if((iter2->second.ProcFlag&flag))
{
//Fixes for spells that dont lose charges when dmg is absorbd
if(iter2->second.ProcFlag==680&&dmg==0) continue;
if(CastingSpell)
{
SpellCastTime *sd = sCastTime.LookupEntry(CastingSpell->CastingTimeIndex);
if(!sd) continue; // this shouldnt happen though :P
switch(iter2->second.spellId)
{
case 12043:
{
//Presence of Mind and Nature's Swiftness should only get removed
//when a non-instant and bellow 10 sec. Also must be nature :>
// if(!sd->CastTime||sd->CastTime>10000) continue;
if(sd->CastTime==0)
continue;
}break;
case 16188:
{
// if(CastingSpell->School!=SCHOOL_NATURE||(!sd->CastTime||sd->CastTime>10000)) continue;
if(CastingSpell->School!=SCHOOL_NATURE||(sd->CastTime==0)) continue;
}break;
case 16166:
{
if(!(CastingSpell->School==SCHOOL_FIRE||CastingSpell->School==SCHOOL_FROST||CastingSpell->School==SCHOOL_NATURE))
continue;
}break;
case 14177: //cold blood will get removed on offensive spell
{
if(victim==this || isFriendly(this, victim))
continue;
}break;
}
}
if(iter2->second.lastproc!=0)
{
if(iter2->second.procdiff>3000)
{
--(iter2->second.count);
iter2->second.FromProc=true;
RemoveAura(iter2->second.spellId);
iter2->second.FromProc=false;
}
}
else
{
--(iter2->second.count);
iter2->second.FromProc=true;
this->RemoveAura(iter2->second.spellId);
iter2->second.FromProc=false;
}
}
}
if(!iter2->second.count)
{
m_chargeSpells.erase(iter2);
}
}
if(can_delete)
bProcInUse = false;
}
void Unit::HandleProcDmgShield(uint32 flag, Unit* victim)
{
//make sure we do not loop dmg procs
if(this==victim || !victim)
return;
//charges are already removed in handleproc
WorldPacket data(24);
std::list::iterator i;
std::list::iterator i2;
for(i = victim->m_damageShields.begin();i != victim->m_damageShields.end();) // Deal Damage to Attacker
{
i2 = i++; //we should not proc on proc.. not get here again.. not needed.Better safe then sorry.
if( (flag & (*i2).m_flags) )
{
if(PROC_MISC & (*i2).m_flags)
{
data.Initialize(SMSG_SPELLDAMAGESHIELD);
data << victim->GetGUID();
data << this->GetGUID();
data << (*i2).m_damage;
data << (*i2).m_school;
SendMessageToSet(&data,true);
victim->DealDamage(this,(*i2).m_damage,0,0,(*i2).m_spellId);
}
else
{
SpellEntry *ability=sSpellStore.LookupEntry((*i2).m_spellId);
// victim->Strike(this,(*i2).m_school,ability,0,0,(*i2).m_damage, true);
victim->Strike(this,RANGED,ability,0,0,(*i2).m_damage, true);
}
}
}
}
/*
void Unit::HandleProcSpellOnSpell(Unit* Victim,uint32 damage,bool critical)
{
}
*/
bool Unit::isCasting()
{
return (m_currentSpell != NULL);
}
void Unit::RegenerateHealth()
{
m_H_regenTimer = 2000;//set next regen time
if (!isAlive())
return;
// player regen
if(this->IsPlayer())
{
// These only NOT in combat
if(!static_cast(this)->isInCombat())
{
static_cast(this)->RegenerateHealth(false);
}
else
static_cast(this)->RegenerateHealth(true);
}
else
{
// Only regen health out of combat
if(!isInCombat())
static_cast(this)->RegenerateHealth();
}
}
void Unit::RegeneratePower(bool isinterrupted)
{
// This is only 2000 IF the power is not rage
m_P_regenTimer = 2000;//set next regen time
m_P_I_regenTimer = 2000;//set next interrupted regen time
if (!isAlive())
return;
// player regen
if(this->IsPlayer())
{
uint32 powertype = GetPowerType();
float RegenPct = 1.0f;
switch(powertype)
{
case POWER_TYPE_MANA:
if (isinterrupted)
RegenPct = static_cast(this)->m_ModInterrMRegenPCT/100.0f;
static_cast(this)->RegenerateMana(RegenPct);
break;
case POWER_TYPE_ENERGY:
if (isinterrupted)
RegenPct = 0.0f;
static_cast(this)->RegenerateEnergy(RegenPct);
break;
}
/*
There is a problem here for druids.
Druids when shapeshifted into bear have 2 power with different regen timers
a) Mana (which regenerates while shapeshifted
b) Rage
Mana has a regen timer of 2 seconds
Rage has a regen timer of 3 seconds
I think the only viable way of fixing this is to have 2 different timers
to check each individual power.
Atm, mana is being regen at 3 seconds while shapeshifted...
*/
// druids regen mana when shapeshifted
if(getClass() == DRUID && powertype != POWER_TYPE_MANA)
static_cast(this)->RegenerateMana(RegenPct);
// These only NOT in combat
if(!static_cast(this)->isInCombat())
{
// Rage timer is 3 seconds not 2
if(powertype == POWER_TYPE_RAGE)
{
m_P_regenTimer = 3000;
m_P_I_regenTimer = 3000;
static_cast(this)->LooseRage();
}
}
}
else
{
uint32 powertype = GetPowerType();
switch(powertype)
{
case POWER_TYPE_MANA:
static_cast(this)->RegenerateMana();
break;
case POWER_TYPE_FOCUS:
static_cast(this)->RegenerateFocus();
break;
}
}
}
void Unit::CalculateResistanceReduction(Unit *pVictim,dealdamage * dmg)
{
// lvl diff: 0+ 1+ 2+
float resist[3]={4.0f,5.0f,6.0f};
int32 lvldiff = 0;
float resistchance = 1.0f;
float miscchance = 0.0f;
float AverageResistance = 0.0f;
// bool pvp = false;
if(GetTypeId() == TYPEID_PLAYER && pVictim->GetTypeId() == TYPEID_PLAYER) //PvP
miscchance = 7.0f;
else
miscchance = 11.0f;
if((*dmg).damage_type == 0)//physical
{
// double Reduction = double(pVictim->GetResistance(0)) / double(pVictim->GetResistance(0)+400+(85*getLevel()));
//dmg reduction formula from xinef
double Reduction = 0;
if (getLevel() < 60) Reduction = double(pVictim->GetResistance(0)) / double(pVictim->GetResistance(0)+400+(85*getLevel()));
else if (getLevel() > 59 && getLevel() < 70) Reduction = double(pVictim->GetResistance(0)) / double(pVictim->GetResistance(0)-22167.5+(467.5*getLevel()));
//
else Reduction = double(pVictim->GetResistance(0)) / double(pVictim->GetResistance(0)+10557.5);
if(Reduction > 0.75f) Reduction = 0.75f;
else if(Reduction < 0) Reduction = 0;
if(Reduction) dmg[0].resisted_damage = (uint32)(dmg[0].full_damage*Reduction); // no multiply by 0
}
else
{
// applying resistance to other type of damage
lvldiff = pVictim->getLevel() - getLevel();
if(lvldiff >= 0)
{
if(lvldiff < 3)
resistchance = resist[lvldiff];
else
resistchance = resist[2] + miscchance*(lvldiff-2);
if(IsPlayer())
{
float spellHitMod = static_cast(this)->GetHitFromSpell();
resistchance -= spellHitMod;
}
}
if(m_objectTypeId == TYPEID_UNIT)
{
Creature * c = (Creature*)(this);
if (c&&c->GetCreatureName()&&c->GetCreatureName()->Rank == 3) //boss
resistchance = 1.0f; //can't resist boss spells even if lvl70 -vs- lvl63 boss.
}
if (pVictim->IsPlayer())
resistchance-=static_cast(pVictim)->m_resist_hit[2];
if (resistchance<1)
resistchance=1.0f;
if(Rand(resistchance))
{
(*dmg).resisted_damage = (*dmg).full_damage;
}
else
{
AverageResistance = ((float)pVictim->GetResistance( (*dmg).damage_type)- PowerCostPctMod[(*dmg).damage_type]) / (float)(getLevel() * 5) * 0.75f;
if(AverageResistance > 0.75f)
AverageResistance = 0.75f;
if(AverageResistance>0)
(*dmg).resisted_damage = (uint32)(((*dmg).full_damage)*AverageResistance);
else
(*dmg).resisted_damage=0;
}
}
// Checks for random bugs on spells
if ( (*dmg).full_damage > 10000 && GetTypeId() == TYPEID_PLAYER && ((Player*)this)->GetSession()->GetPermissionCount() == 0) // hits higher then 5.5k must be bugged
{
sCheatLog.writefromsession(static_cast(this)->GetSession(),"some how caused %u damage using spell %u",(*dmg).full_damage,GetCurrentSpell());
(*dmg).resisted_damage = (*dmg).full_damage;
sChatHandler.RedSystemMessage(static_cast(this)->GetSession(),"Your actions have been logged, please tell a GM how to re-produce this damage bug or risk getting banned.");
}
//sLog.outDebug("calc resistance - damage: %d , dmg type: %d , dmg abs: %d\n",*damage,damage_type,*dmgabs);
}
void Unit::Strike(Unit *pVictim, uint32 damage_type, SpellEntry *ability, int32 add_damage, int32 pct_dmg_mod, uint32 exclusive_damage, bool disable_proc)
{
//==========================================================================================
//==============================Unacceptable Cases Processing===============================
//==========================================================================================
if (!pVictim->isAlive() || !isAlive() || IsStunned() || IsPacified())
return;
if(!isInFront(pVictim))
if(IsPlayer())
{
static_cast(this)->GetSession()->OutPacket(SMSG_ATTACKSWING_BADFACING);
return;
}
//==========================================================================================
//==============================Variables Initialization====================================
//==========================================================================================
dealdamage dmg = {0,0,0};
Item * it = NULL;
float hitchance = 0.0f;
float dodge = 0.0f;
float parry = 0.0f;
float glanc = 0.0f;
float block = 0.0f;
float crit = 0.0f;
float crush = 0.0f;
uint32 targetEvent = 0;
uint32 hit_status = 0;
uint32 blocked_damage = 0;
int32 realdamage = 0;
uint32 vstate = 1;
uint32 aproc = 0;
uint32 vproc = 0;
float hitmodifier = 0;
int32 self_skill;
int32 victim_skill;
uint32 SubClassSkill = SKILL_UNARMED;
bool backAttack = isInBack( pVictim );
uint32 vskill = 0;
//==========================================================================================
//==============================Victim Skill Base Calculation===============================
//==========================================================================================
if(pVictim->IsPlayer())
{
vskill = ((Player*)pVictim)->_GetSkillLineCurrent(SKILL_DEFENSE);
if((damage_type != RANGED) && !backAttack)
{
//--------------------------------block chance----------------------------------------------
it = ((Player*)pVictim)->GetItemInterface()->GetInventoryItem(EQUIPMENT_SLOT_OFFHAND);
if(it && it->GetProto()->InventoryType==INVTYPE_SHIELD)
{
block = pVictim->GetFloatValue(PLAYER_BLOCK_PERCENTAGE);
}
//--------------------------------dodge chance----------------------------------------------
if (pVictim->m_stunned<=0)
{
dodge = pVictim->GetFloatValue(PLAYER_DODGE_PERCENTAGE);
}
//--------------------------------parry chance----------------------------------------------
if(pVictim->can_parry && !disarmed)
{
parry = pVictim->GetFloatValue(PLAYER_PARRY_PERCENTAGE);
}
}
victim_skill = float2int32(vskill+((Player*)pVictim)->CalcRating(1));
}
//--------------------------------mob defensive chances-------------------------------------
else
{
if(damage_type != RANGED && !backAttack)
dodge = pVictim->GetUInt32Value(UNIT_FIELD_STAT1) / 14.5f; // what is this value?
victim_skill = pVictim->getLevel() * 5;
if(pVictim->m_objectTypeId == TYPEID_UNIT)
{
Creature * c = (Creature*)(pVictim);
if (c&&c->GetCreatureName()&&c->GetCreatureName()->Rank == 3) //boss
{
victim_skill = max(victim_skill,((int32)this->getLevel()+3)*5); //used max to avoid situation when lowlvl hits boss.
}
}
}
//==========================================================================================
//==============================Attacker Skill Base Calculation=============================
//==========================================================================================
if(this->IsPlayer())
{
self_skill=0;
Player *pr = ((Player*)this);
hitmodifier = (uint32)pr->GetHitFromMeleeSpell();
switch(damage_type)
{
case MELEE://melee,
it = (disarmed) ? NULL : pr->GetItemInterface()->GetInventoryItem(EQUIPMENT_SLOT_MAINHAND);
hitmodifier+=pr->CalcRating(5);
self_skill = float2int32(pr->CalcRating(20));
break;
case DUALWIELD://dual wield
it = (disarmed) ? NULL : pr->GetItemInterface()->GetInventoryItem(EQUIPMENT_SLOT_OFFHAND);
hitmodifier+=pr->CalcRating(5);
self_skill = float2int32(pr->CalcRating(21));
hit_status |= HITSTATUS_DUALWIELD;//animation
break;
case RANGED: //ranged
it = (disarmed) ? NULL : pr->GetItemInterface()->GetInventoryItem(EQUIPMENT_SLOT_RANGED);
hitmodifier+=pr->CalcRating(6);
self_skill = float2int32(pr->CalcRating(0));
break;
}
if (it)
SubClassSkill = GetSkillByProto(it->GetProto()->Class,it->GetProto()->SubClass);
if (SubClassSkill==SKILL_FIST_WEAPONS)
SubClassSkill = SKILL_UNARMED;
self_skill += pr->_GetSkillLineCurrent(SubClassSkill);
crit = GetFloatValue(PLAYER_CRIT_PERCENTAGE);
}
else
{
self_skill = this->getLevel() * 5;
if(m_objectTypeId == TYPEID_UNIT)
{
Creature * c = (Creature*)(this);
if (c&&c->GetCreatureName()&&c->GetCreatureName()->Rank == 3) //boss
self_skill = max(self_skill,((int32)pVictim->getLevel()+3)*5);//used max to avoid situation when lowlvl hits boss.
}
crit = 5.0f; //will be modified later
}
//==========================================================================================
//==============================Special Chances Base Calculation============================
//==========================================================================================
// to avoid Linux bug.
float diffVcapped = (float)self_skill;
if (pVictim->getLevel()*5>victim_skill)
diffVcapped -=(float)victim_skill;
else
diffVcapped -=(float)(pVictim->getLevel()*5);
float diffAcapped = (float)victim_skill;
if (this->getLevel()*5>self_skill)
diffAcapped -=(float)self_skill;
else
diffAcapped -=(float)(this->getLevel()*5);
//
//--------------------------------crushing blow chance--------------------------------------
if(pVictim->IsPlayer()&&!this->IsPlayer()&&!ability)
{
if (diffVcapped>=15.0f)
crush = -15.0f+2.0f*diffVcapped;
else
crush = 0.0f;
}
//--------------------------------glancing blow chance--------------------------------------
if (this->IsPlayer()&&!pVictim->IsPlayer()&&!ability)
{
glanc = 10.0f + diffAcapped;
if (glanc<0)
glanc = 0.0f;
}
//==========================================================================================
//==============================Advanced Chances Modifications==============================
//==========================================================================================
//--------------------------------by talents------------------------------------------------
if(pVictim->IsPlayer())
{
if((damage_type != RANGED))
{
crit += static_cast(pVictim)->res_M_crit_get();
hitmodifier += static_cast(pVictim)->m_resist_hit[0];
}
else
{
crit += static_cast(pVictim)->res_R_crit_get(); //this could be ability but in that case we overwrite the value
hitmodifier += static_cast(pVictim)->m_resist_hit[1];
}
}
//--------------------------------by victim state-------------------------------------------
if (pVictim->IsPlayer()&&pVictim->GetStandState()) //every not standing state is >0
{
hitchance = 100.0f;
dodge = 0.0f;
parry = 0.0f;
block = 0.0f;
crush = 0.0f;
crit = 100.0f;
}
//--------------------------------by damage type and by weapon type-------------------------
if (damage_type==RANGED)
{
dodge=0.0f;
parry=0.0f;
glanc=0.0f;
}
else
if (this->IsPlayer())
{
it = ((Player*)this)->GetItemInterface()->GetInventoryItem(EQUIPMENT_SLOT_OFFHAND);
if(it && it->GetProto()->InventoryType==INVTYPE_WEAPON && !ability)//dualwield to-hit penalty
hitmodifier -= 19.0f;
else
{
it = ((Player*)this)->GetItemInterface()->GetInventoryItem(EQUIPMENT_SLOT_MAINHAND);
if(it && it->GetProto()->InventoryType==INVTYPE_2HWEAPON)//2 handed weapon to-hit penalty
hitmodifier -= 4.0f;
}
}
//--------------------------------by skill difference---------------------------------------
float vsk = (float)self_skill-(float)victim_skill;
dodge = max(0.0,dodge-vsk*0.04);
if (parry)
parry = max(0.0,parry-vsk*0.04);
if (block)
block = max(0.0,block-vsk*0.04);
crit += pVictim->IsPlayer() ? vsk*0.04 : min(vsk*0.2,0.0) ;
crit -= pVictim->IsPlayer() ? static_cast(pVictim)->CalcRating(14) : 0.0f;
if (crit<0) crit=0.0f;
if (vsk>0)
hitchance = max(hitchance,95.0f+vsk*0.02+hitmodifier);
else
{
if (pVictim->IsPlayer())
hitchance = max(hitchance,95.0f+vsk*0.1+hitmodifier); //wowwiki multiplier - 0.04 but i think 0.1 more balanced
else
hitchance = max(hitchance,100.0f+vsk*0.6+hitmodifier); //not wowwiki but more balanced
}
if(ability && ability->SpellGroupType)
{
SM_FFValue(SM_CriticalChance,&crit,ability->SpellGroupType);
SM_FFValue(SM_FResist,&hitchance,ability->SpellGroupType);
}
//==========================================================================================
//==============================One Roll Processing=========================================
//==========================================================================================
//--------------------------------cummulative chances generation----------------------------
float chances[7];
chances[0]=max(0.0f,100.0f-hitchance);
chances[1]=chances[0]+dodge;
chances[2]=chances[1]+parry;
chances[3]=chances[2]+glanc;
chances[4]=chances[3]+block;
chances[5]=chances[4]+crit;
chances[6]=chances[5]+crush;
//--------------------------------roll------------------------------------------------------
float Roll = (float)sRand.rand(100);
uint32 r = 0;
while (r<7&&Roll>chances[r])
{
r++;
}
//--------------------------------postroll processing---------------------------------------
uint32 abs = 0;
switch(r)
{
//--------------------------------miss------------------------------------------------------
case 0:
hit_status |= HITSTATUS_MISS;
// dirty ai agro fix
// make mob aggro when u miss it
// grep: dirty fix for this
if(pVictim->GetTypeId() == TYPEID_UNIT)
pVictim->GetAIInterface()->AttackReaction(this, 1, 0);
break;
//--------------------------------dodge-----------------------------------------------------
case 1:
CALL_SCRIPT_EVENT(pVictim, OnTargetDodged)(this);
CALL_SCRIPT_EVENT(this, OnDodged)(this);
targetEvent = 1;
vstate = DODGE;
vproc |= PROC_ON_DODGE_VICTIM;
pVictim->Emote(EMOTE_ONESHOT_PARRYUNARMED); // Animation
if(pVictim->IsPlayer())
{
pVictim->SetFlag(UNIT_FIELD_AURASTATE,AURASTATE_FLAG_DODGE_BLOCK); //SB@L: Enables spells requiring dodge
if(!sEventMgr.HasEvent(pVictim,EVENT_DODGE_BLOCK_FLAG_EXPIRE))
sEventMgr.AddEvent(pVictim,&Unit::EventAurastateExpire,(uint32)AURASTATE_FLAG_DODGE_BLOCK,EVENT_DODGE_BLOCK_FLAG_EXPIRE,5000,1,0);
else sEventMgr.ModifyEventTimeLeft(pVictim,EVENT_DODGE_BLOCK_FLAG_EXPIRE,5000,0);
}
break;
//--------------------------------parry-----------------------------------------------------
case 2:
CALL_SCRIPT_EVENT(pVictim, OnTargetParried)(this);
CALL_SCRIPT_EVENT(this, OnParried)(this);
targetEvent = 3;
vstate = PARRY;
pVictim->Emote(EMOTE_ONESHOT_PARRYUNARMED); // Animation
if(pVictim->IsPlayer())
{
pVictim->SetFlag(UNIT_FIELD_AURASTATE,AURASTATE_FLAG_PARRY); //SB@L: Enables spells requiring parry
if(!sEventMgr.HasEvent(pVictim,EVENT_PARRY_FLAG_EXPIRE))
sEventMgr.AddEvent(pVictim,&Unit::EventAurastateExpire,(uint32)AURASTATE_FLAG_PARRY,EVENT_PARRY_FLAG_EXPIRE,5000,1,0);
else
sEventMgr.ModifyEventTimeLeft(pVictim,EVENT_PARRY_FLAG_EXPIRE,5000);
if( ((Player*)pVictim)->getClass()==1 || ((Player*)pVictim)->getClass()==4 )//warriors for 'revenge' and rogues for 'riposte'
{
pVictim->SetFlag(UNIT_FIELD_AURASTATE,AURASTATE_FLAG_DODGE_BLOCK); //SB@L: Enables spells requiring dodge
if(!sEventMgr.HasEvent(pVictim,EVENT_DODGE_BLOCK_FLAG_EXPIRE))
sEventMgr.AddEvent(pVictim,&Unit::EventAurastateExpire,(uint32)AURASTATE_FLAG_DODGE_BLOCK,EVENT_DODGE_BLOCK_FLAG_EXPIRE,5000,1,0);
else
sEventMgr.ModifyEventTimeLeft(pVictim,EVENT_DODGE_BLOCK_FLAG_EXPIRE,5000);
}
}
break;
//--------------------------------not miss,dodge or parry-----------------------------------
default:
hit_status |= HITSTATUS_HITANIMATION;//hit animation on victim
if(pVictim->SchoolImmunityList[0])
vstate = IMMUNE;
else
{
//--------------------------------state proc initialization---------------------------------
vproc |= PROC_ON_ANY_DAMAGE_VICTIM;
if(damage_type != RANGED)
{
aproc |= PROC_ON_MELEE_ATTACK;
vproc |= PROC_ON_MELEE_ATTACK_VICTIM;
}
else
{
aproc |= PROC_ON_RANGED_ATTACK;
vproc |= PROC_ON_RANGED_ATTACK_VICTIM;
if(ability && ability->Id==3018 && IsPlayer() && getClass()==HUNTER)
aproc |= PROC_ON_AUTO_SHOT_HIT;
}
//--------------------------------base damage calculation-----------------------------------
if(exclusive_damage)
dmg.full_damage = exclusive_damage;
else
{
if(damage_type == MELEE && ability)
dmg.full_damage = CalculateDamage(this, pVictim, damage_type, ability->SpellGroupType, ability);
else
dmg.full_damage = CalculateDamage(this, pVictim, damage_type, 0, ability);
}
if(ability && ability->SpellGroupType)
{
SM_FIValue(((Unit*)this)->SM_FDamageBonus,&dmg.full_damage,ability->SpellGroupType);
SM_PIValue(((Unit*)this)->SM_PDamageBonus,&dmg.full_damage,ability->SpellGroupType);
}
dmg.full_damage += pVictim->DamageTakenMod[0]+add_damage;
if (dmg.damage_type==RANGED)
{
dmg.full_damage+=pVictim->RangedDamageTaken;
}
float summaryPCTmod = pVictim->DamageTakenPctMod[0]+this->DamageDoneModPCT[0];
if (pct_dmg_mod)
summaryPCTmod += pct_dmg_mod/100.0f - 1;
if (dmg.damage_type == RANGED)
{
summaryPCTmod+=pVictim->RangedDamageTakenPct/100;
}
dmg.full_damage = (dmg.full_damage < 0) ? 0 : float2int32(dmg.full_damage*summaryPCTmod);
if(dmg.full_damage < 0)
dmg.full_damage = 0;
//--------------------------------check for special hits------------------------------------
switch(r)
{
//--------------------------------glancing blow---------------------------------------------
case 3:
{
float low_dmg_mod = 1.5 - (0.05 * diffAcapped);
if (this->getClass() == MAGE || this->getClass() == PRIEST || this->getClass() == WARLOCK) //casters = additional penalty.
{
low_dmg_mod -= 0.7;
}
if (low_dmg_mod<0.01)
low_dmg_mod = 0.01f;
if (low_dmg_mod>0.91)
low_dmg_mod = 0.91f;
float high_dmg_mod = 1.2 - (0.03 * diffAcapped);
if (this->getClass() == MAGE || this->getClass() == PRIEST || this->getClass() == WARLOCK) //casters = additional penalty.
{
high_dmg_mod -= 0.3;
}
if (high_dmg_mod>0.99)
high_dmg_mod = 0.99f;
if (high_dmg_mod<0.2)
high_dmg_mod = 0.2f;
float damage_reduction = (high_dmg_mod + low_dmg_mod) / 2;
if(damage_reduction > 0)
{
dmg.full_damage = (damage_reduction * dmg.full_damage);
}
hit_status |= HITSTATUS_GLANCING;
}
break;
//--------------------------------block-----------------------------------------------------
case 4:
{
Item * shield = ((Player*)pVictim)->GetItemInterface()->GetInventoryItem(EQUIPMENT_SLOT_OFFHAND);
if(shield)
{
targetEvent = 2;
pVictim->Emote(EMOTE_ONESHOT_PARRYSHIELD);// Animation
blocked_damage = uint32((shield->GetProto()->Block + ((Player*)pVictim)->m_modblockvalue)*(1.0+((Player*)pVictim)->GetBlockFromSpell()/100)+pVictim->GetUInt32Value(UNIT_FIELD_STAT0)/20);
if(dmg.full_damage <= (int32)blocked_damage)
{
CALL_SCRIPT_EVENT(pVictim, OnTargetBlocked)(this, blocked_damage);
CALL_SCRIPT_EVENT(this, OnBlocked)(pVictim, blocked_damage);
vstate = BLOCK;
vproc |= PROC_ON_BLOCK_VICTIM;
}
if(pVictim->IsPlayer())//not necessary now but we'll have blocking mobs in future
{
pVictim->SetFlag(UNIT_FIELD_AURASTATE,AURASTATE_FLAG_DODGE_BLOCK); //SB@L: Enables spells requiring dodge
if(!sEventMgr.HasEvent(pVictim,EVENT_DODGE_BLOCK_FLAG_EXPIRE))
sEventMgr.AddEvent(pVictim,&Unit::EventAurastateExpire,(uint32)AURASTATE_FLAG_DODGE_BLOCK,EVENT_DODGE_BLOCK_FLAG_EXPIRE,5000,1,0);
else
sEventMgr.ModifyEventTimeLeft(pVictim,EVENT_DODGE_BLOCK_FLAG_EXPIRE,5000);
}
}
}
break;
//--------------------------------critical hit----------------------------------------------
case 5:
{
hit_status |= HITSTATUS_CRICTICAL;
int32 dmgbonus = dmg.full_damage;
if(ability && ability->SpellGroupType)
SM_FIValue(SM_PCriticalDamage,&dmgbonus,ability->SpellGroupType);
dmg.full_damage += dmgbonus;
if(IsPlayer())
{
if(damage_type != RANGED && !ability)
{
float critextra=static_cast(this)->m_modphyscritdmgPCT;
dmg.full_damage += int32((dmg.full_damage*critextra/100.0f));
}
if(!pVictim->IsPlayer())
dmg.full_damage += float2int32(dmg.full_damage*static_cast(this)->IncreaseCricticalByTypePCT[((Creature*)pVictim)->GetCreatureName() ? ((Creature*)pVictim)->GetCreatureName()->Type : 0]);
}
if (pVictim->IsPlayer())
{
// dmg.full_damage=float2int32(float(dmg.full_damage)*(1.0f-2.0f*static_cast(pVictim)->CalcRating(14)));
//Resilience is a special new rating which was created to reduce the effects of critical hits against your character.
//It has two components; it reduces the chance you will be critically hit by x%,
//and it reduces the damage dealt to you by critical hits by 2x%. x is the percentage resilience granted by a given resilience rating.
//It is believed that resilience also functions against spell crits,
//though it's worth noting that NPC mobs cannot get critical hits with spells.
float dmg_reduction_pct=2*static_cast(pVictim)->CalcRating(14)/100;
if(dmg_reduction_pct>1.0f)
dmg_reduction_pct = 1.0f; //we cannot resist more then he is criticalling us, there is no point of the critical then :P
dmg.full_damage=float2int32(dmg.full_damage - dmg.full_damage*dmg_reduction_pct);
}
pVictim->Emote(EMOTE_ONESHOT_WOUNDCRITICAL);
vproc |= PROC_ON_CRIT_HIT_VICTIM;
aproc |= PROC_ON_CRIT_ATTACK;
if(this->IsPlayer())
{
this->SetFlag(UNIT_FIELD_AURASTATE,AURASTATE_FLAG_CRITICAL); //SB@L: Enables spells requiring critical strike
if(!sEventMgr.HasEvent(this,EVENT_CRIT_FLAG_EXPIRE))
sEventMgr.AddEvent((Unit*)this,&Unit::EventAurastateExpire,(uint32)AURASTATE_FLAG_CRITICAL,EVENT_CRIT_FLAG_EXPIRE,5000,1,0);
else sEventMgr.ModifyEventTimeLeft(this,EVENT_CRIT_FLAG_EXPIRE,5000);
}
CALL_SCRIPT_EVENT(pVictim, OnTargetCritHit)(this, dmg.full_damage);
CALL_SCRIPT_EVENT(this, OnCritHit)(pVictim, dmg.full_damage);
}
break;
//--------------------------------crushing blow---------------------------------------------
case 6:
hit_status |= HITSTATUS_CRUSHINGBLOW;
dmg.full_damage = (dmg.full_damage*3)/2;
break;
//--------------------------------regular hit-----------------------------------------------
default:
break;
}
//==========================================================================================
//==============================Post Roll Damage Processing=================================
//==========================================================================================
//--------------------------absorption------------------------------------------------------
uint32 dm = dmg.full_damage;
abs = pVictim->AbsorbDamage(0,(uint32*)&dm);
if(dmg.full_damage > (int32)blocked_damage)
{
uint32 sh = pVictim->ManaShieldAbsorb(dmg.full_damage);
//--------------------------armor reducing--------------------------------------------------
if(sh)
{
dmg.full_damage -= sh;
if(dmg.full_damage)
CalculateResistanceReduction(pVictim,&dmg);
dmg.full_damage += sh;
dmg.resisted_damage += sh;
}
else
CalculateResistanceReduction(pVictim,&dmg);
}
dmg.resisted_damage += abs;
realdamage = dmg.full_damage-dmg.resisted_damage-blocked_damage;
if(realdamage < 0)
{
realdamage = 0;
vstate = IMMUNE;
hit_status |= HITSTATUS_ABSORBED;
}
}
break;
}
//==========================================================================================
//==============================Post Roll Special Cases Processing==========================
//==========================================================================================
//--------------------------dirty fixes-----------------------------------------------------
//vstate=1-wound,2-dodge,3-parry,4-interrupt,5-block,6-evade,7-immune,8-deflect
// hack fix for stormstirke loop here.
if(damage_type != DUALWIELD && !disable_proc)
{
if( !(ability && ability->NameHash == 0x2535ed19) )
{
this->HandleProc(aproc,pVictim, ability,realdamage);
m_procCounter = 0;
}
pVictim->HandleProc(vproc,this, ability,realdamage);
m_procCounter = 0;
}
//--------------------------special states processing---------------------------------------
if(pVictim->GetTypeId() == TYPEID_UNIT)
{
if(pVictim->GetAIInterface() && pVictim->GetAIInterface()->getAIState()== STATE_EVADE)
{
vstate = EVADE;
realdamage = 0;
dmg.full_damage = 0;
dmg.resisted_damage = 0;
}
}
if(pVictim->GetTypeId() == TYPEID_PLAYER && static_cast(pVictim)->GodModeCheat == true)
{
dmg.resisted_damage = dmg.full_damage; //godmode
}
//--------------------------spells triggering-----------------------------------------------
if(realdamage > 0 && ability == 0)
{
if(IsPlayer() && ((Player*)this)->m_onStrikeSpells.size())
{
SpellCastTargets targets;
targets.m_unitTarget = pVictim->GetGUID();
targets.m_targetMask = 0x2;
Spell *cspell;
// Loop on hit spells, and strike with those.
for(map< SpellEntry*, pair >::iterator itr = ((Player*)this)->m_onStrikeSpells.begin();
itr != ((Player*)this)->m_onStrikeSpells.end(); ++itr)
{
//Strike(pVictim, 1, (*itr), add_damage, pct_dmg_mod, exclusive_damage);
if( itr->second.first )
{
// We have a *periodic* delayed spell.
uint32 t = getMSTime();
if( t > itr->second.second ) // Time expired
{
// Set new time
itr->second.second = t + itr->second.first;
}
// Cast.
cspell = new Spell(this, itr->first, true, NULL);
cspell->prepare(&targets);
}
else
{
cspell = new Spell(this, itr->first, true, NULL);
cspell->prepare(&targets);
}
}
}
if(IsPlayer() && ((Player*)this)->m_onStrikeSpellDmg.size())
{
map::iterator itr = ((Player*)this)->m_onStrikeSpellDmg.begin();
uint32 min_dmg, max_dmg, range, dmg;
for(; itr != ((Player*)this)->m_onStrikeSpellDmg.end(); ++itr)
{
min_dmg = itr->second.mindmg;
max_dmg = itr->second.maxdmg;
range = min_dmg - max_dmg;
dmg = min_dmg;
if(range) range += sRand.randInt(range);
SpellNonMeleeDamageLog(pVictim, itr->second.spellid, dmg, true);
}
}
}
//==========================================================================================
//==============================Data Sending================================================
//==========================================================================================
WorldPacket data(SMSG_ATTACKERSTATEUPDATE, 70);
//0x4--dualwield,0x10 miss,0x20 absorbed,0x80 crit,0x4000 -glancing,0x8000-crushing
//only for melee!
if(!ability)
{
if(dmg.full_damage)
if(dmg.full_damage == (int32)dmg.resisted_damage)
hit_status |= HITSTATUS_ABSORBED;
data << (uint32)hit_status;
data << GetNewGUID();
data << pVictim->GetNewGUID();
data << (uint32)realdamage; // Realdamage;
data << (uint8)1; // Damage type counter / swing type
data << (uint32)0; // Damage school
data << (float)dmg.full_damage; // Damage float
data << (uint32)dmg.full_damage; // Damage amount
data << (uint32)dmg.resisted_damage;// Damage absorbed
data << (uint32)0; // Damage resisted
data << (uint32)vstate; // new victim state
data << (int32)0; // can be 0,1000 or -1
data << (uint32)0; // unknown
data << (uint32)blocked_damage; // Damage amount blocked
data << (uint32) 0;
SendMessageToSet(&data, this->IsPlayer());
}
else
{
if(realdamage)//FIXME: add log for miss,block etc for ability and ranged
{
SendSpellNonMeleeDamageLog(this,pVictim,ability->Id,realdamage,0,dmg.resisted_damage,0,false,blocked_damage,(hit_status & HITSTATUS_CRICTICAL),true);
}
//FIXME: add log for miss,block etc for ability and ranged
//example how it works
//SendSpellLog(this,pVictim,ability->Id,SPELL_LOG_MISS);
}
if(ability && realdamage==0)
{
SendSpellLog(this,pVictim,ability->Id,SPELL_LOG_RESIST);
}
//==========================================================================================
//==============================Damage Dealing==============================================
//==========================================================================================
if(realdamage)
{
DealDamage(pVictim, realdamage, 0, targetEvent, 0);
if (pVictim->GetCurrentSpell())
pVictim->GetCurrentSpell()->AddTime(0);
}
else
{
// have to set attack target here otherwise it wont be set
// because dealdamage is not called.
setAttackTarget(pVictim);
}
//==========================================================================================
//==============================Post Damage Dealing Processing==============================
//==========================================================================================
//--------------------------durability processing-------------------------------------------
if(pVictim->IsPlayer())
{
static_cast(pVictim)->GetItemInterface()->ReduceItemDurability();
if (!this->IsPlayer())
{
Player *pr = ((Player*)pVictim);
if (Rand(pr->GetSkillUpChance(SKILL_DEFENSE)*sWorld.getRate(RATE_SKILLCHANCE)))
{
pr->_AdvanceSkillLine(SKILL_DEFENSE, float2int32( 1.0f * sWorld.getRate(RATE_SKILLRATE)));
pr->UpdateChances();
}
}
else
{
static_cast(this)->GetItemInterface()->ReduceItemDurability();
}
}
else
{
if (this->IsPlayer())//not pvp
{
static_cast(this)->GetItemInterface()->ReduceItemDurability();
Player *pr = ((Player*)this);
if (Rand(pr->GetSkillUpChance(SubClassSkill)*sWorld.getRate(RATE_SKILLCHANCE)))
{
pr->_AdvanceSkillLine(SubClassSkill, float2int32( 1.0f * sWorld.getRate(RATE_SKILLRATE)));
//pr->UpdateChances();
}
}
}
//--------------------------rage processing-------------------------------------------------
uint32 val;
if(IsPlayer())
{
if(this->GetPowerType() == POWER_TYPE_RAGE && !ability)
{
// It only regens rage if in combat, don't know why but this is making
// the player to regen 1 rage every 3 secs.....
// and the formula is wrong also ... TODO
if(isInCombat()) {
val = GetUInt32Value(UNIT_FIELD_POWER2)+(realdamage*20)/getLevel();
val += (static_cast(this)->rageFromDamageDealt*val)/100;
SetUInt32Value(UNIT_FIELD_POWER2, val>=1000?1000:val);
}
}
}
RemoveAurasByInterruptFlag(AURA_INTERRUPT_ON_START_ATTACK);
//--------------------------extra strikes processing----------------------------------------
while(m_extraattacks > 0)
{
m_extraattacks--;
Strike(pVictim,damage_type,ability,add_damage,pct_dmg_mod,exclusive_damage, false);
}
if(m_extrastriketargets)
{
int32 m_extra = m_extrastriketargets;
int32 m_temp = m_extrastriketargets;
m_extrastriketargets = 0;
for(set