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
This commit is contained in:
Charles A Edwards 2016-09-15 14:56:42 +01:00 committed by Antz
parent c4c83f5b58
commit 600205641d
20 changed files with 634 additions and 507 deletions

View file

@ -52,10 +52,6 @@ Pet::Pet(PetType type) :
if (type == MINI_PET) // always passive if (type == MINI_PET) // always passive
charmInfo->SetReactState(REACT_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() Pet::~Pet()
@ -158,13 +154,6 @@ bool Pet::LoadPetFromDB(Player* owner, uint32 petentry, uint32 petnumber, bool c
uint32 pet_number = fields[0].GetUInt32(); uint32 pet_number = fields[0].GetUInt32();
if (current && owner->IsPetNeedBeTemporaryUnsummoned())
{
owner->SetTemporaryUnsummonedPetNumber(pet_number);
delete result;
return false;
}
Map* map = owner->GetMap(); Map* map = owner->GetMap();
CreatureCreatePos pos(owner, owner->GetOrientation(), PET_FOLLOW_DIST, PET_FOLLOW_ANGLE); 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); SetByteValue(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_SUPPORTABLE | UNIT_BYTE2_FLAG_AURAS);
SetUInt32Value(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE); SetUInt32Value(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE);
switch (getPetType()) if (getPetType() == HUNTER_PET)
{ {
case SUMMON_PET: SetByteFlag(UNIT_FIELD_BYTES_2, 2, fields[9].GetBool() ? UNIT_CAN_BE_ABANDONED : UNIT_CAN_BE_RENAMED | UNIT_CAN_BE_ABANDONED);
petlevel = owner->getLevel(); SetPowerType(POWER_FOCUS);
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());
} }
else if (getPetType() != SUMMON_PET)
sLog.outError("Pet have incorrect type (%u) for pet loading.", getPetType());
if (owner->IsPvP()) if (owner->IsPvP())
SetPvP(true); SetPvP(true);
@ -282,21 +266,16 @@ bool Pet::LoadPetFromDB(Player* owner, uint32 petentry, uint32 petnumber, bool c
CastOwnerTalentAuras(); 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(savedhealth > GetMaxHealth() ? GetMaxHealth() : savedhealth);
{ SetPower(powerType, savedpower > GetMaxPower(powerType) ? GetMaxPower(powerType) : savedpower);
SetHealth(GetMaxHealth());
SetPower(powerType, GetMaxPower(powerType)); if (getPetType() == HUNTER_PET && savedhealth <= 0)
} SetDeathState(JUST_DIED);
else
{
SetHealth(savedhealth > GetMaxHealth() ? GetMaxHealth() : savedhealth);
SetPower(powerType, savedpower > GetMaxPower(powerType) ? GetMaxPower(powerType) : savedpower);
}
AIM_Initialize();
map->Add((Creature*)this); map->Add((Creature*)this);
AIM_Initialize();
// Spells should be loaded after pet is added to map, because in CheckCast is check on it // Spells should be loaded after pet is added to map, because in CheckCast is check on it
_LoadSpells(); _LoadSpells();
@ -363,7 +342,13 @@ void Pet::SavePetToDB(PetSaveMode mode)
{ {
// reagents must be returned before save call // reagents must be returned before save call
if (mode == PET_SAVE_REAGENTS) 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 // not save pet as current if another pet temporary unsummoned
else if (mode == PET_SAVE_AS_CURRENT && pOwner->GetTemporaryUnsummonedPetNumber() && else if (mode == PET_SAVE_AS_CURRENT && pOwner->GetTemporaryUnsummonedPetNumber() &&
pOwner->GetTemporaryUnsummonedPetNumber() != m_charmInfo->GetPetNumber()) pOwner->GetTemporaryUnsummonedPetNumber() != m_charmInfo->GetPetNumber())
@ -430,7 +415,7 @@ void Pet::SavePetToDB(PetSaveMode mode)
savePet.addUInt32(uint32(mode)); savePet.addUInt32(uint32(mode));
savePet.addString(m_name); savePet.addString(m_name);
savePet.addUInt32(uint32(HasByteFlag(UNIT_FIELD_BYTES_2, 2, UNIT_CAN_BE_RENAMED) ? 0 : 1)); 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); savePet.addUInt32(curpower);
std::ostringstream ss; std::ostringstream ss;
@ -441,7 +426,7 @@ void Pet::SavePetToDB(PetSaveMode mode)
}; };
savePet.addString(ss); savePet.addString(ss);
savePet.addUInt64(uint64(time(NULL))); savePet.addUInt64(uint64(time(nullptr)));
savePet.addUInt32(uint32(m_resetTalentsCost)); savePet.addUInt32(uint32(m_resetTalentsCost));
savePet.addUInt64(uint64(m_resetTalentsTime)); savePet.addUInt64(uint64(m_resetTalentsTime));
savePet.addUInt32(GetUInt32Value(UNIT_CREATED_BY_SPELL)); savePet.addUInt32(GetUInt32Value(UNIT_CREATED_BY_SPELL));
@ -774,18 +759,13 @@ bool Pet::CreateBaseAtCreature(Creature* creature)
if (!Create(guid, pos, creature->GetCreatureInfo(), pet_number)) if (!Create(guid, pos, creature->GetCreatureInfo(), pet_number))
return false; return false;
CreatureInfo const* cinfo = GetCreatureInfo(); CreatureInfo const* cInfo = GetCreatureInfo();
if (!cinfo) if (!cInfo)
{ {
sLog.outError("CreateBaseAtCreature() failed, creatureInfo is missing!"); sLog.outError("CreateBaseAtCreature() failed, creatureInfo is missing!");
return false; return false;
} }
if (cinfo->CreatureType == CREATURE_TYPE_CRITTER)
{
setPetType(MINI_PET);
return true;
}
SetDisplayId(creature->GetDisplayId()); SetDisplayId(creature->GetDisplayId());
SetNativeDisplayId(creature->GetNativeDisplayId()); SetNativeDisplayId(creature->GetNativeDisplayId());
SetPowerType(POWER_FOCUS); SetPowerType(POWER_FOCUS);
@ -794,118 +774,141 @@ bool Pet::CreateBaseAtCreature(Creature* creature)
SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP, sObjectMgr.GetXPForPetLevel(creature->getLevel())); SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP, sObjectMgr.GetXPForPetLevel(creature->getLevel()));
SetUInt32Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_NONE); 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()]); SetName(cFamily->Name[sWorld.GetDefaultDbcLocale()]);
else else
SetName(creature->GetNameForLocaleIdx(sObjectMgr.GetDBCLocaleIndex())); 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, 1, CLASS_WARRIOR); SetByteValue(UNIT_FIELD_BYTES_0, 3, POWER_FOCUS);
SetByteValue(UNIT_FIELD_BYTES_0, 2, GENDER_NONE); SetSheath(SHEATH_STATE_MELEE);
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); 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));
} SetUInt32Value(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE | UNIT_FLAG_RENAME);
SetUInt32Value(UNIT_MOD_CAST_SPEED, creature->GetUInt32Value(UNIT_MOD_CAST_SPEED));
return true; return true;
} }
bool Pet::InitStatsForLevel(uint32 petlevel, Unit* owner) void Pet::InitStatsForLevel(uint32 petlevel)
{ {
CreatureInfo const* cinfo = GetCreatureInfo(); Unit* owner = GetOwner();
MANGOS_ASSERT(cinfo); 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;
}
SetLevel(petlevel); 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); 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}; 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; SetMeleeDamageSchool(SpellSchools(SPELL_SCHOOL_NORMAL));
createResistance[SPELL_SCHOOL_FIRE] = cinfo->ResistanceFire; SetAttackTime(BASE_ATTACK, BASE_ATTACK_TIME);
createResistance[SPELL_SCHOOL_NATURE] = cinfo->ResistanceNature; SetAttackTime(OFF_ATTACK, BASE_ATTACK_TIME);
createResistance[SPELL_SCHOOL_FROST] = cinfo->ResistanceFrost; SetAttackTime(RANGED_ATTACK, BASE_ATTACK_TIME);
createResistance[SPELL_SCHOOL_SHADOW] = cinfo->ResistanceShadow;
createResistance[SPELL_SCHOOL_ARCANE] = cinfo->ResistanceArcane;
} }
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()) 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: case SUMMON_PET:
{ {
if (owner->GetTypeId() == TYPEID_PLAYER) if (owner)
{ {
switch (owner->getClass()) switch (owner->getClass())
{ {
case CLASS_WARLOCK: case CLASS_WARLOCK:
{ {
// the damage bonus used for pets is either fire or shadow damage, whatever is higher // 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 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)); SetBonusDamage(int32(val * 0.15f));
// bonusAP += val * 0.57; // bonusAP += val * 0.57;
@ -924,109 +927,172 @@ bool Pet::InitStatsForLevel(uint32 petlevel, Unit* owner)
break; 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_PETEXPERIENCE, 0);
SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP, 1000); SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP, 1000);
SetCreateMana(28 + 10 * petlevel); // Info found in pet_levelstats
SetCreateHealth(28 + 30 * petlevel); 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; 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: default:
sLog.outError("Pet have incorrect type (%u) for levelup.", getPetType()); sLog.outError("Pet have incorrect type (%u) for level handling.", getPetType());
break;
} }
for (int i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; ++i) // Hunter's pets' should NOT use creature's original modifiers/multipliers
SetModifierValue(UnitMods(UNIT_MOD_RESISTANCE_START + i), BASE_VALUE, float(createResistance[i])); 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(); UpdateAllStats();
SetHealth(GetMaxHealth()); // A pet cannot not have health
SetPower(GetPowerType(), GetMaxPower(GetPowerType())); 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 bool Pet::HaveInDiet(ItemPrototype const* item) const

View file

@ -184,10 +184,11 @@ class Pet : public Creature
void GivePetXP(uint32 xp); void GivePetXP(uint32 xp);
void GivePetLevel(uint32 level); void GivePetLevel(uint32 level);
void SynchronizeLevelWithOwner(); void SynchronizeLevelWithOwner();
bool InitStatsForLevel(uint32 level, Unit* owner = NULL); void InitStatsForLevel(uint32 level);
bool HaveInDiet(ItemPrototype const* item) const; bool HaveInDiet(ItemPrototype const* item) const;
uint32 GetCurrentFoodBenefitLevel(uint32 itemlevel); uint32 GetCurrentFoodBenefitLevel(uint32 itemlevel);
void SetDuration(int32 dur) { m_duration = dur; } void SetDuration(int32 dur) { m_duration = dur; }
int32 GetDuration() { return m_duration; }
int32 GetBonusDamage() { return m_bonusdamage; } int32 GetBonusDamage() { return m_bonusdamage; }
void SetBonusDamage(int32 damage) { m_bonusdamage = damage; } void SetBonusDamage(int32 damage) { m_bonusdamage = damage; }
@ -202,10 +203,6 @@ class Pet : public Creature
void UpdateDamagePhysical(WeaponAttackType attType) override; void UpdateDamagePhysical(WeaponAttackType attType) override;
uint32 GetCurrentEquipmentId() const { return m_equipmentId; } 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); bool CanTakeMoreActiveSpells(uint32 SpellIconID);
void ToggleAutocast(uint32 spellid, bool apply); void ToggleAutocast(uint32 spellid, bool apply);

View file

@ -50,22 +50,24 @@ PetAI::PetAI(Creature* c) : CreatureAI(c), i_tracker(TIME_INTERVAL_LOOK), inComb
void PetAI::MoveInLineOfSight(Unit* pWho) void PetAI::MoveInLineOfSight(Unit* pWho)
{ {
if (m_creature->getVictim()) if (Unit* victim = m_creature->getVictim())
return; if (victim->IsAlive())
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)
return; 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); 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 // thus with the following clear the original TMG gets invalidated and crash, doh
// hope it doesn't start to leak memory without this :-/ // hope it doesn't start to leak memory without this :-/
// i_pet->Clear(); // i_pet->Clear();
HandleMovementOnAttackStart(u); if (!m_creature->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE))
HandleMovementOnAttackStart(u);
inCombat = true; inCombat = true;
} }
} }

View file

@ -2687,6 +2687,9 @@ void Player::GiveLevel(uint32 level)
MailDraft(mailReward->mailTemplateId).SendMailTo(this, MailSender(MAIL_CREATURE, mailReward->senderEntry)); MailDraft(mailReward->mailTemplateId).SendMailTo(this, MailSender(MAIL_CREATURE, mailReward->senderEntry));
GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_REACH_LEVEL); GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_REACH_LEVEL);
// resend quests status directly
SendQuestGiverStatusMultiple();
} }
void Player::UpdateFreeTalentPoints(bool resetIfNeed) 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); 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 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 // restore if need some important flags
SetUInt32Value(PLAYER_FIELD_BYTES2, 0); // flags empty by default 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); saBounds = sSpellMgr.GetSpellAreaForAreaMapBounds(0);
for (SpellAreaForAreaMap::const_iterator itr = saBounds.first; itr != saBounds.second; ++itr) for (SpellAreaForAreaMap::const_iterator itr = saBounds.first; itr != saBounds.second; ++itr)
itr->second->ApplyOrRemoveSpellIfCan(this, zone, area, false); itr->second->ApplyOrRemoveSpellIfCan(this, zone, area, false);
// resend quests status directly
SendQuestGiverStatusMultiple();
} }
void Player::IncompleteQuest(uint32 quest_id) void Player::IncompleteQuest(uint32 quest_id)
@ -15199,6 +15205,60 @@ void Player::SendQuestUpdateAddCreatureOrGo(Quest const* pQuest, ObjectGuid guid
SetQuestSlotCounter(log_slot, creatureOrGO_idx, count); 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<uint32>(0, count); // write real count
GetSession()->SendPacket(&data);
}
/*********************************************************/ /*********************************************************/
/*** LOAD SYSTEM ***/ /*** LOAD SYSTEM ***/
/*********************************************************/ /*********************************************************/
@ -16840,6 +16900,7 @@ void Player::_LoadTalents(QueryResult* result)
while (result->NextRow()); while (result->NextRow());
delete result; delete result;
} }
UpdateGroupLeaderFlag();
} }
void Player::_LoadGroup(QueryResult* result) void Player::_LoadGroup(QueryResult* result)
@ -21497,6 +21558,18 @@ PartyResult Player::CanUninviteFromGroup() const
return ERR_PARTY_RESULT_OK; 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) void Player::SetBattleGroundRaid(Group* group, int8 subgroup)
{ {
// we must move references from m_group to m_originalGroup // we must move references from m_group to m_originalGroup

View file

@ -1558,6 +1558,7 @@ class Player : public Unit
void SendQuestConfirmAccept(const Quest* pQuest, Player* pReceiver); void SendQuestConfirmAccept(const Quest* pQuest, Player* pReceiver);
void SendPushToPartyResponse(Player* pPlayer, uint32 msg); void SendPushToPartyResponse(Player* pPlayer, uint32 msg);
void SendQuestUpdateAddCreatureOrGo(Quest const* pQuest, ObjectGuid guid, uint32 creatureOrGO_idx, uint32 count); void SendQuestUpdateAddCreatureOrGo(Quest const* pQuest, ObjectGuid guid, uint32 creatureOrGO_idx, uint32 count);
void SendQuestGiverStatusMultiple();
ObjectGuid GetDividerGuid() const { return m_dividerGuid; } ObjectGuid GetDividerGuid() const { return m_dividerGuid; }
void SetDividerGuid(ObjectGuid guid) { m_dividerGuid = guid; } void SetDividerGuid(ObjectGuid guid) { m_dividerGuid = guid; }
@ -2531,6 +2532,7 @@ class Player : public Unit
uint32 GetNextResetTalentsCost() const; uint32 GetNextResetTalentsCost() const;
Player* GetNextRandomRaidMember(float radius); Player* GetNextRandomRaidMember(float radius);
PartyResult CanUninviteFromGroup() const; PartyResult CanUninviteFromGroup() const;
void UpdateGroupLeaderFlag(const bool remove = false);
// BattleGround Group System // BattleGround Group System
void SetBattleGroundRaid(Group* group, int8 subgroup = -1); void SetBattleGroundRaid(Group* group, int8 subgroup = -1);
void RemoveFromBattleGroundRaid(); void RemoveFromBattleGroundRaid();

View file

@ -855,7 +855,7 @@ bool IsPositiveEffect(SpellEntry const* spellproto, SpellEffectIndex effIndex)
case SPELL_AURA_MOD_INCREASE_HEALTH_PERCENT: case SPELL_AURA_MOD_INCREASE_HEALTH_PERCENT:
case SPELL_AURA_MOD_DAMAGE_PERCENT_DONE: case SPELL_AURA_MOD_DAMAGE_PERCENT_DONE:
if (spellEffect->CalculateSimpleValue() > 0) 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; break;
case SPELL_AURA_ADD_TARGET_TRIGGER: case SPELL_AURA_ADD_TARGET_TRIGGER:
return true; return true;
@ -924,7 +924,7 @@ bool IsPositiveEffect(SpellEntry const* spellproto, SpellEffectIndex effIndex)
spellproto->GetSpellFamilyName() == SPELLFAMILY_GENERIC) spellproto->GetSpellFamilyName() == SPELLFAMILY_GENERIC)
return false; return false;
// but not this if this first effect (don't found better check) // 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; return false;
break; break;
case SPELL_AURA_TRANSFORM: case SPELL_AURA_TRANSFORM:
@ -1032,7 +1032,7 @@ bool IsPositiveEffect(SpellEntry const* spellproto, SpellEffectIndex effIndex)
return false; return false;
// AttributesEx check // AttributesEx check
if (spellproto->HasAttribute(SPELL_ATTR_EX_NEGATIVE)) if (spellproto->HasAttribute(SPELL_ATTR_NEGATIVE))
return false; return false;
// ok, positive // ok, positive
@ -2728,6 +2728,17 @@ bool SpellMgr::IsNoStackSpellDueToSpell(uint32 spellId_1, uint32 spellId_2) cons
return true; 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) bool SpellMgr::IsProfessionOrRidingSpell(uint32 spellId)
{ {
SpellEntry const* spellInfo = sSpellStore.LookupEntry(spellId); SpellEntry const* spellInfo = sSpellStore.LookupEntry(spellId);

View file

@ -1155,6 +1155,9 @@ class SpellMgr
return !canStackSpellRanksInSpellBook(spellInfo) && GetSpellRank(spellInfo->Id) != 0; 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; SpellEntry const* SelectAuraRankForLevel(SpellEntry const* spellInfo, uint32 Level) const;
// Spell learning // Spell learning

View file

@ -2898,12 +2898,7 @@ void Unit::AttackerStateUpdate(Unit* pVictim, WeaponAttackType attType, bool ext
if (IsNonMeleeSpellCasted(false)) if (IsNonMeleeSpellCasted(false))
return; return;
uint32 hitInfo; if (attType == RANGED_ATTACK)
if (attType == BASE_ATTACK)
hitInfo = HITINFO_NORMALSWING2;
else if (attType == OFF_ATTACK)
hitInfo = HITINFO_LEFTSWING;
else
return; // ignore ranged case return; // ignore ranged case
uint32 extraAttacks = m_extraAttacks; uint32 extraAttacks = m_extraAttacks;
@ -2945,7 +2940,7 @@ void Unit::AttackerStateUpdate(Unit* pVictim, WeaponAttackType attType, bool ext
else else
DEBUG_FILTER_LOG(LOG_FILTER_COMBAT, "AttackerStateUpdate: (NPC) %u attacked %u (TypeId: %u) for %u dmg, absorbed %u, blocked %u, resisted %u.", 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); GetGUIDLow(), pVictim->GetGUIDLow(), pVictim->GetTypeId(), damageInfo.damage, damageInfo.absorb, damageInfo.blocked_amount, damageInfo.resist);
if (Unit* owner = GetOwner()) if (Unit* owner = GetOwner())
if (owner->GetTypeId() == TYPEID_UNIT) 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) // 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) SpellEntry const* triggeredBy = holder->GetTriggeredBy();
if ((*i).second->IsInUse()) if (triggeredBy && sSpellMgr.IsSpellCanAffectSpell(triggeredBy, i_spellProto)) // check if this spell can be triggered by any talent aura
{
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; 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; 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) for (SpellAuraHolderMap::const_iterator itr = GetSpellAuraHolderMap().begin(); itr != GetSpellAuraHolderMap().end(); ++itr)
{ {
// skip deleted auras (possible at recursive triggered call // skip deleted auras (possible at recursive triggered call
if (itr->second->IsDeleted()) if (itr->second->GetState() != SPELLAURAHOLDER_STATE_READY || itr->second->IsDeleted())
continue; continue;
SpellProcEventEntry const* spellProcEvent = NULL; SpellProcEventEntry const* spellProcEvent = NULL;

View file

@ -297,7 +297,7 @@ enum SpellAttributes
SPELL_ATTR_UNK23 = 0x00800000,// 23 castable while dead? SPELL_ATTR_UNK23 = 0x00800000,// 23 castable while dead?
SPELL_ATTR_CASTABLE_WHILE_MOUNTED = 0x01000000,// 24 castable while mounted 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_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_CASTABLE_WHILE_SITTING = 0x08000000,// 27 castable while sitting
SPELL_ATTR_CANT_USED_IN_COMBAT = 0x10000000,// 28 Cannot be used in combat 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...) 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_UNK4 = 0x00000010,// 4
SPELL_ATTR_EX_NOT_BREAK_STEALTH = 0x00000020,// 5 Not break stealth SPELL_ATTR_EX_NOT_BREAK_STEALTH = 0x00000020,// 5 Not break stealth
SPELL_ATTR_EX_CHANNELED_2 = 0x00000040,// 6 channeled 2 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_NOT_IN_COMBAT_TARGET = 0x00000100,// 8 Spell req target not to be in combat state
SPELL_ATTR_EX_UNK9 = 0x00000200,// 9 SPELL_ATTR_EX_UNK9 = 0x00000200,// 9
SPELL_ATTR_EX_NO_THREAT = 0x00000400,// 10 no generates threat on cast 100% SPELL_ATTR_EX_NO_THREAT = 0x00000400,// 10 no generates threat on cast 100%

View file

@ -130,7 +130,8 @@ bool Group::Create(ObjectGuid guid, const char* name)
m_lootMethod = GROUP_LOOT; m_lootMethod = GROUP_LOOT;
m_lootThreshold = ITEM_QUALITY_UNCOMMON; 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_dungeonDifficulty = DUNGEON_DIFFICULTY_NORMAL;
m_raidDifficulty = RAID_DIFFICULTY_10MAN_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) " 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')", "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_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[0].GetRawValue(), m_targetIcons[1].GetRawValue(),
m_targetIcons[2].GetRawValue(), m_targetIcons[3].GetRawValue(), m_targetIcons[2].GetRawValue(), m_targetIcons[3].GetRawValue(),
m_targetIcons[4].GetRawValue(), m_targetIcons[5].GetRawValue(), m_targetIcons[4].GetRawValue(), m_targetIcons[5].GetRawValue(),
@ -169,6 +170,8 @@ bool Group::Create(ObjectGuid guid, const char* name)
if (!isBGGroup()) if (!isBGGroup())
CharacterDatabase.CommitTransaction(); CharacterDatabase.CommitTransaction();
_updateLeaderFlag();
return true; return true;
} }
@ -269,8 +272,10 @@ bool Group::AddLeaderInvite(Player* player)
if (!AddInvite(player)) if (!AddInvite(player))
return false; return false;
_updateLeaderFlag(true);
m_leaderGuid = player->GetObjectGuid(); m_leaderGuid = player->GetObjectGuid();
m_leaderName = player->GetName(); m_leaderName = player->GetName();
_updateLeaderFlag();
return true; return true;
} }
@ -484,6 +489,7 @@ void Group::Disband(bool hideDestroy)
ResetInstances(INSTANCE_RESET_GROUP_DISBAND, true, NULL); ResetInstances(INSTANCE_RESET_GROUP_DISBAND, true, NULL);
} }
_updateLeaderFlag(true);
m_leaderGuid.Clear(); m_leaderGuid.Clear();
m_leaderName.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) bool Group::_setMembersGroup(ObjectGuid guid, uint8 group)
{ {
member_witerator slot = _getMemberWSlot(guid); member_witerator slot = _getMemberWSlot(guid);

View file

@ -406,6 +406,8 @@ class Group
void _setLeader(ObjectGuid guid); void _setLeader(ObjectGuid guid);
void _removeRolls(ObjectGuid guid); void _removeRolls(ObjectGuid guid);
void _updateLeaderFlag(const bool remove = false);
bool _setMembersGroup(ObjectGuid guid, uint8 group); bool _setMembersGroup(ObjectGuid guid, uint8 group);
bool _setAssistantFlag(ObjectGuid guid, const bool& state); bool _setAssistantFlag(ObjectGuid guid, const bool& state);
@ -489,6 +491,8 @@ class Group
LootMethod m_lootMethod; LootMethod m_lootMethod;
ItemQualities m_lootThreshold; ItemQualities m_lootThreshold;
ObjectGuid m_looterGuid; ObjectGuid m_looterGuid;
ObjectGuid m_masterLooterGuid;
ObjectGuid m_currentLooterGuid;
Rolls RollId; Rolls RollId;
BoundInstancesMap m_boundInstances[MAX_DIFFICULTY]; BoundInstancesMap m_boundInstances[MAX_DIFFICULTY];
uint8* m_subGroupsCounts; uint8* m_subGroupsCounts;

View file

@ -60,9 +60,9 @@ void WorldSession::HandlePetAction(WorldPacket& recv_data)
return; 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; return;
} }
@ -118,45 +118,35 @@ void WorldSession::HandlePetAction(WorldPacket& recv_data)
} }
case COMMAND_ATTACK: // spellid=1792 // ATTACK case COMMAND_ATTACK: // spellid=1792 // ATTACK
{ {
Unit* TargetUnit = _player->GetMap()->GetUnit(targetGuid); ((Pet*)pet)->SetIsRetreating();
if (!TargetUnit) ((Pet*)pet)->SetSpellOpener();
return;
// not let attack friendly units. Unit* targetUnit = targetGuid ? _player->GetMap()->GetUnit(targetGuid) : nullptr;
if (GetPlayer()->IsFriendlyTo(TargetUnit))
return;
// Not let attack through obstructions
if (!pet->IsWithinLOSInMap(TargetUnit))
return;
// This is true if pet has no target or has target but targets differs. if (targetUnit && targetUnit != pet && targetUnit->IsTargetableForAttack() && targetUnit->isInAccessablePlaceFor((Creature*)pet))
if (pet->getVictim() != TargetUnit)
{ {
if (pet->getVictim()) _player->SetInCombatState(true, targetUnit);
pet->AttackStop();
if (pet->hasUnitState(UNIT_STAT_CONTROLLED)) // This is true if pet has no target or has target but targets differs.
{ if (pet->getVictim() != targetUnit)
pet->Attack(TargetUnit, true);
pet->SendPetAIReaction();
}
else
{ {
pet->AttackStop();
pet->GetMotionMaster()->Clear(); pet->GetMotionMaster()->Clear();
if (((Creature*)pet)->AI()) 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(); pet->SendPetAIReaction();
} }
else
pet->Attack(targetUnit, true);
} }
} }
break; break;
} }
case COMMAND_ABANDON: // abandon (hunter pet) or dismiss (summoned pet) 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_PASSIVE: // 0x01
case ACT_ENABLED: // 0xC1 spell case ACT_ENABLED: // 0xC1 spell
{ {
Unit* unit_target = NULL; ((Pet*)pet)->SetIsRetreating();
if (targetGuid) ((Pet*)pet)->SetSpellOpener();
unit_target = _player->GetMap()->GetUnit(targetGuid);
Unit* unit_target = targetGuid ? _player->GetMap()->GetUnit(targetGuid) : nullptr;
// do not cast unknown spells // do not cast unknown spells
SpellEntry const* spellInfo = sSpellStore.LookupEntry(spellid); SpellEntry const* spellInfo = sSpellStore.LookupEntry(spellid);
@ -228,7 +219,10 @@ void WorldSession::HandlePetAction(WorldPacket& recv_data)
SpellEffectEntry const* spellEffect = spellInfo->GetSpellEffect(SpellEffectIndex(i)); SpellEffectEntry const* spellEffect = spellInfo->GetSpellEffect(SpellEffectIndex(i));
if (!spellEffect) if (!spellEffect)
continue; 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; return;
} }
@ -236,6 +230,8 @@ void WorldSession::HandlePetAction(WorldPacket& recv_data)
if (!pet->HasSpell(spellid) || IsPassiveSpell(spellInfo)) if (!pet->HasSpell(spellid) || IsPassiveSpell(spellInfo))
return; return;
_player->SetInCombatState(true, unit_target);
pet->clearUnitState(UNIT_STAT_MOVING); pet->clearUnitState(UNIT_STAT_MOVING);
Spell* spell = new Spell(pet, spellInfo, false); Spell* spell = new Spell(pet, spellInfo, false);

View file

@ -550,14 +550,10 @@ uint32 WorldSession::getDialogStatus(Player* pPlayer, Object* questgiver, uint32
QuestStatus status = pPlayer->GetQuestStatus(quest_id); QuestStatus status = pPlayer->GetQuestStatus(quest_id);
if ((status == QUEST_STATUS_COMPLETE && !pPlayer->GetQuestRewardStatus(quest_id)) || if (status == QUEST_STATUS_COMPLETE && !pPlayer->GetQuestRewardStatus(quest_id))
(pQuest->IsAutoComplete() && pPlayer->CanTakeQuest(pQuest, false))) dialogStatusNew = pQuest->IsRepeatable() ? DIALOG_STATUS_REWARD_REP : DIALOG_STATUS_REWARD;
{ else if (pQuest->IsAutoComplete() && pPlayer->CanTakeQuest(pQuest, false))
if (pQuest->IsAutoComplete() && pQuest->IsRepeatable()) dialogStatusNew = pQuest->IsRepeatable() ? DIALOG_STATUS_AVAILABLE_REP : DIALOG_STATUS_AVAILABLE;
dialogStatusNew = DIALOG_STATUS_REWARD_REP;
else
dialogStatusNew = DIALOG_STATUS_REWARD;
}
else if (status == QUEST_STATUS_INCOMPLETE) else if (status == QUEST_STATUS_INCOMPLETE)
dialogStatusNew = DIALOG_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"); DEBUG_LOG("WORLD: Received opcode CMSG_QUESTGIVER_STATUS_MULTIPLE_QUERY");
uint32 count = 0; _player->SendQuestGiverStatusMultiple();
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<uint32>(0, count); // write real count
SendPacket(&data);
} }
bool WorldSession::CanInteractWithQuestGiver(ObjectGuid guid, char const* descr) bool WorldSession::CanInteractWithQuestGiver(ObjectGuid guid, char const* descr)

View file

@ -432,6 +432,7 @@ Spell::Spell(Unit* caster, SpellEntry const* info, bool triggered, ObjectGuid or
m_cast_count = 0; m_cast_count = 0;
m_glyphIndex = 0; m_glyphIndex = 0;
m_triggeredByAuraSpell = NULL; m_triggeredByAuraSpell = NULL;
m_spellAuraHolder = NULL;
// Auto Shot & Shoot (wand) // Auto Shot & Shoot (wand)
m_autoRepeat = IsAutoRepeatRangedSpell(m_spellInfo); 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)) if(!IsPositiveTarget(spellEffect->EffectImplicitTargetA, spellEffect->EffectImplicitTargetB))
m_canReflect = true; m_canReflect = true;
else else
m_canReflect = m_spellInfo->HasAttribute(SPELL_ATTR_EX_NEGATIVE); m_canReflect = m_spellInfo->HasAttribute(SPELL_ATTR_EX_UNK7);
if (m_canReflect) if (m_canReflect)
continue; continue;
@ -1285,6 +1286,9 @@ void Spell::DoAllEffectOnTarget(TargetInfo* target)
((Creature*)m_caster)->AI()->SpellHitTarget(unit, m_spellInfo); ((Creature*)m_caster)->AI()->SpellHitTarget(unit, m_spellInfo);
if (real_caster && real_caster != m_caster && real_caster->GetTypeId() == TYPEID_UNIT && ((Creature*)real_caster)->AI()) if (real_caster && real_caster != m_caster && real_caster->GetTypeId() == TYPEID_UNIT && ((Creature*)real_caster)->AI())
((Creature*)real_caster)->AI()->SpellHitTarget(unit, m_spellInfo); ((Creature*)real_caster)->AI()->SpellHitTarget(unit, m_spellInfo);
if (m_spellAuraHolder)
m_spellAuraHolder->SetState(SPELLAURAHOLDER_STATE_READY);
} }
void Spell::DoSpellHitOnUnit(Unit* unit, uint32 effectMask) void Spell::DoSpellHitOnUnit(Unit* unit, uint32 effectMask)
@ -1403,7 +1407,7 @@ void Spell::DoSpellHitOnUnit(Unit* unit, uint32 effectMask)
if (IsSpellAppliesAura(m_spellInfo, 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); m_spellAuraHolder->setDiminishGroup(m_diminishGroup);
} }
else else

View file

@ -611,7 +611,7 @@ Aura* CreateAura(SpellEntry const* spellproto, SpellEffectIndex eff, int32* curr
return new Aura(spellproto, eff, currentBasePoints, holder, target, caster, castItem); 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); return new SpellAuraHolder(spellproto, target, caster, castItem, triggeredBy);
} }
@ -7526,107 +7526,102 @@ void Aura::PeriodicTick()
case SPELL_AURA_PERIODIC_HEAL: case SPELL_AURA_PERIODIC_HEAL:
case SPELL_AURA_OBS_MOD_HEALTH: 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(); Unit* pCaster = GetCaster();
if (!pCaster) if (!pCaster)
return; return;
// Don't heal target if it is already at max health bool canApplyHealthPart = true;
if (target->GetHealth() == target->GetMaxHealth())
return; // 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) // heal for caster damage (must be alive)
if (target != pCaster && spellProto->SpellVisual[0] == 163 && !pCaster->IsAlive()) if (target != pCaster && spellProto->SpellVisual[0] == 163 && !pCaster->IsAlive())
{ return; } canApplyHealthPart = false;
// ignore non positive values (can be result apply spellmods to aura damage if (canApplyHealthPart)
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
{ {
{ 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) % uint32 pdamage;
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) if (m_modifier.m_auraname == SPELL_AURA_OBS_MOD_HEALTH)
// Item - Druid T10 Restoration 2P Bonus pdamage = uint32(target->GetMaxHealth() * amount / 100);
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);
}
else else
{ {
uint32 damage = gain; pdamage = amount;
uint32 absorb = 0;
pCaster->DealDamageMods(pCaster, damage, &absorb);
pCaster->SendSpellNonMeleeDamageLog(pCaster, GetId(), damage, GetSpellSchoolMask(spellProto), absorb, 0, false, 0, false);
CleanDamage cleanDamage = CleanDamage(0, BASE_ATTACK, MELEE_HIT_NORMAL); // Wild Growth (1/7 - 6 + 2*ramainTicks) %
pCaster->DealDamage(pCaster, damage, &cleanDamage, NODAMAGE, GetSpellSchoolMask(spellProto), spellProto, true); 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; break;
} }
case SPELL_AURA_PERIODIC_MANA_LEECH: case SPELL_AURA_PERIODIC_MANA_LEECH:
@ -10322,11 +10317,15 @@ SpellAuraHolder::~SpellAuraHolder()
void SpellAuraHolder::Update(uint32 diff) 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) if (m_duration > 0)
{ {
m_duration -= diff; m_duration -= diff;
if (m_duration < 0) if (m_duration < 0)
{ m_duration = 0; } m_duration = 0;
m_timeCla -= diff; m_timeCla -= diff;
@ -10335,7 +10334,7 @@ void SpellAuraHolder::Update(uint32 diff)
if (Unit* caster = GetCaster()) if (Unit* caster = GetCaster())
{ {
Powers powertype = Powers(GetSpellProto()->powerType); Powers powertype = Powers(GetSpellProto()->powerType);
m_timeCla = 1 * IN_MILLISECONDS; m_timeCla = 1*IN_MILLISECONDS;
if (SpellPowerEntry const* spellPower = GetSpellProto()->GetSpellPower()) if (SpellPowerEntry const* spellPower = GetSpellProto()->GetSpellPower())
{ {
@ -10348,36 +10347,32 @@ void SpellAuraHolder::Update(uint32 diff)
} }
} }
} }
}
}
for (int32 i = 0; i < MAX_EFFECT_INDEX; ++i) // Channeled aura required check distance from caster
if (Aura* aura = m_auras[i]) if (IsChanneledSpell(m_spellProto) && GetCasterGuid() != m_target->GetObjectGuid())
{ 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))
{ {
caster->InterruptSpell(CURRENT_CHANNELED_SPELL); Unit* caster = GetCaster();
return; 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;
}
}
} }
} }
} }

View file

@ -80,6 +80,13 @@ class Aura;
// internal helper // internal helper
struct ReapplyAffectedPassiveAurasHelper; 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 class SpellAuraHolder
{ {
public: public:
@ -108,6 +115,8 @@ class SpellAuraHolder
uint32 GetId() const { return m_spellProto->Id; } uint32 GetId() const { return m_spellProto->Id; }
SpellEntry const* GetSpellProto() const { return m_spellProto; } 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; } ObjectGuid const& GetCasterGuid() const { return m_casterGuid; }
void SetCasterGuid(ObjectGuid guid) { m_casterGuid = guid; } 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 ObjectGuid m_castItemGuid; // it is NOT safe to keep a pointer to the item because it may get deleted
time_t m_applyTime; time_t m_applyTime;
SpellEntry const* m_triggeredBy; // Spell responsible for this holder 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_auraSlot; // Aura slot on unit (for show in client)
uint8 m_auraFlags; // Aura info flag (for send data to client) uint8 m_auraFlags; // Aura info flag (for send data to client)

View file

@ -6085,7 +6085,7 @@ void Spell::EffectDispel(SpellEffectEntry const* effect)
if (!holder->IsPositive()) if (!holder->IsPositive())
positive = false; positive = false;
else else
positive = !holder->GetSpellProto()->HasAttribute(SPELL_ATTR_EX_NEGATIVE); positive = !holder->GetSpellProto()->HasAttribute(SPELL_ATTR_NEGATIVE);
// do not remove positive auras if friendly target // do not remove positive auras if friendly target
// negative auras if non-friendly target // negative auras if non-friendly target
@ -6583,41 +6583,44 @@ void Spell::EffectTameCreature(SpellEffectEntry const* /*effect*/)
if (plr->IsFFAPvP()) if (plr->IsFFAPvP())
pet->SetFFAPvP(true); 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); pet->GetCharmInfo()->SetPetNumber(sObjectMgr.GeneratePetNumber(), true);
// this enables pet details window (Shift+P)
pet->AIM_Initialize(); pet->GetCharmInfo()->SetReactState(REACT_DEFENSIVE);
pet->InitPetCreateSpells();
// 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->InitLevelupSpellsForLevel();
pet->InitTalentForLevel(); pet->InitTalentForLevel();
pet->SetHealth(pet->GetMaxHealth());
// "kill" original creature pet->SetHealthPercent(creatureTarget->GetHealthPercent());
pet->GetCharmInfo()->SetPetNumber(sObjectMgr.GeneratePetNumber(), true);
// destroy creature object
creatureTarget->ForcedDespawn(); creatureTarget->ForcedDespawn();
// prepare visual effect for levelup // prepare visual effect for levelup
pet->SetUInt32Value(UNIT_FIELD_LEVEL, level - 1); pet->SetUInt32Value(UNIT_FIELD_LEVEL, level - 1);
// add to world // add pet object to the world
pet->GetMap()->Add((Creature*)pet); pet->GetMap()->Add((Creature*)pet);
pet->AIM_Initialize();
// visual effect for levelup // visual effect for levelup
pet->SetUInt32Value(UNIT_FIELD_LEVEL, level); pet->SetUInt32Value(UNIT_FIELD_LEVEL, level);
// this enables pet details window (Shift+P)
pet->InitPetCreateSpells();
// caster have pet now // caster have pet now
plr->SetPet(pet); plr->SetPet(pet);
pet->SavePetToDB(PET_SAVE_AS_CURRENT);
plr->PetSpellInitialize(); plr->PetSpellInitialize();
pet->SavePetToDB(PET_SAVE_AS_CURRENT);
} }
void Spell::EffectSummonPet(SpellEffectEntry const* effect) void Spell::EffectSummonPet(SpellEffectEntry const* effect)
@ -11090,8 +11093,13 @@ void Spell::EffectSummonDeadPet(SpellEffectEntry const* /*effect*/)
return; return;
if (pet->IsAlive()) if (pet->IsAlive())
return; 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->SetUInt32Value(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_NONE);
pet->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE); pet->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE);
@ -11872,7 +11880,7 @@ void Spell::EffectGravityPull(SpellEffectEntry const* effect)
void Spell::EffectCreateTamedPet(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; return;
uint32 creatureEntry = effect->EffectMiscValue; uint32 creatureEntry = effect->EffectMiscValue;
@ -11884,7 +11892,7 @@ void Spell::EffectCreateTamedPet(SpellEffectEntry const* effect)
return; return;
} }
Pet* newTamedPet = new Pet; Pet* newTamedPet = new Pet(HUNTER_PET);
CreatureCreatePos pos(unitTarget, unitTarget->GetOrientation()); CreatureCreatePos pos(unitTarget, unitTarget->GetOrientation());
Map* map = unitTarget->GetMap(); Map* map = unitTarget->GetMap();
@ -11896,15 +11904,12 @@ void Spell::EffectCreateTamedPet(SpellEffectEntry const* effect)
} }
newTamedPet->SetRespawnCoord(pos); newTamedPet->SetRespawnCoord(pos);
newTamedPet->setPetType(HUNTER_PET);
newTamedPet->SetOwnerGuid(unitTarget->GetObjectGuid()); newTamedPet->SetOwnerGuid(unitTarget->GetObjectGuid());
newTamedPet->SetCreatorGuid(unitTarget->GetObjectGuid()); newTamedPet->SetCreatorGuid(unitTarget->GetObjectGuid());
newTamedPet->SetUInt32Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_NONE); newTamedPet->SetUInt32Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_NONE);
newTamedPet->setFaction(unitTarget->getFaction()); newTamedPet->setFaction(unitTarget->getFaction());
newTamedPet->SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP, uint32(time(NULL))); newTamedPet->SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP, uint32(time(nullptr)));
newTamedPet->SetUInt32Value(UNIT_FIELD_PETEXPERIENCE, 0);
newTamedPet->SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP, 1000);
newTamedPet->SetUInt32Value(UNIT_CREATED_BY_SPELL, m_spellInfo->Id); newTamedPet->SetUInt32Value(UNIT_CREATED_BY_SPELL, m_spellInfo->Id);
newTamedPet->GetCharmInfo()->SetPetNumber(petNumber, true); newTamedPet->GetCharmInfo()->SetPetNumber(petNumber, true);
@ -11915,17 +11920,15 @@ void Spell::EffectCreateTamedPet(SpellEffectEntry const* effect)
if (unitTarget->IsFFAPvP()) if (unitTarget->IsFFAPvP())
newTamedPet->SetFFAPvP(true); newTamedPet->SetFFAPvP(true);
newTamedPet->InitStatsForLevel(unitTarget->getLevel(), unitTarget); newTamedPet->InitStatsForLevel(unitTarget->getLevel());
newTamedPet->InitPetCreateSpells(); newTamedPet->InitPetCreateSpells();
newTamedPet->InitLevelupSpellsForLevel(); newTamedPet->InitLevelupSpellsForLevel();
newTamedPet->InitTalentForLevel(); 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->SetByteFlag(UNIT_FIELD_BYTES_2, 2, UNIT_CAN_BE_ABANDONED);
newTamedPet->AIM_Initialize(); newTamedPet->AIM_Initialize();
newTamedPet->SetHealth(newTamedPet->GetMaxHealth());
newTamedPet->SetPower(POWER_MANA, newTamedPet->GetMaxPower(POWER_MANA));
float x, y, z; float x, y, z;
unitTarget->GetClosePoint(x, y, z, newTamedPet->GetObjectBoundingRadius()); unitTarget->GetClosePoint(x, y, z, newTamedPet->GetObjectBoundingRadius());

View file

@ -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) 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; return SPELL_AURA_PROC_FAILED;
RemoveAurasDueToSpell(triggeredByAura->GetId()); RemoveAurasDueToSpell(triggeredByAura->GetId());

View file

@ -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_GMLEVEL, "TimerBar.Fire.GMLevel", SEC_CONSOLE);
setConfig(CONFIG_UINT32_TIMERBAR_FIRE_MAX, "TimerBar.Fire.Max", 1); 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 // Warden
/* badly broken on m3 :( - this causes all these defaults to be set to 0 /* badly broken on m3 :( - this causes all these defaults to be set to 0

View file

@ -755,8 +755,8 @@ SD2ErrorLogFile = "scriptdev2-errors.log"
# #
# PetUnsummonAtMount # PetUnsummonAtMount
# Permanent pet will unsummoned at player mount # Permanent pet will unsummoned at player mount
# 0 - unsummon only for flying mounts # Default: 0 - unsummon only when appropriate (don't unsummon temp summons on ground mounting)
# Default: 1 - unsummon for any mount # 1 - always unsummon controlled pets on mounting
# #
# ClientCacheVersion # ClientCacheVersion
# Client cache version for client cache data reset. Use any different from DB value and not recently used for triggering reset. # 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.Prospecting = 0
SkillChance.Milling = 0 SkillChance.Milling = 0
OffhandCheckAtTalentsReset = 0 OffhandCheckAtTalentsReset = 0
PetUnsummonAtMount = 1 PetUnsummonAtMount = 0
ClientCacheVersion = 0 ClientCacheVersion = 0
Event.Announce = 0 Event.Announce = 0
BeepAtStart = 1 BeepAtStart = 1