From 600205641d50b9ea79be21c42887d36b8171374a Mon Sep 17 00:00:00 2001 From: Charles A Edwards Date: Thu, 15 Sep 2016 14:56:42 +0100 Subject: [PATCH] Cmangos Cata commits applied I thankee, cmangos :-) Commits: 13271d6 Commit Ported Core Pet DB cleanup and simplification 60d6e29 Commit Imported Core Utilize flags in PetAI (And uniform extra flags with the other cores) 06d30ce Commit Ported Core Make pet assist owner on summon if not a passive 2821da8 Commit Ported Core Fix Guardian reactions 4f88a9e Commit Ported Core Pet AI Fixup 67e0558 Commit Imported Core Fix pet unsummon on mount f50041f Commit Imported Core Fix player rooted after possesing an unit. df59a93 Commit Imported Core Syncing up pet work 056f4f5 Commit Imported Core Fix a couple of invalid name for spell attributes 6a58f1f Commit Imported Core only save correct auras on pet::SavePetToDB 34ab59b Commit Imported Core Hunter summon pet (call pet) checkcast fixup dfbb69c Commit Imported Core Handle owner entering combat properly 4b10eb4 Commit Imported Core Pet Aggro 1bdb7e3 Commit Ported Core Clean up pet stay functionality 1bdb7e3 Commit Ported Core Clean up pet stay functionality 9b7b50e Commit Imported Core UNIT_BYTE2_FLAG work 0777235 Commit Imported Core Implement displaying group leader indicators on players 5efab47 Commit Imported Core Health funnel fixes 60e6a84 Commit Ported Core Fix SMSG_QUESTGIVER_STATUS_MULTIPLE and GetDialogStatus 60e6a84 Commit Ported Core Fix SMSG_QUESTGIVER_STATUS_MULTIPLE and GetDialogStatus f8d3cbd Commit Imported Core Fix talent spell cannot stack 32ba32e Commit Imported Core Fix channeled spell distance check interval 47ec2fa Commit Imported Core Adding state to aura holder Now proc cannot remove an aura not finalized 34588dc Commit Ported Core Unbreak creature pets bd079a1 Commit Imported Core The (not so)Great Pet Rework --- src/game/Object/Pet.cpp | 498 ++++++++++-------- src/game/Object/Pet.h | 7 +- src/game/Object/PetAI.cpp | 30 +- src/game/Object/Player.cpp | 75 ++- src/game/Object/Player.h | 2 + src/game/Object/SpellMgr.cpp | 17 +- src/game/Object/SpellMgr.h | 3 + src/game/Object/Unit.cpp | 41 +- src/game/Server/SharedDefines.h | 4 +- src/game/WorldHandlers/Group.cpp | 16 +- src/game/WorldHandlers/Group.h | 4 + src/game/WorldHandlers/PetHandler.cpp | 60 +-- src/game/WorldHandlers/QuestHandler.cpp | 65 +-- src/game/WorldHandlers/Spell.cpp | 8 +- src/game/WorldHandlers/SpellAuras.cpp | 227 ++++---- src/game/WorldHandlers/SpellAuras.h | 11 + src/game/WorldHandlers/SpellEffects.cpp | 63 +-- .../WorldHandlers/UnitAuraProcHandler.cpp | 2 +- src/game/WorldHandlers/World.cpp | 2 +- src/mangosd/mangosd.conf.dist.in | 6 +- 20 files changed, 634 insertions(+), 507 deletions(-) diff --git a/src/game/Object/Pet.cpp b/src/game/Object/Pet.cpp index 6eb008d40..aa3a57947 100644 --- a/src/game/Object/Pet.cpp +++ b/src/game/Object/Pet.cpp @@ -52,10 +52,6 @@ Pet::Pet(PetType type) : if (type == MINI_PET) // always passive charmInfo->SetReactState(REACT_PASSIVE); - else if (type == PROTECTOR_PET) // always defensive - charmInfo->SetReactState(REACT_DEFENSIVE); - else if (type == GUARDIAN_PET) // always aggressive - charmInfo->SetReactState(REACT_AGGRESSIVE); } Pet::~Pet() @@ -158,13 +154,6 @@ bool Pet::LoadPetFromDB(Player* owner, uint32 petentry, uint32 petnumber, bool c uint32 pet_number = fields[0].GetUInt32(); - if (current && owner->IsPetNeedBeTemporaryUnsummoned()) - { - owner->SetTemporaryUnsummonedPetNumber(pet_number); - delete result; - return false; - } - Map* map = owner->GetMap(); CreatureCreatePos pos(owner, owner->GetOrientation(), PET_FOLLOW_DIST, PET_FOLLOW_ANGLE); @@ -202,18 +191,13 @@ bool Pet::LoadPetFromDB(Player* owner, uint32 petentry, uint32 petnumber, bool c SetByteValue(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_SUPPORTABLE | UNIT_BYTE2_FLAG_AURAS); SetUInt32Value(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE); - switch (getPetType()) + if (getPetType() == HUNTER_PET) { - case SUMMON_PET: - petlevel = owner->getLevel(); - break; - case HUNTER_PET: - SetByteFlag(UNIT_FIELD_BYTES_2, 2, fields[9].GetBool() ? UNIT_CAN_BE_ABANDONED : UNIT_CAN_BE_RENAMED | UNIT_CAN_BE_ABANDONED); - SetPowerType(POWER_FOCUS); - break; - default: - sLog.outError("Pet have incorrect type (%u) for pet loading.", getPetType()); + SetByteFlag(UNIT_FIELD_BYTES_2, 2, fields[9].GetBool() ? UNIT_CAN_BE_ABANDONED : UNIT_CAN_BE_RENAMED | UNIT_CAN_BE_ABANDONED); + SetPowerType(POWER_FOCUS); } + else if (getPetType() != SUMMON_PET) + sLog.outError("Pet have incorrect type (%u) for pet loading.", getPetType()); if (owner->IsPvP()) SetPvP(true); @@ -282,21 +266,16 @@ bool Pet::LoadPetFromDB(Player* owner, uint32 petentry, uint32 petnumber, bool c CastOwnerTalentAuras(); } - Powers powerType = GetPowerType(); + Powers powerType = GetPowerType();; - if (getPetType() == SUMMON_PET && !current) // all (?) summon pets come with full health when called, but not when they are current - { - SetHealth(GetMaxHealth()); - SetPower(powerType, GetMaxPower(powerType)); - } - else - { - SetHealth(savedhealth > GetMaxHealth() ? GetMaxHealth() : savedhealth); - SetPower(powerType, savedpower > GetMaxPower(powerType) ? GetMaxPower(powerType) : savedpower); - } + SetHealth(savedhealth > GetMaxHealth() ? GetMaxHealth() : savedhealth); + SetPower(powerType, savedpower > GetMaxPower(powerType) ? GetMaxPower(powerType) : savedpower); + + if (getPetType() == HUNTER_PET && savedhealth <= 0) + SetDeathState(JUST_DIED); - AIM_Initialize(); map->Add((Creature*)this); + AIM_Initialize(); // Spells should be loaded after pet is added to map, because in CheckCast is check on it _LoadSpells(); @@ -363,7 +342,13 @@ void Pet::SavePetToDB(PetSaveMode mode) { // reagents must be returned before save call if (mode == PET_SAVE_REAGENTS) - mode = PET_SAVE_NOT_IN_SLOT; + { + // Hunter Pets always save as current if dismissed or unsummoned due to range/etc. + if (getPetType() == HUNTER_PET) + mode = PET_SAVE_AS_CURRENT; + else + mode = PET_SAVE_NOT_IN_SLOT; + } // not save pet as current if another pet temporary unsummoned else if (mode == PET_SAVE_AS_CURRENT && pOwner->GetTemporaryUnsummonedPetNumber() && pOwner->GetTemporaryUnsummonedPetNumber() != m_charmInfo->GetPetNumber()) @@ -430,7 +415,7 @@ void Pet::SavePetToDB(PetSaveMode mode) savePet.addUInt32(uint32(mode)); savePet.addString(m_name); savePet.addUInt32(uint32(HasByteFlag(UNIT_FIELD_BYTES_2, 2, UNIT_CAN_BE_RENAMED) ? 0 : 1)); - savePet.addUInt32((curhealth < 1 ? 1 : curhealth)); + savePet.addUInt32(curhealth); savePet.addUInt32(curpower); std::ostringstream ss; @@ -441,7 +426,7 @@ void Pet::SavePetToDB(PetSaveMode mode) }; savePet.addString(ss); - savePet.addUInt64(uint64(time(NULL))); + savePet.addUInt64(uint64(time(nullptr))); savePet.addUInt32(uint32(m_resetTalentsCost)); savePet.addUInt64(uint64(m_resetTalentsTime)); savePet.addUInt32(GetUInt32Value(UNIT_CREATED_BY_SPELL)); @@ -774,18 +759,13 @@ bool Pet::CreateBaseAtCreature(Creature* creature) if (!Create(guid, pos, creature->GetCreatureInfo(), pet_number)) return false; - CreatureInfo const* cinfo = GetCreatureInfo(); - if (!cinfo) + CreatureInfo const* cInfo = GetCreatureInfo(); + if (!cInfo) { sLog.outError("CreateBaseAtCreature() failed, creatureInfo is missing!"); return false; } - if (cinfo->CreatureType == CREATURE_TYPE_CRITTER) - { - setPetType(MINI_PET); - return true; - } SetDisplayId(creature->GetDisplayId()); SetNativeDisplayId(creature->GetNativeDisplayId()); SetPowerType(POWER_FOCUS); @@ -794,118 +774,141 @@ bool Pet::CreateBaseAtCreature(Creature* creature) SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP, sObjectMgr.GetXPForPetLevel(creature->getLevel())); SetUInt32Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_NONE); - if (CreatureFamilyEntry const* cFamily = sCreatureFamilyStore.LookupEntry(cinfo->Family)) + if (CreatureFamilyEntry const* cFamily = sCreatureFamilyStore.LookupEntry(cInfo->Family)) SetName(cFamily->Name[sWorld.GetDefaultDbcLocale()]); else SetName(creature->GetNameForLocaleIdx(sObjectMgr.GetDBCLocaleIndex())); - if (cinfo->CreatureType == CREATURE_TYPE_BEAST) - { - SetByteValue(UNIT_FIELD_BYTES_0, 1, CLASS_WARRIOR); - SetByteValue(UNIT_FIELD_BYTES_0, 2, GENDER_NONE); - SetByteValue(UNIT_FIELD_BYTES_0, 3, POWER_FOCUS); - SetSheath(SHEATH_STATE_MELEE); - SetByteFlag(UNIT_FIELD_BYTES_2, 2, UNIT_CAN_BE_RENAMED | UNIT_CAN_BE_ABANDONED); - SetUInt32Value(UNIT_MOD_CAST_SPEED, creature->GetUInt32Value(UNIT_MOD_CAST_SPEED)); - } + SetByteValue(UNIT_FIELD_BYTES_0, 1, CLASS_WARRIOR); + SetByteValue(UNIT_FIELD_BYTES_0, 2, GENDER_NONE); + SetByteValue(UNIT_FIELD_BYTES_0, 3, POWER_FOCUS); + SetSheath(SHEATH_STATE_MELEE); + + SetByteValue(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_SUPPORTABLE | UNIT_BYTE2_FLAG_AURAS); + SetByteFlag(UNIT_FIELD_BYTES_2, 2, UNIT_CAN_BE_RENAMED | UNIT_CAN_BE_ABANDONED); + + SetUInt32Value(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE | UNIT_FLAG_RENAME); + + SetUInt32Value(UNIT_MOD_CAST_SPEED, creature->GetUInt32Value(UNIT_MOD_CAST_SPEED)); + return true; } -bool Pet::InitStatsForLevel(uint32 petlevel, Unit* owner) +void Pet::InitStatsForLevel(uint32 petlevel) { - CreatureInfo const* cinfo = GetCreatureInfo(); - MANGOS_ASSERT(cinfo); - - if (!owner) - { - owner = GetOwner(); - if (!owner) - { - sLog.outError("attempt to summon pet (Entry %u) without owner! Attempt terminated.", cinfo->Entry); - return false; - } - } - - uint32 creature_ID = (getPetType() == HUNTER_PET) ? 1 : cinfo->Entry; - - switch (getPetType()) - { - case SUMMON_PET: - SetByteValue(UNIT_FIELD_BYTES_0, 1, CLASS_MAGE); - - // this enables popup window (pet dismiss, cancel) - SetUInt32Value(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE); - break; - case HUNTER_PET: - SetByteValue(UNIT_FIELD_BYTES_0, 1, CLASS_WARRIOR); - SetByteValue(UNIT_FIELD_BYTES_0, 2, GENDER_NONE); - SetSheath(SHEATH_STATE_MELEE); - - // this enables popup window (pet abandon, cancel) - SetUInt32Value(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE); - break; - case GUARDIAN_PET: - case MINI_PET: - case PROTECTOR_PET: - default: - break; - } + Unit* owner = GetOwner(); + CreatureInfo const* cInfo = GetCreatureInfo(); + MANGOS_ASSERT(cInfo); SetLevel(petlevel); - SetMeleeDamageSchool(SpellSchools(cinfo->DamageSchool)); - - SetModifierValue(UNIT_MOD_ARMOR, BASE_VALUE, float(petlevel * 50)); - - SetAttackTime(BASE_ATTACK, BASE_ATTACK_TIME); - SetAttackTime(OFF_ATTACK, BASE_ATTACK_TIME); - SetAttackTime(RANGED_ATTACK, BASE_ATTACK_TIME); - SetFloatValue(UNIT_MOD_CAST_SPEED, 1.0); - CreatureFamilyEntry const* cFamily = sCreatureFamilyStore.LookupEntry(cinfo->Family); - if (cFamily && cFamily->minScale > 0.0f && getPetType() == HUNTER_PET) - { - float Scale; - if (getLevel() >= cFamily->maxScaleLevel) - Scale = cFamily->maxScale; - else if (getLevel() <= cFamily->minScaleLevel) - Scale = cFamily->minScale; - else - Scale = cFamily->minScale + float(getLevel() - cFamily->minScaleLevel) / cFamily->maxScaleLevel * (cFamily->maxScale - cFamily->minScale); - - SetObjectScale(Scale); - UpdateModelData(); - } - m_bonusdamage = 0; - int32 createResistance[MAX_SPELL_SCHOOL] = {0, 0, 0, 0, 0, 0, 0}; - if (getPetType() != HUNTER_PET) + if (getPetType() == HUNTER_PET) { - createResistance[SPELL_SCHOOL_HOLY] = cinfo->ResistanceHoly; - createResistance[SPELL_SCHOOL_FIRE] = cinfo->ResistanceFire; - createResistance[SPELL_SCHOOL_NATURE] = cinfo->ResistanceNature; - createResistance[SPELL_SCHOOL_FROST] = cinfo->ResistanceFrost; - createResistance[SPELL_SCHOOL_SHADOW] = cinfo->ResistanceShadow; - createResistance[SPELL_SCHOOL_ARCANE] = cinfo->ResistanceArcane; + SetMeleeDamageSchool(SpellSchools(SPELL_SCHOOL_NORMAL)); + SetAttackTime(BASE_ATTACK, BASE_ATTACK_TIME); + SetAttackTime(OFF_ATTACK, BASE_ATTACK_TIME); + SetAttackTime(RANGED_ATTACK, BASE_ATTACK_TIME); } + else + { + SetMeleeDamageSchool(SpellSchools(cInfo->DamageSchool)); + SetAttackTime(BASE_ATTACK, cInfo->MeleeBaseAttackTime); + SetAttackTime(OFF_ATTACK, cInfo->MeleeBaseAttackTime); + SetAttackTime(RANGED_ATTACK, cInfo->RangedBaseAttackTime); + + createResistance[SPELL_SCHOOL_HOLY] = cInfo->ResistanceHoly; + createResistance[SPELL_SCHOOL_FIRE] = cInfo->ResistanceFire; + createResistance[SPELL_SCHOOL_NATURE] = cInfo->ResistanceNature; + createResistance[SPELL_SCHOOL_FROST] = cInfo->ResistanceFrost; + createResistance[SPELL_SCHOOL_SHADOW] = cInfo->ResistanceShadow; + createResistance[SPELL_SCHOOL_ARCANE] = cInfo->ResistanceArcane; + } + + for (int i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; ++i) + SetModifierValue(UnitMods(UNIT_MOD_RESISTANCE_START + i), BASE_VALUE, float(createResistance[i])); + + float health, mana, armor, minDmg; switch (getPetType()) { + case HUNTER_PET: + { + CreatureFamilyEntry const* cFamily = sCreatureFamilyStore.LookupEntry(cInfo->Family); + + if (cFamily && cFamily->minScale > 0.0f) + { + float scale; + if (getLevel() >= cFamily->maxScaleLevel) + scale = cFamily->maxScale; + else if (getLevel() <= cFamily->minScaleLevel) + scale = cFamily->minScale; + else + scale = cFamily->minScale + float(getLevel() - cFamily->minScaleLevel) / cFamily->maxScaleLevel * (cFamily->maxScale - cFamily->minScale); + + SetObjectScale(scale); + UpdateModelData(); + } + + // Max level + if (petlevel < sWorld.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL)) + SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP, sObjectMgr.GetXPForPetLevel(petlevel)); + else + { + SetUInt32Value(UNIT_FIELD_PETEXPERIENCE, 0); + SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP, 1000); + } + + // Info found in pet_levelstats + if (PetLevelInfo const* pInfo = sObjectMgr.GetPetLevelInfo(1, petlevel)) + { + for (int i = STAT_STRENGTH; i < MAX_STATS;++i) + SetCreateStat(Stats(i), float(pInfo->stats[i])); + + health = pInfo->health; + mana = 0; + armor = pInfo->armor; + + // First we divide attack time by standard attack time, and then multipy by level and damage mod. + uint32 mDmg = (GetAttackTime(BASE_ATTACK) * petlevel) / 2000; + + // Set damage + SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, float(mDmg - mDmg / 4)); + SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, float((mDmg - mDmg / 4) * 1.5)); + } + else + { + sLog.outErrorDb("HUNTER PET levelstats missing in DB! 'Weakifying' pet"); + + for (int i = STAT_STRENGTH; i < MAX_STATS;++i) + SetCreateStat(Stats(i), 1.0f); + + health = 1; + mana = 0; + armor = 0; + + // Set damage + SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, 1); + SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, 1); + } + + break; + } case SUMMON_PET: { - if (owner->GetTypeId() == TYPEID_PLAYER) + if (owner) { switch (owner->getClass()) { case CLASS_WARLOCK: { - // the damage bonus used for pets is either fire or shadow damage, whatever is higher - uint32 fire = owner->GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS + SPELL_SCHOOL_FIRE); + uint32 fire = owner->GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS + SPELL_SCHOOL_FIRE); uint32 shadow = owner->GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS + SPELL_SCHOOL_SHADOW); - uint32 val = (fire > shadow) ? fire : shadow; + uint32 val = (fire > shadow) ? fire : shadow; SetBonusDamage(int32(val * 0.15f)); // bonusAP += val * 0.57; @@ -924,109 +927,172 @@ bool Pet::InitStatsForLevel(uint32 petlevel, Unit* owner) break; } } + else + sLog.outError("Pet::InitStatsForLevel> No owner for creature pet %s !", GetGuidStr().c_str()); - SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, float(petlevel - (petlevel / 4))); - SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, float(petlevel + (petlevel / 4))); - - // SetModifierValue(UNIT_MOD_ATTACK_POWER, BASE_VALUE, float(cinfo->attackpower)); - - PetLevelInfo const* pInfo = sObjectMgr.GetPetLevelInfo(creature_ID, petlevel); - if (pInfo) // exist in DB - { - SetCreateHealth(pInfo->health); - SetCreateMana(pInfo->mana); - - if (pInfo->armor > 0) - SetModifierValue(UNIT_MOD_ARMOR, BASE_VALUE, float(pInfo->armor)); - - for (int stat = 0; stat < MAX_STATS; ++stat) - { - SetCreateStat(Stats(stat), float(pInfo->stats[stat])); - } - } - else // not exist in DB, use some default fake data - { - sLog.outErrorDb("Summoned pet (Entry: %u) not have pet stats data in DB", cinfo->Entry); - - // remove elite bonuses included in DB values - SetCreateHealth(uint32(((float(cinfo->MaxLevelHealth) / cinfo->MaxLevel) / (1 + 2 * cinfo->Rank)) * petlevel)); - SetCreateMana(uint32(((float(cinfo->MaxLevelMana) / cinfo->MaxLevel) / (1 + 2 * cinfo->Rank)) * petlevel)); - - SetCreateStat(STAT_STRENGTH, 22); - SetCreateStat(STAT_AGILITY, 22); - SetCreateStat(STAT_STAMINA, 25); - SetCreateStat(STAT_INTELLECT, 28); - SetCreateStat(STAT_SPIRIT, 27); - } - break; - } - case HUNTER_PET: - { - SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP, sObjectMgr.GetXPForPetLevel(petlevel)); - // these formula may not be correct; however, it is designed to be close to what it should be - // this makes dps 0.5 of pets level - SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, float(petlevel - (petlevel / 4))); - // damage range is then petlevel / 2 - SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, float(petlevel + (petlevel / 4))); - // damage is increased afterwards as strength and pet scaling modify attack power - - // stored standard pet stats are entry 1 in pet_levelinfo - PetLevelInfo const* pInfo = sObjectMgr.GetPetLevelInfo(creature_ID, petlevel); - if (pInfo) // exist in DB - { - SetCreateHealth(pInfo->health); - SetModifierValue(UNIT_MOD_ARMOR, BASE_VALUE, float(pInfo->armor)); - // SetModifierValue(UNIT_MOD_ATTACK_POWER, BASE_VALUE, float(cinfo->attackpower)); - - for (int i = STAT_STRENGTH; i < MAX_STATS; ++i) - { - SetCreateStat(Stats(i), float(pInfo->stats[i])); - } - } - else // not exist in DB, use some default fake data - { - sLog.outErrorDb("Hunter pet levelstats missing in DB"); - - // remove elite bonuses included in DB values - SetCreateHealth(uint32(((float(cinfo->MaxLevelHealth) / cinfo->MaxLevel) / (1 + 2 * cinfo->Rank)) * petlevel)); - - SetCreateStat(STAT_STRENGTH, 22); - SetCreateStat(STAT_AGILITY, 22); - SetCreateStat(STAT_STAMINA, 25); - SetCreateStat(STAT_INTELLECT, 28); - SetCreateStat(STAT_SPIRIT, 27); - } - break; - } - case GUARDIAN_PET: - case PROTECTOR_PET: SetUInt32Value(UNIT_FIELD_PETEXPERIENCE, 0); SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP, 1000); - SetCreateMana(28 + 10 * petlevel); - SetCreateHealth(28 + 30 * petlevel); + // Info found in pet_levelstats + if (PetLevelInfo const* pInfo = sObjectMgr.GetPetLevelInfo(cInfo->Entry, petlevel)) + { + for (int i = STAT_STRENGTH; i < MAX_STATS;++i) + SetCreateStat(Stats(i), float(pInfo->stats[i])); + + health = pInfo->health; + mana = pInfo->mana; + armor = pInfo->armor; + + // Info found in ClassLevelStats + if (CreatureClassLvlStats const* cCLS = sObjectMgr.GetCreatureClassLvlStats(petlevel, cInfo->UnitClass, cInfo->Expansion)) + { + minDmg = (cCLS->BaseDamage * cInfo->DamageVariance + (cCLS->BaseMeleeAttackPower / 14) * (cInfo->MeleeBaseAttackTime/1000)) * cInfo->DamageMultiplier; + + // Apply custom damage setting (from config) + minDmg *= _GetDamageMod(cInfo->Rank); + + SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, float(minDmg)); + SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, float(minDmg * 1.5)); + } + else + { + sLog.outErrorDb("SUMMON_PET creature_template not finished (expansion field = -1) on creature %s! (entry: %u)", GetGuidStr().c_str(), cInfo->Entry); + + float dMinLevel = cInfo->MinMeleeDmg / cInfo->MinLevel; + float dMaxLevel = cInfo->MaxMeleeDmg / cInfo->MaxLevel; + float mDmg = (dMaxLevel - ((dMaxLevel - dMinLevel) / 2)) * petlevel; + + // Set damage + SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, float(mDmg - mDmg / 4)); + SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, float((mDmg - mDmg / 4) * 1.5)); + } + } + else + { + sLog.outErrorDb("SUMMON_PET levelstats missing in DB! 'Weakifying' pet and giving it mana to make it obvious"); + + for (int i = STAT_STRENGTH; i < MAX_STATS;++i) + SetCreateStat(Stats(i), 1.0f); + + health = 1; + mana = 1; + armor = 1; + + // Set damage + SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, 1); + SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, 1); + } - // FIXME: this is wrong formula, possible each guardian pet have own damage formula - // these formula may not be correct; however, it is designed to be close to what it should be - // this makes dps 0.5 of pets level - SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, float(petlevel - (petlevel / 4))); - // damage range is then petlevel / 2 - SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, float(petlevel + (petlevel / 4))); break; + } + case PROTECTOR_PET: + case GUARDIAN_PET: + { + if (CreatureClassLvlStats const* cCLS = sObjectMgr.GetCreatureClassLvlStats(petlevel, cInfo->UnitClass, cInfo->Expansion)) + { + health = cCLS->BaseHealth; + mana = cCLS->BaseMana; + armor = cCLS->BaseArmor; + + // Melee + minDmg = (cCLS->BaseDamage * cInfo->DamageVariance + (cCLS->BaseMeleeAttackPower / 14) * (cInfo->MeleeBaseAttackTime/1000)) * cInfo->DamageMultiplier; + + // Get custom setting + minDmg *= _GetDamageMod(cInfo->Rank); + + // If the damage value is not passed on as float it will result in damage = 1; but only for guardian type pets, though... + SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, float(minDmg)); + SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, float(minDmg * 1.5)); + + // Ranged + minDmg = (cCLS->BaseDamage * cInfo->DamageVariance + (cCLS->BaseRangedAttackPower / 14) * (cInfo->RangedBaseAttackTime/1000)) * cInfo->DamageMultiplier; + + // Get custom setting + minDmg *= _GetDamageMod(cInfo->Rank); + + SetBaseWeaponDamage(RANGED_ATTACK, MINDAMAGE, float(minDmg)); + SetBaseWeaponDamage(RANGED_ATTACK, MAXDAMAGE, float(minDmg * 1.5)); + } + else // TODO: Remove fallback to creature_template data when DB is ready + { + if (petlevel >= cInfo->MaxLevel) + { + health = cInfo->MaxLevelHealth; + mana = cInfo->MaxLevelMana; + } + else if (petlevel <= cInfo->MinLevel) + { + health = cInfo->MinLevelHealth; + mana = cInfo->MinLevelMana; + } + else + { + float hMinLevel = cInfo->MinLevelHealth / cInfo->MinLevel; + float hMaxLevel = cInfo->MaxLevelHealth / cInfo->MaxLevel; + float mMinLevel = cInfo->MinLevelMana / cInfo->MinLevel; + float mMaxLevel = cInfo->MaxLevelMana / cInfo->MaxLevel; + + health = (hMaxLevel - ((hMaxLevel - hMinLevel) / 2)) * petlevel; + mana = (mMaxLevel - ((mMaxLevel - mMinLevel) / 2)) * petlevel; + } + + sLog.outErrorDb("Pet::InitStatsForLevel> Error trying to set stats for creature %s (entry: %u) using ClassLevelStats; not enough data to do it!", GetGuidStr().c_str(), cInfo->Entry); + + SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, float(cInfo->MinMeleeDmg)); + SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, float(cInfo->MaxMeleeDmg)); + + SetBaseWeaponDamage(RANGED_ATTACK, MINDAMAGE, float(cInfo->MinRangedDmg)); + SetBaseWeaponDamage(RANGED_ATTACK, MAXDAMAGE, float(cInfo->MaxRangedDmg)); + } + + break; + } default: - sLog.outError("Pet have incorrect type (%u) for levelup.", getPetType()); - break; + sLog.outError("Pet have incorrect type (%u) for level handling.", getPetType()); } - for (int i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; ++i) - SetModifierValue(UnitMods(UNIT_MOD_RESISTANCE_START + i), BASE_VALUE, float(createResistance[i])); + // Hunter's pets' should NOT use creature's original modifiers/multipliers + if (getPetType() != HUNTER_PET) + { + health *= cInfo->HealthMultiplier; + if (mana > 0) + mana *= cInfo->PowerMultiplier; + + armor *= cInfo->ArmorMultiplier; + } + + // Apply custom health setting (from config) + health *= _GetHealthMod(cInfo->Rank); + + // Need to update stats before setting health and power or it will bug out in-game displaying it as the mob missing about 2/3 UpdateAllStats(); - SetHealth(GetMaxHealth()); - SetPower(GetPowerType(), GetMaxPower(GetPowerType())); + // A pet cannot not have health + if (health < 1) + health = 1; - return true; + // Set health + SetCreateHealth(health); + SetMaxHealth(health); + SetHealth(health); + SetModifierValue(UNIT_MOD_HEALTH, BASE_VALUE, health); + + // Set mana + SetCreateMana(mana); + SetMaxPower(POWER_MANA, mana); + SetPower(POWER_MANA, mana); + SetModifierValue(UNIT_MOD_MANA, BASE_VALUE, mana); + + // Remove rage bar from pets (By setting rage = 0, and ensuring it stays that way by setting max rage = 0 as well) + SetMaxPower(POWER_RAGE, 0); + SetPower(POWER_RAGE, 0); + SetModifierValue(UNIT_MOD_RAGE, BASE_VALUE, 0); + + // Set armor + SetModifierValue(UNIT_MOD_ARMOR, BASE_VALUE, armor); + + return; } bool Pet::HaveInDiet(ItemPrototype const* item) const diff --git a/src/game/Object/Pet.h b/src/game/Object/Pet.h index a9b010bbc..987599840 100644 --- a/src/game/Object/Pet.h +++ b/src/game/Object/Pet.h @@ -184,10 +184,11 @@ class Pet : public Creature void GivePetXP(uint32 xp); void GivePetLevel(uint32 level); void SynchronizeLevelWithOwner(); - bool InitStatsForLevel(uint32 level, Unit* owner = NULL); + void InitStatsForLevel(uint32 level); bool HaveInDiet(ItemPrototype const* item) const; uint32 GetCurrentFoodBenefitLevel(uint32 itemlevel); void SetDuration(int32 dur) { m_duration = dur; } + int32 GetDuration() { return m_duration; } int32 GetBonusDamage() { return m_bonusdamage; } void SetBonusDamage(int32 damage) { m_bonusdamage = damage; } @@ -202,10 +203,6 @@ class Pet : public Creature void UpdateDamagePhysical(WeaponAttackType attType) override; uint32 GetCurrentEquipmentId() const { return m_equipmentId; } - static float _GetHealthMod(int32 Rank); ///< Get custom factor to scale health (default 1, CONFIG_FLOAT_RATE_CREATURE_*_HP) - static float _GetDamageMod(int32 Rank); ///< Get custom factor to scale damage (default 1, CONFIG_FLOAT_RATE_*_DAMAGE) - static float _GetSpellDamageMod(int32 Rank); ///< Get custom factor to scale spell damage (default 1, CONFIG_FLOAT_RATE_*_SPELLDAMAGE) - bool CanTakeMoreActiveSpells(uint32 SpellIconID); void ToggleAutocast(uint32 spellid, bool apply); diff --git a/src/game/Object/PetAI.cpp b/src/game/Object/PetAI.cpp index 36da35e93..9dd029f2d 100644 --- a/src/game/Object/PetAI.cpp +++ b/src/game/Object/PetAI.cpp @@ -50,22 +50,24 @@ PetAI::PetAI(Creature* c) : CreatureAI(c), i_tracker(TIME_INTERVAL_LOOK), inComb void PetAI::MoveInLineOfSight(Unit* pWho) { - if (m_creature->getVictim()) - return; - - if (!m_creature->GetCharmInfo() || !m_creature->GetCharmInfo()->HasReactState(REACT_AGGRESSIVE)) - return; - - if (m_creature->CanInitiateAttack() && pWho->IsTargetableForAttack() && - m_creature->IsHostileTo(pWho) && pWho->isInAccessablePlaceFor(m_creature)) - { - if (!m_creature->CanFly() && m_creature->GetDistanceZ(pWho) > CREATURE_Z_ATTACK_RANGE) + if (Unit* victim = m_creature->getVictim()) + if (victim->IsAlive()) return; - if (m_creature->IsWithinDistInMap(pWho, m_creature->GetAttackDistance(pWho)) && m_creature->IsWithinLOSInMap(pWho)) + if (CharmInfo* charmInfo = m_creature->GetCharmInfo()) + { + if (charmInfo->HasReactState(REACT_AGGRESSIVE) + && !(m_creature->IsPet() && ((Pet*)m_creature)->GetModeFlags() & PET_MODE_DISABLE_ACTIONS) + && pWho && pWho->IsTargetableForAttack() && pWho->isInAccessablePlaceFor(m_creature) + && (m_creature->IsHostileTo(pWho) || pWho->IsHostileTo(m_creature->GetCharmerOrOwner())) + && m_creature->IsWithinDistInMap(pWho, m_creature->GetAttackDistance(pWho)) + && m_creature->GetDistanceZ(pWho) <= CREATURE_Z_ATTACK_RANGE + && m_creature->IsWithinLOSInMap(pWho)) { - pWho->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH); AttackStart(pWho); + + if (Unit* owner = m_creature->GetOwner()) + owner->SetInCombatState(true, pWho); } } } @@ -81,7 +83,9 @@ void PetAI::AttackStart(Unit* u) // thus with the following clear the original TMG gets invalidated and crash, doh // hope it doesn't start to leak memory without this :-/ // i_pet->Clear(); - HandleMovementOnAttackStart(u); + if (!m_creature->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE)) + HandleMovementOnAttackStart(u); + inCombat = true; } } diff --git a/src/game/Object/Player.cpp b/src/game/Object/Player.cpp index 43f41859a..3e2779fb5 100644 --- a/src/game/Object/Player.cpp +++ b/src/game/Object/Player.cpp @@ -2687,6 +2687,9 @@ void Player::GiveLevel(uint32 level) MailDraft(mailReward->mailTemplateId).SendMailTo(this, MailSender(MAIL_CREATURE, mailReward->senderEntry)); GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_REACH_LEVEL); + + // resend quests status directly + SendQuestGiverStatusMultiple(); } void Player::UpdateFreeTalentPoints(bool resetIfNeed) @@ -2877,7 +2880,7 @@ void Player::InitStatsForLevel(bool reapplyMods) RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_AFK | PLAYER_FLAGS_DND | PLAYER_FLAGS_GM | PLAYER_FLAGS_GHOST); RemoveStandFlags(UNIT_STAND_FLAGS_ALL); // one form stealth modified bytes - RemoveByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP | UNIT_BYTE2_FLAG_SUPPORTABLE); + RemoveByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP); // restore if need some important flags SetUInt32Value(PLAYER_FIELD_BYTES2, 0); // flags empty by default @@ -13897,6 +13900,9 @@ void Player::RewardQuest(Quest const* pQuest, uint32 reward, Object* questGiver, saBounds = sSpellMgr.GetSpellAreaForAreaMapBounds(0); for (SpellAreaForAreaMap::const_iterator itr = saBounds.first; itr != saBounds.second; ++itr) itr->second->ApplyOrRemoveSpellIfCan(this, zone, area, false); + + // resend quests status directly + SendQuestGiverStatusMultiple(); } void Player::IncompleteQuest(uint32 quest_id) @@ -15199,6 +15205,60 @@ void Player::SendQuestUpdateAddCreatureOrGo(Quest const* pQuest, ObjectGuid guid SetQuestSlotCounter(log_slot, creatureOrGO_idx, count); } +void Player::SendQuestGiverStatusMultiple() +{ + uint32 count = 0; + + WorldPacket data(SMSG_QUESTGIVER_STATUS_MULTIPLE, 4); + data << uint32(count); // placeholder + + for (GuidSet::const_iterator itr = m_clientGUIDs.begin(); itr != m_clientGUIDs.end(); ++itr) + { + if (itr->IsAnyTypeCreature()) + { + // need also pet quests case support + Creature* questgiver = GetMap()->GetAnyTypeCreature(*itr); + + if (!questgiver || questgiver->IsHostileTo(this)) + continue; + + if (!questgiver->HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_QUESTGIVER)) + continue; + + uint8 dialogStatus = sScriptMgr.GetDialogStatus(this, questgiver); + + if (dialogStatus == DIALOG_STATUS_REWARD_REP) + dialogStatus = GetSession()->getDialogStatus(this, questgiver, DIALOG_STATUS_NONE); + + data << questgiver->GetObjectGuid(); + data << uint8(dialogStatus); + ++count; + } + else if (itr->IsGameObject()) + { + GameObject* questgiver = GetMap()->GetGameObject(*itr); + + if (!questgiver) + continue; + + if (questgiver->GetGoType() != GAMEOBJECT_TYPE_QUESTGIVER) + continue; + + uint8 dialogStatus = sScriptMgr.GetDialogStatus(this, questgiver); + + if (dialogStatus == DIALOG_STATUS_REWARD_REP) + dialogStatus = GetSession()->getDialogStatus(this, questgiver, DIALOG_STATUS_NONE); + + data << questgiver->GetObjectGuid(); + data << uint8(dialogStatus); + ++count; + } + } + + data.put(0, count); // write real count + GetSession()->SendPacket(&data); +} + /*********************************************************/ /*** LOAD SYSTEM ***/ /*********************************************************/ @@ -16840,6 +16900,7 @@ void Player::_LoadTalents(QueryResult* result) while (result->NextRow()); delete result; } + UpdateGroupLeaderFlag(); } void Player::_LoadGroup(QueryResult* result) @@ -21497,6 +21558,18 @@ PartyResult Player::CanUninviteFromGroup() const return ERR_PARTY_RESULT_OK; } +void Player::UpdateGroupLeaderFlag(const bool remove /*= false*/) +{ + const Group* group = GetGroup(); + if (HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GROUP_LEADER)) + { + if (remove || !group || group->GetLeaderGuid() != GetObjectGuid()) + RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_GROUP_LEADER); + } + else if (!remove && group && group->GetLeaderGuid() == GetObjectGuid()) + SetFlag(PLAYER_FLAGS, PLAYER_FLAGS_GROUP_LEADER); +} + void Player::SetBattleGroundRaid(Group* group, int8 subgroup) { // we must move references from m_group to m_originalGroup diff --git a/src/game/Object/Player.h b/src/game/Object/Player.h index 79157843e..0fde40855 100644 --- a/src/game/Object/Player.h +++ b/src/game/Object/Player.h @@ -1558,6 +1558,7 @@ class Player : public Unit void SendQuestConfirmAccept(const Quest* pQuest, Player* pReceiver); void SendPushToPartyResponse(Player* pPlayer, uint32 msg); void SendQuestUpdateAddCreatureOrGo(Quest const* pQuest, ObjectGuid guid, uint32 creatureOrGO_idx, uint32 count); + void SendQuestGiverStatusMultiple(); ObjectGuid GetDividerGuid() const { return m_dividerGuid; } void SetDividerGuid(ObjectGuid guid) { m_dividerGuid = guid; } @@ -2531,6 +2532,7 @@ class Player : public Unit uint32 GetNextResetTalentsCost() const; Player* GetNextRandomRaidMember(float radius); PartyResult CanUninviteFromGroup() const; + void UpdateGroupLeaderFlag(const bool remove = false); // BattleGround Group System void SetBattleGroundRaid(Group* group, int8 subgroup = -1); void RemoveFromBattleGroundRaid(); diff --git a/src/game/Object/SpellMgr.cpp b/src/game/Object/SpellMgr.cpp index eef79caaa..e902fd101 100644 --- a/src/game/Object/SpellMgr.cpp +++ b/src/game/Object/SpellMgr.cpp @@ -855,7 +855,7 @@ bool IsPositiveEffect(SpellEntry const* spellproto, SpellEffectIndex effIndex) case SPELL_AURA_MOD_INCREASE_HEALTH_PERCENT: case SPELL_AURA_MOD_DAMAGE_PERCENT_DONE: if (spellEffect->CalculateSimpleValue() > 0) - return true; // some expected positive spells have SPELL_ATTR_EX_NEGATIVE or unclear target modes + return true; // some expected positive spells have SPELL_ATTR_NEGATIVE or unclear target modes break; case SPELL_AURA_ADD_TARGET_TRIGGER: return true; @@ -924,7 +924,7 @@ bool IsPositiveEffect(SpellEntry const* spellproto, SpellEffectIndex effIndex) spellproto->GetSpellFamilyName() == SPELLFAMILY_GENERIC) return false; // but not this if this first effect (don't found better check) - if (spellproto->HasAttribute(SPELL_ATTR_UNK26) && effIndex == EFFECT_INDEX_0) + if (spellproto->HasAttribute(SPELL_ATTR_NEGATIVE) && effIndex == EFFECT_INDEX_0) return false; break; case SPELL_AURA_TRANSFORM: @@ -1032,7 +1032,7 @@ bool IsPositiveEffect(SpellEntry const* spellproto, SpellEffectIndex effIndex) return false; // AttributesEx check - if (spellproto->HasAttribute(SPELL_ATTR_EX_NEGATIVE)) + if (spellproto->HasAttribute(SPELL_ATTR_NEGATIVE)) return false; // ok, positive @@ -2728,6 +2728,17 @@ bool SpellMgr::IsNoStackSpellDueToSpell(uint32 spellId_1, uint32 spellId_2) cons return true; } +bool SpellMgr::IsSpellCanAffectSpell(SpellEntry const* spellInfo_1, SpellEntry const* spellInfo_2) const +{ + for (int i = 0; i < MAX_EFFECT_INDEX; ++i) + { + ClassFamilyMask mask = spellInfo_1->GetEffectSpellClassMask(SpellEffectIndex(i)); + if (spellInfo_2->IsFitToFamilyMask(mask)) + return true; + } + return false; +} + bool SpellMgr::IsProfessionOrRidingSpell(uint32 spellId) { SpellEntry const* spellInfo = sSpellStore.LookupEntry(spellId); diff --git a/src/game/Object/SpellMgr.h b/src/game/Object/SpellMgr.h index 48d16dbb7..e462969a6 100644 --- a/src/game/Object/SpellMgr.h +++ b/src/game/Object/SpellMgr.h @@ -1155,6 +1155,9 @@ class SpellMgr return !canStackSpellRanksInSpellBook(spellInfo) && GetSpellRank(spellInfo->Id) != 0; } + // return true if spell1 can affect spell2 + bool IsSpellCanAffectSpell(SpellEntry const* spellInfo_1, SpellEntry const* spellInfo_2) const; + SpellEntry const* SelectAuraRankForLevel(SpellEntry const* spellInfo, uint32 Level) const; // Spell learning diff --git a/src/game/Object/Unit.cpp b/src/game/Object/Unit.cpp index 3937eae55..ebd68dbc7 100644 --- a/src/game/Object/Unit.cpp +++ b/src/game/Object/Unit.cpp @@ -2898,12 +2898,7 @@ void Unit::AttackerStateUpdate(Unit* pVictim, WeaponAttackType attType, bool ext if (IsNonMeleeSpellCasted(false)) return; - uint32 hitInfo; - if (attType == BASE_ATTACK) - hitInfo = HITINFO_NORMALSWING2; - else if (attType == OFF_ATTACK) - hitInfo = HITINFO_LEFTSWING; - else + if (attType == RANGED_ATTACK) return; // ignore ranged case uint32 extraAttacks = m_extraAttacks; @@ -2945,7 +2940,7 @@ void Unit::AttackerStateUpdate(Unit* pVictim, WeaponAttackType attType, bool ext else DEBUG_FILTER_LOG(LOG_FILTER_COMBAT, "AttackerStateUpdate: (NPC) %u attacked %u (TypeId: %u) for %u dmg, absorbed %u, blocked %u, resisted %u.", GetGUIDLow(), pVictim->GetGUIDLow(), pVictim->GetTypeId(), damageInfo.damage, damageInfo.absorb, damageInfo.blocked_amount, damageInfo.resist); - + if (Unit* owner = GetOwner()) if (owner->GetTypeId() == TYPEID_UNIT) { @@ -4637,21 +4632,27 @@ bool Unit::RemoveNoStackAurasDueToAuraHolder(SpellAuraHolder* holder) } // non single (per caster) per target spell specific (possible single spell per target at caster) - if (!is_spellSpecPerTargetPerCaster && !is_spellSpecPerTarget && sSpellMgr.IsNoStackSpellDueToSpell(spellId, i_spellId)) + if (!is_spellSpecPerTargetPerCaster && !is_spellSpecPerTarget) { - // Its a parent aura (create this aura in ApplyModifier) - if ((*i).second->IsInUse()) - { - sLog.outError("SpellAuraHolder (Spell %u) is in process but attempt removed at SpellAuraHolder (Spell %u) adding, need add stack rule for Unit::RemoveNoStackAurasDueToAuraHolder", i->second->GetId(), holder->GetId()); + SpellEntry const* triggeredBy = holder->GetTriggeredBy(); + if (triggeredBy && sSpellMgr.IsSpellCanAffectSpell(triggeredBy, i_spellProto)) // check if this spell can be triggered by any talent aura continue; + + if (sSpellMgr.IsNoStackSpellDueToSpell(spellProto->Id, i_spellProto->Id)) + { + // Its a parent aura (create this aura in ApplyModifier) + if ((*i).second->IsInUse()) + { + sLog.outError("SpellAuraHolder (Spell %u) is in process but attempt removed at SpellAuraHolder (Spell %u) adding, need add stack rule for Unit::RemoveNoStackAurasDueToAuraHolder", i->second->GetId(), holder->GetId()); + continue; + } + RemoveAurasDueToSpell(i_spellId); + + if (m_spellAuraHolders.empty()) + break; + else + next = m_spellAuraHolders.begin(); } - RemoveAurasDueToSpell(i_spellId); - - if (m_spellAuraHolders.empty()) - break; - else - next = m_spellAuraHolders.begin(); - continue; } @@ -10945,7 +10946,7 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* pTarget, uint32 procFlag, for (SpellAuraHolderMap::const_iterator itr = GetSpellAuraHolderMap().begin(); itr != GetSpellAuraHolderMap().end(); ++itr) { // skip deleted auras (possible at recursive triggered call - if (itr->second->IsDeleted()) + if (itr->second->GetState() != SPELLAURAHOLDER_STATE_READY || itr->second->IsDeleted()) continue; SpellProcEventEntry const* spellProcEvent = NULL; diff --git a/src/game/Server/SharedDefines.h b/src/game/Server/SharedDefines.h index 8bb80d6d7..b74fb265f 100644 --- a/src/game/Server/SharedDefines.h +++ b/src/game/Server/SharedDefines.h @@ -297,7 +297,7 @@ enum SpellAttributes SPELL_ATTR_UNK23 = 0x00800000,// 23 castable while dead? SPELL_ATTR_CASTABLE_WHILE_MOUNTED = 0x01000000,// 24 castable while mounted SPELL_ATTR_DISABLED_WHILE_ACTIVE = 0x02000000,// 25 Activate and start cooldown after aura fade or remove summoned creature or go - SPELL_ATTR_UNK26 = 0x04000000,// 26 + SPELL_ATTR_NEGATIVE = 0x04000000,// 26 Almost all negative spell have it SPELL_ATTR_CASTABLE_WHILE_SITTING = 0x08000000,// 27 castable while sitting SPELL_ATTR_CANT_USED_IN_COMBAT = 0x10000000,// 28 Cannot be used in combat SPELL_ATTR_UNAFFECTED_BY_INVULNERABILITY = 0x20000000,// 29 unaffected by invulnerability (hmm possible not...) @@ -314,7 +314,7 @@ enum SpellAttributesEx SPELL_ATTR_EX_UNK4 = 0x00000010,// 4 SPELL_ATTR_EX_NOT_BREAK_STEALTH = 0x00000020,// 5 Not break stealth SPELL_ATTR_EX_CHANNELED_2 = 0x00000040,// 6 channeled 2 - SPELL_ATTR_EX_NEGATIVE = 0x00000080,// 7 + SPELL_ATTR_EX_UNK7 = 0x00000080,// 7 SPELL_ATTR_EX_NOT_IN_COMBAT_TARGET = 0x00000100,// 8 Spell req target not to be in combat state SPELL_ATTR_EX_UNK9 = 0x00000200,// 9 SPELL_ATTR_EX_NO_THREAT = 0x00000400,// 10 no generates threat on cast 100% diff --git a/src/game/WorldHandlers/Group.cpp b/src/game/WorldHandlers/Group.cpp index f81cc143f..ef98b985b 100644 --- a/src/game/WorldHandlers/Group.cpp +++ b/src/game/WorldHandlers/Group.cpp @@ -130,7 +130,8 @@ bool Group::Create(ObjectGuid guid, const char* name) m_lootMethod = GROUP_LOOT; m_lootThreshold = ITEM_QUALITY_UNCOMMON; - m_looterGuid = guid; + m_masterLooterGuid = guid; + m_currentLooterGuid = guid; // used for round robin looter m_dungeonDifficulty = DUNGEON_DIFFICULTY_NORMAL; m_raidDifficulty = RAID_DIFFICULTY_10MAN_NORMAL; @@ -155,7 +156,7 @@ bool Group::Create(ObjectGuid guid, const char* name) CharacterDatabase.PExecute("INSERT INTO groups (groupId,leaderGuid,mainTank,mainAssistant,lootMethod,looterGuid,lootThreshold,icon1,icon2,icon3,icon4,icon5,icon6,icon7,icon8,groupType,difficulty,raiddifficulty) " "VALUES ('%u','%u','%u','%u','%u','%u','%u','" UI64FMTD "','" UI64FMTD "','" UI64FMTD "','" UI64FMTD "','" UI64FMTD "','" UI64FMTD "','" UI64FMTD "','" UI64FMTD "','%u','%u','%u')", m_Id, m_leaderGuid.GetCounter(), m_mainTankGuid.GetCounter(), m_mainAssistantGuid.GetCounter(), uint32(m_lootMethod), - m_looterGuid.GetCounter(), uint32(m_lootThreshold), + m_masterLooterGuid.GetCounter(), uint32(m_lootThreshold), m_targetIcons[0].GetRawValue(), m_targetIcons[1].GetRawValue(), m_targetIcons[2].GetRawValue(), m_targetIcons[3].GetRawValue(), m_targetIcons[4].GetRawValue(), m_targetIcons[5].GetRawValue(), @@ -169,6 +170,8 @@ bool Group::Create(ObjectGuid guid, const char* name) if (!isBGGroup()) CharacterDatabase.CommitTransaction(); + _updateLeaderFlag(); + return true; } @@ -269,8 +272,10 @@ bool Group::AddLeaderInvite(Player* player) if (!AddInvite(player)) return false; + _updateLeaderFlag(true); m_leaderGuid = player->GetObjectGuid(); m_leaderName = player->GetName(); + _updateLeaderFlag(); return true; } @@ -484,6 +489,7 @@ void Group::Disband(bool hideDestroy) ResetInstances(INSTANCE_RESET_GROUP_DISBAND, true, NULL); } + _updateLeaderFlag(true); m_leaderGuid.Clear(); m_leaderName.clear(); } @@ -1367,6 +1373,12 @@ void Group::_removeRolls(ObjectGuid guid) } } +void Group::_updateLeaderFlag(const bool remove /*= false*/) +{ + if (Player* player = sObjectMgr.GetPlayer(m_leaderGuid)) + player->UpdateGroupLeaderFlag(remove); +} + bool Group::_setMembersGroup(ObjectGuid guid, uint8 group) { member_witerator slot = _getMemberWSlot(guid); diff --git a/src/game/WorldHandlers/Group.h b/src/game/WorldHandlers/Group.h index 858a85c55..d03bb6305 100644 --- a/src/game/WorldHandlers/Group.h +++ b/src/game/WorldHandlers/Group.h @@ -406,6 +406,8 @@ class Group void _setLeader(ObjectGuid guid); void _removeRolls(ObjectGuid guid); + + void _updateLeaderFlag(const bool remove = false); bool _setMembersGroup(ObjectGuid guid, uint8 group); bool _setAssistantFlag(ObjectGuid guid, const bool& state); @@ -489,6 +491,8 @@ class Group LootMethod m_lootMethod; ItemQualities m_lootThreshold; ObjectGuid m_looterGuid; + ObjectGuid m_masterLooterGuid; + ObjectGuid m_currentLooterGuid; Rolls RollId; BoundInstancesMap m_boundInstances[MAX_DIFFICULTY]; uint8* m_subGroupsCounts; diff --git a/src/game/WorldHandlers/PetHandler.cpp b/src/game/WorldHandlers/PetHandler.cpp index 019eada63..1c55f19e0 100644 --- a/src/game/WorldHandlers/PetHandler.cpp +++ b/src/game/WorldHandlers/PetHandler.cpp @@ -60,9 +60,9 @@ void WorldSession::HandlePetAction(WorldPacket& recv_data) return; } - if (GetPlayer()->GetObjectGuid() != pet->GetCharmerOrOwnerGuid()) + if (_player->GetObjectGuid() != pet->GetCharmerOrOwnerGuid()) { - sLog.outError("HandlePetAction: %s isn't controlled by %s.", petGuid.GetString().c_str(), GetPlayer()->GetGuidStr().c_str()); + sLog.outError("HandlePetAction: %s isn't controlled by %s.", petGuid.GetString().c_str(), _player->GetGuidStr().c_str()); return; } @@ -118,45 +118,35 @@ void WorldSession::HandlePetAction(WorldPacket& recv_data) } case COMMAND_ATTACK: // spellid=1792 // ATTACK { - Unit* TargetUnit = _player->GetMap()->GetUnit(targetGuid); - if (!TargetUnit) - return; + ((Pet*)pet)->SetIsRetreating(); + ((Pet*)pet)->SetSpellOpener(); - // not let attack friendly units. - if (GetPlayer()->IsFriendlyTo(TargetUnit)) - return; - // Not let attack through obstructions - if (!pet->IsWithinLOSInMap(TargetUnit)) - return; + Unit* targetUnit = targetGuid ? _player->GetMap()->GetUnit(targetGuid) : nullptr; - // This is true if pet has no target or has target but targets differs. - if (pet->getVictim() != TargetUnit) + if (targetUnit && targetUnit != pet && targetUnit->IsTargetableForAttack() && targetUnit->isInAccessablePlaceFor((Creature*)pet)) { - if (pet->getVictim()) - pet->AttackStop(); + _player->SetInCombatState(true, targetUnit); - if (pet->hasUnitState(UNIT_STAT_CONTROLLED)) - { - pet->Attack(TargetUnit, true); - pet->SendPetAIReaction(); - } - else + // This is true if pet has no target or has target but targets differs. + if (pet->getVictim() != targetUnit) { + pet->AttackStop(); pet->GetMotionMaster()->Clear(); if (((Creature*)pet)->AI()) - ((Creature*)pet)->AI()->AttackStart(TargetUnit); - - // 10% chance to play special pet attack talk, else growl - if (((Creature*)pet)->IsPet() && ((Pet*)pet)->getPetType() == SUMMON_PET && pet != TargetUnit && roll_chance_i(10)) - pet->SendPetTalk((uint32)PET_TALK_ATTACK); - else { - // 90% chance for pet and 100% chance for charmed creature + ((Creature*)pet)->AI()->AttackStart(targetUnit); + // 10% chance to play special warlock pet attack talk, else growl + if (((Creature*)pet)->IsPet() && ((Pet*)pet)->getPetType() == SUMMON_PET && roll_chance_i(10)) + pet->SendPetTalk((uint32)PET_TALK_ATTACK); + pet->SendPetAIReaction(); } + else + pet->Attack(targetUnit, true); } } + break; } case COMMAND_ABANDON: // abandon (hunter pet) or dismiss (summoned pet) @@ -208,9 +198,10 @@ void WorldSession::HandlePetAction(WorldPacket& recv_data) case ACT_PASSIVE: // 0x01 case ACT_ENABLED: // 0xC1 spell { - Unit* unit_target = NULL; - if (targetGuid) - unit_target = _player->GetMap()->GetUnit(targetGuid); + ((Pet*)pet)->SetIsRetreating(); + ((Pet*)pet)->SetSpellOpener(); + + Unit* unit_target = targetGuid ? _player->GetMap()->GetUnit(targetGuid) : nullptr; // do not cast unknown spells SpellEntry const* spellInfo = sSpellStore.LookupEntry(spellid); @@ -228,7 +219,10 @@ void WorldSession::HandlePetAction(WorldPacket& recv_data) SpellEffectEntry const* spellEffect = spellInfo->GetSpellEffect(SpellEffectIndex(i)); if (!spellEffect) continue; - if (spellEffect->EffectImplicitTargetA == TARGET_ALL_ENEMY_IN_AREA || spellEffect->EffectImplicitTargetA == TARGET_ALL_ENEMY_IN_AREA_INSTANT || spellEffect->EffectImplicitTargetA == TARGET_ALL_ENEMY_IN_AREA_CHANNELED) + + if (spellEffect->EffectImplicitTargetA == TARGET_ALL_ENEMY_IN_AREA + || spellEffect->EffectImplicitTargetA == TARGET_ALL_ENEMY_IN_AREA_INSTANT + || spellEffect->EffectImplicitTargetA == TARGET_ALL_ENEMY_IN_AREA_CHANNELED) return; } @@ -236,6 +230,8 @@ void WorldSession::HandlePetAction(WorldPacket& recv_data) if (!pet->HasSpell(spellid) || IsPassiveSpell(spellInfo)) return; + _player->SetInCombatState(true, unit_target); + pet->clearUnitState(UNIT_STAT_MOVING); Spell* spell = new Spell(pet, spellInfo, false); diff --git a/src/game/WorldHandlers/QuestHandler.cpp b/src/game/WorldHandlers/QuestHandler.cpp index cf41734ca..bf6a564fd 100644 --- a/src/game/WorldHandlers/QuestHandler.cpp +++ b/src/game/WorldHandlers/QuestHandler.cpp @@ -550,14 +550,10 @@ uint32 WorldSession::getDialogStatus(Player* pPlayer, Object* questgiver, uint32 QuestStatus status = pPlayer->GetQuestStatus(quest_id); - if ((status == QUEST_STATUS_COMPLETE && !pPlayer->GetQuestRewardStatus(quest_id)) || - (pQuest->IsAutoComplete() && pPlayer->CanTakeQuest(pQuest, false))) - { - if (pQuest->IsAutoComplete() && pQuest->IsRepeatable()) - dialogStatusNew = DIALOG_STATUS_REWARD_REP; - else - dialogStatusNew = DIALOG_STATUS_REWARD; - } + if (status == QUEST_STATUS_COMPLETE && !pPlayer->GetQuestRewardStatus(quest_id)) + dialogStatusNew = pQuest->IsRepeatable() ? DIALOG_STATUS_REWARD_REP : DIALOG_STATUS_REWARD; + else if (pQuest->IsAutoComplete() && pPlayer->CanTakeQuest(pQuest, false)) + dialogStatusNew = pQuest->IsRepeatable() ? DIALOG_STATUS_AVAILABLE_REP : DIALOG_STATUS_AVAILABLE; else if (status == QUEST_STATUS_INCOMPLETE) dialogStatusNew = DIALOG_STATUS_INCOMPLETE; @@ -614,58 +610,7 @@ void WorldSession::HandleQuestgiverStatusMultipleQuery(WorldPacket& /*recvPacket { DEBUG_LOG("WORLD: Received opcode CMSG_QUESTGIVER_STATUS_MULTIPLE_QUERY"); - uint32 count = 0; - - WorldPacket data(SMSG_QUESTGIVER_STATUS_MULTIPLE, 4); - data << uint32(count); // placeholder - - for (GuidSet::const_iterator itr = _player->m_clientGUIDs.begin(); itr != _player->m_clientGUIDs.end(); ++itr) - { - uint32 dialogStatus = DIALOG_STATUS_NONE; - - if (itr->IsAnyTypeCreature()) - { - // need also pet quests case support - Creature* questgiver = GetPlayer()->GetMap()->GetAnyTypeCreature(*itr); - - if (!questgiver || questgiver->IsHostileTo(_player)) - continue; - - if (!questgiver->HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_QUESTGIVER)) - continue; - - dialogStatus = sScriptMgr.GetDialogStatus(_player, questgiver); - - if (dialogStatus > DIALOG_STATUS_REWARD_REP) - dialogStatus = getDialogStatus(_player, questgiver, DIALOG_STATUS_NONE); - - data << questgiver->GetObjectGuid(); - data << uint32(dialogStatus); - ++count; - } - else if (itr->IsGameObject()) - { - GameObject* questgiver = GetPlayer()->GetMap()->GetGameObject(*itr); - - if (!questgiver) - continue; - - if (questgiver->GetGoType() != GAMEOBJECT_TYPE_QUESTGIVER) - continue; - - dialogStatus = sScriptMgr.GetDialogStatus(_player, questgiver); - - if (dialogStatus > DIALOG_STATUS_REWARD_REP) - dialogStatus = getDialogStatus(_player, questgiver, DIALOG_STATUS_NONE); - - data << questgiver->GetObjectGuid(); - data << uint32(dialogStatus); - ++count; - } - } - - data.put(0, count); // write real count - SendPacket(&data); + _player->SendQuestGiverStatusMultiple(); } bool WorldSession::CanInteractWithQuestGiver(ObjectGuid guid, char const* descr) diff --git a/src/game/WorldHandlers/Spell.cpp b/src/game/WorldHandlers/Spell.cpp index 6e4c6a842..8c35a01cf 100644 --- a/src/game/WorldHandlers/Spell.cpp +++ b/src/game/WorldHandlers/Spell.cpp @@ -432,6 +432,7 @@ Spell::Spell(Unit* caster, SpellEntry const* info, bool triggered, ObjectGuid or m_cast_count = 0; m_glyphIndex = 0; m_triggeredByAuraSpell = NULL; + m_spellAuraHolder = NULL; // Auto Shot & Shoot (wand) m_autoRepeat = IsAutoRepeatRangedSpell(m_spellInfo); @@ -463,7 +464,7 @@ Spell::Spell(Unit* caster, SpellEntry const* info, bool triggered, ObjectGuid or if(!IsPositiveTarget(spellEffect->EffectImplicitTargetA, spellEffect->EffectImplicitTargetB)) m_canReflect = true; else - m_canReflect = m_spellInfo->HasAttribute(SPELL_ATTR_EX_NEGATIVE); + m_canReflect = m_spellInfo->HasAttribute(SPELL_ATTR_EX_UNK7); if (m_canReflect) continue; @@ -1285,6 +1286,9 @@ void Spell::DoAllEffectOnTarget(TargetInfo* target) ((Creature*)m_caster)->AI()->SpellHitTarget(unit, m_spellInfo); if (real_caster && real_caster != m_caster && real_caster->GetTypeId() == TYPEID_UNIT && ((Creature*)real_caster)->AI()) ((Creature*)real_caster)->AI()->SpellHitTarget(unit, m_spellInfo); + + if (m_spellAuraHolder) + m_spellAuraHolder->SetState(SPELLAURAHOLDER_STATE_READY); } void Spell::DoSpellHitOnUnit(Unit* unit, uint32 effectMask) @@ -1403,7 +1407,7 @@ void Spell::DoSpellHitOnUnit(Unit* unit, uint32 effectMask) if (IsSpellAppliesAura(m_spellInfo, effectMask)) { - m_spellAuraHolder = CreateSpellAuraHolder(m_spellInfo, unit, realCaster, m_CastItem); + m_spellAuraHolder = CreateSpellAuraHolder(m_spellInfo, unit, realCaster, m_CastItem, m_triggeredBySpellInfo); m_spellAuraHolder->setDiminishGroup(m_diminishGroup); } else diff --git a/src/game/WorldHandlers/SpellAuras.cpp b/src/game/WorldHandlers/SpellAuras.cpp index a36989dda..7b97c89cf 100644 --- a/src/game/WorldHandlers/SpellAuras.cpp +++ b/src/game/WorldHandlers/SpellAuras.cpp @@ -611,7 +611,7 @@ Aura* CreateAura(SpellEntry const* spellproto, SpellEffectIndex eff, int32* curr return new Aura(spellproto, eff, currentBasePoints, holder, target, caster, castItem); } -SpellAuraHolder* CreateSpellAuraHolder(SpellEntry const* spellproto, Unit* target, WorldObject* caster, Item* castItem /*= nullptr*/, SpellEntry const* triggeredBy /*= nullptr*/) +SpellAuraHolder* CreateSpellAuraHolder(SpellEntry const* spellproto, Unit* target, WorldObject* caster, Item* castItem /*= NULL*/, SpellEntry const* triggeredBy /*= NULL*/) { return new SpellAuraHolder(spellproto, target, caster, castItem, triggeredBy); } @@ -7526,107 +7526,102 @@ void Aura::PeriodicTick() case SPELL_AURA_PERIODIC_HEAL: case SPELL_AURA_OBS_MOD_HEALTH: { - // don't heal target if not alive, mostly death persistent effects from items - if (!target->IsAlive()) - { return; } - Unit* pCaster = GetCaster(); if (!pCaster) return; - // Don't heal target if it is already at max health - if (target->GetHealth() == target->GetMaxHealth()) - return; + bool canApplyHealthPart = true; + + // don't heal target if max health or if not alive, mostly death persistent effects from items + if (!target->IsAlive() || (target->GetHealth() == target->GetMaxHealth())) + canApplyHealthPart = false; // heal for caster damage (must be alive) if (target != pCaster && spellProto->SpellVisual[0] == 163 && !pCaster->IsAlive()) - { return; } + canApplyHealthPart = false; - // ignore non positive values (can be result apply spellmods to aura damage - uint32 amount = m_modifier.m_amount > 0 ? m_modifier.m_amount : 0; - - uint32 pdamage; - - if (m_modifier.m_auraname == SPELL_AURA_OBS_MOD_HEALTH) - { pdamage = uint32(target->GetMaxHealth() * amount / 100); } - else + if (canApplyHealthPart) { - { pdamage = amount; } + // ignore non positive values (can be result apply spellmods to aura damage + uint32 amount = m_modifier.m_amount > 0 ? m_modifier.m_amount : 0; - // Wild Growth (1/7 - 6 + 2*ramainTicks) % - if (classOptions && classOptions->SpellFamilyName == SPELLFAMILY_DRUID && spellProto->SpellIconID == 2864) - { - int32 ticks = GetAuraMaxTicks(); - int32 remainingTicks = ticks - GetAuraTicks(); - int32 addition = int32(amount) * ticks * (-6 + 2 * remainingTicks) / 100; + uint32 pdamage; - if (GetAuraTicks() != 1) - // Item - Druid T10 Restoration 2P Bonus - if (Aura* aura = pCaster->GetAura(70658, EFFECT_INDEX_0)) - addition += abs(int32((addition * aura->GetModifier()->m_amount) / ((ticks - 1) * 100))); - - pdamage = int32(pdamage) + addition; - } - } - - pdamage = target->SpellHealingBonusTaken(pCaster, spellProto, pdamage, DOT, GetStackAmount()); - - // This method can modify pdamage - bool isCrit = IsCritFromAbilityAura(pCaster, pdamage); - - uint32 absorbHeal = 0; - pCaster->CalculateHealAbsorb(pdamage, &absorbHeal); - pdamage -= absorbHeal; - - DETAIL_FILTER_LOG(LOG_FILTER_PERIODIC_AFFECTS, "PeriodicTick: %s heal of %s for %u health (absorbed %u) inflicted by %u", - GetCasterGuid().GetString().c_str(), target->GetGuidStr().c_str(), pdamage, absorbHeal, GetId()); - - int32 gain = target->ModifyHealth(pdamage); - SpellPeriodicAuraLogInfo pInfo(this, pdamage, (pdamage - uint32(gain)), absorbHeal, 0, 0.0f, isCrit); - target->SendPeriodicAuraLog(&pInfo); - - // Set trigger flag - uint32 procAttacker = PROC_FLAG_ON_DO_PERIODIC; - uint32 procVictim = PROC_FLAG_ON_TAKE_PERIODIC; - uint32 procEx = PROC_EX_PERIODIC_POSITIVE | (isCrit ? PROC_EX_CRITICAL_HIT : PROC_EX_NORMAL_HIT); - pCaster->ProcDamageAndSpell(target, procAttacker, procVictim, procEx, gain, BASE_ATTACK, spellProto); - - // add HoTs to amount healed in bgs - if (pCaster->GetTypeId() == TYPEID_PLAYER) - if (BattleGround* bg = ((Player*)pCaster)->GetBattleGround()) - bg->UpdatePlayerScore(((Player*)pCaster), SCORE_HEALING_DONE, gain); - - target->getHostileRefManager().threatAssist(pCaster, float(gain) * 0.5f * sSpellMgr.GetSpellThreatMultiplier(spellProto), spellProto); - - // heal for caster damage - if (target != pCaster && spellProto->SpellVisual[0] == 163) - { - uint32 dmg = spellProto->GetManaPerSecond(); - if (pCaster->GetHealth() <= dmg && pCaster->GetTypeId() == TYPEID_PLAYER) - { - pCaster->RemoveAurasDueToSpell(GetId()); - - // finish current generic/channeling spells, don't affect autorepeat - pCaster->FinishSpell(CURRENT_GENERIC_SPELL); - pCaster->FinishSpell(CURRENT_CHANNELED_SPELL); - } + if (m_modifier.m_auraname == SPELL_AURA_OBS_MOD_HEALTH) + pdamage = uint32(target->GetMaxHealth() * amount / 100); else { - uint32 damage = gain; - uint32 absorb = 0; - pCaster->DealDamageMods(pCaster, damage, &absorb); - pCaster->SendSpellNonMeleeDamageLog(pCaster, GetId(), damage, GetSpellSchoolMask(spellProto), absorb, 0, false, 0, false); + pdamage = amount; - CleanDamage cleanDamage = CleanDamage(0, BASE_ATTACK, MELEE_HIT_NORMAL); - pCaster->DealDamage(pCaster, damage, &cleanDamage, NODAMAGE, GetSpellSchoolMask(spellProto), spellProto, true); + // Wild Growth (1/7 - 6 + 2*ramainTicks) % + if (classOptions && classOptions->SpellFamilyName == SPELLFAMILY_DRUID && spellProto->SpellIconID == 2864) + { + int32 ticks = GetAuraMaxTicks(); + int32 remainingTicks = ticks - GetAuraTicks(); + int32 addition = int32(amount) * ticks * (-6 + 2 * remainingTicks) / 100; + + if (GetAuraTicks() != 1) + // Item - Druid T10 Restoration 2P Bonus + if (Aura* aura = pCaster->GetAura(70658, EFFECT_INDEX_0)) + addition += abs(int32((addition * aura->GetModifier()->m_amount) / ((ticks - 1) * 100))); + + pdamage = int32(pdamage) + addition; + } + } + + pdamage = target->SpellHealingBonusTaken(pCaster, spellProto, pdamage, DOT, GetStackAmount()); + + // This method can modify pdamage + bool isCrit = IsCritFromAbilityAura(pCaster, pdamage); + + uint32 absorbHeal = 0; + pCaster->CalculateHealAbsorb(pdamage, &absorbHeal); + pdamage -= absorbHeal; + + DETAIL_FILTER_LOG(LOG_FILTER_PERIODIC_AFFECTS, "PeriodicTick: %s heal of %s for %u health (absorbed %u) inflicted by %u", + GetCasterGuid().GetString().c_str(), target->GetGuidStr().c_str(), pdamage, absorbHeal, GetId()); + + int32 gain = target->ModifyHealth(pdamage); + SpellPeriodicAuraLogInfo pInfo(this, pdamage, (pdamage - uint32(gain)), absorbHeal, 0, 0.0f, isCrit); + target->SendPeriodicAuraLog(&pInfo); + + // Set trigger flag + uint32 procAttacker = PROC_FLAG_ON_DO_PERIODIC; + uint32 procVictim = PROC_FLAG_ON_TAKE_PERIODIC; + uint32 procEx = PROC_EX_PERIODIC_POSITIVE | (isCrit ? PROC_EX_CRITICAL_HIT : PROC_EX_NORMAL_HIT); + pCaster->ProcDamageAndSpell(target, procAttacker, procVictim, procEx, gain, BASE_ATTACK, spellProto); + + // add HoTs to amount healed in bgs + if (pCaster->GetTypeId() == TYPEID_PLAYER) + if (BattleGround* bg = ((Player*)pCaster)->GetBattleGround()) + bg->UpdatePlayerScore(((Player*)pCaster), SCORE_HEALING_DONE, gain); + + target->getHostileRefManager().threatAssist(pCaster, float(gain) * 0.5f * sSpellMgr.GetSpellThreatMultiplier(spellProto), spellProto); + + // apply damage part to caster if needed (ex. health funnel) + if (target != pCaster && spellProto->SpellVisual[0] == 163) + { + uint32 damage = spellProto->GetManaPerSecond(); + uint32 absorb = 0; + + pCaster->DealDamageMods(pCaster, damage, &absorb); + if (pCaster->GetHealth() > damage) + { + pCaster->SendSpellNonMeleeDamageLog(pCaster, GetId(), damage, GetSpellSchoolMask(spellProto), absorb, 0, false, 0, false); + CleanDamage cleanDamage = CleanDamage(0, BASE_ATTACK, MELEE_HIT_NORMAL); + pCaster->DealDamage(pCaster, damage, &cleanDamage, NODAMAGE, GetSpellSchoolMask(spellProto), spellProto, true); + } + else + { + // cannot apply damage part so we have to cancel responsible aura + pCaster->RemoveAurasDueToSpell(GetId()); + + // finish current generic/channeling spells, don't affect autorepeat + pCaster->FinishSpell(CURRENT_GENERIC_SPELL); + pCaster->FinishSpell(CURRENT_CHANNELED_SPELL); + } } } - -// uint32 procAttacker = PROC_FLAG_ON_DO_PERIODIC;// | PROC_FLAG_SUCCESSFUL_HEAL; -// uint32 procVictim = 0;// ROC_FLAG_ON_TAKE_PERIODIC | PROC_FLAG_TAKEN_HEAL; - // ignore item heals -// if(procSpell && !haveCastItem) -// pCaster->ProcDamageAndSpell(target, procAttacker, procVictim, PROC_EX_NORMAL_HIT, pdamage, BASE_ATTACK, spellProto); break; } case SPELL_AURA_PERIODIC_MANA_LEECH: @@ -10322,11 +10317,15 @@ SpellAuraHolder::~SpellAuraHolder() void SpellAuraHolder::Update(uint32 diff) { + for (int32 i = 0; i < MAX_EFFECT_INDEX; ++i) + if (Aura* aura = m_auras[i]) + aura->UpdateAura(diff); + if (m_duration > 0) { m_duration -= diff; if (m_duration < 0) - { m_duration = 0; } + m_duration = 0; m_timeCla -= diff; @@ -10335,7 +10334,7 @@ void SpellAuraHolder::Update(uint32 diff) if (Unit* caster = GetCaster()) { Powers powertype = Powers(GetSpellProto()->powerType); - m_timeCla = 1 * IN_MILLISECONDS; + m_timeCla = 1*IN_MILLISECONDS; if (SpellPowerEntry const* spellPower = GetSpellProto()->GetSpellPower()) { @@ -10348,36 +10347,32 @@ void SpellAuraHolder::Update(uint32 diff) } } } - } - } - for (int32 i = 0; i < MAX_EFFECT_INDEX; ++i) - if (Aura* aura = m_auras[i]) - { aura->UpdateAura(diff); } - - // Channeled aura required check distance from caster - if (IsChanneledSpell(m_spellProto) && GetCasterGuid() != m_target->GetObjectGuid()) - { - Unit* caster = GetCaster(); - if (!caster) - { - m_target->RemoveAurasByCasterSpell(GetId(), GetCasterGuid()); - return; - } - - // need check distance for channeled target only - if (caster->GetChannelObjectGuid() == m_target->GetObjectGuid()) - { - // Get spell range - float max_range = GetSpellMaxRange(sSpellRangeStore.LookupEntry(m_spellProto->rangeIndex)); - - if (Player* modOwner = caster->GetSpellModOwner()) - { modOwner->ApplySpellMod(GetId(), SPELLMOD_RANGE, max_range, NULL); } - - if (!caster->IsWithinDistInMap(m_target, max_range)) + // Channeled aura required check distance from caster + if (IsChanneledSpell(m_spellProto) && GetCasterGuid() != m_target->GetObjectGuid()) { - caster->InterruptSpell(CURRENT_CHANNELED_SPELL); - return; + Unit* caster = GetCaster(); + if (!caster) + { + m_target->RemoveAurasByCasterSpell(GetId(), GetCasterGuid()); + return; + } + + // need check distance for channeled target only + if (caster->GetChannelObjectGuid() == m_target->GetObjectGuid()) + { + // Get spell range + float max_range = GetSpellMaxRange(sSpellRangeStore.LookupEntry(m_spellProto->rangeIndex)); + + if (Player* modOwner = caster->GetSpellModOwner()) + modOwner->ApplySpellMod(GetId(), SPELLMOD_RANGE, max_range); + + if (!caster->IsWithinDistInMap(m_target, max_range)) + { + caster->InterruptSpell(CURRENT_CHANNELED_SPELL); + return; + } + } } } } diff --git a/src/game/WorldHandlers/SpellAuras.h b/src/game/WorldHandlers/SpellAuras.h index 2ec8e95a5..f3ef1ebdb 100644 --- a/src/game/WorldHandlers/SpellAuras.h +++ b/src/game/WorldHandlers/SpellAuras.h @@ -80,6 +80,13 @@ class Aura; // internal helper struct ReapplyAffectedPassiveAurasHelper; +enum SpellAuraHolderState +{ + SPELLAURAHOLDER_STATE_CREATED = 0, // just created, initialization steps + SPELLAURAHOLDER_STATE_READY = 1, // all initialization steps are done + SPELLAURAHOLDER_STATE_REMOVING = 2 // removing steps +}; + class SpellAuraHolder { public: @@ -108,6 +115,8 @@ class SpellAuraHolder uint32 GetId() const { return m_spellProto->Id; } SpellEntry const* GetSpellProto() const { return m_spellProto; } + SpellAuraHolderState GetState() const { return m_spellAuraHolderState; } + void SetState(SpellAuraHolderState state) { m_spellAuraHolderState = state; } ObjectGuid const& GetCasterGuid() const { return m_casterGuid; } void SetCasterGuid(ObjectGuid guid) { m_casterGuid = guid; } @@ -208,6 +217,8 @@ class SpellAuraHolder ObjectGuid m_castItemGuid; // it is NOT safe to keep a pointer to the item because it may get deleted time_t m_applyTime; SpellEntry const* m_triggeredBy; // Spell responsible for this holder + SpellAuraHolderState m_spellAuraHolderState; // State used to be sure init part is finished (ex there is still some aura to add or effect to process) + uint8 m_auraSlot; // Aura slot on unit (for show in client) uint8 m_auraFlags; // Aura info flag (for send data to client) diff --git a/src/game/WorldHandlers/SpellEffects.cpp b/src/game/WorldHandlers/SpellEffects.cpp index 154a6e1b1..1294e552a 100644 --- a/src/game/WorldHandlers/SpellEffects.cpp +++ b/src/game/WorldHandlers/SpellEffects.cpp @@ -6085,7 +6085,7 @@ void Spell::EffectDispel(SpellEffectEntry const* effect) if (!holder->IsPositive()) positive = false; else - positive = !holder->GetSpellProto()->HasAttribute(SPELL_ATTR_EX_NEGATIVE); + positive = !holder->GetSpellProto()->HasAttribute(SPELL_ATTR_NEGATIVE); // do not remove positive auras if friendly target // negative auras if non-friendly target @@ -6583,41 +6583,44 @@ void Spell::EffectTameCreature(SpellEffectEntry const* /*effect*/) if (plr->IsFFAPvP()) pet->SetFFAPvP(true); - // level of hunter pet can't be less owner level at 5 levels - uint32 level = creatureTarget->getLevel() + 5 < plr->getLevel() ? (plr->getLevel() - 5) : creatureTarget->getLevel(); - - if (!pet->InitStatsForLevel(level)) - { - sLog.outError("Pet::InitStatsForLevel() failed for creature (Entry: %u)!", creatureTarget->GetEntry()); - delete pet; - return; - } - pet->GetCharmInfo()->SetPetNumber(sObjectMgr.GeneratePetNumber(), true); - // this enables pet details window (Shift+P) - pet->AIM_Initialize(); - pet->InitPetCreateSpells(); + + pet->GetCharmInfo()->SetReactState(REACT_DEFENSIVE); + + // level of hunter pet can't be less owner level at 5 levels + uint32 cLevel = creatureTarget->getLevel(); + uint32 plLevel = plr->getLevel(); + uint32 level = (cLevel + 5) < plLevel ? (plLevel - 5) : cLevel; + pet->InitStatsForLevel(level); pet->InitLevelupSpellsForLevel(); pet->InitTalentForLevel(); - pet->SetHealth(pet->GetMaxHealth()); - // "kill" original creature + pet->SetHealthPercent(creatureTarget->GetHealthPercent()); + + pet->GetCharmInfo()->SetPetNumber(sObjectMgr.GeneratePetNumber(), true); + + // destroy creature object creatureTarget->ForcedDespawn(); // prepare visual effect for levelup pet->SetUInt32Value(UNIT_FIELD_LEVEL, level - 1); - // add to world + // add pet object to the world pet->GetMap()->Add((Creature*)pet); + pet->AIM_Initialize(); // visual effect for levelup pet->SetUInt32Value(UNIT_FIELD_LEVEL, level); + // this enables pet details window (Shift+P) + pet->InitPetCreateSpells(); + // caster have pet now plr->SetPet(pet); - pet->SavePetToDB(PET_SAVE_AS_CURRENT); plr->PetSpellInitialize(); + + pet->SavePetToDB(PET_SAVE_AS_CURRENT); } void Spell::EffectSummonPet(SpellEffectEntry const* effect) @@ -11090,8 +11093,13 @@ void Spell::EffectSummonDeadPet(SpellEffectEntry const* /*effect*/) return; if (pet->IsAlive()) return; - if (damage < 0) - return; + + if (_player->GetDistance(pet) >= 2.0f) + { + float px, py, pz; + m_caster->GetClosePoint(px, py, pz, pet->GetObjectBoundingRadius()); + ((Unit*)pet)->NearTeleportTo(px, py, pz, -m_caster->GetOrientation()); + } pet->SetUInt32Value(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_NONE); pet->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE); @@ -11872,7 +11880,7 @@ void Spell::EffectGravityPull(SpellEffectEntry const* effect) void Spell::EffectCreateTamedPet(SpellEffectEntry const* effect) { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER || unitTarget->getClass() != CLASS_HUNTER) + if (!unitTarget || unitTarget->getClass() != CLASS_HUNTER) return; uint32 creatureEntry = effect->EffectMiscValue; @@ -11884,7 +11892,7 @@ void Spell::EffectCreateTamedPet(SpellEffectEntry const* effect) return; } - Pet* newTamedPet = new Pet; + Pet* newTamedPet = new Pet(HUNTER_PET); CreatureCreatePos pos(unitTarget, unitTarget->GetOrientation()); Map* map = unitTarget->GetMap(); @@ -11896,15 +11904,12 @@ void Spell::EffectCreateTamedPet(SpellEffectEntry const* effect) } newTamedPet->SetRespawnCoord(pos); - newTamedPet->setPetType(HUNTER_PET); newTamedPet->SetOwnerGuid(unitTarget->GetObjectGuid()); newTamedPet->SetCreatorGuid(unitTarget->GetObjectGuid()); newTamedPet->SetUInt32Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_NONE); newTamedPet->setFaction(unitTarget->getFaction()); - newTamedPet->SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP, uint32(time(NULL))); - newTamedPet->SetUInt32Value(UNIT_FIELD_PETEXPERIENCE, 0); - newTamedPet->SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP, 1000); + newTamedPet->SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP, uint32(time(nullptr))); newTamedPet->SetUInt32Value(UNIT_CREATED_BY_SPELL, m_spellInfo->Id); newTamedPet->GetCharmInfo()->SetPetNumber(petNumber, true); @@ -11915,17 +11920,15 @@ void Spell::EffectCreateTamedPet(SpellEffectEntry const* effect) if (unitTarget->IsFFAPvP()) newTamedPet->SetFFAPvP(true); - newTamedPet->InitStatsForLevel(unitTarget->getLevel(), unitTarget); + newTamedPet->InitStatsForLevel(unitTarget->getLevel()); newTamedPet->InitPetCreateSpells(); newTamedPet->InitLevelupSpellsForLevel(); newTamedPet->InitTalentForLevel(); - newTamedPet->RemoveByteFlag(UNIT_FIELD_BYTES_2, 2, UNIT_CAN_BE_RENAMED); + newTamedPet->SetByteFlag(UNIT_FIELD_BYTES_2, 2, UNIT_CAN_BE_RENAMED); newTamedPet->SetByteFlag(UNIT_FIELD_BYTES_2, 2, UNIT_CAN_BE_ABANDONED); newTamedPet->AIM_Initialize(); - newTamedPet->SetHealth(newTamedPet->GetMaxHealth()); - newTamedPet->SetPower(POWER_MANA, newTamedPet->GetMaxPower(POWER_MANA)); float x, y, z; unitTarget->GetClosePoint(x, y, z, newTamedPet->GetObjectBoundingRadius()); diff --git a/src/game/WorldHandlers/UnitAuraProcHandler.cpp b/src/game/WorldHandlers/UnitAuraProcHandler.cpp index fabf1042b..963b3fefb 100644 --- a/src/game/WorldHandlers/UnitAuraProcHandler.cpp +++ b/src/game/WorldHandlers/UnitAuraProcHandler.cpp @@ -4263,7 +4263,7 @@ SpellAuraProcResult Unit::HandleRemoveByDamageChanceProc(Unit* /*pVictim*/, uint SpellAuraProcResult Unit::HandleInvisibilityAuraProc(Unit* pVictim, uint32 damage, Aura* triggeredByAura, SpellEntry const* procSpell, uint32 procFlag, uint32 procEx, uint32 cooldown) { - if (triggeredByAura->GetSpellProto()->HasAttribute(SPELL_ATTR_PASSIVE) || triggeredByAura->GetSpellProto()->HasAttribute(SPELL_ATTR_EX_NEGATIVE)) + if (triggeredByAura->GetSpellProto()->HasAttribute(SPELL_ATTR_PASSIVE) || triggeredByAura->GetSpellProto()->HasAttribute(SPELL_ATTR_NEGATIVE)) return SPELL_AURA_PROC_FAILED; RemoveAurasDueToSpell(triggeredByAura->GetId()); diff --git a/src/game/WorldHandlers/World.cpp b/src/game/WorldHandlers/World.cpp index 4e413818d..52d0656ef 100644 --- a/src/game/WorldHandlers/World.cpp +++ b/src/game/WorldHandlers/World.cpp @@ -805,7 +805,7 @@ void World::LoadConfigSettings(bool reload) setConfig(CONFIG_UINT32_TIMERBAR_FIRE_GMLEVEL, "TimerBar.Fire.GMLevel", SEC_CONSOLE); setConfig(CONFIG_UINT32_TIMERBAR_FIRE_MAX, "TimerBar.Fire.Max", 1); - setConfig(CONFIG_BOOL_PET_UNSUMMON_AT_MOUNT, "PetUnsummonAtMount", true); + setConfig(CONFIG_BOOL_PET_UNSUMMON_AT_MOUNT, "PetUnsummonAtMount", false); // Warden /* badly broken on m3 :( - this causes all these defaults to be set to 0 diff --git a/src/mangosd/mangosd.conf.dist.in b/src/mangosd/mangosd.conf.dist.in index 6da300937..ea1458591 100644 --- a/src/mangosd/mangosd.conf.dist.in +++ b/src/mangosd/mangosd.conf.dist.in @@ -755,8 +755,8 @@ SD2ErrorLogFile = "scriptdev2-errors.log" # # PetUnsummonAtMount # Permanent pet will unsummoned at player mount -# 0 - unsummon only for flying mounts -# Default: 1 - unsummon for any mount +# Default: 0 - unsummon only when appropriate (don't unsummon temp summons on ground mounting) +# 1 - always unsummon controlled pets on mounting # # ClientCacheVersion # Client cache version for client cache data reset. Use any different from DB value and not recently used for triggering reset. @@ -858,7 +858,7 @@ MassMailer.SendPerTick = 10 SkillChance.Prospecting = 0 SkillChance.Milling = 0 OffhandCheckAtTalentsReset = 0 -PetUnsummonAtMount = 1 +PetUnsummonAtMount = 0 ClientCacheVersion = 0 Event.Announce = 0 BeepAtStart = 1