diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index bdc7bffea7..42eaae6f3e 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -553,6 +553,15 @@ bool Creature::InitEntry(uint32 entry, CreatureData const* data /*= nullptr*/) for (uint8 i = 0; i < MAX_CREATURE_SPELLS; ++i) m_spells[i] = GetCreatureTemplate()->spells[i]; + ApplyAllStaticFlags(cinfo->StaticFlags); + + _staticFlags.ApplyFlag(CREATURE_STATIC_FLAG_NO_XP, cinfo->type == CREATURE_TYPE_CRITTER + || IsPet() + || IsTotem() + || cinfo->flags_extra & CREATURE_FLAG_EXTRA_NO_XP_AT_KILL); + + _staticFlags.ApplyFlag(CREATURE_STATIC_FLAG_4_TREAT_AS_RAID_UNIT_FOR_HELPFUL_SPELLS, (cinfo->type_flags & CREATURE_TYPE_FLAG_TREAT_AS_RAID_UNIT) != 0); + SetNoNpcDamageBelowPctHealthValue(sObjectMgr->GetSparringHealthLimitFor(GetEntry())); return true; @@ -652,7 +661,7 @@ bool Creature::UpdateEntry(uint32 entry, CreatureData const* data /*= nullptr*/, if (cInfo->flags_extra & CREATURE_FLAG_EXTRA_NO_COMBAT) SetIgnoringCombat(true); - UpdateMovementFlags(true); + InitializeMovementFlags(); LoadCreaturesAddon(); LoadTemplateImmunities(); @@ -660,6 +669,14 @@ bool Creature::UpdateEntry(uint32 entry, CreatureData const* data /*= nullptr*/, return true; } +void Creature::ApplyAllStaticFlags(CreatureStaticFlagsHolder const& flags) +{ + _staticFlags = flags; + + // Apply all other side effects of flag changes + SetTemplateRooted(flags.HasFlag(CREATURE_STATIC_FLAG_SESSILE)); +} + void Creature::Update(uint32 diff) { if (IsAIEnabled() && m_triggerJustAppeared && m_deathState != DEAD) @@ -1720,6 +1737,17 @@ void Creature::SetSpawnHealth() SetHealth((m_deathState == ALIVE || m_deathState == JUST_RESPAWNED) ? curhealth : 0); } +void Creature::LoadTemplateRoot() +{ + SetTemplateRooted(GetMovementTemplate().IsRooted()); +} + +void Creature::SetTemplateRooted(bool rooted) +{ + _staticFlags.ApplyFlag(CREATURE_STATIC_FLAG_SESSILE, rooted); + SetControlled(rooted, UNIT_STATE_ROOT); +} + bool Creature::hasQuest(uint32 quest_id) const { return sObjectMgr->GetCreatureQuestRelations(GetEntry()).HasQuest(quest_id); @@ -2983,6 +3011,13 @@ float Creature::GetNativeObjectScale() const return GetCreatureTemplate()->scale; } +void Creature::InitializeMovementFlags() +{ + LoadTemplateRoot(); + // It does the same, for now + UpdateMovementFlags(true); +} + void Creature::UpdateMovementFlags(bool initializeDBStates) { // Do not update movement flags if creature is controlled by a player (charm/vehicle) diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h index cc9f3c1fb3..927dfdbbfe 100644 --- a/src/server/game/Entities/Creature/Creature.h +++ b/src/server/game/Entities/Creature/Creature.h @@ -79,6 +79,9 @@ class TC_GAME_API Creature : public Unit, public GridObject, public Ma void UpdateLevelDependantStats(); void LoadEquipment(int8 id = 1, bool force = false); void SetSpawnHealth(); + void LoadTemplateRoot(); + bool IsTemplateRooted() const { return _staticFlags.HasFlag(CREATURE_STATIC_FLAG_SESSILE); } + void SetTemplateRooted(bool rooted); ObjectGuid::LowType GetSpawnId() const { return m_spawnId; } @@ -147,11 +150,15 @@ class TC_GAME_API Creature : public Unit, public GridObject, public Ma SpellSchoolMask GetMeleeDamageSchoolMask(WeaponAttackType /*attackType*/ = BASE_ATTACK) const override { return m_meleeDamageSchoolMask; } void SetMeleeDamageSchool(SpellSchools school) { m_meleeDamageSchoolMask = SpellSchoolMask(1 << school); } + bool CanMelee() const { return !_staticFlags.HasFlag(CREATURE_STATIC_FLAG_NO_MELEE); } + void SetCanMelee(bool canMelee) { _staticFlags.ApplyFlag(CREATURE_STATIC_FLAG_NO_MELEE, !canMelee); } + bool CanIgnoreLineOfSightWhenCastingOnMe() const { return _staticFlags.HasFlag(CREATURE_STATIC_FLAG_4_IGNORE_LOS_WHEN_CASTING_ON_ME); } bool HasSpell(uint32 spellID) const override; bool UpdateEntry(uint32 entry, CreatureData const* data = nullptr, bool updateLevel = true); + void InitializeMovementFlags(); void UpdateMovementFlags(bool initializeDBStates); bool UpdateStats(Stats stat) override; @@ -364,6 +371,12 @@ class TC_GAME_API Creature : public Unit, public GridObject, public Ma void AtEngage(Unit* target) override; void AtDisengage() override; + bool HasStaticFlag(CreatureStaticFlags flag) const { return _staticFlags.HasFlag(flag); } + bool HasStaticFlag(CreatureStaticFlags2 flag) const { return _staticFlags.HasFlag(flag); } + bool HasStaticFlag(CreatureStaticFlags3 flag) const { return _staticFlags.HasFlag(flag); } + bool HasStaticFlag(CreatureStaticFlags4 flag) const { return _staticFlags.HasFlag(flag); } + bool HasStaticFlag(CreatureStaticFlags5 flag) const { return _staticFlags.HasFlag(flag); } + bool HasSwimmingFlagOutOfCombat() const { return !_isMissingSwimmingFlagOutOfCombat; @@ -457,6 +470,10 @@ class TC_GAME_API Creature : public Unit, public GridObject, public Ma time_t _lastDamagedTime; // Part of Evade mechanics CreatureTextRepeatGroup m_textRepeat; + void ApplyAllStaticFlags(CreatureStaticFlagsHolder const& flags); + + CreatureStaticFlagsHolder _staticFlags; + bool _isMissingSwimmingFlagOutOfCombat; CreatureMovementInfo _creatureMovementInfo; diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp index 611128ddb5..15bb571b1c 100644 --- a/src/server/game/Entities/Object/Object.cpp +++ b/src/server/game/Entities/Object/Object.cpp @@ -3099,10 +3099,6 @@ bool WorldObject::IsValidAttackTarget(WorldObject const* target, SpellInfo const } } - Creature const* creatureAttacker = ToCreature(); - if (creatureAttacker && (creatureAttacker->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_TREAT_AS_RAID_UNIT)) - return false; - if (playerAffectingAttacker && playerAffectingTarget) if (playerAffectingAttacker->duel && playerAffectingAttacker->duel->opponent == playerAffectingTarget && playerAffectingAttacker->duel->startTime != 0) return true; @@ -3191,7 +3187,7 @@ bool WorldObject::IsValidAssistTarget(WorldObject const* target, SpellInfo const } // can't assist non-friendly targets - if (GetReactionTo(target) < REP_NEUTRAL && target->GetReactionTo(this) < REP_NEUTRAL && (!ToCreature() || !(ToCreature()->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_TREAT_AS_RAID_UNIT))) + if (GetReactionTo(target) < REP_NEUTRAL && target->GetReactionTo(this) < REP_NEUTRAL && (!ToCreature() || !ToCreature()->HasStaticFlag(CREATURE_STATIC_FLAG_4_TREAT_AS_RAID_UNIT_FOR_HELPFUL_SPELLS))) return false; // PvP case @@ -3224,7 +3220,7 @@ bool WorldObject::IsValidAssistTarget(WorldObject const* target, SpellInfo const if (!bySpell || !bySpell->HasAttribute(SPELL_ATTR6_CAN_ASSIST_IMMUNE_PC)) if (unitTarget && !unitTarget->IsPvP()) if (Creature const* creatureTarget = target->ToCreature()) - return ((creatureTarget->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_TREAT_AS_RAID_UNIT) || (creatureTarget->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_CAN_ASSIST)); + return creatureTarget->HasStaticFlag(CREATURE_STATIC_FLAG_4_TREAT_AS_RAID_UNIT_FOR_HELPFUL_SPELLS) || (creatureTarget->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_CAN_ASSIST); } return true; diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 1b934ed311..d854c86761 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -5299,9 +5299,15 @@ bool Unit::Attack(Unit* victim, bool meleeAttack) return false; Creature* creature = ToCreature(); - // creatures cannot attack while evading - if (creature && creature->IsInEvadeMode()) - return false; + if (creature) + { + // creatures cannot attack while evading + if (creature->IsInEvadeMode()) + return false; + + if (!creature->CanMelee()) + meleeAttack = false; + } if (HasAuraType(SPELL_AURA_DISABLE_ATTACKING_EXCEPT_ABILITIES)) return false; @@ -11842,8 +11848,8 @@ bool Unit::IsInPartyWith(Unit const* unit) const if (u1->GetTypeId() == TYPEID_PLAYER && u2->GetTypeId() == TYPEID_PLAYER) return u1->ToPlayer()->IsInSameGroupWith(u2->ToPlayer()); - else if ((u2->GetTypeId() == TYPEID_PLAYER && u1->GetTypeId() == TYPEID_UNIT && u1->ToCreature()->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_TREAT_AS_RAID_UNIT) || - (u1->GetTypeId() == TYPEID_PLAYER && u2->GetTypeId() == TYPEID_UNIT && u2->ToCreature()->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_TREAT_AS_RAID_UNIT)) + else if ((u2->GetTypeId() == TYPEID_PLAYER && u1->GetTypeId() == TYPEID_UNIT && u1->ToCreature()->HasStaticFlag(CREATURE_STATIC_FLAG_4_TREAT_AS_RAID_UNIT_FOR_HELPFUL_SPELLS)) || + (u1->GetTypeId() == TYPEID_PLAYER && u2->GetTypeId() == TYPEID_UNIT && u2->ToCreature()->HasStaticFlag(CREATURE_STATIC_FLAG_4_TREAT_AS_RAID_UNIT_FOR_HELPFUL_SPELLS))) return true; return u1->GetTypeId() == TYPEID_UNIT && u2->GetTypeId() == TYPEID_UNIT && u1->GetFaction() == u2->GetFaction(); @@ -11861,8 +11867,8 @@ bool Unit::IsInRaidWith(Unit const* unit) const if (u1->GetTypeId() == TYPEID_PLAYER && u2->GetTypeId() == TYPEID_PLAYER) return u1->ToPlayer()->IsInSameRaidWith(u2->ToPlayer()); - else if ((u2->GetTypeId() == TYPEID_PLAYER && u1->GetTypeId() == TYPEID_UNIT && u1->ToCreature()->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_TREAT_AS_RAID_UNIT) || - (u1->GetTypeId() == TYPEID_PLAYER && u2->GetTypeId() == TYPEID_UNIT && u2->ToCreature()->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_TREAT_AS_RAID_UNIT)) + else if ((u2->GetTypeId() == TYPEID_PLAYER && u1->GetTypeId() == TYPEID_UNIT && u1->ToCreature()->HasStaticFlag(CREATURE_STATIC_FLAG_4_TREAT_AS_RAID_UNIT_FOR_HELPFUL_SPELLS)) || + (u1->GetTypeId() == TYPEID_PLAYER && u2->GetTypeId() == TYPEID_UNIT && u2->ToCreature()->HasStaticFlag(CREATURE_STATIC_FLAG_4_TREAT_AS_RAID_UNIT_FOR_HELPFUL_SPELLS))) return true; return u1->GetTypeId() == TYPEID_UNIT && u2->GetTypeId() == TYPEID_UNIT && u1->GetFaction() == u2->GetFaction(); diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index 4bdc226588..9d2009ab75 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -557,7 +557,7 @@ void ObjectMgr::LoadCreatureTemplate(Field* fields) creatureTemplate.MechanicImmuneMask = fields[79].GetUInt32(); creatureTemplate.SpellSchoolImmuneMask = fields[80].GetUInt32(); creatureTemplate.flags_extra = fields[81].GetUInt32(); - CreatureStaticFlagsHolder(CreatureStaticFlags(fields[82].GetUInt32()), CreatureStaticFlags2(fields[83].GetUInt32()), + creatureTemplate.StaticFlags = CreatureStaticFlagsHolder(CreatureStaticFlags(fields[82].GetUInt32()), CreatureStaticFlags2(fields[83].GetUInt32()), CreatureStaticFlags3(fields[84].GetUInt32()), CreatureStaticFlags4(fields[85].GetUInt32()), CreatureStaticFlags5(fields[86].GetUInt32())); creatureTemplate.ScriptID = GetScriptId(fields[87].GetCString()); } diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index 059509e816..1cf00dad0b 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -2111,7 +2111,7 @@ void Spell::SearchChainTargets(std::list& targets, uint32 chainTar if (Unit* unit = (*itr)->ToUnit()) { uint32 deficit = unit->GetMaxHealth() - unit->GetHealth(); - if ((deficit > maxHPDeficit || foundItr == tempTargets.end()) && chainSource->IsWithinDist(unit, jumpRadius) && chainSource->IsWithinLOSInMap(unit, LINEOFSIGHT_ALL_CHECKS, VMAP::ModelIgnoreFlags::M2)) + if ((deficit > maxHPDeficit || foundItr == tempTargets.end()) && chainSource->IsWithinDist(unit, jumpRadius) && IsWithinLOS(chainSource, unit, false, VMAP::ModelIgnoreFlags::M2)) { foundItr = itr; maxHPDeficit = deficit; @@ -2126,10 +2126,10 @@ void Spell::SearchChainTargets(std::list& targets, uint32 chainTar { if (foundItr == tempTargets.end()) { - if (chainSource->IsWithinDist(*itr, jumpRadius) && chainSource->IsWithinLOSInMap(*itr, LINEOFSIGHT_ALL_CHECKS, VMAP::ModelIgnoreFlags::M2)) + if (chainSource->IsWithinDist(*itr, jumpRadius) && IsWithinLOS(chainSource , *itr, false, VMAP::ModelIgnoreFlags::M2)) foundItr = itr; } - else if (chainSource->GetDistanceOrder(*itr, *foundItr) && chainSource->IsWithinLOSInMap(*itr, LINEOFSIGHT_ALL_CHECKS, VMAP::ModelIgnoreFlags::M2)) + else if (chainSource->GetDistanceOrder(*itr, *foundItr) && IsWithinLOS(chainSource , *itr, false, VMAP::ModelIgnoreFlags::M2)) foundItr = itr; } } @@ -5832,7 +5832,7 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint if (DynamicObject* dynObj = m_caster->ToUnit()->GetDynObject(m_triggeredByAuraSpell->Id)) losTarget = dynObj; - if (!m_spellInfo->HasAttribute(SPELL_ATTR2_IGNORE_LINE_OF_SIGHT) && !DisableMgr::IsDisabledFor(DISABLE_TYPE_SPELL, m_spellInfo->Id, nullptr, SPELL_DISABLE_LOS) && !target->IsWithinLOSInMap(losTarget, LINEOFSIGHT_ALL_CHECKS, VMAP::ModelIgnoreFlags::M2)) + if (!IsWithinLOS(losTarget, target, true, VMAP::ModelIgnoreFlags::M2)) return SPELL_FAILED_LINE_OF_SIGHT; } } @@ -5840,13 +5840,8 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint // Check for line of sight for spells with dest if (m_targets.HasDst()) - { - float x, y, z; - m_targets.GetDstPos()->GetPosition(x, y, z); - - if (!m_spellInfo->HasAttribute(SPELL_ATTR2_IGNORE_LINE_OF_SIGHT) && !DisableMgr::IsDisabledFor(DISABLE_TYPE_SPELL, m_spellInfo->Id, nullptr, SPELL_DISABLE_LOS) && !m_caster->IsWithinLOS(x, y, z, LINEOFSIGHT_ALL_CHECKS, VMAP::ModelIgnoreFlags::M2)) + if (!IsWithinLOS(m_caster, *m_targets.GetDstPos(), VMAP::ModelIgnoreFlags::M2)) return SPELL_FAILED_LINE_OF_SIGHT; - } // check pet presence if (Unit* unitCaster = m_caster->ToUnit()) @@ -6103,7 +6098,7 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint return SPELL_FAILED_DONT_REPORT; // first we must check to see if the target is in LoS. A path can usually be built but LoS matters for charge spells - if (!target->IsWithinLOSInMap(unitCaster)) //Do full LoS/Path check. Don't exclude m2 + if (!IsWithinLOS(unitCaster, target, true, VMAP::ModelIgnoreFlags::Nothing)) //Do full LoS/Path check. Don't exclude m2 return SPELL_FAILED_LINE_OF_SIGHT; float objSize = target->GetCombatReach(); @@ -7745,7 +7740,7 @@ bool Spell::CheckEffectTarget(Unit const* target, uint32 eff, Position const* lo { case SPELL_EFFECT_RESURRECT_NEW: // player far away, maybe his corpse near? - if (target != m_caster && !target->IsWithinLOSInMap(m_caster)) + if (target != m_caster && !IsWithinLOS(m_caster, target, true, VMAP::ModelIgnoreFlags::M2)) { if (!m_targets.GetCorpseTargetGUID()) return false; @@ -7767,7 +7762,7 @@ bool Spell::CheckEffectTarget(Unit const* target, uint32 eff, Position const* lo { if (!m_targets.GetCorpseTargetGUID()) { - if (target->IsWithinLOSInMap(m_caster, LINEOFSIGHT_ALL_CHECKS, VMAP::ModelIgnoreFlags::M2) && target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE)) + if (IsWithinLOS(m_caster, target, true, VMAP::ModelIgnoreFlags::M2) && target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE)) return true; return false; @@ -7798,12 +7793,12 @@ bool Spell::CheckEffectTarget(Unit const* target, uint32 eff, Position const* lo caster = m_caster->GetMap()->GetGameObject(m_originalCasterGUID); if (!caster) caster = m_caster; - if (target != m_caster && !target->IsWithinLOSInMap(caster, LINEOFSIGHT_ALL_CHECKS, VMAP::ModelIgnoreFlags::M2)) + if (target != m_caster && !IsWithinLOS(caster, target, true, VMAP::ModelIgnoreFlags::M2)) return false; } if (losPosition) - if (!target->IsWithinLOS(losPosition->GetPositionX(), losPosition->GetPositionY(), losPosition->GetPositionZ(), LINEOFSIGHT_ALL_CHECKS, VMAP::ModelIgnoreFlags::M2)) + if (!IsWithinLOS(target, *losPosition, VMAP::ModelIgnoreFlags::M2)) return false; break; } @@ -8643,6 +8638,33 @@ void Spell::CancelGlobalCooldown() m_caster->ToUnit()->GetSpellHistory()->CancelGlobalCooldown(m_spellInfo); } +bool Spell::IsWithinLOS(WorldObject const* source, WorldObject const* target, bool targetAsSourceLocation, VMAP::ModelIgnoreFlags ignoreFlags) const +{ + if (m_spellInfo->HasAttribute(SPELL_ATTR2_IGNORE_LINE_OF_SIGHT)) + return true; + + if (DisableMgr::IsDisabledFor(DISABLE_TYPE_SPELL, m_spellInfo->Id, nullptr, SPELL_DISABLE_LOS)) + return true; + + if (target->IsCreature() && target->ToCreature()->CanIgnoreLineOfSightWhenCastingOnMe()) + return true; + + WorldObject const* src = targetAsSourceLocation ? target : source; + WorldObject const* dst = targetAsSourceLocation ? source : target; + return src->IsWithinLOSInMap(dst, LINEOFSIGHT_ALL_CHECKS, ignoreFlags); +} + +bool Spell::IsWithinLOS(WorldObject const* source, Position const& target, VMAP::ModelIgnoreFlags ignoreFlags) const +{ + if (m_spellInfo->HasAttribute(SPELL_ATTR2_IGNORE_LINE_OF_SIGHT)) + return true; + + if (DisableMgr::IsDisabledFor(DISABLE_TYPE_SPELL, m_spellInfo->Id, nullptr, SPELL_DISABLE_LOS)) + return true; + + return source->IsWithinLOS(target.GetPositionX(), target.GetPositionY(), target.GetPositionZ(), LINEOFSIGHT_ALL_CHECKS, ignoreFlags); +} + namespace Trinity { diff --git a/src/server/game/Spells/Spell.h b/src/server/game/Spells/Spell.h index 47212ef4dd..8ab0b132bd 100644 --- a/src/server/game/Spells/Spell.h +++ b/src/server/game/Spells/Spell.h @@ -20,6 +20,7 @@ #include "ConditionMgr.h" #include "DBCEnums.h" +#include "ModelIgnoreFlags.h" #include "ObjectGuid.h" #include "Optional.h" #include "Position.h" @@ -579,6 +580,9 @@ class TC_GAME_API Spell Spell** m_selfContainer; // pointer to our spell container (if applicable) + bool IsWithinLOS(WorldObject const* source, WorldObject const* target, bool targetAsSourceLocation, VMAP::ModelIgnoreFlags ignoreFlags) const; + bool IsWithinLOS(WorldObject const* source, Position const& target, VMAP::ModelIgnoreFlags ignoreFlags) const; + protected: bool HasGlobalCooldown() const; void TriggerGlobalCooldown();