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