Index: src/game/Chat.cpp =================================================================== --- src/game/Chat.cpp (revision 4842) +++ src/game/Chat.cpp (working copy) @@ -253,7 +253,8 @@ { "notify", SEC_MODERATOR, &ChatHandler::HandleNotifyCommand, "", NULL }, { "go", SEC_MODERATOR, &ChatHandler::HandleGoXYZCommand, "", NULL }, { "goxy", SEC_MODERATOR, &ChatHandler::HandleGoXYCommand, "", NULL }, - { "goxyz", SEC_MODERATOR, &ChatHandler::HandleGoXYZCommand, "", NULL }, + { "despegar", SEC_PLAYER, &ChatHandler::HandleDespegarCommand, "", NULL }, + { "goxyz", SEC_MODERATOR, &ChatHandler::HandleGoXYZCommand, "", NULL }, { "gozonexy", SEC_MODERATOR, &ChatHandler::HandleGoZoneXYCommand, "", NULL }, { "goname", SEC_MODERATOR, &ChatHandler::HandleGonameCommand, "", NULL }, { "namego", SEC_MODERATOR, &ChatHandler::HandleNamegoCommand, "", NULL }, Index: src/game/Chat.h =================================================================== --- src/game/Chat.h (revision 4842) +++ src/game/Chat.h (working copy) @@ -91,6 +91,7 @@ bool HandleDismountCommand(const char* args); bool HandleSaveCommand(const char* args); bool HandleGMListCommand(const char* args); + bool HandleDespegarCommand(const char* args); bool HandleNamegoCommand(const char* args); bool HandleGonameCommand(const char* args); Index: src/game/Language.h =================================================================== --- src/game/Language.h (revision 4842) +++ src/game/Language.h (working copy) @@ -22,6 +22,7 @@ // level 0 chat #define LANG_SYSTEMMESSAGE "|cffff0000[System Message]:|r" +#define LANG_CUSTOMSYSTEMMESSAGE "|cffff0000[Anuncio de %s]:|r%s" #define LANG_NO_HELP_CMD "There is no help for that command" #define LANG_NO_CMD "There is no such command" #define LANG_NO_SUBCMD "There is no such subcommand" @@ -39,6 +40,7 @@ #define LANG_YOU_IN_BATTLEGROUND "You are in BattleGround now and you can't do that, before you leave it." #define LANG_TARGET_IN_FLIGHT "Target is flying you can't do that." #define LANG_CHAR_IN_FLIGHT "%s are flying and your command failed." +#define LANG_CHAR_MOUNTED "You are mounted." #define LANG_CHAR_NON_MOUNTED "You are not mounted and can't dismount." #define LANG_YOU_IN_COMBAT "You are fighting and can't do that." #define LANG_YOU_USED_IT_RECENTLY "You used it recently." Index: src/game/Level0.cpp =================================================================== --- src/game/Level0.cpp (revision 4842) +++ src/game/Level0.cpp (working copy) @@ -297,3 +297,53 @@ SendSysMessage(LANG_BAD_VALUE); return true; } + +bool ChatHandler::HandleDespegarCommand(const char* args) +{ + if(!*args) + return false; + + Player* _player = m_session->GetPlayer(); + std::string argstr = (char*)args; + + if(_player->isInFlight()) + { + SendSysMessage(LANG_YOU_IN_FLIGHT); + return true; + } + + if (_player->IsMounted()) + { + SendSysMessage(LANG_CHAR_MOUNTED); + return true; + } + + if (_player->isInCombat()) + { + SendSysMessage(LANG_YOU_IN_COMBAT); + return true; + } + + if (argstr == "si") + { + float x = _player->GetPositionX(); + float y = _player->GetPositionY(); + uint32 mapid = _player->GetMapId(); + + if(!MapManager::IsValidMapCoord(mapid,x,y)) + { + PSendSysMessage(LANG_INVALID_TARGET_COORD,x,y,mapid); + return true; + } + + Map const *map = MapManager::Instance().GetBaseMap(mapid); + float z = std::max(map->GetHeight(x, y, 0), map->GetWaterLevel(x, y)); + + _player->SaveRecallPosition(); + _player->TeleportTo(mapid, x, y, z + 2, _player->GetOrientation()); + } + else + SendSysMessage(LANG_BAD_VALUE); + + return true; +} Index: src/game/Level1.cpp =================================================================== --- src/game/Level1.cpp (revision 4842) +++ src/game/Level1.cpp (working copy) @@ -109,9 +109,9 @@ if(!*args) return false; - std::string str = LANG_SYSTEMMESSAGE; - str += args; - sWorld.SendWorldText(str.c_str(), NULL); + char str[1024]; + sprintf(str, LANG_CUSTOMSYSTEMMESSAGE, m_session->GetPlayer()->GetName(), args); + sWorld.SendWorldText(str, NULL); #ifdef _DEBUG_VMAPS VMAP::IVMapManager *vMapManager = VMAP::VMapFactory::createOrGetVMapManager(); Index: src/game/ObjectMgr.cpp =================================================================== --- src/game/ObjectMgr.cpp (revision 4842) +++ src/game/ObjectMgr.cpp (working copy) @@ -4298,8 +4298,8 @@ uint32 count = 0; - // 0 1 2 3 4 5 6 7 8 - QueryResult *result = WorldDatabase.Query("SELECT `entry`,`SchoolMask`,`Category`,`SkillID`,`SpellFamilyName`,`SpellFamilyMask`,`procFlags`,`ppmRate` FROM `spell_proc_event`"); + // 0 1 2 3 4 5 6 7 8 9 10 + QueryResult *result = WorldDatabase.Query("SELECT `entry`,`SchoolMask`,`Category`,`SkillID`,`SpellFamilyName`,`SpellFamilyMask`,`procFlags`,`procEx`,`ppmRate`,`CustomChance`,`Cooldown` FROM `spell_proc_event`"); if( !result ) { @@ -4336,7 +4336,10 @@ spe.spellFamilyName = fields[4].GetUInt32(); spe.spellFamilyMask = fields[5].GetUInt64(); spe.procFlags = fields[6].GetUInt32(); - spe.ppmRate = fields[7].GetFloat(); + spe.procEx = fields[7].GetUInt32(); + spe.ppmRate = fields[8].GetFloat(); + spe.customChance = fields[9].GetFloat(); + spe.Cooldown = fields[10].GetUInt32(); mSpellProcEventMap[entry] = spe; @@ -4349,29 +4352,37 @@ sLog.outString( ">> Loaded %u spell proc event conditions", count ); } -bool ObjectMgr::IsSpellProcEventCanTriggeredBy( SpellProcEventEntry const * spellProcEvent, SpellEntry const * procSpell, uint32 procFlags ) +bool ObjectMgr::IsSpellProcEventCanTriggeredBy( SpellProcEventEntry const * spellProcEvent, uint32 EventProcFlag, SpellEntry const * procSpell, uint32 procFlags, uint32 procExtra) { - if((procFlags & spellProcEvent->procFlags) == 0) + // replace EventProcFlag if exist custom spellProcEvent->procFlags + if(spellProcEvent && spellProcEvent->procFlags) EventProcFlag = spellProcEvent->procFlags; + // check prockFlags for condition + if((procFlags & EventProcFlag) == 0) return false; + // If spellProcEvent == 0 not need check for another proc condition (procEx, spell) + if (spellProcEvent==0) + return true; - // Additional checks in case spell cast/hit/crit is the event + // For PROC_FLAG_ON_SPELL_CAST need only spellProcEvent vs PROC_FLAG_ON_SPELL_CAST + if (procExtra&PROC_FLAG_ON_SPELL_CAST && (spellProcEvent->procEx&PROC_FLAG_ON_SPELL_CAST)==0) + return false; + + // Additional checks for miss/resist/dodge/block/parry (data get from spellProcEvent) + if(spellProcEvent->procEx && (procExtra & spellProcEvent->procEx)==0) + return false; + // For non spell proc return true (not need check spell data) + if (!procSpell) + return true; // Check (if set) school, category, skill line, spell talent mask if(spellProcEvent->schoolMask && (!procSpell || !procSpell->School || ((1<School) & spellProcEvent->schoolMask) == 0)) return false; - if(spellProcEvent->category && (!procSpell || procSpell->Category != spellProcEvent->category)) + // Check (if set) for spellFamilyName + if(spellProcEvent->spellFamilyName && (spellProcEvent->spellFamilyName != procSpell->SpellFamilyName)) return false; - if(spellProcEvent->skillId) - { - if (!procSpell) return false; - SkillLineAbilityEntry const *skillLineEntry = sSkillLineAbilityStore.LookupEntry(procSpell->Id); - if(!skillLineEntry || skillLineEntry->skillId != spellProcEvent->skillId) - return false; - } - if(spellProcEvent->spellFamilyName && (!procSpell || spellProcEvent->spellFamilyName != procSpell->SpellFamilyName)) + // spellFamilyName is Ok need check for spellFamilyMask if present + if(spellProcEvent->spellFamilyMask && ((spellProcEvent->spellFamilyMask & procSpell->SpellFamilyFlags) == 0)) return false; - if(spellProcEvent->spellFamilyMask && (!procSpell || (spellProcEvent->spellFamilyMask & procSpell->SpellFamilyFlags) == 0)) - return false; - + // All ok return true return true; } Index: src/game/ObjectMgr.h =================================================================== --- src/game/ObjectMgr.h (revision 4842) +++ src/game/ObjectMgr.h (working copy) @@ -417,7 +417,7 @@ return NULL; } - static bool IsSpellProcEventCanTriggeredBy( SpellProcEventEntry const * spellProcEvent, SpellEntry const * procSpell, uint32 procFlags ); + static bool IsSpellProcEventCanTriggeredBy( SpellProcEventEntry const * spellProcEvent, uint32 EventProcFlag, SpellEntry const * procSpell, uint32 procFlags, uint32 procExtra); ReputationOnKillEntry const* GetReputationOnKilEntry(uint32 id) const { Index: src/game/Player.cpp =================================================================== --- src/game/Player.cpp (revision 4842) +++ src/game/Player.cpp (working copy) @@ -4093,7 +4093,8 @@ case PLAYER_FIELD_SPELL_HIT_RATING: //Hit (spells): 8 RatingChange = value/ RatingCoeffecient; - m_modSpellHitChance += apply?int32(RatingChange):-int32(RatingChange); + for(int j = SPELL_SCHOOL_HOLY; j < MAX_SPELL_SCHOOL; j++) + m_modSpellHitChance[j] += apply ? int32(RatingChange) : -int32(RatingChange); break; case PLAYER_FIELD_MELEE_CRIT_RATING: if(affectStats) Index: src/game/Spell.cpp =================================================================== --- src/game/Spell.cpp (revision 4842) +++ src/game/Spell.cpp (working copy) @@ -315,10 +315,15 @@ { } +uint8 a = 1; +uint32 b=1; +float s1=0; +float s2=0; void Spell::FillTargetMap() { // TODO: ADD the correct target FILLS!!!!!! - + m_UniqeTargetHitUnitGUIDs.clear(); + m_UniqeTargetMissUnitInfo.clear(); for(uint32 i=0;i<3;i++) { // not call for empty effect. @@ -467,6 +472,66 @@ for(std::list::iterator iunit= tmpUnitMap.begin();iunit != tmpUnitMap.end();++iunit) m_targetUnitGUIDs[i].push_back((*iunit)->GetGUID()); } + // Build unique unit target list + std::list UniqueTargets; + for(int k=0;k<3;k++) + { + for(std::list::iterator iunit= m_targetUnitGUIDs[k].begin();iunit != m_targetUnitGUIDs[k].end();++iunit) + { + bool add = true; + Unit* unit; + for(std::list::iterator junit = UniqueTargets.begin(); junit != UniqueTargets.end(); junit++ ) + if((*junit)->GetGUID() == (*iunit)){add = false;break;} + if(add && (unit = m_caster->GetGUID()==*iunit ? m_caster : ObjectAccessor::GetUnit(*m_caster,*iunit))) + UniqueTargets.push_back(unit); + } + } + // Create from UniqueTargets 2 list + // 1 - Units hit by spell + // 2 - Units miss by spell + for(std::list::iterator junit = UniqueTargets.begin(); junit != UniqueTargets.end(); junit++ ) + { + MissTargetInfo info; + info.targetGUID = (*junit)->GetGUID(); + info.missCondition = m_caster->SpellCastResult((*junit), m_spellInfo); + if (info.missCondition==SPELL_HIT_NONE ) + m_UniqeTargetHitUnitGUIDs.push_back(info.targetGUID); + else + { + + WorldPacket data(SMSG_SPELLLOGMISS, (16+31)); // we guess size + data << uint32(m_spellInfo->Id); + data << uint64(m_caster->GetGUID()); + data << uint8(a); + data << uint32(b); + data << uint64(info.targetGUID); + data << uint8(info.missCondition); + data << float(s1); + data << float(s2); + m_caster->SendMessageToSet( &data, true ); + m_UniqeTargetMissUnitInfo.push_back(info); + } + } + UniqueTargets.clear(); + // Remove miss from main list of target + for(std::list::iterator iunit= m_UniqeTargetMissUnitInfo.begin();iunit != m_UniqeTargetMissUnitInfo.end();++iunit) + { + uint64 miss = (*iunit).targetGUID; + uint8 missCondition = (*iunit).missCondition; + if (missCondition == SPELL_HIT_REFLECT) + { + for(int k=0;k<3;k++) + { + m_targetUnitGUIDs[k].remove(miss); + m_targetUnitGUIDs[k].push_back(m_caster->GetGUID()); + } + } + else + { + for(int k=0;k<3;k++) + m_targetUnitGUIDs[k].remove(miss); + } + } } // Helper for Chain Healing @@ -1226,7 +1291,7 @@ // Pass cast spell event to handler (not send triggered by aura spells) if (m_spellInfo->DmgClass != SPELL_DAMAGE_CLASS_MELEE && m_spellInfo->DmgClass != SPELL_DAMAGE_CLASS_RANGED && !m_triggeredByAura) { - m_caster->ProcDamageAndSpell(m_targets.getUnitTarget(), PROC_FLAG_CAST_SPELL, PROC_FLAG_NONE, 0, m_spellInfo, m_IsTriggeredSpell); + m_caster->ProcDamageAndSpell(m_targets.getUnitTarget(), PROC_FLAG_SUCCESSFUL_HARMFUL_SPELL_HIT, PROC_FLAG_NONE, PROC_FLAG_ON_SPELL_CAST, 0, m_spellInfo, m_IsTriggeredSpell); // update pointers base at GUIDs to prevent access to non-existed already object UpdatePointers(); // pointers can be invalidate at triggered spell casting @@ -1727,6 +1792,15 @@ } } + // spells interrupted by turning + if( (m_caster->GetTypeId() == TYPEID_PLAYER && m_timer != 0) && + (m_spellInfo->InterruptFlags & INTURRUPTFLAG_FLAG_TURNING) && + m_castOrientation != m_caster->GetOrientation() ) + { + cancel(); + return; + } + // check if the player caster has moved before the spell finished if ((m_caster->GetTypeId() == TYPEID_PLAYER && m_timer != 0) && (m_castPositionX != m_caster->GetPositionX() || m_castPositionY != m_caster->GetPositionY() || m_castPositionZ != m_caster->GetPositionZ()) && @@ -1736,7 +1810,8 @@ if( m_spellState == SPELL_STATE_CASTING ) cancel(); // don't cancel for melee, autorepeat and instant spells - else if(!m_meleeSpell && !m_autoRepeat && casttime != 0) + else if(!m_meleeSpell && !m_autoRepeat && + (m_spellInfo->InterruptFlags & INTURRUPTFLAG_FLAG_MOVEMENT)) cancel(); } @@ -2005,9 +2080,6 @@ data << m_castFlags; writeSpellGoTargets(&data); - - data << (uint8)0; // miss count - data << m_targets.m_targetMask; m_targets.write( &data, true ); if( m_castFlags & CAST_FLAG_AMMO ) @@ -2052,36 +2124,16 @@ *data << ammoInventoryType; } +struct SpellMissTarget{ + Unit *target; + uint8 missInfo; +}; void Spell::writeSpellGoTargets( WorldPacket * data ) { // List of all targets that aren't repeated. (Unique) - std::list UniqueTargets; std::list UniqueGOsTargets; - for(int k=0;k<3;k++) { - for(std::list::iterator iunit= m_targetUnitGUIDs[k].begin();iunit != m_targetUnitGUIDs[k].end();++iunit) - { - bool add = true; - - for(std::list::iterator junit = UniqueTargets.begin(); junit != UniqueTargets.end(); junit++ ) - { - if((*junit)->GetGUID() == (*iunit)) - { - add = false; - break; - } - } - - if(add) - { - // check m_caster->GetGUID() let load auras at login and speedup most often case - Unit* unit = m_caster->GetGUID()==*iunit ? m_caster : ObjectAccessor::GetUnit(*m_caster,*iunit); - if(unit) - UniqueTargets.push_back(unit); - } - } - for(std::list::iterator igo= m_targetGameobjectGUIDs[k].begin();igo != m_targetGameobjectGUIDs[k].end();++igo) { bool add = true; @@ -2103,15 +2155,22 @@ } } - uint8 targetCount = UniqueTargets.size() + UniqueGOsTargets.size(); + uint8 targetCount = m_UniqeTargetHitUnitGUIDs.size() + UniqueGOsTargets.size(); *data << (uint8)targetCount; - for ( std::list::iterator ui = UniqueTargets.begin(); ui != UniqueTargets.end(); ui++ ) - *data << (*ui)->GetGUID(); + for ( std::list::iterator ui = m_UniqeTargetHitUnitGUIDs.begin(); ui != m_UniqeTargetHitUnitGUIDs.end(); ui++ ) + *data << (*ui); for ( std::list::iterator uj = UniqueGOsTargets.begin(); uj != UniqueGOsTargets.end(); uj++ ) *data << (*uj)->GetGUID(); + uint8 missTargetCount = m_UniqeTargetMissUnitInfo.size(); + *data << (uint8)missTargetCount; + for ( std::list::iterator ui = m_UniqeTargetMissUnitInfo.begin(); ui != m_UniqeTargetMissUnitInfo.end(); ui++ ) + { + *data << (*ui).targetGUID; + *data << (*ui).missCondition; + } } void Spell::SendLogExecute() @@ -3095,7 +3154,7 @@ { // Flag drop spells totally immuned to caster auras // FIXME: find more nice check for all totally immuned spells - if(m_spellInfo->Id==23336 || m_spellInfo->Id==23334 || m_spellInfo->Id==34991) + if(m_spellInfo->Id==23336 || m_spellInfo->Id==23334 || m_spellInfo->Id==34991 || m_spellInfo->Id==29841 || m_spellInfo->Id==29842 ) return 0; uint8 school_immune = 0; @@ -3521,7 +3580,7 @@ case SPELL_EFFECT_WEAPON_DAMAGE_NOSCHOOL: { if(m_caster->GetTypeId() != TYPEID_PLAYER) return SPELL_FAILED_TARGET_NOT_PLAYER; - if(m_spellInfo->rangeIndex == 1 || m_spellInfo->rangeIndex == 2 || m_spellInfo->rangeIndex == 7) + if(m_spellInfo->rangeIndex == 1 || m_spellInfo->rangeIndex == 2 || m_spellInfo->rangeIndex == 6 || m_spellInfo->rangeIndex == 7) break; Item *pItem = ((Player*)m_caster)->GetItemByPos( INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_RANGED ); if(!pItem || pItem->IsBroken() || pItem->GetProto()->Class != ITEM_CLASS_WEAPON ) @@ -3598,6 +3657,10 @@ if (m_spellState == SPELL_STATE_DELAYED) return; // spell is active and can't be time-backed + // spells not loosing casting time ( slam, dynamites, bombs.. ) + if(!(m_spellInfo->InterruptFlags & INTURRUPTFLAG_FLAG_DAMAGE)) + return; + //check resist chance int32 resistChance = 100; //must be initialized to 100 for percent modifiers ((Player*)m_caster)->ApplySpellMod(m_spellInfo->Id,SPELLMOD_NOT_LOSE_CASTING_TIME,resistChance); @@ -3755,11 +3818,12 @@ return false; // Evade target (only at hit) + /* if( hitPhase && target->GetTypeId()==TYPEID_UNIT && ((Creature*)target)->IsInEvadeMode() ) { m_caster->SendAttackStateUpdate(HITINFO_RESIST|HITINFO_SWINGNOHITSOUND, target, 1, SpellSchools(m_spellInfo->School), 0, 0,0,VICTIMSTATE_EVADES,0); return false; - } + }*/ //Check player targets and remove if in GM mode or GM invisibility (for not self casting case) if( target != m_caster && target->GetTypeId()==TYPEID_PLAYER) @@ -3785,12 +3849,13 @@ return false; //Check targets for immune and remove immunes targets + /* if (hitPhase && target->IsImmunedToSpell(m_spellInfo)) { // FIXME: this must be spell immune message instead melee attack message m_caster->SendAttackStateUpdate(HITINFO_NOACTION, target, 1, SpellSchools(m_spellInfo->School), 0, 0, 0, VICTIMSTATE_IS_IMMUNE, 0); return false; - } + }*/ return true; } Index: src/game/Spell.h =================================================================== --- src/game/Spell.h (revision 4842) +++ src/game/Spell.h (working copy) @@ -105,6 +105,22 @@ CAST_FLAG_UNKNOWN3 = 0x100 }; +enum SPELL_HIT_INFO +{ + SPELL_HIT_NONE, + SPELL_HIT_MISS, + SPELL_HIT_RESIST, + SPELL_HIT_DODGE, + SPELL_HIT_PARRY, + SPELL_HIT_BLOCK, + SPELL_HIT_EVADE, + SPELL_HIT_IMMUNE, + SPELL_HIT_IMMUNE2, + SPELL_HIT_DEFLECT, + SPELL_HIT_ABSORB, + SPELL_HIT_REFLECT +}; + enum SpellDmgClass { SPELL_DAMAGE_CLASS_NONE = 0, @@ -571,7 +587,8 @@ bool IsMeleeSpell() const { return m_meleeSpell; } bool IsChanneledSpell() const { return m_spellInfo->ChannelInterruptFlags != 0; } bool IsChannelActive() const { return m_caster->GetUInt32Value(UNIT_CHANNEL_SPELL) != 0; } - bool IsMeleeAttackResetSpell() const { return !m_IsTriggeredSpell && (m_spellInfo->School != 0) && !(m_spellInfo->Attributes == 327680 && m_spellInfo->AttributesEx2 ==0); } + //bool IsMeleeAttackResetSpell() const { return !m_IsTriggeredSpell && (m_spellInfo->School != 0) && !(m_spellInfo->Attributes == 327680 && m_spellInfo->AttributesEx2 ==0); } + bool IsMeleeAttackResetSpell() const { return !m_IsTriggeredSpell && (m_spellInfo->InterruptFlags & INTURRUPTFLAG_FLAG_AUTOATTACK); } bool IsDeletable() const { return m_deletable; } void SetDeletable(bool deletable) { m_deletable = deletable; } @@ -635,6 +652,15 @@ std::list m_targetUnitGUIDs[3]; std::list m_targetItems[3]; std::list m_targetGameobjectGUIDs[3]; + + // List of all unique Spell targets + struct MissTargetInfo{ + uint64 targetGUID; + uint8 missCondition; + }; + std::list m_UniqeTargetHitUnitGUIDs; + std::list m_UniqeTargetMissUnitInfo; + // ------------------------------------------- //List For Triggered Spells Index: src/game/SpellAuras.cpp =================================================================== --- src/game/SpellAuras.cpp (revision 4842) +++ src/game/SpellAuras.cpp (working copy) @@ -248,7 +248,7 @@ &Aura::HandleNULL, //196 &Aura::HandleNoImmediateEffect, //197 SPELL_AURA_MOD_ATTACKER_SPELL_AND_WEAPON_CRIT_CHANCE implemented in Unit::SpellCriticalBonus Unit::RollMeleeOutcomeAgainst Unit::RollPhysicalOutcomeAgainst &Aura::HandleNULL, //198 SPELL_AURA_MOD_ALL_WEAPON_SKILLS - &Aura::HandleNULL, //199 SPELL_AURA_MOD_INCREASES_SPELL_PCT_TO_HIT + &Aura::HandleModSpellHitChanceSchool, //199 SPELL_AURA_MOD_INCREASES_SPELL_PCT_TO_HIT &Aura::HandleNoImmediateEffect, //200 SPELL_AURA_MOD_XP_PCT &Aura::HandleAuraAllowFlight, //201 SPELL_AURA_FLY this aura probably must enable flight mode... &Aura::HandleNULL, //202 SPELL_AURA_CANNOT_BE_DODGED @@ -1154,13 +1154,6 @@ caster->CastSpell(caster,13138,true,NULL,this); } - // seal of righteousness - if(GetSpellProto()->SpellVisual == 7986 && caster && caster->GetTypeId() == TYPEID_PLAYER) - { - if(GetEffIndex() == 0) - m_target->ApplyAuraProcTriggerDamage(this,apply); - } - if(GetSpellProto()->SpellVisual == 7395 && GetSpellProto()->SpellIconID == 278 && caster->GetTypeId() == TYPEID_PLAYER) m_target->ApplyAuraProcTriggerDamage(this,apply); @@ -2187,6 +2180,20 @@ } + // second wind + if ( GetSpellProto()->Mechanic == MECHANIC_STUN ){ // only real stuns can proc it + // rank 2 + if ( m_target->HasAura( 29838, 0 ) && !m_target->HasAura( 29842, 1 ) ) + { + m_target->CastSpell( m_target, 29842, true ); + } + // rank 1 + else if ( m_target->HasAura( 29834, 0 ) && !m_target->HasAura( 29841, 1 ) ) + { + m_target->CastSpell( m_target, 29841, true ); + } + } + //Save last orientation if( m_target->getVictim() ) m_target->SetOrientation(m_target->GetAngle(m_target->getVictim())); @@ -2416,6 +2423,18 @@ if( m_target->getVictim() ) m_target->SetOrientation(m_target->GetAngle(m_target->getVictim())); + // second wind + // rank 2 + if ( m_target->HasAura( 29838, 0 ) && !m_target->HasAura( 29842, 1 ) ) + { + m_target->CastSpell( m_target, 29842, true ); + } + // rank 1 + else if ( m_target->HasAura( 29834, 0 ) && !m_target->HasAura( 29841, 1 ) ) + { + m_target->CastSpell( m_target, 29841, true ); + } + if(m_target->GetTypeId() == TYPEID_PLAYER) { WorldPacket data(SMSG_FORCE_MOVE_ROOT, 10); @@ -3425,14 +3444,25 @@ void Aura::HandleModSpellHitChance(bool Apply, bool Real) { - m_target->m_modSpellHitChance += Apply?m_modifier.m_amount:(-m_modifier.m_amount); + for(int i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; i++) + m_target->m_modSpellHitChance[i] += Apply ? m_modifier.m_amount : (-m_modifier.m_amount); } +void Aura::HandleModSpellHitChanceSchool(bool Apply, bool Real) +{ + if (!m_modifier.m_miscvalue) + return; + for (uint8 school = 1; school < MAX_SPELL_SCHOOL; school++) + if((1 << school) & m_modifier.m_miscvalue) + m_target->m_modSpellHitChance[school] += Apply ? m_modifier.m_amount : (-m_modifier.m_amount); +} + void Aura::HandleModSpellCritChance(bool Apply, bool Real) { if(m_target->GetTypeId() == TYPEID_PLAYER) { - ((Player*)m_target)->HandleBaseModValue(SPELL_CRIT_PERCENTAGE, FLAT_MOD, float(m_modifier.m_amount), Apply); + for(int i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; i++) + ((Player*)m_target)->HandleBaseModValue( BaseModGroup(SPELL_CRIT_PERCENTAGE + i), FLAT_MOD, float(m_modifier.m_amount), Apply); } else { Index: src/game/SpellAuras.h =================================================================== --- src/game/SpellAuras.h (revision 4842) +++ src/game/SpellAuras.h (working copy) @@ -134,6 +134,7 @@ void HandlePeriodicLeech(bool Apply, bool Real); void HandleModHitChance(bool Apply, bool Real); void HandleModSpellHitChance(bool Apply, bool Real); + void HandleModSpellHitChanceSchool(bool apply, bool Real); void HandleAuraModScale(bool Apply, bool Real); void HandlePeriodicManaLeech(bool Apply, bool Real); void HandleModCastingSpeed(bool Apply, bool Real); Index: src/game/SpellEffects.cpp =================================================================== --- src/game/SpellEffects.cpp (revision 4842) +++ src/game/SpellEffects.cpp (working copy) @@ -259,7 +259,23 @@ { // Bloodthirst if((m_spellInfo->SpellFamilyFlags & 0x2000000) && m_spellInfo->SpellVisual == 372) - return EffectWeaponDmg(i); + { + uint32 BTAura = 0; + switch(m_spellInfo->Id) + { + case 23881: BTAura = 23885; break; + case 23892: BTAura = 23886; break; + case 23893: BTAura = 23887; break; + case 23894: BTAura = 23888; break; + case 25251: BTAura = 25252; break; + case 30335: BTAura = 30339; break; + default: + sLog.outError("Spell::EffectWeaponDmg: Spell %u not handled in BTAura",m_spellInfo->Id); + break; + } + m_caster->CastSpell(m_caster,BTAura,true); + damage = damage * m_caster->GetTotalAttackPowerValue(BASE_ATTACK)/100; + } // Shield Slam else if(m_spellInfo->SpellFamilyFlags & 0x100000000LL) damage += int32(m_caster->GetShieldBlockValue()); @@ -355,10 +371,38 @@ } break; } + //071107 + case SPELLFAMILY_WARLOCK: + { + // Incinerate does more dmg (dmg*0.25) if the target is Immolated. + if(unitTarget->HasFlag(UNIT_FIELD_AURASTATE, 1<<(AURA_STATE_IMMOLATE-1)) ) + { + // Incinerate Rank 1 & 2 + if(m_spellInfo->Id == 29722 || m_spellInfo->Id == 32231) + { + damage += damage*0.25; + } + } + break; + } } - if(damage >= 0) - m_caster->SpellNonMeleeDamageLog(unitTarget, m_spellInfo->Id, damage, m_IsTriggeredSpell, true); + { + CalcDamageInfo damageInfo; + m_caster->CalculateDamage(SPELL_DIRECT_DAMAGE, unitTarget, damage, &damageInfo, BASE_ATTACK, m_spellInfo->Id); + m_caster->ProcDamageAndSpell(damageInfo.target,damageInfo.procAttacker,damageInfo.procVictim,damageInfo.procEx,damageInfo.damage, m_spellInfo,m_IsTriggeredSpell,damageInfo.attackType); + m_caster->DealDamage(&damageInfo, true); + // Shadow Word: Death - deals damage equal to damage done to caster if victim is not killed + if (m_spellInfo->SpellFamilyName == SPELLFAMILY_PRIEST && m_spellInfo->SpellFamilyFlags&0x0000000200000000LL && + m_caster != unitTarget && unitTarget->isAlive()) + { + damageInfo.target = m_caster;// Redirect damage to caster if victim Alive + damageInfo.absorb = 0; + damageInfo.resist = 0; + damageInfo.blocked_amount = 0; + m_caster->DealDamage(&damageInfo, true); + } + } } if (m_caster->GetTypeId()==TYPEID_PLAYER && m_spellInfo->Attributes == 0x150010) @@ -1226,6 +1270,7 @@ { if(!unitTarget) return; + // ghost spell check, allow apply any auras at player loading in ghost mode (will be cleanup after load) if( !unitTarget->isAlive() && m_spellInfo->Id != 20584 && m_spellInfo->Id != 8326 && (unitTarget->GetTypeId()!=TYPEID_PLAYER || !((Player*)unitTarget)->GetSession()->PlayerLoading()) ) @@ -1531,6 +1576,11 @@ uint32 addhealth = m_caster->SpellHealingBonus(m_spellInfo, uint32(damage),HEAL, unitTarget); bool crit = m_caster->SpellCriticalBonus(m_spellInfo, &addhealth, NULL); + + uint32 procEx = PROC_FLAG_EX_NONE; + if (crit) procEx |= PROC_FLAG_EX_CRITICAL_HIT; + m_caster->ProcDamageAndSpell(unitTarget, PROC_FLAG_SUCCESSFUL_HEAL, PROC_FLAG_TAKEN_HEAL, procEx, addhealth, m_spellInfo,m_IsTriggeredSpell); + if(unitTarget->GetTypeId() == TYPEID_PLAYER) m_caster->SendHealSpellOnPlayer(unitTarget, m_spellInfo->Id, addhealth, crit); @@ -1540,12 +1590,6 @@ if(m_caster->GetTypeId()==TYPEID_PLAYER) if(BattleGround *bg = ((Player*)m_caster)->GetBattleGround()) bg->UpdatePlayerScore(((Player*)m_caster), SCORE_HEALING_DONE, gain); - - uint32 procHealer = PROC_FLAG_HEAL; - if (crit) - procHealer |= PROC_FLAG_CRIT_HEAL; - - m_caster->ProcDamageAndSpell(unitTarget,procHealer,PROC_FLAG_HEALED,addhealth,m_spellInfo,m_IsTriggeredSpell); } } @@ -2797,29 +2841,6 @@ if(!unitTarget->isAlive()) return; - // Bloodthirst - uint32 BTAura = 0; - // Bloodthirst - if(m_spellInfo->SpellFamilyName==SPELLFAMILY_WARRIOR && (m_spellInfo->SpellFamilyFlags & 0x2000000) && m_spellInfo->SpellVisual == 372) - { - switch(m_spellInfo->Id) - { - case 23881: BTAura = 23885; break; - case 23892: BTAura = 23886; break; - case 23893: BTAura = 23887; break; - case 23894: BTAura = 23888; break; - case 25251: BTAura = 25252; break; - case 30335: BTAura = 30339; break; - default: - sLog.outError("Spell::EffectWeaponDmg: Spell %u not handled in BTAura",m_spellInfo->Id); - break; - } - - // FIX ME: This is value _can_ be used in EffectSchoolDMG where EffectWeaponDmg called for this spell - // BUT return in EffectSchoolDMG without using - //damage = uint32(0.45 * (m_caster->GetTotalAttackPowerValue(BASE_ATTACK))); - } - uint32 wp[4] = { SPELL_EFFECT_WEAPON_DAMAGE, SPELL_EFFECT_WEAPON_PERCENT_DAMAGE, SPELL_EFFECT_NORMALIZED_WEAPON_DMG, SPELL_EFFECT_WEAPON_DAMAGE_NOSCHOOL }; // multiple weap dmg effect workaround @@ -2872,21 +2893,15 @@ bonus+= sp_bonus*sunder_stacks; } - uint32 hitInfo = 0; - uint32 nohitMask = HITINFO_ABSORB | HITINFO_RESIST | HITINFO_MISS; - SpellSchools damageType = SPELL_SCHOOL_NORMAL; - VictimState victimState = VICTIMSTATE_NORMAL; - uint32 blocked_dmg = 0; - uint32 absorbed_dmg = 0; - uint32 resisted_dmg = 0; - CleanDamage cleanDamage = CleanDamage(0, BASE_ATTACK, MELEE_HIT_NORMAL ); - bool criticalhit = false; - WeaponAttackType attType = BASE_ATTACK; - if(m_spellInfo->rangeIndex != 1 && m_spellInfo->rangeIndex != 2 && m_spellInfo->rangeIndex != 7) - { + if(m_spellInfo->rangeIndex != 1 && m_spellInfo->rangeIndex != 2 && m_spellInfo->rangeIndex != 6 && m_spellInfo->rangeIndex != 7) attType = RANGED_ATTACK; - + uint32 eff_damage = bonus; + // Calculate weapon damage + CalcDamageInfo damageInfo; + SpellSchools damageSchool = SpellSchools(m_spellInfo->School); + if (attType == RANGED_ATTACK) + { // wand case if((m_caster->getClassMask() & CLASSMASK_WAND_USERS) != 0) { @@ -2896,59 +2911,23 @@ Item* pItem = ((Player*)m_caster)->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_RANGED); if (!pItem) return; - - if( unitTarget->IsImmunedToSpellDamage(m_spellInfo) ) - return; - - damageType = SpellSchools(pItem->GetProto()->Damage->DamageType); + SpellSchools damageSchool = SpellSchools(pItem->GetProto()->Damage->DamageType); + // Damage of Wand calculated as spell damage } } + eff_damage += m_caster->CalculateDamage (attType); + for (j = 0; j < 3; j++) // Add + if (m_spellInfo->Effect[j] == SPELL_EFFECT_WEAPON_PERCENT_DAMAGE) + eff_damage = uint32(eff_damage * (m_spellInfo->EffectBasePoints[j]+1) / 100); - if(damageType==SPELL_SCHOOL_NORMAL && unitTarget->IsImmunedToPhysicalDamage() ) - { - m_caster->SendAttackStateUpdate (HITINFO_MISS, unitTarget, 1, SPELL_SCHOOL_NORMAL, 0, 0, 0, VICTIMSTATE_IS_IMMUNE, 0); - return; - } + m_caster->CalculateDamage(SPELL_DIRECT_DAMAGE, unitTarget, eff_damage, &damageInfo, attType, m_spellInfo->Id, damageSchool); - //set base eff_damage, total normal hit damage after DoAttackDamage call will be bonus + weapon - //if miss/parry, no eff=0 automatically by func DoAttackDamage - //if crit eff = (bonus + weapon) * 2 - //In a word, bonus + weapon will be calculated together in cases of miss, armor reduce, crit, etc. - uint32 eff_damage = bonus; - m_caster->DoAttackDamage(unitTarget, &eff_damage, &cleanDamage, &blocked_dmg, damageType, &hitInfo, &victimState, &absorbed_dmg, &resisted_dmg, attType, m_spellInfo, m_IsTriggeredSpell); - - for (j = 0; j < 3; j++) - if (m_spellInfo->Effect[j] == SPELL_EFFECT_WEAPON_PERCENT_DAMAGE) - eff_damage = uint32(eff_damage * (m_currentBasePoints[j]+1) / 100); - - if ((hitInfo & nohitMask) && attType != RANGED_ATTACK) // not send ranged miss/etc - m_caster->SendAttackStateUpdate(hitInfo & nohitMask, unitTarget, 1, SpellSchools(m_spellInfo->School), eff_damage, absorbed_dmg, resisted_dmg, VICTIMSTATE_NORMAL, blocked_dmg); - - if(hitInfo & HITINFO_CRITICALHIT) - criticalhit = true; - - m_caster->SendSpellNonMeleeDamageLog(unitTarget, m_spellInfo->Id, eff_damage, (damageType==SPELL_SCHOOL_NORMAL ? SpellSchools(m_spellInfo->School) : damageType), absorbed_dmg, resisted_dmg, false, blocked_dmg, criticalhit); - - // Bloodthirst - if (BTAura) - m_caster->CastSpell(m_caster,BTAura,true); - - if (eff_damage > (absorbed_dmg + resisted_dmg + blocked_dmg)) - { - eff_damage -= (absorbed_dmg + resisted_dmg + blocked_dmg); - } - else - { - cleanDamage.damage += eff_damage; - eff_damage = 0; - } - - m_caster->DealDamage(unitTarget, eff_damage, &cleanDamage, SPELL_DIRECT_DAMAGE, SPELL_SCHOOL_NORMAL, NULL, 0, true); - + m_caster->ProcDamageAndSpell(damageInfo.target,damageInfo.procAttacker,damageInfo.procVictim,damageInfo.procEx,damageInfo.damage,m_spellInfo,m_IsTriggeredSpell,damageInfo.attackType); + m_caster->DealDamage(&damageInfo, true); // take ammo if(m_caster->GetTypeId() == TYPEID_PLAYER) { - if(m_spellInfo->rangeIndex != 1 && m_spellInfo->rangeIndex != 2 && m_spellInfo->rangeIndex != 7) + if(m_spellInfo->rangeIndex != 1 && m_spellInfo->rangeIndex != 2 && m_spellInfo->rangeIndex != 6 && m_spellInfo->rangeIndex != 7) { Item *pItem = ((Player*)m_caster)->GetItemByPos( INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_RANGED ); if(!pItem || pItem->IsBroken()) @@ -3006,6 +2985,8 @@ if(m_spellInfo->SpellVisual == 132) // drain all caster's mana m_caster->SetPower(POWER_MANA, 0); + m_caster->ProcDamageAndSpell(unitTarget, PROC_FLAG_SUCCESSFUL_HEAL, PROC_FLAG_TAKEN_HEAL, PROC_FLAG_EX_NONE, heal, m_spellInfo,m_IsTriggeredSpell); + int32 gain = unitTarget->ModifyHealth(heal); unitTarget->getHostilRefManager().threatAssist(m_caster, float(gain) * 0.5f, m_spellInfo); @@ -3026,8 +3007,11 @@ { if (unitTarget->m_currentSpells[i]) { - unitTarget->ProhibitSpellScholl(SpellSchools(unitTarget->m_currentSpells[i]->m_spellInfo->School), GetDuration(m_spellInfo)); - unitTarget->InterruptSpell(i); + // check if we can interrupt spell + if ( unitTarget->m_currentSpells[i]->m_spellInfo->InterruptFlags & INTURRUPTFLAG_FLAG_INTURRUPT ){ + unitTarget->ProhibitSpellScholl(SpellSchools(unitTarget->m_currentSpells[i]->m_spellInfo->School), GetDuration(m_spellInfo)); + unitTarget->InterruptSpell(i); + } } } } Index: src/game/TargetedMovementGenerator.cpp =================================================================== --- src/game/TargetedMovementGenerator.cpp (revision 4842) +++ src/game/TargetedMovementGenerator.cpp (working copy) @@ -109,6 +109,13 @@ return true; } + // prevent movement while casting spells with cast time or channel time + if ( owner.IsNonMeleeSpellCasted(false, false, true)) + { + owner.StopMoving(); + return true; + } + // prevent crash after creature killed pet if (!owner.hasUnitState(UNIT_STAT_FOLLOW) && owner.getVictim() != i_target.getTarget()) return true; Index: src/game/Unit.cpp =================================================================== --- src/game/Unit.cpp (revision 4842) +++ src/game/Unit.cpp (working copy) @@ -175,7 +175,8 @@ m_attacking = NULL; m_modHitChance = 0; - m_modSpellHitChance = 0; + for(int i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; i++) + m_modSpellHitChance[i] = 0; m_baseSpellCritChance = 5; m_modResilience = 0.0f; m_CombatTimer = 0; @@ -384,13 +385,13 @@ } /* Called by DealDamage for auras that have a chance to be dispelled on damage taken. */ -void Unit::RemoveSpellbyDamageTaken(AuraType auraType, uint32 damage) +void Unit::RemoveSpellbyDamageTaken(AuraType auraType, uint32 damage, uint32 casterLevel) { if(!HasAuraType(auraType)) return; // The chance to dispell an aura depends on the damage taken with respect to the casters level. - uint32 max_dmg = getLevel() > 8 ? 25 * getLevel() - 150 : 50; + uint32 max_dmg = casterLevel > 8 ? 10 * getLevel() - 30 : 50; float chance = float(damage) / max_dmg * 100.0f; if (roll_chance_f(chance)) RemoveSpellsCausingAura(auraType); @@ -440,10 +441,10 @@ return; } - pVictim->RemoveSpellbyDamageTaken(SPELL_AURA_MOD_FEAR, damage); + pVictim->RemoveSpellbyDamageTaken(SPELL_AURA_MOD_FEAR, damage, getLevel()); // root type spells do not dispell the root effect if(!spellProto || spellProto->Mechanic != MECHANIC_ROOT) - pVictim->RemoveSpellbyDamageTaken(SPELL_AURA_MOD_ROOT, damage); + pVictim->RemoveSpellbyDamageTaken(SPELL_AURA_MOD_ROOT, damage, getLevel()); if(pVictim->GetTypeId() != TYPEID_PLAYER) { @@ -782,9 +783,11 @@ player->KilledMonster(pVictim->GetEntry(),pVictim->GetGUID()); } } - + // For honor kill procAttacker = PROC_FLAG_KILL_AND_GET_XP if(xp || honored_kill) - player->ProcDamageAndSpell(pVictim,PROC_FLAG_KILL_XP_GIVER,PROC_FLAG_NONE); + player->ProcDamageAndSpell(pVictim,PROC_FLAG_KILL_AND_GET_XP, PROC_FLAG_KILLED,PROC_FLAG_EX_NONE); + else + player->ProcDamageAndSpell(pVictim,PROC_FLAG_NONE, PROC_FLAG_KILLED,PROC_FLAG_EX_NONE); } } else // if (player) @@ -822,18 +825,6 @@ pVictim->ModifyHealth(- (int32)damage); - // Check if health is below 20% (apply damage before to prevent case when after ProcDamageAndSpell health < damage - if(pVictim->GetHealth()*5 < pVictim->GetMaxHealth()) - { - uint32 procVictim = PROC_FLAG_NONE; - - // if just dropped below 20% (for CheatDeath) - if((pVictim->GetHealth()+damage)*5 > pVictim->GetMaxHealth()) - procVictim = PROC_FLAG_LOW_HEALTH; - - ProcDamageAndSpell(pVictim,PROC_FLAG_TARGET_LOW_HEALTH,procVictim); - } - if(damagetype != DOT) { //start melee attacks only after melee hit @@ -1011,7 +1002,7 @@ spell->m_CastItem = castItem; spell->prepare(&targets); } - +/* void Unit::DealDamageBySchool(Unit *pVictim, SpellEntry const *spellInfo, uint32 *damage, CleanDamage *cleanDamage, bool *crit, bool isTriggeredSpell) { @@ -1318,10 +1309,22 @@ ((Player*)pVictim)->RewardRage(cleanDamage.damage, 0, false); } } +*/ +void Unit::SpellNonMeleeDamageLog(Unit *pVictim, uint32 spellID, uint32 damage, bool isTriggeredSpell, bool useSpellDamage) +{ + SpellEntry const *procSpell = sSpellStore.LookupEntry(spellID); + CalcDamageInfo damageInfo; + CalculateDamage(SPELL_DIRECT_DAMAGE, pVictim, damage, &damageInfo, BASE_ATTACK, spellID); + ProcDamageAndSpell(damageInfo.target,damageInfo.procAttacker,damageInfo.procVictim,damageInfo.procEx,damageInfo.damage, procSpell, isTriggeredSpell, damageInfo.attackType); + DealDamage(&damageInfo, true); +} + void Unit::PeriodicAuraLog(Unit *pVictim, SpellEntry const *spellProto, Modifier *mod, uint8 effect_idx) { - uint32 procFlag = 0; + uint32 procAttacker = PROC_FLAG_NONE; + uint32 procVictim = PROC_FLAG_NONE; + uint32 procEx = PROC_FLAG_EX_NONE; if(!this || !pVictim || !isAlive() || !pVictim->isAlive()) { return; @@ -1332,48 +1335,10 @@ case SPELL_AURA_PERIODIC_DAMAGE: case SPELL_AURA_PERIODIC_DAMAGE_PERCENT: { - uint32 absorb=0; - uint32 resist=0; - CleanDamage cleanDamage = CleanDamage(0, BASE_ATTACK, MELEE_HIT_NORMAL ); - - uint32 pdamage; - - if(mod->m_auraname == SPELL_AURA_PERIODIC_DAMAGE) - { - pdamage = mod->m_amount; - - //Calculate armor mitigation if it is a physical spell - if (spellProto->School == SPELL_SCHOOL_NORMAL) - { - uint32 pdamageReductedArmor = CalcArmorReducedDamage(pVictim, pdamage); - cleanDamage.damage += pdamage - pdamageReductedArmor; - pdamage = pdamageReductedArmor; - } - - CalcAbsorbResist(pVictim, SpellSchools(spellProto->School), DOT, pdamage, &absorb, &resist); - - pdamage = SpellDamageBonus(pVictim,spellProto,pdamage,DOT); - } - else - pdamage = uint32(pVictim->GetMaxHealth()*mod->m_amount/100); - - sLog.outDetail("PeriodicAuraLog: %u (TypeId: %u) attacked %u (TypeId: %u) for %u dmg inflicted by %u abs is %u", - GetGUIDLow(), GetTypeId(), pVictim->GetGUIDLow(), pVictim->GetTypeId(), pdamage, spellProto->Id,absorb); - - WorldPacket data(SMSG_PERIODICAURALOG, (21+16));// we guess size - data.append(pVictim->GetPackGUID()); - data.append(GetPackGUID()); - data << uint32(spellProto->Id); - data << uint32(1); - data << uint32(mod->m_auraname); - data << (uint32)pdamage; - data << (uint32)spellProto->School; - data << (uint32)absorb; - data << (uint32)resist; - SendMessageToSet(&data,true); - - DealDamage(pVictim, (pdamage <= absorb+resist) ? 0 : (pdamage-absorb-resist), &cleanDamage, DOT, SpellSchools(spellProto->School), spellProto, procFlag, true); - ProcDamageAndSpell(pVictim, PROC_FLAG_HIT_SPELL, PROC_FLAG_TAKE_DAMAGE, (pdamage <= absorb+resist) ? 0 : (pdamage-absorb-resist), spellProto); + CalcDamageInfo damageInfo; + CalculateDamage(DOT, pVictim, mod->m_amount, &damageInfo, BASE_ATTACK, spellProto->Id, SpellSchools(spellProto->School), mod->m_auraname); + ProcDamageAndSpell(damageInfo.target,damageInfo.procAttacker,damageInfo.procVictim,damageInfo.procEx,damageInfo.damage, spellProto, false, damageInfo.attackType); + DealDamage(&damageInfo, true); break; } case SPELL_AURA_PERIODIC_LEECH: @@ -1401,10 +1366,17 @@ sLog.outDetail("PeriodicAuraLog: %u (TypeId: %u) health leech of %u (TypeId: %u) for %u dmg inflicted by %u abs is %u", GetGUIDLow(), GetTypeId(), pVictim->GetGUIDLow(), pVictim->GetTypeId(), pdamage, spellProto->Id,absorb); +// Possibly SPELL_AURA_PERIODIC_LEECH not trigger on tick + procAttacker |= PROC_FLAG_SUCCESSFUL_PERIODIC_DAMAGE; + procVictim |= PROC_FLAG_TAKEN_PERIODIC_DAMAGE; + if (absorb)procEx |= PROC_FLAG_EX_ABSORB; + if (resist>=pdamage) procEx|= PROC_FLAG_EX_FULL_RESISTS; + if (pdamage <= absorb+resist) pdamage = 0; + else pdamage-=absorb+resist; + ProcDamageAndSpell(pVictim, procAttacker, procVictim, procEx, pdamage, spellProto); SendSpellNonMeleeDamageLog(pVictim, spellProto->Id, pdamage, SpellSchools(spellProto->School), absorb, resist, false, 0); - DealDamage(pVictim, (pdamage <= absorb+resist) ? 0 : (pdamage-absorb-resist), &cleanDamage, DOT, SpellSchools(spellProto->School), spellProto, procFlag, false); - ProcDamageAndSpell(pVictim, PROC_FLAG_HIT_SPELL, PROC_FLAG_TAKE_DAMAGE, (pdamage <= absorb+resist) ? 0 : (pdamage-absorb-resist), spellProto); + DealDamage(pVictim, (pdamage <= absorb+resist) ? 0 : (pdamage-absorb-resist), &cleanDamage, DOT, SpellSchools(spellProto->School), spellProto, PROC_FLAG_NONE, false); if (!pVictim->isAlive() && IsNonMeleeSpellCasted(false)) { for (uint32 i = CURRENT_FIRST_NON_MELEE_SPELL; i < CURRENT_MAX_SPELL; i++) @@ -1481,12 +1453,12 @@ SendSpellNonMeleeDamageLog(this, spellProto->Id, gain, SpellSchools(spellProto->School), 0, 0, false, 0, false); CleanDamage cleanDamage = CleanDamage(0, BASE_ATTACK, MELEE_HIT_NORMAL ); - DealDamage(this, gain, &cleanDamage, NODAMAGE, SpellSchools(spellProto->School), spellProto, PROC_FLAG_HEAL, true); + DealDamage(this, gain, &cleanDamage, NODAMAGE, SpellSchools(spellProto->School), spellProto, PROC_FLAG_SUCCESSFUL_HEAL, true); } } - if(procSpell) - ProcDamageAndSpell(pVictim, PROC_FLAG_HEAL, PROC_FLAG_HEALED, pdamage, spellProto); +// if(procSpell) +// ProcDamageAndSpell(pVictim, PROC_FLAG_HEAL, PROC_FLAG_HEALED, pdamage, spellProto); break; } case SPELL_AURA_PERIODIC_MANA_LEECH: @@ -1591,6 +1563,746 @@ } } + +void Unit::CalculateDamage(DamageEffectType type, Unit *pVictim, uint32 damage, CalcDamageInfo *damageInfo, WeaponAttackType attackType, uint32 spellID, SpellSchools damageSchool, uint32 auraname) +{ + damageInfo->attackType = attackType; + damageInfo->hitOutCome = MELEE_HIT_EVADE; + damageInfo->damage = 0; + damageInfo->cleanDamage = 0; + damageInfo->absorb = 0; + damageInfo->resist = 0; + damageInfo->blocked_amount = 0; + damageInfo->procAttacker = PROC_FLAG_NONE; + damageInfo->procVictim = PROC_FLAG_NONE; + damageInfo->procEx = PROC_FLAG_EX_NONE; + damageInfo->damagetype = type; + damageInfo->target = pVictim; + damageInfo->TargetState = 0; + + if (damageInfo->attackType == BASE_ATTACK) damageInfo->HitInfo = HITINFO_NORMALSWING2; + else if (damageInfo->attackType == OFF_ATTACK ) damageInfo->HitInfo = HITINFO_LEFTSWING; + else if (damageInfo->attackType == RANGED_ATTACK ) damageInfo->HitInfo = 0x08;// test + + damageInfo->SpellID = spellID; + damageInfo->damageSchool = damageSchool; + damageInfo->auraname = auraname; + + if(!this || !pVictim) + return; + if(!this->isAlive() || !pVictim->isAlive()) + return; + + + switch (type) + { + case DIRECT_DAMAGE: CalculateMeleeDamage(damage, damageInfo); break; + case SPELL_DIRECT_DAMAGE:CalculateSpellDamage(damage, damageInfo); break; + case DOT: CalculateDotDamage(damage, damageInfo); break; + // TODO add special func for this + case DAMAGE_SHIELD: + damageInfo->damage=damage; + SpellEntry const *spellInfo = sSpellStore.LookupEntry(damageInfo->SpellID); + if(!spellInfo) + break; + damageInfo->damageSchool = SpellSchools(spellInfo->School); + break; + } + // Calculate absorb resist + if(damageInfo->damage > 0) + { + // Immune check + if(pVictim->IsImmunedToSchoolDamage(damageInfo->damageSchool) ) + { + damageInfo->HitInfo |= HITINFO_MISS|HITINFO_SWINGNOHITSOUND; + damageInfo->TargetState = VICTIMSTATE_IS_IMMUNE; + damageInfo->damage = 0; + damageInfo->cleanDamage = 0; + damageInfo->absorb = 0; + damageInfo->resist = 0; + damageInfo->blocked_amount = 0; + return; + } + // Calculate absorb & resists + if (damageInfo->damage) + CalcAbsorbResist(pVictim, damageInfo->damageSchool, damageInfo->damagetype, damageInfo->damage, &damageInfo->absorb, &damageInfo->resist); + // Only send absorbed message if we actually absorbed some damage + if (damageInfo->absorb) + { + damageInfo->HitInfo|= HITINFO_ABSORB; + damageInfo->procEx |= PROC_FLAG_EX_ABSORB; + if (damageInfo->damage< damageInfo->absorb)damageInfo->absorb = damageInfo->damage; + damageInfo->damage-=damageInfo->absorb; + } + if (damageInfo->resist && damageInfo->damage) + { + damageInfo->HitInfo|= HITINFO_RESIST; + if (damageInfo->damage< damageInfo->resist) + { + damageInfo->resist = damageInfo->damage; + damageInfo->procEx |= PROC_FLAG_EX_FULL_RESISTS; + } + damageInfo->damage-=damageInfo->resist; + } + } + + if (damageInfo->damage==0) + damageInfo->HitInfo|=HITINFO_SWINGNOHITSOUND; +} +void Unit::CalculateDotDamage(uint32 damage, CalcDamageInfo *damageInfo) +{ + SpellEntry const *spellInfo = sSpellStore.LookupEntry(damageInfo->SpellID); + if(!spellInfo) + return; + + if (damageInfo->damageSchool==SPELL_SCHOOL_NORMAL) + damageInfo->damageSchool = SpellSchools(spellInfo->School); + switch(damageInfo->auraname) + { + case SPELL_AURA_PERIODIC_DAMAGE: break;//damage = damage; + case SPELL_AURA_PERIODIC_DAMAGE_PERCENT: damage = uint32(damageInfo->target->GetMaxHealth()*damage/100);break; + + } + //Calculate armor mitigation if it is a physical spell + if (damageInfo->damageSchool == SPELL_SCHOOL_NORMAL) + { + //Calculate armor mitigation + damageInfo->damage = CalcArmorReducedDamage(damageInfo->target, damage); + damageInfo->cleanDamage += damageInfo->damage - damage; + } + else + damageInfo->damage = SpellDamageBonus(damageInfo->target, spellInfo, damage, DOT); + damageInfo->procAttacker |= PROC_FLAG_SUCCESSFUL_PERIODIC_DAMAGE; + damageInfo->procVictim |= PROC_FLAG_TAKEN_PERIODIC_DAMAGE; + if (damageInfo->damage) damageInfo->procVictim |= PROC_FLAG_TAKEN_ANY_DAMAGE; +} +void Unit::CalculateSpellDamage(uint32 damage, CalcDamageInfo *damageInfo) +{ + SpellEntry const *spellInfo = sSpellStore.LookupEntry(damageInfo->SpellID); + if(!spellInfo) + return; + if (damageInfo->damageSchool==SPELL_SCHOOL_NORMAL) + damageInfo->damageSchool = SpellSchools(spellInfo->School); + // Per-school calc + switch (damageInfo->damageSchool) + { + // Physical damage school + case SPELL_SCHOOL_NORMAL: + { + // Add melee damage bonuses + MeleeDamageBonus(damageInfo->target, &damage, damageInfo->attackType); + + //Calculate armor mitigation + damageInfo->damage = CalcArmorReducedDamage(damageInfo->target, damage); + damageInfo->cleanDamage += damageInfo->damage - damage; + + // Calculate physical outcome + damageInfo->hitOutCome = RollPhysicalOutcomeAgainst(damageInfo->target, damageInfo->attackType, spellInfo); + // Disable parry for ranged attack + if(damageInfo->attackType == RANGED_ATTACK && (damageInfo->hitOutCome == MELEE_HIT_PARRY)) + damageInfo->hitOutCome = MELEE_HIT_NORMAL; + + switch(damageInfo->hitOutCome) + { + case MELEE_HIT_EVADE: + { + damageInfo->HitInfo |= HITINFO_MISS|HITINFO_SWINGNOHITSOUND; + damageInfo->TargetState = VICTIMSTATE_EVADES; + damageInfo->procEx|=PROC_FLAG_EX_HIT_EVADE_TARGET; + damageInfo->damage = 0; + damageInfo->cleanDamage = 0; + return; + } + case MELEE_HIT_MISS: + { + damageInfo->HitInfo |= HITINFO_MISS; + damageInfo->TargetState = VICTIMSTATE_NORMAL; + damageInfo->procEx|=PROC_FLAG_EX_MISS; + damageInfo->damage = 0; + damageInfo->cleanDamage = 0; + break; + } + case MELEE_HIT_CRIT: + { + damageInfo->HitInfo |= HITINFO_CRITICALHIT; + damageInfo->TargetState = VICTIMSTATE_NORMAL; + damageInfo->procEx|=PROC_FLAG_EX_CRITICAL_HIT; + uint32 crit_bonus = damageInfo->damage; + // Apply crit_damage bonus for melee spells + if(Player* modOwner = GetSpellModOwner()) + modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_CRIT_DAMAGE_BONUS, crit_bonus); + damageInfo->damage += crit_bonus; + if(damageInfo->attackType == RANGED_ATTACK) + { + int32 mod = damageInfo->target->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_RANGED_CRIT_DAMAGE); + damageInfo->damage = int32((damageInfo->damage) * float((100.0f + mod)/100.0f)); + } + else + { + int32 mod = damageInfo->target->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_MELEE_CRIT_DAMAGE); + damageInfo->damage = int32((damageInfo->damage) * float((100.0f + mod)/100.0f)); + } + + // Resilience - reduce crit damage by 2x% + uint32 resilienceReduction = uint32(damageInfo->target->m_modResilience * 2/100 * (damageInfo->damage)); + damageInfo->cleanDamage += resilienceReduction; + damageInfo->damage -= resilienceReduction; + break; + } + case MELEE_HIT_PARRY: + { + damageInfo->HitInfo |= HITINFO_NORMALSWING; + damageInfo->TargetState = VICTIMSTATE_PARRY; + damageInfo->procEx|=PROC_FLAG_EX_PARRY; + damageInfo->cleanDamage += damageInfo->damage; // To Help Calculate Rage + damageInfo->damage = 0; + break; + } + case MELEE_HIT_DODGE: + { + damageInfo->HitInfo |= HITINFO_NORMALSWING; + damageInfo->TargetState = VICTIMSTATE_DODGE; + damageInfo->procEx|=PROC_FLAG_EX_DODGE; + damageInfo->cleanDamage += damageInfo->damage; // To Help Calculate Rage + damageInfo->damage = 0; + break; + } + case MELEE_HIT_BLOCK: + { + damageInfo->HitInfo |= HITINFO_NORMALSWING; + damageInfo->TargetState = VICTIMSTATE_BLOCKS; + damageInfo->procEx|=PROC_FLAG_EX_BLOCK; + damageInfo->blocked_amount = uint32(damageInfo->target->GetShieldBlockValue()); + if (damageInfo->blocked_amount > damageInfo->damage) + damageInfo->blocked_amount = damageInfo->damage; + + damageInfo->cleanDamage += damageInfo->blocked_amount; // To Help Calculate Rage + damageInfo->damage -= damageInfo->blocked_amount; + break; + } + case MELEE_HIT_GLANCING: + case MELEE_HIT_CRUSHING: + case MELEE_HIT_NORMAL: + damageInfo->TargetState = VICTIMSTATE_NORMAL; + break; + } + break; + } + + // Other schools + case SPELL_SCHOOL_HOLY: + case SPELL_SCHOOL_FIRE: + case SPELL_SCHOOL_NATURE: + case SPELL_SCHOOL_FROST: + case SPELL_SCHOOL_SHADOW: + case SPELL_SCHOOL_ARCANE: + damageInfo->TargetState = VICTIMSTATE_NORMAL; +/* if( SpellMissChanceCalc(damageInfo->target, damageInfo->damageSchool) > urand(0,10000) ) + { + damageInfo->HitInfo|= HITINFO_RESIST; + + damageInfo->procEx|=PROC_FLAG_EX_MISS; + damageInfo->damage = 0; + break; + }*/ + + // Calculate damage bonus + damageInfo->damage = SpellDamageBonus(damageInfo->target, spellInfo, damage, SPELL_DIRECT_DAMAGE); + bool crit=false; + // Calculate critical bonus + if(spellInfo->DmgClass==SPELL_DAMAGE_CLASS_MELEE) + { + // Calculate physical outcome + if(RollPhysicalOutcomeAgainst(damageInfo->target, BASE_ATTACK, spellInfo) == MELEE_HIT_CRIT) + { + crit = true; + damageInfo->damage *= 2; + // Resilience - reduce crit damage by 2x% + uint32 resilienceReduction = uint32(damageInfo->target->m_modResilience * 2/100 * (damageInfo->damage)); + damageInfo->cleanDamage += resilienceReduction; + damageInfo->damage -= resilienceReduction; + } + } + else + crit = SpellCriticalBonus(spellInfo, &damageInfo->damage, damageInfo->target); + if (crit) + { + damageInfo->HitInfo|= HITINFO_CRITICALHIT; + damageInfo->procEx|=PROC_FLAG_EX_CRITICAL_HIT; + } + break; + } + if(spellInfo->DmgClass == SPELL_DAMAGE_CLASS_MELEE) + { + damageInfo->procAttacker = PROC_FLAG_SUCCESSFUL_MELEE_SPELL_HIT; + damageInfo->procVictim = PROC_FLAG_TAKEN_MELEE_SPELL_HIT; + } + else if (spellInfo->DmgClass == SPELL_DAMAGE_CLASS_RANGED) + { + damageInfo->procAttacker = PROC_FLAG_SUCCESSFUL_RANGED_SPELL_HIT; + damageInfo->procVictim = PROC_FLAG_TAKEN_RANGED_SPELL_HIT; + } + else + { + damageInfo->procAttacker = PROC_FLAG_SUCCESSFUL_HARMFUL_SPELL_HIT; + damageInfo->procVictim = PROC_FLAG_TAKEN_HARMFUL_SPELL_HIT; + } + if (damageInfo->damage) damageInfo->procVictim |= PROC_FLAG_TAKEN_ANY_DAMAGE; +} + +void Unit::CalculateMeleeDamage(uint32 damage, CalcDamageInfo *damageInfo) +{ + damage += CalculateDamage (damageInfo->attackType); + // Add melee damage bonus + MeleeDamageBonus(damageInfo->target, &damage, damageInfo->attackType); + // Calculate armor reduction + damageInfo->damage = CalcArmorReducedDamage(damageInfo->target, damage); + damageInfo->cleanDamage += damage - damageInfo->damage; + + damageInfo->hitOutCome = RollMeleeOutcomeAgainst(damageInfo->target, damageInfo->attackType); + + // Disable parry for ranged attack + if(damageInfo->attackType == RANGED_ATTACK && (damageInfo->hitOutCome == MELEE_HIT_PARRY)) + damageInfo->hitOutCome = MELEE_HIT_NORMAL; + + switch(damageInfo->hitOutCome) + { + case MELEE_HIT_EVADE: + { + damageInfo->HitInfo |= HITINFO_MISS|HITINFO_SWINGNOHITSOUND; + damageInfo->TargetState = VICTIMSTATE_EVADES; + + damageInfo->procEx|=PROC_FLAG_EX_HIT_EVADE_TARGET; + damageInfo->damage = 0; + damageInfo->cleanDamage = 0; + return; + } + case MELEE_HIT_MISS: + { + damageInfo->HitInfo |= HITINFO_MISS; + damageInfo->TargetState = VICTIMSTATE_NORMAL; + + damageInfo->procEx|=PROC_FLAG_EX_MISS; + damageInfo->damage = 0; + damageInfo->cleanDamage = 0; + break; + } + case MELEE_HIT_NORMAL: + damageInfo->TargetState = VICTIMSTATE_NORMAL; + break; + case MELEE_HIT_CRIT: + { + damageInfo->HitInfo |= 0x8|HITINFO_CRITICALHIT; + damageInfo->TargetState = VICTIMSTATE_NORMAL; + + damageInfo->procEx|=PROC_FLAG_EX_CRITICAL_HIT; + // Crit bonus calc + damageInfo->damage += damageInfo->damage; + if(damageInfo->attackType == RANGED_ATTACK) + damageInfo->damage *= (damageInfo->target->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_RANGED_CRIT_DAMAGE)+100)/100; + else + damageInfo->damage *= (damageInfo->target->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_MELEE_CRIT_DAMAGE)+100)/100; + + // Resilience - reduce crit damage by 2x% + uint32 resilienceReduction; + resilienceReduction = uint32(damageInfo->target->m_modResilience * 2/100 * (damageInfo->damage)); + + damageInfo->damage -= resilienceReduction; + damageInfo->cleanDamage += resilienceReduction; + } + break; + + case MELEE_HIT_PARRY: + damageInfo->TargetState = VICTIMSTATE_PARRY; + damageInfo->procEx|=PROC_FLAG_EX_PARRY; + damageInfo->cleanDamage += damageInfo->damage; + damageInfo->damage = 0; + break; + + case MELEE_HIT_DODGE: + damageInfo->TargetState = VICTIMSTATE_DODGE; + damageInfo->procEx|=PROC_FLAG_EX_DODGE; + damageInfo->cleanDamage += damageInfo->damage; + damageInfo->damage = 0; + break; + + case MELEE_HIT_BLOCK: + { + damageInfo->TargetState = VICTIMSTATE_BLOCKS; + damageInfo->procEx|=PROC_FLAG_EX_BLOCK; + damageInfo->blocked_amount = damageInfo->target->GetShieldBlockValue(); + if (damageInfo->blocked_amount >= damageInfo->damage) + damageInfo->blocked_amount = damageInfo->damage; + + damageInfo->damage -= damageInfo->blocked_amount; + damageInfo->cleanDamage += damageInfo->blocked_amount; + } + break; + + case MELEE_HIT_GLANCING: + { + damageInfo->HitInfo |= HITINFO_GLANCING; + damageInfo->TargetState = VICTIMSTATE_NORMAL; + float reducePercent = 1.0f; //damage factor + // calculate base values and mods + float baseLowEnd = 1.3; + float baseHighEnd = 1.2; + switch(getClass()) // lowering base values for casters + { + case CLASS_SHAMAN: + case CLASS_PRIEST: + case CLASS_MAGE: + case CLASS_WARLOCK: + case CLASS_DRUID: + baseLowEnd -= 0.7; + baseHighEnd -= 0.3; + break; + } + + float maxLowEnd = 0.6; + switch(getClass()) // upper for melee classes + { + case CLASS_WARRIOR: + case CLASS_ROGUE: + maxLowEnd = 0.91; //If the attacker is a melee class then instead the lower value of 0.91 + } + + // calculate values + int32 diff = damageInfo->target->GetDefenseSkillValue() - GetWeaponSkillValue(damageInfo->attackType); + float lowEnd = baseLowEnd - ( 0.05f * diff ); + float highEnd = baseHighEnd - ( 0.03f * diff ); + + // apply max/min bounds + if ( lowEnd < 0.01f ) //the low end must not go bellow 0.01f + lowEnd = 0.01f; + else if ( lowEnd > maxLowEnd ) //the smaller value of this and 0.6 is kept as the low end + lowEnd = maxLowEnd; + + if ( highEnd < 0.2f ) //high end limits + highEnd = 0.2f; + if ( highEnd > 0.99f ) + highEnd = 0.99f; + + if(lowEnd > highEnd) // prevent negative range size + lowEnd = highEnd; + + reducePercent = lowEnd + rand_norm() * ( highEnd - lowEnd ); + + damageInfo->cleanDamage += damageInfo->damage-uint32(reducePercent * damageInfo->damage); + damageInfo->damage = uint32(reducePercent * damageInfo->damage); + break; + } + case MELEE_HIT_CRUSHING: + { + damageInfo->HitInfo |= HITINFO_CRUSHING; + damageInfo->TargetState = VICTIMSTATE_NORMAL; + // 150% normal damage + damageInfo->damage += (damageInfo->damage / 2); + // cleanDamage->damage = *damage; + break; + } + default: + + break; + } + + if (damageInfo->attackType == BASE_ATTACK) + { + damageInfo->procAttacker |= PROC_FLAG_SUCCESSFUL_MILEE_HIT; + damageInfo->procVictim |= PROC_FLAG_TAKEN_MELEE_HIT; + } + else if (damageInfo->attackType == OFF_ATTACK) + { + damageInfo->procAttacker |= PROC_FLAG_SUCCESSFUL_MILEE_HIT | PROC_FLAG_SUCCESSFUL_OFFHAND_HIT; + damageInfo->procVictim |= PROC_FLAG_TAKEN_MELEE_HIT;//|PROC_FLAG_TAKEN_OFFHAND_HIT // not used + } + else if (damageInfo->attackType == RANGED_ATTACK) + { + damageInfo->procAttacker |= PROC_FLAG_SUCCESSFUL_RANGED_HIT; + damageInfo->procVictim |= PROC_FLAG_TAKEN_RANGED_HIT; + } + if (damageInfo->damage) damageInfo->procVictim |= PROC_FLAG_TAKEN_ANY_DAMAGE; +} + +void Unit::SendAttackStateUpdate(CalcDamageInfo *damageInfo) +{ + sLog.outDebug("WORLD: Sending SMSG_ATTACKERSTATEUPDATE"); + WorldPacket data(SMSG_ATTACKERSTATEUPDATE, (16+45)); // we guess size + data << (uint32)damageInfo->HitInfo; + data.append(GetPackGUID()); + data.append(damageInfo->target->GetPackGUID()); + data << (uint32)(damageInfo->damage); // Full damage + + data << (uint8)1; // Sub damage count + //=== Sub damage description + data << (uint32)damageInfo->damageSchool; // School of sub damage + data << (float)damageInfo->damage; // sub damage + data << (uint32)damageInfo->damage; // Sub Damage + data << (uint32)damageInfo->absorb; // Absorb + data << (uint32)damageInfo->resist; // Resist + //================================================= + data << (uint32)damageInfo->TargetState; +// if(damageInfo->absorb == 0 ) //also 0x3E8 = 0x3E8, check when that happens + data << (uint32)0; +// else +// data << (uint32)-1; + data << (uint32)0; + data << (uint32)damageInfo->blocked_amount; + SendMessageToSet( &data, true ); +} +void Unit::SendSpellNonMeleeDamageLog(CalcDamageInfo *damageInfo) +{ + uint8 CriticalHit = damageInfo->HitInfo&HITINFO_CRITICALHIT ? 2 : 0; + WorldPacket data(SMSG_SPELLNONMELEEDAMAGELOG, (16+31)); // we guess size + data.append(damageInfo->target->GetPackGUID()); + data.append(GetPackGUID()); + data << uint32(damageInfo->SpellID); + data << uint32(damageInfo->damage); + data << uint8(damageInfo->damageSchool); //damagetype + data << uint32(damageInfo->absorb); //AbsorbedDamage + data << uint32(damageInfo->resist); //resist + data << uint8(0);//PhysicalDamage); + data << uint8(0); + data << uint32(damageInfo->blocked_amount); //blocked + data << uint8(CriticalHit); //seen 0x05 also... + data << uint32(0); + SendMessageToSet( &data, true );/**/ +} + +void Unit::SendSpellDotDamageLog(CalcDamageInfo *damageInfo) +{ + WorldPacket data(SMSG_PERIODICAURALOG, (21+16));// we guess size + data.append(damageInfo->target->GetPackGUID()); + data.append(GetPackGUID()); + data << uint32(damageInfo->SpellID); + data << uint32(1); + data << uint32(damageInfo->auraname); + switch(damageInfo->auraname) + { + case SPELL_AURA_PERIODIC_DAMAGE: + case SPELL_AURA_PERIODIC_DAMAGE_PERCENT: + data << uint32(damageInfo->damage); + data << uint32(damageInfo->damageSchool); + data << uint32(damageInfo->absorb); + data << uint32(damageInfo->resist); + break; + case SPELL_AURA_PERIODIC_HEAL: + case SPELL_AURA_OBS_MOD_HEALTH: + data << uint32(damageInfo->damage); + break; + default: + return; + } + SendMessageToSet(&data,true); +} + +void Unit::DealDamage(CalcDamageInfo *damageInfo, bool durabilityLoss) +{ + if (damageInfo==0) return; + Unit *pVictim = damageInfo->target; + if(!this || !pVictim) + return; + if (!pVictim->isAlive() || pVictim->isInFlight() || pVictim->GetTypeId() == TYPEID_UNIT && ((Creature*)pVictim)->IsInEvadeMode()) + return; + SpellEntry const *spellProto=NULL; + if (damageInfo->SpellID) + { + spellProto = sSpellStore.LookupEntry(damageInfo->SpellID); + if (spellProto == NULL) + { + sLog.outDebug("Unit::DealDamage have wrong damageInfo->SpellID: %u", damageInfo->SpellID); + return; + } + } + //You don't lose health from damage taken from another player while in a sanctuary + //You still see it in the combat log though + if(pVictim != this && GetTypeId() == TYPEID_PLAYER && pVictim->GetTypeId() == TYPEID_PLAYER) + { + const AreaTableEntry *area = GetAreaEntryByAreaID(pVictim->GetAreaId()); + if(area && area->flags & 0x800) //sanctuary + return; + } + // Send log damage message to client + switch (damageInfo->damagetype) + { + case DIRECT_DAMAGE: + SendAttackStateUpdate(damageInfo); + break; + case DAMAGE_SHIELD: + { + WorldPacket data(SMSG_SPELLDAMAGESHIELD,(16+31)); + data << uint64(GetGUID()); + data << uint64(damageInfo->target->GetGUID()); + data << uint32(damageInfo->damage); + data << uint32(damageInfo->damageSchool); + damageInfo->target->SendMessageToSet( &data, true ); + } + break; + case SPELL_DIRECT_DAMAGE: + if (damageInfo->damage==0) + SendAttackStateUpdate(damageInfo); + else + SendSpellNonMeleeDamageLog(damageInfo); + break; + case DOT: + SendSpellDotDamageLog(damageInfo); + break; + default: + SendSpellNonMeleeDamageLog(damageInfo); + break; + } + // Enable AURA_STATE_CRIT on crit + if (damageInfo->HitInfo&HITINFO_CRITICALHIT) + { + ModifyAuraState(AURA_STATE_CRIT, true); + if(getClass()==CLASS_HUNTER) + ModifyAuraState(AURA_STATE_HUNTER_CRIT_STRIKE, true); + + pVictim->HandleEmoteCommand(EMOTE_ONESHOT_WOUNDCRITICAL); + } + // For melee attack UpdateCombatSkills + if (damageInfo->hitOutCome) + { + // Clear aura states for meele + pVictim->ModifyAuraState(AURA_STATE_DEFENSE, false); + if(getClass() == CLASS_WARRIOR && GetTypeId() == TYPEID_PLAYER) // Clear OVERPOWER + ((Player*)this)->ClearComboPoints(); + ModifyAuraState(AURA_STATE_CRIT, false); + + if(getClass()==CLASS_HUNTER) + ModifyAuraState(AURA_STATE_HUNTER_CRIT_STRIKE, false); + if(pVictim->getClass()==CLASS_HUNTER) + pVictim->ModifyAuraState(AURA_STATE_HUNTER_PARRY, false); + + if(GetTypeId() == TYPEID_PLAYER && pVictim->GetTypeId() != TYPEID_PLAYER && pVictim->GetCreatureType() != CREATURE_TYPE_CRITTER ) + ((Player*)this)->UpdateCombatSkills(pVictim, damageInfo->attackType, damageInfo->hitOutCome, false); + + if(GetTypeId() != TYPEID_PLAYER && pVictim->GetTypeId() == TYPEID_PLAYER) + ((Player*)pVictim)->UpdateCombatSkills(this, damageInfo->attackType, damageInfo->hitOutCome, true); + } + // Modify victim state depends on damageInfo->TargetState + switch(damageInfo->TargetState) + { + case VICTIMSTATE_PARRY: + { + // Get attack timers + float offtime = float(pVictim->getAttackTimer(OFF_ATTACK)); + float basetime = float(pVictim->getAttackTimer(BASE_ATTACK)); + + // Reduce attack time + if (pVictim->haveOffhandWeapon() && offtime < basetime) + { + float percent20 = pVictim->GetAttackTime(OFF_ATTACK) * 0.20; + float percent60 = 3 * percent20; + if(offtime > percent20 && offtime <= percent60) + { + pVictim->setAttackTimer(OFF_ATTACK, uint32(percent20)); + } + else if(offtime > percent60) + { + offtime -= 2 * percent20; + pVictim->setAttackTimer(OFF_ATTACK, uint32(offtime)); + } + } + else + { + float percent20 = pVictim->GetAttackTime(BASE_ATTACK) * 0.20; + float percent60 = 3 * percent20; + if(basetime > percent20 && basetime <= percent60) + { + pVictim->setAttackTimer(BASE_ATTACK, uint32(percent20)); + } + else if(basetime > percent60) + { + basetime -= 2 * percent20; + pVictim->setAttackTimer(BASE_ATTACK, uint32(basetime)); + } + } + // Update victim defense ? + if(pVictim->GetTypeId() == TYPEID_PLAYER) + ((Player*)pVictim)->UpdateDefense(); + + // Set parry flags + pVictim->HandleEmoteCommand(EMOTE_ONESHOT_PARRYUNARMED); + + if (pVictim->getClass() == CLASS_HUNTER) // Mongoose Bite + pVictim->ModifyAuraState(AURA_STATE_HUNTER_PARRY, true); + else + pVictim->ModifyAuraState(AURA_STATE_DEFENSE, true); + } + break; + case VICTIMSTATE_BLOCKS: + { + if (pVictim->GetUnitBlockChance()) + pVictim->HandleEmoteCommand(EMOTE_ONESHOT_PARRYSHIELD); + else + pVictim->HandleEmoteCommand(EMOTE_ONESHOT_PARRYUNARMED); + if(pVictim->GetTypeId() == TYPEID_PLAYER) + ((Player*)pVictim)->UpdateDefense(); + pVictim->ModifyAuraState(AURA_STATE_DEFENSE,true); + } + break; + case VICTIMSTATE_DODGE: + { + if(pVictim->GetTypeId() == TYPEID_PLAYER) + ((Player*)pVictim)->UpdateDefense(); + + pVictim->HandleEmoteCommand(EMOTE_ONESHOT_PARRYUNARMED); + if (pVictim->getClass() != CLASS_ROGUE) // Riposte + pVictim->ModifyAuraState(AURA_STATE_DEFENSE, true); + // Overpower + if (GetTypeId() == TYPEID_PLAYER && getClass() == CLASS_WARRIOR) + ((Player*)this)->AddComboPoints(pVictim, 1); + } + break; + default: + break; + } + /// If this is a creature and it attacks from behind it has a probability to daze it's victim + if( (damageInfo->hitOutCome==MELEE_HIT_CRIT || damageInfo->hitOutCome==MELEE_HIT_CRUSHING || damageInfo->hitOutCome==MELEE_HIT_NORMAL || damageInfo->hitOutCome==MELEE_HIT_GLANCING) && + GetTypeId() != TYPEID_PLAYER && !((Creature*)this)->GetCharmerOrOwnerGUID() && !pVictim->HasInArc(M_PI, this) ) + { + // -probability is between 0% and 40% + // 20% base chance + float Probability = 20; + + //there is a newbie protection, at level 10 just 7% base chance; assuming linear function + if( pVictim->getLevel() < 30 ) + Probability = 0.65f*pVictim->getLevel()+0.5; + + uint32 VictimDefense=pVictim->GetDefenseSkillValue(); + uint32 AttackerMeleeSkill=GetUnitMeleeSkill(); + + Probability *= AttackerMeleeSkill/(float)VictimDefense; + + if(Probability > 40) + Probability = 40; + + if(roll_chance_f(Probability)) + CastSpell(pVictim, 1604, true); + } + + // update at damage Judgement aura duration that applied by attacker at victim + if(damageInfo->damage) + { + AuraMap& vAuras = pVictim->GetAuras(); + for(AuraMap::iterator itr = vAuras.begin(); itr != vAuras.end(); ++itr) + { + SpellEntry const *spellInfo = (*itr).second->GetSpellProto(); + if( spellInfo->AttributesEx3 == 0x40000 && spellInfo->SpellFamilyName == SPELLFAMILY_PALADIN && + ((*itr).second->GetCasterGUID() == GetGUID() || (spellProto && spellProto->Id == 35395) )) + { + (*itr).second->SetAuraDuration((*itr).second->GetAuraMaxDuration()); + (*itr).second->UpdateAuraDuration(); + } + } + } + // Call default DealDamage + CleanDamage cleanDamage(damageInfo->cleanDamage,damageInfo->attackType,damageInfo->hitOutCome); + DealDamage(pVictim, damageInfo->damage, &cleanDamage, damageInfo->damagetype, damageInfo->damageSchool, spellProto, 0, durabilityLoss); +} + + void Unit::HandleEmoteCommand(uint32 anim_id) { WorldPacket data( SMSG_EMOTE, 12 ); @@ -1729,7 +2441,7 @@ *absorb = damage - RemainingDamage - *resist; } - +/* void Unit::DoAttackDamage (Unit *pVictim, uint32 *damage, CleanDamage *cleanDamage, uint32 *blocked_amount, SpellSchools damageType, uint32 *hitInfo, VictimState *victimState, uint32 *absorbDamage, uint32 *resistDamage, WeaponAttackType attType, SpellEntry const *spellCasted, bool isTriggeredSpell) { pVictim->ModifyAuraState(AURA_STATE_DEFENSE, false); @@ -2141,7 +2853,7 @@ } } } - +*/ void Unit::AttackerStateUpdate (Unit *pVictim, WeaponAttackType attType, bool isTriggered) { if(hasUnitState(UNIT_STAT_CONFUSED | UNIT_STAT_STUNDED | UNIT_STAT_FLEEING) || HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PACIFIED) ) @@ -2162,61 +2874,85 @@ return; } } - - uint32 hitInfo; - if (attType == BASE_ATTACK) - hitInfo = HITINFO_NORMALSWING2; - else if (attType == OFF_ATTACK) - hitInfo = HITINFO_LEFTSWING; - else - return; - - VictimState victimState = VICTIMSTATE_NORMAL; - - uint32 damage = 0; - CleanDamage cleanDamage = CleanDamage(0, BASE_ATTACK, MELEE_HIT_NORMAL ); - uint32 blocked_dmg = 0; - uint32 absorbed_dmg = 0; - uint32 resisted_dmg = 0; - - SpellSchools meleeSchool = GetMeleeDamageSchool(); - - if( meleeSchool == SPELL_SCHOOL_NORMAL && pVictim->IsImmunedToPhysicalDamage() ) + CalcDamageInfo damageInfo; + CalculateDamage(DIRECT_DAMAGE, pVictim, 0, &damageInfo, attType); + ProcDamageAndSpell(damageInfo.target,damageInfo.procAttacker,damageInfo.procVictim,damageInfo.procEx,damageInfo.damage, 0, false, damageInfo.attackType); + DealDamage(&damageInfo,true); + pVictim = damageInfo.target; + if (pVictim && !(damageInfo.HitInfo & HITINFO_MISS)) { - SendAttackStateUpdate (HITINFO_MISS, pVictim, 1, meleeSchool, 0, 0, 0, VICTIMSTATE_IS_IMMUNE, 0); - return; - } - - DoAttackDamage (pVictim, &damage, &cleanDamage, &blocked_dmg, meleeSchool, &hitInfo, &victimState, &absorbed_dmg, &resisted_dmg, attType); - - if (hitInfo & HITINFO_MISS) - //send miss - SendAttackStateUpdate (hitInfo, pVictim, 1, meleeSchool, damage, absorbed_dmg, resisted_dmg, victimState, blocked_dmg); - else - { - //do animation - SendAttackStateUpdate (hitInfo, pVictim, 1, meleeSchool, damage, absorbed_dmg, resisted_dmg, victimState, blocked_dmg); - - if (damage > (absorbed_dmg + resisted_dmg + blocked_dmg)) - damage -= (absorbed_dmg + resisted_dmg + blocked_dmg); - else - damage = 0; - - DealDamage (pVictim, damage, &cleanDamage, DIRECT_DAMAGE, meleeSchool, NULL, 0, true); - if(GetTypeId() == TYPEID_PLAYER && pVictim->isAlive()) { for(int i = EQUIPMENT_SLOT_START; i < EQUIPMENT_SLOT_END; i++) ((Player*)this)->CastItemCombatSpell(((Player*)this)->GetItemByPos(INVENTORY_SLOT_BAG_0,i),pVictim,attType); } + // victim's damage shield + // yet another hack to fix crashes related to the aura getting removed during iteration + std::set alreadyDone; + uint32 removedAuras = pVictim->m_removedAuras; + AuraList const& vDamageShields = pVictim->GetAurasByType(SPELL_AURA_DAMAGE_SHIELD); + for(AuraList::const_iterator i = vDamageShields.begin(), next = vDamageShields.begin(); i != vDamageShields.end(); i = next) + { + next++; + if (alreadyDone.find(*i) == alreadyDone.end()) + { + alreadyDone.insert(*i); + CalcDamageInfo damageInfo; + pVictim->CalculateDamage(DAMAGE_SHIELD, this, (*i)->GetModifier()->m_amount, &damageInfo, BASE_ATTACK, (*i)->GetId()); + pVictim->DealDamage(&damageInfo, true); + if (pVictim->m_removedAuras > removedAuras) + { + removedAuras = pVictim->m_removedAuras; + next = vDamageShields.begin(); + } + } + } + if (pVictim->GetTypeId() == TYPEID_PLAYER && damageInfo.damage) + { + for (uint32 i = CURRENT_FIRST_NON_MELEE_SPELL; i < CURRENT_MAX_SPELL; i++) + { + // skip channeled spell (processed differently below) + if (i == CURRENT_CHANNELED_SPELL) + continue; + if(pVictim->m_currentSpells[i]) + { + sLog.outDetail("Spell Delayed!%d",(int32)(0.25f * pVictim->m_currentSpells[i]->casttime)); + pVictim->m_currentSpells[i]->Delayed((int32)(0.25f * pVictim->m_currentSpells[i]->casttime)); + } + } + // process channeled spell separately + if (pVictim->m_currentSpells[CURRENT_CHANNELED_SPELL]) + { + if (pVictim->m_currentSpells[CURRENT_CHANNELED_SPELL]->getState() == SPELL_STATE_CASTING) + { + uint32 channelInterruptFlags = pVictim->m_currentSpells[CURRENT_CHANNELED_SPELL]->m_spellInfo->ChannelInterruptFlags; + if( channelInterruptFlags & CHANNEL_FLAG_DELAY ) + { + sLog.outDetail("Spell Delayed!%d",(int32)(0.25f * GetDuration(pVictim->m_currentSpells[CURRENT_CHANNELED_SPELL]->m_spellInfo))); + pVictim->m_currentSpells[CURRENT_CHANNELED_SPELL]->DelayedChannel((int32)(0.25f * GetDuration(pVictim->m_currentSpells[CURRENT_CHANNELED_SPELL]->m_spellInfo))); + return; + } + else if( !(channelInterruptFlags & (CHANNEL_FLAG_DAMAGE | CHANNEL_FLAG_DAMAGE2)) ) + return; + sLog.outDetail("Spell Canceled!"); + pVictim->m_currentSpells[CURRENT_CHANNELED_SPELL]->cancel(); + } + else if (pVictim->m_currentSpells[CURRENT_CHANNELED_SPELL]->getState() == SPELL_STATE_DELAYED) + { + // break channeled spell in delayed state on damage + sLog.outDetail("Spell Canceled!"); + pVictim->m_currentSpells[CURRENT_CHANNELED_SPELL]->cancel(); + } + } + } } if (GetTypeId() == TYPEID_PLAYER) DEBUG_LOG("AttackerStateUpdate: (Player) %u attacked %u (TypeId: %u) for %u dmg, absorbed %u, blocked %u, resisted %u.", - GetGUIDLow(), pVictim->GetGUIDLow(), pVictim->GetTypeId(), damage, absorbed_dmg, blocked_dmg, resisted_dmg); + GetGUIDLow(), pVictim->GetGUIDLow(), pVictim->GetTypeId(), damageInfo.damage, damageInfo.absorb, damageInfo.blocked_amount, damageInfo.resist); else DEBUG_LOG("AttackerStateUpdate: (NPC) %u attacked %u (TypeId: %u) for %u dmg, absorbed %u, blocked %u, resisted %u.", - GetGUIDLow(), pVictim->GetGUIDLow(), pVictim->GetTypeId(), damage, absorbed_dmg, blocked_dmg, resisted_dmg); + GetGUIDLow(), pVictim->GetGUIDLow(), pVictim->GetTypeId(), damageInfo.damage, damageInfo.absorb, damageInfo.blocked_amount, damageInfo.resist); } MeleeHitOutcome Unit::RollPhysicalOutcomeAgainst (Unit const *pVictim, WeaponAttackType attType, SpellEntry const *spellInfo) @@ -2471,7 +3207,7 @@ ((Creature*)victim)->AI().EnterEvadeMode(this);*/ } -uint32 Unit::SpellMissChanceCalc(Unit *pVictim) const +uint32 Unit::SpellMissChanceCalc(Unit *pVictim, SpellSchools school) const { if(!pVictim) return 0; @@ -2483,7 +3219,7 @@ if(leveldif < 0) leveldif = 0; - int32 misschance = 400 - m_modSpellHitChance*100; + int32 misschance = 400 - m_modSpellHitChance[school]*100; if(leveldif < 3) misschance += leveldif * 100; else @@ -2495,6 +3231,50 @@ return misschance < 100 ? 100 : misschance; } +uint32 Unit::SpellResistChanceCalc(Unit *pVictim, SpellSchools school) const +{ + + return 0; +} + +//static uint8 temp = 0; +uint8 Unit::SpellCastResult(Unit *pVictim, SpellEntry const *spell) const +{ +// return temp; + // Return evade for units in evade mode + if(pVictim->GetTypeId()==TYPEID_UNIT && ((Creature*)pVictim)->IsInEvadeMode()) + return SPELL_HIT_EVADE; + // All positive spells cant miss + // TODO: client not show miss log for this spells - so need find info for this in dbc and use it! + if (IsPositiveSpell(spell->Id)) return SPELL_HIT_NONE; + + // Immune check + // Possibly need use SPELL_HIT_IMMUNE for damage immune + // SPELL_HIT_IMMUNE2 for immune from school + if(pVictim->IsImmunedToSpellDamage(spell)) + return SPELL_HIT_IMMUNE; + + switch (spell->School) + { + // TODO remove hack for normal school + case SPELL_SCHOOL_NORMAL: + return SPELL_HIT_NONE; + + case SPELL_SCHOOL_HOLY: + case SPELL_SCHOOL_FIRE: + case SPELL_SCHOOL_NATURE: + case SPELL_SCHOOL_FROST: + case SPELL_SCHOOL_SHADOW: + case SPELL_SCHOOL_ARCANE: + { + uint32 missChance = SpellMissChanceCalc(pVictim, SpellSchools(spell->School)); + if (missChance>urand(0,10000)) return SPELL_HIT_RESIST; + } + } + return SPELL_HIT_NONE; +} + + int32 Unit::MeleeMissChanceCalc(const Unit *pVictim) const { if(!pVictim) @@ -3375,6 +4155,8 @@ AuraMap::iterator iter; while((iter = m_Auras.find(spellEffectPair(spellId, effindex))) != m_Auras.end()) RemoveAura(iter); + + } void Unit::RemoveSingleAuraFromStack(uint32 spellId, uint32 effindex) @@ -3679,7 +4461,7 @@ data.append(target->GetPackGUID()); data.append(GetPackGUID()); data << uint32(SpellID); - data << uint32(Damage-AbsorbedDamage-Resist-Blocked); + data << uint32(Damage); data << uint8(DamageType); //damagetype data << uint32(AbsorbedDamage); //AbsorbedDamage data << uint32(Resist); //resist @@ -3690,7 +4472,7 @@ data << uint32(0); SendMessageToSet( &data, true ); } - +/* void Unit::SendAttackStateUpdate(uint32 HitInfo, Unit *target, uint8 SwingType, SpellSchools DamageType, uint32 Damage, uint32 AbsorbDamage, uint32 Resist, VictimState TargetState, uint32 BlockedAmount) { sLog.outDebug("WORLD: Sending SMSG_ATTACKERSTATEUPDATE"); @@ -3722,52 +4504,38 @@ SendMessageToSet( &data, true ); } - -void Unit::ProcDamageAndSpell(Unit *pVictim, uint32 procAttacker, uint32 procVictim, uint32 damage, SpellEntry const *procSpell, bool isTriggeredSpell, WeaponAttackType attType) +*/ +void Unit::ProcDamageAndSpell(Unit *pVictim, uint32 procAttacker, uint32 procVictim, uint32 procExtra, uint32 damage, SpellEntry const *procSpell, bool isTriggeredSpell, WeaponAttackType attType) { sLog.outDebug("ProcDamageAndSpell: attacker flags are 0x%x, victim flags 0x%x", procAttacker, procVictim); if(procSpell) sLog.outDebug("ProcDamageAndSpell: invoked due to spell id %u %s", procSpell->Id, (isTriggeredSpell?"(triggered)":"")); - - // Assign melee/ranged proc flags for magic attacks, that are actually melee/ranged abilities - // not assign for spell proc triggered spell to prevent infinity (or unexpacted 2-3 times) melee damage spell proc call with melee damage effect - // That is the question though if it's fully correct - if(procSpell && !isTriggeredSpell) + if (procSpell) { - if(procSpell->DmgClass == SPELL_DAMAGE_CLASS_MELEE) + switch (procSpell->SpellFamilyName) { - if(procAttacker & PROC_FLAG_HIT_SPELL) procAttacker |= PROC_FLAG_HIT_MELEE; - if(procAttacker & PROC_FLAG_CRIT_SPELL) procAttacker |= PROC_FLAG_CRIT_MELEE; - if(procVictim & PROC_FLAG_STRUCK_SPELL) procVictim |= PROC_FLAG_STRUCK_MELEE; - if(procVictim & PROC_FLAG_STRUCK_CRIT_SPELL) procVictim |= PROC_FLAG_STRUCK_CRIT_MELEE; - attType = BASE_ATTACK; // Melee abilities are assumed to be dealt with mainhand weapon + case SPELLFAMILY_WARLOCK: // For Hellfire Effect and Rain of Fire triggers need do it + if (procSpell->SpellFamilyFlags & 0x0000000000000060LL) isTriggeredSpell = false; + break; + case SPELLFAMILY_PALADIN:// For Holy Shock triggers need do it + if (procSpell->SpellFamilyFlags & 0x0000000000200000LL) isTriggeredSpell = false; + break; } - else if (procSpell->DmgClass == SPELL_DAMAGE_CLASS_RANGED) - { - if(procAttacker & PROC_FLAG_HIT_SPELL) procAttacker |= PROC_FLAG_HIT_RANGED; - if(procAttacker & PROC_FLAG_CRIT_SPELL) procAttacker |= PROC_FLAG_CRIT_RANGED; - if(procVictim & PROC_FLAG_STRUCK_SPELL) procVictim |= PROC_FLAG_STRUCK_RANGED; - if(procVictim & PROC_FLAG_STRUCK_CRIT_SPELL) procVictim |= PROC_FLAG_STRUCK_CRIT_RANGED; - attType = RANGED_ATTACK; - } } - if(damage && (procVictim & (PROC_FLAG_STRUCK_MELEE|PROC_FLAG_STRUCK_RANGED|PROC_FLAG_STRUCK_SPELL))) - procVictim |= (PROC_FLAG_TAKE_DAMAGE|PROC_FLAG_TOUCH); - - // Not much to do if no flags are set. + // not assign for spell proc triggered spell to prevent infinity (or unexpacted 2-3 times) melee damage spell proc call with melee damage effect + // That is the question though if it's fully correct + if (isTriggeredSpell) + return; + // Not much to do if no flags are set. if (procAttacker) - { - ProcDamageAndSpellFor(false,pVictim,procAttacker,attackerProcAuraTypes,attType, procSpell, damage); - } - + ProcDamageAndSpellFor(false,pVictim,procAttacker, procExtra,attType, procSpell, damage); // Now go on with a victim's events'n'auras // Not much to do if no flags are set or there is no victim if(pVictim && pVictim->isAlive() && procVictim) - { - pVictim->ProcDamageAndSpellFor(true,this,procVictim,victimProcAuraTypes,attType,procSpell, damage); - } + pVictim->ProcDamageAndSpellFor(true,this,procVictim, procExtra,attType,procSpell, damage); } +/* void Unit::CastMeleeProcDamageAndSpell(Unit* pVictim, uint32 damage, WeaponAttackType attType, MeleeHitOutcome outcome, SpellEntry const *spellCasted, bool isTriggeredSpell) { if(!pVictim) @@ -3847,8 +4615,8 @@ if(procAttacker != PROC_FLAG_NONE || procVictim != PROC_FLAG_NONE) ProcDamageAndSpell(pVictim, procAttacker, procVictim, damage, spellCasted, isTriggeredSpell, attType); } - -void Unit::HandleDummyAuraProc(Unit *pVictim, SpellEntry const *dummySpell, uint32 effIndex, uint32 damage, Aura* triggredByAura, SpellEntry const * procSpell, uint32 procFlag) +*/ +void Unit::HandleDummyAuraProc(Unit *pVictim, SpellEntry const *dummySpell, uint32 effIndex, uint32 damage, Aura* triggredByAura, SpellEntry const * procSpell, uint32 procFlag, uint32 procEx) { switch(dummySpell->Id ) { @@ -3882,10 +4650,11 @@ // Combustion case 11129: { - CastSpell(this, 28682, true, NULL, triggredByAura); - if (!(procFlag & PROC_FLAG_CRIT_SPELL)) //no crit + if (triggredByAura->m_procCharges == triggredByAura->GetSpellProto()->procCharges) // For 1 cast + CastSpell(this, 28682, true, NULL, triggredByAura); + if (!(procEx & PROC_FLAG_EX_CRITICAL_HIT)) //no crit triggredByAura->m_procCharges += 1; //-> reincrease procCharge count since it was decreased before - else if (triggredByAura->m_procCharges == 0) //no more charges left and crit + else if (triggredByAura->m_procCharges == 1) // last charges left and crit RemoveAurasDueToSpell(28682); //-> remove Combustion auras return; } @@ -4020,6 +4789,41 @@ return; } + // Windfury weapon + case 33757: + case 33756: + case 33755: + case 33754: + case 33727: + { + // never proc from another windfury attack or the AP bonus will stack and we will get really huge criticals + if ( !procSpell || ( procSpell->Id != 25504 && procSpell->Id != 33750 ) ){ + // give us temporary AP bonus + + if(GetTypeId() == TYPEID_PLAYER && ((Player*)this)->HasSpellCooldown(25504) ) + return; + + HandleStatModifier(UNIT_MOD_ATTACK_POWER, TOTAL_VALUE, float(dummySpell->EffectBasePoints[0]+1), true); + + int32 EffectBasePoints0 = CalculateDamage(BASE_ATTACK); + + CastCustomSpell(pVictim,25504,&EffectBasePoints0,NULL,NULL,true,NULL,triggredByAura); + // cooldown + ((Player*)this)->AddSpellCooldown(25504,0,time(NULL) + 3); + + if (haveOffhandWeapon()) + { + int32 EffectBasePoints0 = CalculateDamage(OFF_ATTACK); + CastCustomSpell(pVictim,33750,&EffectBasePoints0,NULL,NULL,true,NULL,triggredByAura); + // cooldown + ((Player*)this)->AddSpellCooldown(33750,0,time(NULL) + 3); + } + + // remove it + HandleStatModifier(UNIT_MOD_ATTACK_POWER, TOTAL_VALUE, float(dummySpell->EffectBasePoints[0]+1), false); + } + } + default: break; } @@ -4033,6 +4837,30 @@ return; } break; + case SPELLFAMILY_PALADIN: + // Seal of Righteousness - melee proc dummy + if (dummySpell->SpellFamilyFlags&0x000000008000000LL && triggredByAura->GetEffIndex()==0) + { + if(GetTypeId() == TYPEID_PLAYER) + { + int32 damageBasePoints; + float speed = 2.0f; + Item *item = ((Player*)this)->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND); + if(item) speed = item->GetProto()->Delay/1000.0f; + if(item && item->GetProto()->InventoryType == INVTYPE_2HWEAPON) + damageBasePoints=1.20f*(triggredByAura->GetModifier()->m_amount * 1.2f * 1.03f * speed/100.0f) + 1;// two hand weapon + else + damageBasePoints=0.85f*(triggredByAura->GetModifier()->m_amount * 1.2f * 1.03f * speed/100.0f) - 1;// one hand weapon/no weapon + damageBasePoints +=0.03f * (m_weaponDamage[0][0]+m_weaponDamage[0][1])/2; + // TODO remove hack by using normal bonus calc + CalcDamageInfo damageInfo; + CalculateDamage(DAMAGE_SHIELD, pVictim, damageBasePoints, &damageInfo, BASE_ATTACK, dummySpell->Id); + DealDamage(&damageInfo, true); +// CastCustomSpell(pVictim,25742,&damag,NULL,NULL,true,NULL, triggredByAura); + } + return; + } + break; default: break; } @@ -4111,7 +4939,7 @@ } } } - +/* void Unit::HandleProcTriggerSpell(Unit *pVictim, uint32 damage, Aura* triggeredByAura, SpellEntry const *procSpell, uint32 procFlags) { SpellEntry const* auraSpellInfo = triggeredByAura->GetSpellProto(); @@ -4348,7 +5176,7 @@ ((Player*)this)->AddSpellCooldown(31616,0,time(NULL) + 5); ((Player*)this)->AddSpellCooldown(39301,0,time(NULL) + 5); } - }*/ + } return; } case 2127: @@ -4460,7 +5288,515 @@ else if(pVictim && pVictim->isAlive()) CastSpell(pVictim,trigger_spell_id,true,NULL,triggeredByAura); } +*/ +void Unit::HandleProcTriggerSpell(Unit *pVictim, uint32 damage, Aura* triggeredByAura, SpellEntry const *procSpell, uint32 procFlags, uint32 procEx) +{ + // Get triggered aura spell info + SpellEntry const* auraSpellInfo = triggeredByAura->GetSpellProto(); + // Get trigger spell id + uint32 trigger_spell_id = auraSpellInfo->EffectTriggerSpell[triggeredByAura->GetEffIndex()]; + + // Try handle uncnown trigger spells + if (sSpellStore.LookupEntry(trigger_spell_id)==NULL) + switch (auraSpellInfo->SpellFamilyName) + { + + //===================================================================== + // Generic class + // ==================================================================== + // ..... + // ..... + //===================================================================== + case SPELLFAMILY_GENERIC: +// if (auraSpellInfo->Id==34082) // Advantaged State (DND) +// trigger_spell_id = ???; + if (auraSpellInfo->Id==23780) // Aegis of Preservation + trigger_spell_id = 23781; +// else if (auraSpellInfo->Id==43504) // Alterac Valley OnKill Proc Aura +// trigger_spell_id = ; +// else if (auraSpellInfo->Id==37030) // Chaotic Temperament () +// trigger_spell_id = ; +// else if (auraSpellInfo->Id==43820) // Charm of the Witch Doctor +// trigger_spell_id = ; +// else if (auraSpellInfo->Id==41248) // Consuming Strikes +// trigger_spell_id = 41249; +// else if (auraSpellInfo->Id==41054) // Copy Weapon +// trigger_spell_id = 41055; +// else if (auraSpellInfo->Id==31255) // Deadly Swiftness (Rank 1) +// trigger_spell_id = ; +// else if (auraSpellInfo->Id==5301) // Defensive State (DND) +// trigger_spell_id = ; +// else if (auraSpellInfo->Id==13358) // Defensive State (DND) +// trigger_spell_id = ; +// else if (auraSpellInfo->Id==16092) // Defensive State (DND) +// trigger_spell_id = ; +// else if (auraSpellInfo->Id==24949) // Defensive State 2 (DND) +// trigger_spell_id = ; +// else if (auraSpellInfo->Id==40329) // Demo Shout Sensor +// trigger_spell_id = ; +// else if (auraSpellInfo->Id==33896) // Desperate Defense +// trigger_spell_id = 33898; // ?? possibly this +// else if (auraSpellInfo->Id==18943) // Double Attack +// trigger_spell_id = ; +// else if (auraSpellInfo->Id==19194) // Double Attack +// trigger_spell_id = ; +// else if (auraSpellInfo->Id==19817) // Double Attack +// trigger_spell_id = ; +// else if (auraSpellInfo->Id==19818) // Double Attack +// trigger_spell_id = ; +// else if (auraSpellInfo->Id==22835) // Drunken Rage +// trigger_spell_id = 14822; + /* + else if (auraSpellInfo->SpellIconID==191) // Elemental Response + { + switch (triggeredByAura->GetSpellProto()->Id && triggeredByAura->GetSpellProto()->AttributesEx==0) + { + case 34191: + case 34329: + case 34524: + case 34582: + case 36733:break; + default: + sLog.outError("Unit::HandleProcTriggerSpell: Spell %u miss posibly Elemental Response",triggeredByAura->GetSpellProto()->Id); + return; + } + //This generic aura self-triggers a different spell for each school of magic that lands on the wearer: + switch (procSpell->School) + { + case SPELL_SCHOOL_FIRE: trigger_spell_id = 34192;break;//Fire: 34192 + case SPELL_SCHOOL_FROST: trigger_spell_id = 34193;break;//Frost: 34193 + case SPELL_SCHOOL_ARCANE: trigger_spell_id = 34194;break;//Arcane: 34194 + case SPELL_SCHOOL_NATURE: trigger_spell_id = 34195;break;//Nature: 34195 + case SPELL_SCHOOL_SHADOW: trigger_spell_id = 34196;break;//Shadow: 34196 + case SPELL_SCHOOL_HOLY: trigger_spell_id = 34197;break;//Holy: 34197 + case SPELL_SCHOOL_NORMAL: trigger_spell_id = 34198;break;//Physical: 34198 + default: + sLog.outError("Unit::HandleProcTriggerSpell: Spell %u Elemental Response wrong school",triggeredByAura->GetSpellProto()->Id); + return; + } + }*/ +// else if (auraSpellInfo->Id==6542) // Enraged Defense +// trigger_spell_id = ; +// else if (auraSpellInfo->Id==40364) // Entangling Roots Sensor +// trigger_spell_id = ; +// else if (auraSpellInfo->Id==33207) // Gossip NPC Periodic - Fidget +// trigger_spell_id = ; +// else if (auraSpellInfo->Id==35321) // Gushing Wound +// trigger_spell_id = ; +// else if (auraSpellInfo->Id==38363) // Gushing Wound +// trigger_spell_id = ; +// else if (auraSpellInfo->Id==39215) // Gushing Wound +// trigger_spell_id = ; +// else if (auraSpellInfo->Id==40250) // Improved Duration +// trigger_spell_id = ; + else if (auraSpellInfo->Id==27522)// Mana Drain Trigger + { + // On successful melee or ranged attack gain $29471s1 mana and if possible drain $27526s1 mana from the target. + if (this && this->isAlive()) + CastSpell(this,29471,true,NULL,triggeredByAura); + if (pVictim && pVictim->isAlive()) + CastSpell(pVictim,27526,true,NULL,triggeredByAura); + return; + } +// else if (auraSpellInfo->Id==24905)// Moonkin Form (Passive) +// trigger_spell_id = ; +// else if (auraSpellInfo->Id==43453) // Rune Ward +// trigger_spell_id = ; +// else if (auraSpellInfo->Id==7137) // Shadow Charge (Rank 1) +// trigger_spell_id = ; +// else if (auraSpellInfo->Id==36576) // Shaleskin +// trigger_spell_id = ; +// else if (auraSpellInfo->Id==34783) // Spell Reflection +// trigger_spell_id = ; +// else if (auraSpellInfo->Id==36096) // Spell Reflection +// trigger_spell_id = ; +// else if (auraSpellInfo->Id==36207) // Steal Weapon +// trigger_spell_id = ; +// else if (auraSpellInfo->Id==35205) // Vanish + break; + + //===================================================================== + // Mage + // ==================================================================== + // Blazing Speed (Rank 1,2) trigger = 18350 + //===================================================================== + case SPELLFAMILY_MAGE: + if (auraSpellInfo->SpellIconID == 2127) // Blazing Speed + { + switch (triggeredByAura->GetSpellProto()->Id) + { + case 31641:// Blazing Speed (Rank 1) + case 31642:// Blazing Speed (Rank 2) + trigger_spell_id = 31643; //CastSpell(this,31643,true,NULL,triggeredByAura); + break; + default: + sLog.outError("Unit::HandleProcTriggerSpell: Spell %u miss posibly Blazing Speed",auraSpellInfo->Id); + return; + } + } + break; + + //===================================================================== + // Warrior + // ==================================================================== + // 29801 Rampage (Rank 1-3) trigger = 18350 + //===================================================================== + case SPELLFAMILY_WARRIOR: + if (auraSpellInfo->SpellIconID == 2006 && auraSpellInfo->SpellFamilyFlags==0x100000) //Rampage + { + switch(auraSpellInfo->Id) + { + case 29801: trigger_spell_id = 30029; break; // Rank 1 + case 30030: trigger_spell_id = 30031; break; // Rank 2 + case 30033: trigger_spell_id = 30032; break; // Rank 3 + default: + sLog.outError("Unit::HandleProcTriggerSpell: Spell %u not handled in Rampage",auraSpellInfo->Id); + return; + } + } + break; + //===================================================================== + // Warlock + // ==================================================================== + // Pet Mana Regen trigger = 18350 + // Pet Resistance trigger = 18350 + // Pyroclasm trigger = 18350 (possible 1 effect need to add not standart proc in add aura) + // Drain Soul (Rank 1-5) trigger = 0 ?? (chance = 0? but = 100 then Improved Drain Soul enabled) + //===================================================================== + case SPELLFAMILY_WARLOCK: +// if (triggeredByAura->GetSpellProto()->SpellIconID == 37375) // Pet Mana Regen +// trigger_spell_id = ????;break; +// if (triggeredByAura->GetSpellProto()->SpellIconID == 37386) // Pet Resistance +// trigger_spell_id = ????; + if (auraSpellInfo->SpellIconID == 1137) // Pyroclasm + { + if(!pVictim || !pVictim->isAlive() || pVictim == this || procSpell == NULL) + return; + // Calculate spell tick count for spells + uint32 tick = 1; // Default tick = 1 + if (procSpell->SpellFamilyFlags&0x0000000000000040LL) tick = 15; // Hellfire have 15 tick + if (procSpell->SpellFamilyFlags&0x0000000000000020LL) tick = 4; // Rain of Fire have 4 tick + // Calculate chance = baseChance/tick + float chance = 0; + switch (auraSpellInfo->Id) + { + case 18096:chance = 13.0f/tick;break; + case 18073:chance = 26.0f/tick;break; + } + // Roll chance + if (roll_chance_f(chance)) + CastSpell(pVictim, 18093, true, NULL, triggeredByAura); + return; + } + else if (auraSpellInfo->SpellFamilyFlags&0x0000000000004000LL) // Drain Soul + { + Unit::AuraList const& mAddFlatModifier = GetAurasByType(SPELL_AURA_ADD_FLAT_MODIFIER); + for(Unit::AuraList::const_iterator i = mAddFlatModifier.begin(); i != mAddFlatModifier.end(); ++i) + { + if ((*i)->GetModifier()->m_miscvalue == SPELLMOD_CHANCE_OF_SUCCESS && (*i)->GetSpellProto()->SpellIconID == 113) + { + int32 impDrainSoulBasePoints0 = (*i)->GetSpellProto()->EffectBasePoints[2] * GetMaxPower(POWER_MANA) / 100; + CastCustomSpell(this, 18371, &impDrainSoulBasePoints0, NULL, NULL, true, NULL, triggeredByAura); + } + } + return; + } + break; + + //===================================================================== + // Priest + // ==================================================================== + // Greater Heal Refund trigger = 18350 + // Improved Shadowfiend trigger = 18350 + // Blessed Recovery (Rank 1-3) trigger = 18350 + // Shadowguard (1-7) trigger = 28376 + //===================================================================== + case SPELLFAMILY_PRIEST: + if (auraSpellInfo->Id==37594) //Greater Heal Refund + trigger_spell_id = 37595; +// else if (triggeredByAura->GetSpellProto()->Id==37570)//Improved Shadowfiend +// trigger_spell_id = ???; + else if(auraSpellInfo->SpellFamilyFlags==0x100080000000LL && auraSpellInfo->SpellVisual==7958) // Shadowguard + { + switch(triggeredByAura->GetSpellProto()->Id) + { + case 18137: trigger_spell_id = 28377; break; // Rank 1 + case 19308: trigger_spell_id = 28378; break; // Rank 2 + case 19309: trigger_spell_id = 28379; break; // Rank 3 + case 19310: trigger_spell_id = 28380; break; // Rank 4 + case 19311: trigger_spell_id = 28381; break; // Rank 5 + case 19312: trigger_spell_id = 28382; break; // Rank 6 + case 25477: trigger_spell_id = 28385; break; // Rank 7 + default: + sLog.outError("Unit::HandleProcTriggerSpell: Spell %u not handled in SG",triggeredByAura->GetSpellProto()->Id); + return; + } + } + else if (auraSpellInfo->SpellIconID == 1875) //Blessed Recovery + { + uint32 EffectId = 0; + switch (triggeredByAura->GetSpellProto()->Id) + { + case 27811: EffectId = 27813; break; + case 27815: EffectId = 27817; break; + case 27816: EffectId = 27818; break; + default: + sLog.outError("Unit::HandleProcTriggerSpell: Spell %u not handled in BR",triggeredByAura->GetSpellProto()->Id); + return; + } + int32 heal_amount = damage * triggeredByAura->GetModifier()->m_amount / 100; + int32 BRHealBasePoints0 = heal_amount/3-1; + CastCustomSpell(this, EffectId, &BRHealBasePoints0, NULL, NULL, true, NULL, triggeredByAura); + return; + } + break; + + //===================================================================== + // Druid + // ==================================================================== + // Druid Forms Trinket trigger = 18350 + // Entangling Roots trigger = 30023 + // Leader of the Pack trigger = 18350 + //===================================================================== + case SPELLFAMILY_DRUID: + if (auraSpellInfo->Id==37336)// Druid Forms Trinket + { + switch(m_form) + { + case 0: trigger_spell_id = 37344;break; + case FORM_CAT: trigger_spell_id = 37341;break; + case FORM_BEAR: + case FORM_DIREBEAR: trigger_spell_id = 37340;break; + case FORM_TREE: trigger_spell_id = 37342;break; + case FORM_MOONKIN: trigger_spell_id = 37343;break; + default: + return; + } + } +// else if (auraSpellInfo->Id==40363)// Entangling Roots () +// trigger_spell_id = ????; + else if (triggeredByAura->GetSpellProto()->Id==24932)// Leader of the Pack () + trigger_spell_id = 17007; + break; + + //===================================================================== + // Hunter + // ==================================================================== + // Increased Pet Attack Power trigger = 18350 + // Pet Healing trigger = 18350 + //===================================================================== + case SPELLFAMILY_HUNTER: +// if (auraSpellInfo->Id==38297) // Increased Pet Attack Power +// trigger_spell_id = ????;break; + if (auraSpellInfo->Id==37496) // Pet Healing + trigger_spell_id = 37501; + break; + + //===================================================================== + // Paladin + // ==================================================================== + // Blessed Life trigger = 31934 + // Healing Discount trigger = 18350 + // Illumination (Rank 1-5) trigger = 18350 (for HolyShock possibly use SPELL_AURA_OVERRIDE_CLASS_SCRIPTS) + // Judgement of Light (Rank 1-5) trigger = 5373 + // Judgement of Wisdom (Rank 1-4) trigger = 1826 + // Lightning Capacitor trigger = 18350 + //===================================================================== + case SPELLFAMILY_PALADIN: +// if (auraSpellInfo->SpellIconID == 2137) // Blessed Life +// { +// switch (triggeredByAura->GetSpellProto()->Id) +// { +// case 31828:// Blessed Life (Rank 1) +// case 31829:// Blessed Life (Rank 2) +// case 31830:// Blessed Life (Rank 3) +// need change damage +// break; +// default: +// sLog.outError("Unit::HandleProcTriggerSpell: Spell %u miss posibly Blessed Life",triggeredByAura->GetSpellProto()->Id); +// return; +// } +// } + if (auraSpellInfo->Id==37705) // Healing Discount + { + CastSpell(this,37706,true,NULL,triggeredByAura); + return; + } +/* else if ((auraSpellInfo->SpellFamilyFlags&0x0000000000080000) && + auraSpellInfo->SpellIconID==299)// Judgement of Light + { + switch (triggeredByAura->GetSpellProto()->Id) + { + case 20185:trigger_spell_id = 20267;break;// Judgement of Light (Rank 1) + case 20344:trigger_spell_id = 20341;break;// Judgement of Light (Rank 2) + case 20345:trigger_spell_id = 20342;break;// Judgement of Light (Rank 3) + case 20346:trigger_spell_id = 20343;break;// Judgement of Light (Rank 4) + case 27162:trigger_spell_id = 27163;break;// Judgement of Light (Rank 5) + default: + sLog.outError("Unit::HandleProcTriggerSpell: Spell %u miss posibly Judgement of Light",triggeredByAura->GetSpellProto()->Id); + return; + } + pVictim->CastSpell(pVictim,trigger_spell_id,true,NULL,triggeredByAura); + return; + } + else if ((auraSpellInfo->SpellFamilyFlags&0x0000000000080000) && + auraSpellInfo->SpellIconID==206) // Judgement of Wisdom + { + switch (triggeredByAura->GetSpellProto()->Id) + { + case 20186:trigger_spell_id = 20268;break;// Judgement of Wisdom (Rank 1) + case 20354:trigger_spell_id = 20352;break;// Judgement of Wisdom (Rank 2) + case 20355:trigger_spell_id = 20353;break;// Judgement of Wisdom (Rank 3) + case 27164:trigger_spell_id = 27165;break;// Judgement of Wisdom (Rank 4) + default: + sLog.outError("Unit::HandleProcTriggerSpell: Spell %u miss posibly Judgement of Wisdom",triggeredByAura->GetSpellProto()->Id); + return; + } + pVictim->CastSpell(pVictim,trigger_spell_id,true,NULL,triggeredByAura); + return; + }*/ + if (auraSpellInfo->SpellIconID==241 && trigger_spell_id==18350) //Illumination + { + if(!procSpell) + return; + + SpellEntry const *originalSpell = procSpell; + // in case HShock procspell is triggered spell but we need mana cost of original casted spell + if(procSpell->SpellFamilyName == SPELLFAMILY_PALADIN && procSpell->SpellFamilyFlags == 0x00200000) + { + uint32 originalSpellId = 0; + switch(procSpell->Id) + { + case 25914: originalSpellId = 20473; break; + case 25913: originalSpellId = 20929; break; + case 25903: originalSpellId = 20930; break; + case 27175: originalSpellId = 27174; break; + case 33074: originalSpellId = 33072; break; + default: + sLog.outError("Unit::HandleProcTriggerSpell: Spell %u not handled in HShock",procSpell->Id); + return; + } + SpellEntry const *HSSpell= sSpellStore.LookupEntry(originalSpellId); + if(!HSSpell) + { + sLog.outError("Unit::HandleProcTriggerSpell: Spell %u unknown but used in HShock",originalSpellId); + return; + } + originalSpell = HSSpell; + } + // percent stored in effect 1 (class scripts) base points + int32 percent = auraSpellInfo->EffectBasePoints[1]+1; + // BasePoints = val -1 not required (EffectBaseDice==0) + int32 ILManaSpellBasePoints0 = originalSpell->manaCost*percent/100; + CastCustomSpell(this, 20272, &ILManaSpellBasePoints0, NULL, NULL, true, NULL, triggeredByAura); + return; + } + else if (auraSpellInfo->Id==37657) // Lightning Capacitor + { + if(!pVictim || !pVictim->isAlive()) + return; + // stacking + CastSpell(this, 37658, true, NULL, triggeredByAura); + // counting + uint32 count = 0; + AuraList const& dummyAura = GetAurasByType(SPELL_AURA_DUMMY); + for(AuraList::const_iterator itr = dummyAura.begin(); itr != dummyAura.end(); ++itr) + if((*itr)->GetId()==37658) + ++count; + // release at 3 aura in stack + if(count >2) + { + RemoveAurasDueToSpell(37658); + CastSpell(pVictim, 37661, true, NULL, triggeredByAura); + } + return; + } + break; + //===================================================================== + // Shaman + //==================================================================== + // Lightning Shield trigger = 18350 + // Mana Surge trigger = 18350 + // Nature's Guardian (Rank 1-5) trigger = 18350 + //===================================================================== + case SPELLFAMILY_SHAMAN: + //Lightning Shield (overwrite non existing triggered spell call in spell.dbc + if(auraSpellInfo->SpellFamilyFlags==0x00000400 && auraSpellInfo->SpellVisual==37) + { + switch(triggeredByAura->GetSpellProto()->Id) + { + case 324: trigger_spell_id = 26364; break; // Rank 1 + case 325: trigger_spell_id = 26365; break; // Rank 2 + case 905: trigger_spell_id = 26366; break; // Rank 3 + case 945: trigger_spell_id = 26367; break; // Rank 4 + case 8134: trigger_spell_id = 26369; break; // Rank 5 + case 10431: trigger_spell_id = 26370; break; // Rank 6 + case 10432: trigger_spell_id = 26363; break; // Rank 7 + case 25469: trigger_spell_id = 26371; break; // Rank 8 + case 25472: trigger_spell_id = 26372; break; // Rank 9 + default: + sLog.outError("Unit::HandleProcTriggerSpell: Spell %u not handled in LShield",triggeredByAura->GetSpellProto()->Id); + return; + } + } + else if (auraSpellInfo->Id == 23572) // Mana Surge (Shaman T1 bonus) + { + if(!procSpell) + return; + int32 manaSurgeSpellBasePoints0 = procSpell->manaCost * 35/100; + CastCustomSpell(this, 23571, &manaSurgeSpellBasePoints0, NULL, NULL, true, NULL, triggeredByAura); + return; + } + else if (auraSpellInfo->SpellIconID == 2013) //Nature's Guardian + { + int32 heal = triggeredByAura->GetModifier()->m_amount * GetMaxHealth() / 100 - 1; + CastCustomSpell(this, 31616, &heal, 0, 0, true, NULL, triggeredByAura); + // if (pVictim && pVictim->isAlive()) + // CastSpell(pVictim, 39301, true, NULL, triggredByAura); + return; + } + break; + + // default + default: + break; + } + + // All ok. Check current trigger spell + if (sSpellStore.LookupEntry(trigger_spell_id)==NULL) + { + // Not cast unknown spell + // sLog.outError("Unit::HandleProcTriggerSpell: Spell %u have 0 in EffectTriggered[%d], not handled custom case?",auraSpellInfo->Id,triggeredByAura->GetEffIndex()); + return; + } + + // Costum basepoints/target for exist spell + // dummy basepoints or other customs + switch(trigger_spell_id) + { + case 30824: // Shamanistic Rage triggered spell + { + int32 SRBasePoints0 = int32(GetTotalAttackPowerValue(BASE_ATTACK)*triggeredByAura->GetModifier()->m_amount/100) -1; + CastCustomSpell(this, 30824, &SRBasePoints0, NULL, NULL, true, NULL, triggeredByAura); + return; + } + case 20233: // Improved Lay on Hands (cast on target) + { + if(pVictim && pVictim->isAlive()) + CastSpell(pVictim,trigger_spell_id,true,NULL,triggeredByAura); + return; + } + } + + // TODO need add more effective target/caster select + // default case + if(IsPositiveSpell(trigger_spell_id) && !(procFlags & PROC_FLAG_SUCCESSFUL_HEAL)) + CastSpell(this,trigger_spell_id,true,NULL,triggeredByAura); + else if(pVictim && pVictim->isAlive()) + CastSpell(pVictim,trigger_spell_id,true,NULL,triggeredByAura); +} + void Unit::setPowerType(Powers new_powertype) { uint32 tem_bytes_0 = GetUInt32Value(UNIT_FIELD_BYTES_0); @@ -5013,9 +6349,6 @@ if(!spellProto || !pVictim || damagetype==DIRECT_DAMAGE ) return pdamage; - if(pVictim->IsImmunedToSpellDamage(spellProto)) - return 0; - uint32 creatureTypeMask = GetCreatureTypeMask(); // Damage Done @@ -5395,6 +6728,23 @@ return false; } +bool Unit::IsImmunedToSchoolDamage(SpellSchools School) const +{ + //If m_immuneToDamage type contain magic, IMMUNE damage. + SpellImmuneList const& damageList = m_spellImmune[IMMUNITY_DAMAGE]; + for (SpellImmuneList::const_iterator itr = damageList.begin(); itr != damageList.end(); ++itr) + if(itr->type & uint32(1<type & uint32(1< ProcTriggeredList; - -void Unit::ProcDamageAndSpellFor( bool isVictim, Unit * pTarget, uint32 procFlag, AuraTypeSet const& procAuraTypes, WeaponAttackType attType, SpellEntry const * procSpell, uint32 damage ) +typedef std::list< uint32> RemoveSpellList; +// Trigger auras is SPELL_AURA_DUMMY, SPELL_AURA_PROC_TRIGGER_SPELL, SPELL_AURA_PROC_TRIGGER_DAMAGE +// and procFlags!=0 +static inline bool isTriggerAura(uint8 aur) { - for(AuraTypeSet::const_iterator aur = procAuraTypes.begin(); aur != procAuraTypes.end(); ++aur) + if (aur == SPELL_AURA_DUMMY || + aur == SPELL_AURA_PROC_TRIGGER_SPELL || + aur == SPELL_AURA_PROC_TRIGGER_DAMAGE || + aur == SPELL_AURA_MOD_STUN|| + aur == SPELL_AURA_RANGED_ATTACK_POWER_ATTACKER_BONUS) + return true; + return false; +} +void Unit::ProcDamageAndSpellFor( bool isVictim, Unit * pTarget, uint32 procFlag, uint32 procExtra, WeaponAttackType attType, SpellEntry const * procSpell, uint32 damage ) +{ + for (uint8 aur = 1; aurGetSpellProto(); if(!spellProto) continue; - SpellProcEventEntry const *spellProcEvent = objmgr.GetSpellProcEvent(spellProto->Id); - if(!spellProcEvent) - { - // used to prevent spam in log about same non-handled spells - static std::set nonHandledSpellProcSet; - - if(spellProto->procFlags != 0 && nonHandledSpellProcSet.find(spellProto->Id)==nonHandledSpellProcSet.end()) - { - sLog.outError("ProcDamageAndSpell: spell %u (%s aura source) not have record in `spell_proc_event`)",spellProto->Id,(isVictim?"a victim's":"an attacker's")); - nonHandledSpellProcSet.insert(spellProto->Id); - } - - // spell.dbc use totally different flags, that only can create problems if used. + if (spellProto->procFlags==0 && spellProcEvent && spellProcEvent->procFlags==0) continue; - } - + // If not trigger and spellProcEvent==NULL - skip + if (!isTriggerAura(aur) && spellProcEvent==NULL) + continue; // Check spellProcEvent data requirements - if(!ObjectMgr::IsSpellProcEventCanTriggeredBy(spellProcEvent, procSpell,procFlag)) + if(!ObjectMgr::IsSpellProcEventCanTriggeredBy(spellProcEvent, spellProto->procFlags, procSpell,procFlag,procExtra)) continue; // Check if current equipment allows aura to proc @@ -7232,27 +8583,26 @@ continue; } } - + // Get chance from spell float chance = (float)spellProto->procChance; - - if(Player* modOwner = GetSpellModOwner()) - modOwner->ApplySpellMod(spellProto->Id,SPELLMOD_CHANCE_OF_SUCCESS,chance); - - if(!isVictim && spellProcEvent->ppmRate != 0) + // If in spellProcEvent exist custom chance, chance = spellProcEvent->customChance; + if(spellProcEvent && spellProcEvent->customChance) + chance = spellProcEvent->customChance; + // If PPM exist calculate chance from PPM + if(!isVictim && spellProcEvent && spellProcEvent->ppmRate != 0) { uint32 WeaponSpeed = GetAttackTime(attType); chance = GetPPMProcChance(WeaponSpeed, spellProcEvent->ppmRate); } - + // Apply chance modifer aura + if(Player* modOwner = GetSpellModOwner()) + modOwner->ApplySpellMod(spellProto->Id,SPELLMOD_CHANCE_OF_SUCCESS,chance); + if(roll_chance_f(chance)) { - if((*i)->m_procCharges > 0) - (*i)->m_procCharges -= 1; - uint32 i_spell_eff = (*i)->GetEffIndex(); - int32 i_spell_param; - switch(*aur) + switch(aur) { case SPELL_AURA_PROC_TRIGGER_SPELL: i_spell_param = procFlag; break; case SPELL_AURA_DUMMY: i_spell_param = i_spell_eff; break; @@ -7284,57 +8634,104 @@ if(!found) { - sLog.outError("Spell aura %u (id:%u effect:%u) has been deleted before call spell proc event handler",*aur,i->triggeredByAura_SpellPair.first,i->triggeredByAura_SpellPair.second); + sLog.outError("Spell aura %u (id:%u effect:%u) has been deleted before call spell proc event handler",aur,i->triggeredByAura_SpellPair.first,i->triggeredByAura_SpellPair.second); sLog.outError("It can be deleted one from early proccesed auras:"); for(ProcTriggeredList::iterator i2 = procTriggered.begin(); i != i2; ++i2) - sLog.outError(" Spell aura %u (id:%u effect:%u)",*aur,i2->triggeredByAura_SpellPair.first,i2->triggeredByAura_SpellPair.second); + sLog.outError(" Spell aura %u (id:%u effect:%u)",aur,i2->triggeredByAura_SpellPair.first,i2->triggeredByAura_SpellPair.second); sLog.outError(" "); continue; } } + SpellProcEventEntry const *spellProcEvent = objmgr.GetSpellProcEvent(i->spellInfo->Id); + if (GetTypeId() == TYPEID_PLAYER && spellProcEvent && spellProcEvent->Cooldown && ((Player *)this)->HasSpellCooldown(i->spellInfo->Id)) + continue; - switch(*aur) + switch(aur) { case SPELL_AURA_PROC_TRIGGER_SPELL: { + // TODO: adaptate more for costum requirements (not listed in procEx) and fix + // Warning! damage dealing after call ProcDamageAndSpellFor + switch (i->spellInfo->Id) + { + case 28845:// Cheat Death - When your health drops below 20% .... + if (!(GetHealth()-damageGetMaxHealth()/5)) + continue; + break; + case 31255:// Deadly Swiftness (Rank 1) - ... whenever you deal damage to a target who is below 20% health. + if (pTarget->GetHealth()>pTarget->GetMaxHealth()/5) + continue; + break; + case 40971:// Bonus Healing - If your target is below $s1% health + if (pTarget->GetHealth()>pTarget->GetMaxHealth()*i->triggeredByAura->GetModifier()->m_amount/100) + continue; + break; + case 37594:// Greater Heal Refund - If your Greater Heal brings the target to full health, you gain $37595s1 mana. + if (pTarget->GetHealth()+damageGetMaxHealth()) + continue; + break; + case 30881:// Nature's Guardian (Rank 1-5) Whenever a damaging attack is taken that reduces you below 30% health... + case 30883: + case 30884: + case 30885: + case 30886: + if (!(GetHealth()-damageGetMaxHealth()*0.3)) + continue; + break; + } + sLog.outDebug("ProcDamageAndSpell: casting spell %u (triggered by %s aura of spell %u)", i->spellInfo->Id,(isVictim?"a victim's":"an attacker's"),i->triggeredByAura->GetId()); - HandleProcTriggerSpell(pTarget, damage, i->triggeredByAura, procSpell,i->spellParam); + HandleProcTriggerSpell(pTarget, damage, i->triggeredByAura, procSpell, i->spellParam, procExtra); break; } case SPELL_AURA_PROC_TRIGGER_DAMAGE: { sLog.outDebug("ProcDamageAndSpell: doing %u damage from spell id %u (triggered by %s aura of spell %u)", i->spellParam, i->spellInfo->Id,(isVictim?"a victim's":"an attacker's"),i->triggeredByAura->GetId()); uint32 damage = i->spellParam; - // TODO: remove hack for Seal of Righteousness. That should not be there - if(!isVictim && i->spellInfo->SpellVisual == 7986) - damage = (damage * GetAttackTime(BASE_ATTACK))/60/1000; SpellNonMeleeDamageLog(pTarget, i->spellInfo->Id, damage, true, true); break; } case SPELL_AURA_DUMMY: { sLog.outDebug("ProcDamageAndSpell: casting spell id %u (triggered by %s dummy aura of spell %u)", i->spellInfo->Id,(isVictim?"a victim's":"an attacker's"),i->triggeredByAura->GetId()); - HandleDummyAuraProc(pTarget, i->spellInfo, i->spellParam, damage, i->triggeredByAura, procSpell, procFlag); + HandleDummyAuraProc(pTarget, i->spellInfo, i->spellParam, damage, i->triggeredByAura, procSpell, procFlag, procExtra); break; } + case SPELL_AURA_MOD_STUN: + removedSpells.push_back(i->triggeredByAura->GetId()); + break; + break; + case SPELL_AURA_RANGED_ATTACK_POWER_ATTACKER_BONUS: + // Hunter's Mark (1-4 Rangs) + // For - increasing the ranged attack power of all attackers against by an additional ${$m2/10} each time they are struck by a ranged attack, up to a maximum of ${$m2*4} + if (i->spellInfo->SpellFamilyName == SPELLFAMILY_HUNTER && (i->spellInfo->SpellFamilyFlags&0x0000000000000400LL)) + { + uint32 basevalue = i->triggeredByAura->GetBasePoints()+1; + i->triggeredByAura->GetModifier()->m_amount+=basevalue/10; + if (i->triggeredByAura->GetModifier()->m_amount>basevalue*4) + i->triggeredByAura->GetModifier()->m_amount = basevalue*4; + } default: { // nothing do, just charges counter break; } } - } + if (GetTypeId() == TYPEID_PLAYER && spellProcEvent && spellProcEvent->Cooldown) + ((Player *)this)->AddSpellCooldown(i->spellInfo->Id, 0, time(NULL)+spellProcEvent->Cooldown); - // Safely remove auras with zero charges - for(AuraList::const_iterator i = auras.begin(), next; i != auras.end(); i = next) - { - next = i; ++next; - if((*i)->m_procCharges == 0) + // Remove charge + if(i->triggeredByAura && i->triggeredByAura->m_procCharges > 0) { - RemoveAurasDueToSpell((*i)->GetId()); - next = auras.begin(); + i->triggeredByAura->m_procCharges -=1; + if (i->triggeredByAura->m_procCharges==0) + removedSpells.push_back(i->triggeredByAura->GetId()); } } + + // Remove auras from removedAuras + for(RemoveSpellList::const_iterator i = removedSpells.begin(); i != removedSpells.end();i++) + RemoveAurasDueToSpell(*i); } } Index: src/game/Unit.h =================================================================== --- src/game/Unit.h (revision 4842) +++ src/game/Unit.h (working copy) @@ -56,6 +56,15 @@ // Horde Racial Passives #define SPELL_HORDE_PASSIVE_NATURE_RESISTANCE 20551*/ +enum SpellInturruptFlags +{ + INTURRUPTFLAG_FLAG_MOVEMENT = 0x01, + INTURRUPTFLAG_FLAG_DAMAGE = 0x02, + INTURRUPTFLAG_FLAG_INTURRUPT = 0x04, + INTURRUPTFLAG_FLAG_AUTOATTACK = 0x08, + INTURRUPTFLAG_FLAG_TURNING = 0x10 +}; + enum SpellChannelInterruptFlags { CHANNEL_FLAG_DAMAGE = 0x0002, @@ -355,7 +364,8 @@ DOT = 2, HEAL = 3, NODAMAGE = 4, // used also in case when damage applied to health but not applied to spell channelInterruptFlags/etc - SELF_DAMAGE = 5 + SELF_DAMAGE = 5, + DAMAGE_SHIELD = 6 }; enum UnitVisibility @@ -405,41 +415,59 @@ enum ProcFlags { - PROC_FLAG_NONE = 0x00000000, // None - PROC_FLAG_HIT_MELEE = 0x00000001, // On melee hit - PROC_FLAG_STRUCK_MELEE = 0x00000002, // On being struck melee - PROC_FLAG_KILL_XP_GIVER = 0x00000004, // On kill target giving XP or honor - PROC_FLAG_SPECIAL_DROP = 0x00000008, // - PROC_FLAG_DODGE = 0x00000010, // On dodge melee attack - PROC_FLAG_PARRY = 0x00000020, // On parry melee attack - PROC_FLAG_BLOCK = 0x00000040, // On block attack - PROC_FLAG_TOUCH = 0x00000080, // On being touched (for bombs, probably?) - PROC_FLAG_TARGET_LOW_HEALTH = 0x00000100, // On deal damage to enemy with 20% or less health - PROC_FLAG_LOW_HEALTH = 0x00000200, // On health dropped below 20% - PROC_FLAG_STRUCK_RANGED = 0x00000400, // On being struck ranged - PROC_FLAG_HIT_SPECIAL = 0x00000800, // (!)Removed, may be reassigned in future - PROC_FLAG_CRIT_MELEE = 0x00001000, // On crit melee - PROC_FLAG_STRUCK_CRIT_MELEE = 0x00002000, // On being critically struck in melee - PROC_FLAG_CAST_SPELL = 0x00004000, // On cast spell (and broken Aspect of Hawk) - PROC_FLAG_TAKE_DAMAGE = 0x00008000, // On take damage - PROC_FLAG_CRIT_SPELL = 0x00010000, // On crit spell - PROC_FLAG_HIT_SPELL = 0x00020000, // On hit spell - PROC_FLAG_STRUCK_CRIT_SPELL = 0x00040000, // On being critically struck by a spell - PROC_FLAG_HIT_RANGED = 0x00080000, // On getting ranged hit - PROC_FLAG_STRUCK_SPELL = 0x00100000, // On being struck by a spell - PROC_FLAG_TRAP = 0x00200000, // On trap activation (?) - PROC_FLAG_CRIT_RANGED = 0x00400000, // On getting ranged crit - PROC_FLAG_STRUCK_CRIT_RANGED = 0x00800000, // On being critically struck by a ranged attack - PROC_FLAG_RESIST_SPELL = 0x01000000, // On resist enemy spell - PROC_FLAG_TARGET_RESISTS = 0x02000000, // On enemy resisted spell - PROC_FLAG_TARGET_DODGE_OR_PARRY= 0x04000000, // On enemy dodges/parries - PROC_FLAG_HEAL = 0x08000000, // On heal - PROC_FLAG_CRIT_HEAL = 0x10000000, // On critical healing effect - PROC_FLAG_HEALED = 0x20000000, // On healing - PROC_FLAG_TARGET_BLOCK = 0x40000000, // On enemy blocks - PROC_FLAG_MISS = 0x80000000 // On miss melee attack + PROC_FLAG_NONE = 0x00000000, + + PROC_FLAG_KILLED = 0x00000001, // 00 Killed by agressor + PROC_FLAG_KILL_AND_GET_XP = 0x00000002, // 01 Kill that yields experience or honor + + PROC_FLAG_SUCCESSFUL_MILEE_HIT = 0x00000004, // 02 Successful melee attack + PROC_FLAG_TAKEN_MELEE_HIT = 0x00000008, // 03 Taken damage from melee strike hit + + PROC_FLAG_SUCCESSFUL_MELEE_SPELL_HIT = 0x00000010, // 04 Successful attack by Spell that use melee weapon + PROC_FLAG_TAKEN_MELEE_SPELL_HIT = 0x00000020, // 05 Taken damage by Spell that use melee weapon + + PROC_FLAG_SUCCESSFUL_RANGED_HIT = 0x00000040, // 06 Successful Ranged attack + PROC_FLAG_TAKEN_RANGED_HIT = 0x00000080, // 07 Taken damage from ranged attack + + PROC_FLAG_SUCCESSFUL_RANGED_SPELL_HIT = 0x00000100, // 08 Successful Ranged attack by Spell that use ranged weapon + PROC_FLAG_TAKEN_RANGED_SPELL_HIT = 0x00000200, // 09 Taken damage by Spell that use ranged weapon + + PROC_FLAG_SUCCESSFUL_10 = 0x00000400, // 10 Special flag + PROC_FLAG_TAKEN_11 = 0x00000800, // 11 Special flag + + PROC_FLAG_SUCCESSFUL_12 = 0x00001000, // 12 Successful damage spell hit (spell type???) + PROC_FLAG_TAKEN_13 = 0x00002000, // 13 Taken damage spell hit (spell type???) + + PROC_FLAG_SUCCESSFUL_HEAL = 0x00004000, // 14 Successful cast healing spell + PROC_FLAG_TAKEN_HEAL = 0x00008000, // 15 Taken healing spell hit + + PROC_FLAG_SUCCESSFUL_HARMFUL_SPELL_HIT = 0x00010000, // 16 Successful damage from harmful spell cast + PROC_FLAG_TAKEN_HARMFUL_SPELL_HIT = 0x00020000, // 17 Taken spell damage + + PROC_FLAG_SUCCESSFUL_PERIODIC_DAMAGE = 0x00040000, // 18 Successful deal spell periodic damage + PROC_FLAG_TAKEN_PERIODIC_DAMAGE = 0x00080000, // 19 Taken spell periodic damage + + PROC_FLAG_TAKEN_ANY_DAMAGE = 0x00100000, // 20 Taken any damage + PROC_FLAG_ON_TRAP_ACTIVATION = 0x00200000, // 21 On trap activation + + PROC_FLAG_TAKEN_OFFHAND_HIT = 0x00400000, // 22 Taken off-hand melee attacks(not used) + PROC_FLAG_SUCCESSFUL_OFFHAND_HIT = 0x00800000 // 23 Successful off-hand melee attacks }; +enum ProcFlagsEx +{ + PROC_FLAG_EX_NONE = 0x00000000, + PROC_FLAG_EX_CRITICAL_HIT = 0x00000001, + PROC_FLAG_ON_SPELL_CAST = 0x00000002, + PROC_FLAG_EX_DODGE = 0x00000004, + PROC_FLAG_EX_PARRY = 0x00000008, + PROC_FLAG_EX_BLOCK = 0x00000010, + PROC_FLAG_EX_MISS = 0x00000020, + PROC_FLAG_EX_FULL_RESISTS = 0x00000080, + PROC_FLAG_EX_ABSORB = 0x00000100, + PROC_FLAG_EX_HIT_EVADE_TARGET = 0x00000200 +}; + enum AuraState { // (C) used in caster aurastate (T) used in target aura state AURA_STATE_DEFENSE = 1, // C @@ -600,7 +628,30 @@ WeaponAttackType attackType; MeleeHitOutcome hitOutCome; }; +// Test struct for use in Unit::CalculateSpellDamage, Unit::CalculateMeleeDamage +struct CalcDamageInfo +{ + DamageEffectType damagetype; + uint32 damage; + uint32 cleanDamage; + uint32 absorb; + uint32 resist; + uint32 blocked_amount; + uint32 procAttacker; + uint32 procVictim; + uint32 procEx; + + Unit *target; // Target for damage + uint32 HitInfo; + uint32 TargetState; + WeaponAttackType attackType; // + MeleeHitOutcome hitOutCome; // TODO: remove this field + SpellSchools damageSchool; + uint32 SpellID; + uint32 auraname; + +}; struct UnitActionBarEntry { uint32 Type; @@ -826,15 +877,24 @@ void Unmount(); uint16 GetMaxSkillValueForLevel() const { return getLevel()*5; } + void DealDamage(CalcDamageInfo *damageInfo, bool durabilityLoss); void DealDamage(Unit *pVictim, uint32 damage, CleanDamage const* cleanDamage, DamageEffectType damagetype, SpellSchools damageSchool, SpellEntry const *spellProto, uint32 procFlag, bool durabilityLoss); - void DealDamageBySchool(Unit *pVictim, SpellEntry const *spellInfo, uint32 *damage, CleanDamage *cleanDamage, bool *crit = false, bool isTriggeredSpell = false); - void DoAttackDamage(Unit *pVictim, uint32 *damage, CleanDamage *cleanDamage, uint32 *blocked_amount, SpellSchools damageType, uint32 *hitInfo, VictimState *victimState, uint32 *absorbDamage, uint32 *resistDamage, WeaponAttackType attType, SpellEntry const *spellCasted = NULL, bool isTriggeredSpell = false); +// void DealDamageBySchool(Unit *pVictim, SpellEntry const *spellInfo, uint32 *damage, CleanDamage *cleanDamage, bool *crit = false, bool isTriggeredSpell = false); - void CastMeleeProcDamageAndSpell(Unit* pVictim, uint32 damage, WeaponAttackType attType, MeleeHitOutcome outcome, SpellEntry const *spellCasted = NULL, bool isTriggeredSpell = false); - void ProcDamageAndSpell(Unit *pVictim, uint32 procAttacker, uint32 procVictim, uint32 damage = 0, SpellEntry const *procSpell = NULL, bool isTriggeredSpell = false, WeaponAttackType attType = BASE_ATTACK); +// void DoAttackDamage(Unit *pVictim, uint32 *damage, CleanDamage *cleanDamage, uint32 *blocked_amount, SpellSchools damageType, uint32 *hitInfo, VictimState *victimState, uint32 *absorbDamage, uint32 *resistDamage, WeaponAttackType attType, SpellEntry const *spellCasted = NULL, bool isTriggeredSpell = false); + void CalculateDamage(DamageEffectType type, Unit *pVictim, uint32 damage, CalcDamageInfo *damageInfo, WeaponAttackType attackType = BASE_ATTACK, uint32 spellID = 0, SpellSchools damageSchool = SPELL_SCHOOL_NORMAL, uint32 auraname = 0); + void CalculateMeleeDamage(uint32 damage, CalcDamageInfo *damageInfo); + void CalculateSpellDamage(uint32 damage, CalcDamageInfo *damageInfo); + void CalculateDotDamage(uint32 damage, CalcDamageInfo *damageInfo); +// void CastMeleeProcDamageAndSpell(Unit* pVictim, uint32 damage, WeaponAttackType attType, MeleeHitOutcome outcome, SpellEntry const *spellCasted = NULL, bool isTriggeredSpell = false); + void ProcDamageAndSpell(Unit *pVictim, uint32 procAttacker, uint32 procVictim, uint32 procEx, uint32 damage = 0, SpellEntry const *procSpell = NULL, bool isTriggeredSpell = false, WeaponAttackType attType = BASE_ATTACK); void HandleEmoteCommand(uint32 anim_id); void AttackerStateUpdate (Unit *pVictim, WeaponAttackType attType = BASE_ATTACK, bool isTriggered = false); - uint32 SpellMissChanceCalc(Unit *pVictim) const; + uint32 SpellMissChanceCalc(Unit *pVictim, SpellSchools school) const; + uint32 SpellResistChanceCalc(Unit *pVictim, SpellSchools school) const; + + uint8 SpellCastResult(Unit *pVictim, SpellEntry const *spell) const; + int32 MeleeMissChanceCalc(const Unit *pVictim) const; float GetUnitDodgeChance() const; @@ -917,7 +977,7 @@ bool isFrozen() const; - void RemoveSpellbyDamageTaken(AuraType auraType, uint32 damage); + void RemoveSpellbyDamageTaken(AuraType auraType, uint32 damage, uint32 casterLevel); bool isTargetableForAttack() const; virtual bool IsInWater() const; @@ -928,6 +988,9 @@ void SendHealSpellOnPlayerPet(Unit *pVictim, uint32 SpellID, uint32 Damage,Powers powertype, bool critical = false); void PeriodicAuraLog(Unit *pVictim, SpellEntry const *spellProto, Modifier *mod, uint8 effect_idx); void SpellNonMeleeDamageLog(Unit *pVictim, uint32 spellID, uint32 damage, bool isTriggeredSpell = false, bool useSpellDamage = true); + void SendSpellNonMeleeDamageLog(CalcDamageInfo *damageInfo); + void SendSpellDotDamageLog(CalcDamageInfo *damageInfo); + void CastSpell(Unit* Victim, uint32 spellId, bool triggered, Item *castItem = NULL, Aura* triggredByAura = NULL, uint64 originalCaster = 0); void CastSpell(Unit* Victim,SpellEntry const *spellInfo, bool triggered, Item *castItem= NULL, Aura* triggredByAura = NULL, uint64 originalCaster = 0); void CastCustomSpell(Unit* Victim, uint32 spellId, int32 const* bp0, int32 const* bp1, int32 const* bp2, bool triggered, Item *castItem= NULL, Aura* triggredByAura = NULL, uint64 originalCaster = 0); @@ -937,6 +1000,8 @@ void DeMorph(); void SendAttackStateUpdate(uint32 HitInfo, Unit *target, uint8 SwingType, SpellSchools DamageType, uint32 Damage, uint32 AbsorbDamage, uint32 Resist, VictimState TargetState, uint32 BlockedAmount); + void SendAttackStateUpdate(CalcDamageInfo *damageInfo); + void SendSpellNonMeleeDamageLog(Unit *target,uint32 SpellID,uint32 Damage, SpellSchools DamageType,uint32 AbsorbedDamage, uint32 Resist,bool PhysicalDamage, uint32 Blocked, bool CriticalHit = false); void SendMonsterMove(float NewPosX, float NewPosY, float NewPosZ, uint8 type, bool Run, uint32 Time); @@ -1034,7 +1099,7 @@ uint32 m_ShapeShiftForm; uint32 m_form; int32 m_modHitChance; - int32 m_modSpellHitChance; + int32 m_modSpellHitChance[MAX_SPELL_SCHOOL]; int32 m_baseSpellCritChance; float m_modResilience; float m_threatModifier[MAX_SPELL_SCHOOL]; @@ -1128,6 +1193,7 @@ virtual bool IsImmunedToSpell(SpellEntry const* spellInfo) const; // redefined in Creature bool IsImmunedToPhysicalDamage() const; + bool IsImmunedToSchoolDamage(SpellSchools School) const; bool IsImmunedToSpellDamage(SpellEntry const* spellInfo) const; bool IsImmunedToSpellEffect(uint32 effect) const; @@ -1222,9 +1288,9 @@ void SendAttackStop(Unit* victim); // only from AttackStop(Unit*) void SendAttackStart(Unit* pVictim); // only from Unit::AttackStart(Unit*) - void ProcDamageAndSpellFor( bool isVictim, Unit * pTarget, uint32 procFlag, AuraTypeSet const& procAuraTypes, WeaponAttackType attType, SpellEntry const * procSpell, uint32 damage ); - void HandleDummyAuraProc(Unit *pVictim, SpellEntry const *spellProto, uint32 effIndex, uint32 damage, Aura* triggredByAura, SpellEntry const * procSpell, uint32 procFlag); - void HandleProcTriggerSpell(Unit *pVictim,uint32 damage, Aura* triggredByAura, SpellEntry const *procSpell, uint32 procFlags); + void ProcDamageAndSpellFor( bool isVictim, Unit * pTarget, uint32 procFlag, uint32 procExtra, WeaponAttackType attType, SpellEntry const * procSpell, uint32 damage ); + void HandleDummyAuraProc(Unit *pVictim, SpellEntry const *spellProto, uint32 effIndex, uint32 damage, Aura* triggredByAura, SpellEntry const * procSpell, uint32 procFlag, uint32 procEx); + void HandleProcTriggerSpell(Unit *pVictim,uint32 damage, Aura* triggredByAura, SpellEntry const *procSpell, uint32 procFlags, uint32 procEx); uint32 m_state; // Even derived shouldn't modify uint32 m_CombatTimer; Index: src/shared/Database/DBCStructure.h =================================================================== --- src/shared/Database/DBCStructure.h (revision 4842) +++ src/shared/Database/DBCStructure.h (working copy) @@ -527,7 +527,10 @@ uint32 spellFamilyName; // if nonzero - for matching proc condition based on candidate spell's SpellFamilyNamer value uint64 spellFamilyMask; // if nonzero - for matching proc condition based on candidate spell's SpellFamilyFlags (like auras 107 and 108 do) uint32 procFlags; // bitmask for matching proc event + uint32 procEx; float ppmRate; // for melee (ranged?) damage spells - proc rate per minute. if zero, falls back to flat chance from Spell.dbc + float customChance; + uint32 Cooldown; }; struct SpellThreatEntry