diff --git a/Source/Game/AI/CoreAI/CreatureAI.cs b/Source/Game/AI/CoreAI/CreatureAI.cs index 7d6f7930b..4fadcfc91 100644 --- a/Source/Game/AI/CoreAI/CreatureAI.cs +++ b/Source/Game/AI/CoreAI/CreatureAI.cs @@ -399,10 +399,12 @@ namespace Game.AI public virtual void JustUnregisteredAreaTrigger(AreaTrigger areaTrigger) { } // Called when hit by a spell - public virtual void SpellHit(Unit caster, SpellInfo spell) { } + public virtual void SpellHit(Unit caster, SpellInfo spellInfo) { } + public virtual void SpellHit(GameObject caster, SpellInfo spellInfo) { } // Called when spell hits a target - public virtual void SpellHitTarget(Unit target, SpellInfo spell) { } + public virtual void SpellHitTarget(Unit target, SpellInfo spellInfo) { } + public virtual void SpellHitTarget(GameObject target, SpellInfo spellInfo) { } public virtual bool IsEscorted() { return false; } @@ -459,7 +461,7 @@ namespace Game.AI public virtual void QuestReward(Player player, Quest quest, LootItemType type, uint opt) { } /// == Waypoints system ============================= - + /// public virtual void WaypointPathStarted(uint pathId) { } public virtual void WaypointStarted(uint nodeId, uint pathId) { } @@ -479,7 +481,6 @@ namespace Game.AI // Object destruction is handled by Unit::RemoveCharmedBy public virtual PlayerAI GetAIForCharmedPlayer(Player who) { return null; } - /// /// Should return true if the NPC is target of an escort quest /// If onlyIfActive is set, should return true only if the escort quest is currently active diff --git a/Source/Game/AI/CoreAI/GameObjectAI.cs b/Source/Game/AI/CoreAI/GameObjectAI.cs index 5b99b2846..4d8f644cb 100644 --- a/Source/Game/AI/CoreAI/GameObjectAI.cs +++ b/Source/Game/AI/CoreAI/GameObjectAI.cs @@ -81,8 +81,8 @@ namespace Game.AI // prevents achievement tracking if returning true public virtual bool OnReportUse(Player player) { return false; } - public virtual void Destroyed(Player player, uint eventId) { } - public virtual void Damaged(Player player, uint eventId) { } + public virtual void Destroyed(WorldObject attacker, uint eventId) { } + public virtual void Damaged(WorldObject attacker, uint eventId) { } public virtual void SetData64(uint id, ulong value) { } public virtual ulong GetData64(uint id) { return 0; } @@ -93,6 +93,13 @@ namespace Game.AI public virtual void OnLootStateChanged(uint state, Unit unit) { } public virtual void OnStateChanged(GameObjectState state) { } public virtual void EventInform(uint eventId) { } - public virtual void SpellHit(Unit unit, SpellInfo spellInfo) { } + + // Called when hit by a spell + public virtual void SpellHit(Unit caster, SpellInfo spellInfo) { } + public virtual void SpellHit(GameObject caster, SpellInfo spellInfo) { } + + // Called when spell hits a target + public virtual void SpellHitTarget(Unit target, SpellInfo spellInfo) { } + public virtual void SpellHitTarget(GameObject target, SpellInfo spellInfo) { } } } diff --git a/Source/Game/AI/ScriptedAI/ScriptedAI.cs b/Source/Game/AI/ScriptedAI/ScriptedAI.cs index 44961a860..0f953e2c3 100644 --- a/Source/Game/AI/ScriptedAI/ScriptedAI.cs +++ b/Source/Game/AI/ScriptedAI/ScriptedAI.cs @@ -373,30 +373,6 @@ namespace Game.AI _isCombatMovementAllowed = allowMovement; } - // Called at any Damage from any attacker (before damage apply) - public override void DamageTaken(Unit attacker, ref uint damage) { } - - //Called at creature death - public override void JustDied(Unit killer) { } - - //Called at creature killing another unit - public override void KilledUnit(Unit victim) { } - - // Called when the creature summon successfully other creature - public override void JustSummoned(Creature summon) { } - - // Called when a summoned creature is despawned - public override void SummonedCreatureDespawn(Creature summon) { } - - // Called when hit by a spell - public override void SpellHit(Unit caster, SpellInfo spell) { } - - // Called when spell hits a target - public override void SpellHitTarget(Unit target, SpellInfo spell) { } - - // Called when AI is temporarily replaced or put back when possess is applied or removed - public virtual void OnPossess(bool apply) { } - public static Creature GetClosestCreatureWithEntry(WorldObject source, uint entry, float maxSearchRange, bool alive = true) { return source.FindNearestCreature(entry, maxSearchRange, alive); @@ -407,12 +383,6 @@ namespace Game.AI return source.FindNearestGameObject(entry, maxSearchRange); } - //Called at creature reset either by death or evade - public override void Reset() { } - - //Called at creature aggro either by MoveInLOS or Attack Start - public override void JustEngagedWith(Unit victim) { } - public bool HealthBelowPct(int pct) { return me.HealthBelowPct(pct); } public bool HealthAbovePct(int pct) { return me.HealthAbovePct(pct); } diff --git a/Source/Game/AI/SmartScripts/SmartAI.cs b/Source/Game/AI/SmartScripts/SmartAI.cs index 7456fa3cf..2d1adf70e 100644 --- a/Source/Game/AI/SmartScripts/SmartAI.cs +++ b/Source/Game/AI/SmartScripts/SmartAI.cs @@ -1118,9 +1118,9 @@ namespace Game.AI GetScript().ProcessEventsFor(SmartEvents.RewardQuest, player, quest.Id, opt, false, null, me); } - public override void Destroyed(Player player, uint eventId) + public override void Destroyed(WorldObject attacker, uint eventId) { - GetScript().ProcessEventsFor(SmartEvents.Death, player, eventId, 0, false, null, me); + GetScript().ProcessEventsFor(SmartEvents.Death, attacker != null ? attacker.ToUnit() : null, eventId, 0, false, null, me); } public override void SetData(uint id, uint value) { SetData(id, value, null); } diff --git a/Source/Game/AI/SmartScripts/SmartScript.cs b/Source/Game/AI/SmartScripts/SmartScript.cs index 6686e23b4..ee618b508 100644 --- a/Source/Game/AI/SmartScripts/SmartScript.cs +++ b/Source/Game/AI/SmartScripts/SmartScript.cs @@ -490,7 +490,7 @@ namespace Game.AI _me.CastSpell(target.ToUnit(), e.Action.cast.spell, new CastSpellExtraArgs(triggerFlag)); } else if (_go) - _go.CastSpell(target.ToUnit(), e.Action.cast.spell, triggerFlag); + _go.CastSpell(target.ToUnit(), e.Action.cast.spell, new CastSpellExtraArgs(triggerFlag)); } else Log.outDebug(LogFilter.ScriptsAi, "Spell {0} not casted because it has flag SMARTCAST_AURA_NOT_PRESENT and the target (Guid: {1} Entry: {2} Type: {3}) already has the aura", diff --git a/Source/Game/Achievements/CriteriaHandler.cs b/Source/Game/Achievements/CriteriaHandler.cs index b08690629..f2f5ccdb3 100644 --- a/Source/Game/Achievements/CriteriaHandler.cs +++ b/Source/Game/Achievements/CriteriaHandler.cs @@ -55,9 +55,9 @@ namespace Game.Achievements /// /// /// - /// + /// /// - public void UpdateCriteria(CriteriaTypes type, ulong miscValue1 = 0, ulong miscValue2 = 0, ulong miscValue3 = 0, Unit unit = null, Player referencePlayer = null) + public void UpdateCriteria(CriteriaTypes type, ulong miscValue1 = 0, ulong miscValue2 = 0, ulong miscValue3 = 0, WorldObject refe = null, Player referencePlayer = null) { if (type >= CriteriaTypes.TotalTypes) { @@ -84,13 +84,13 @@ namespace Game.Achievements foreach (Criteria criteria in criteriaList) { List trees = Global.CriteriaMgr.GetCriteriaTreesByCriteria(criteria.Id); - if (!CanUpdateCriteria(criteria, trees, miscValue1, miscValue2, miscValue3, unit, referencePlayer)) + if (!CanUpdateCriteria(criteria, trees, miscValue1, miscValue2, miscValue3, refe, referencePlayer)) continue; // requirements not found in the dbc CriteriaDataSet data = Global.CriteriaMgr.GetCriteriaDataSet(criteria); if (data != null) - if (!data.Meets(referencePlayer, unit, (uint)miscValue1, (uint)miscValue2)) + if (!data.Meets(referencePlayer, refe, (uint)miscValue1, (uint)miscValue2)) continue; switch (type) @@ -199,56 +199,56 @@ namespace Game.Achievements SetCriteriaProgress(criteria, (uint)referencePlayer.GetRewardedQuestCount(), referencePlayer); break; case CriteriaTypes.CompleteDailyQuestDaily: + { + long nextDailyResetTime = Global.WorldMgr.GetNextDailyQuestsResetTime(); + CriteriaProgress progress = GetCriteriaProgress(criteria); + + if (miscValue1 == 0) // Login case. { - long nextDailyResetTime = Global.WorldMgr.GetNextDailyQuestsResetTime(); - CriteriaProgress progress = GetCriteriaProgress(criteria); - - if (miscValue1 == 0) // Login case. - { - // reset if player missed one day. - if (progress != null && progress.Date < (nextDailyResetTime - 2 * Time.Day)) - SetCriteriaProgress(criteria, 0, referencePlayer); - continue; - } - - ProgressType progressType; - if (progress == null) - // 1st time. Start count. - progressType = ProgressType.Set; - else if (progress.Date < (nextDailyResetTime - 2 * Time.Day)) - // last progress is older than 2 days. Player missed 1 day => Restart count. - progressType = ProgressType.Set; - else if (progress.Date < (nextDailyResetTime - Time.Day)) - // last progress is between 1 and 2 days. => 1st time of the day. - progressType = ProgressType.Accumulate; - else - // last progress is within the day before the reset => Already counted today. - continue; - - SetCriteriaProgress(criteria, 1, referencePlayer, progressType); - break; + // reset if player missed one day. + if (progress != null && progress.Date < (nextDailyResetTime - 2 * Time.Day)) + SetCriteriaProgress(criteria, 0, referencePlayer); + continue; } + + ProgressType progressType; + if (progress == null) + // 1st time. Start count. + progressType = ProgressType.Set; + else if (progress.Date < (nextDailyResetTime - 2 * Time.Day)) + // last progress is older than 2 days. Player missed 1 day => Restart count. + progressType = ProgressType.Set; + else if (progress.Date < (nextDailyResetTime - Time.Day)) + // last progress is between 1 and 2 days. => 1st time of the day. + progressType = ProgressType.Accumulate; + else + // last progress is within the day before the reset => Already counted today. + continue; + + SetCriteriaProgress(criteria, 1, referencePlayer, progressType); + break; + } case CriteriaTypes.CompleteQuestsInZone: + { + if (miscValue1 != 0) { - if (miscValue1 != 0) - { - SetCriteriaProgress(criteria, 1, referencePlayer, ProgressType.Accumulate); - } - else // login case - { - uint counter = 0; - - var rewQuests = referencePlayer.GetRewardedQuests(); - foreach (var id in rewQuests) - { - Quest quest = Global.ObjectMgr.GetQuestTemplate(id); - if (quest != null && quest.QuestSortID >= 0 && quest.QuestSortID == criteria.Entry.Asset) - ++counter; - } - SetCriteriaProgress(criteria, counter, referencePlayer); - } - break; + SetCriteriaProgress(criteria, 1, referencePlayer, ProgressType.Accumulate); } + else // login case + { + uint counter = 0; + + var rewQuests = referencePlayer.GetRewardedQuests(); + foreach (var id in rewQuests) + { + Quest quest = Global.ObjectMgr.GetQuestTemplate(id); + if (quest != null && quest.QuestSortID >= 0 && quest.QuestSortID == criteria.Entry.Asset) + ++counter; + } + SetCriteriaProgress(criteria, counter, referencePlayer); + } + break; + } case CriteriaTypes.FallWithoutDying: // miscValue1 is the ingame fallheight*100 as stored in dbc SetCriteriaProgress(criteria, miscValue1, referencePlayer); @@ -269,35 +269,35 @@ namespace Game.Achievements SetCriteriaProgress(criteria, referencePlayer.GetBankBagSlotCount(), referencePlayer); break; case CriteriaTypes.GainReputation: - { - int reputation = referencePlayer.GetReputationMgr().GetReputation(criteria.Entry.Asset); - if (reputation > 0) - SetCriteriaProgress(criteria, (uint)reputation, referencePlayer); - break; - } + { + int reputation = referencePlayer.GetReputationMgr().GetReputation(criteria.Entry.Asset); + if (reputation > 0) + SetCriteriaProgress(criteria, (uint)reputation, referencePlayer); + break; + } case CriteriaTypes.GainExaltedReputation: SetCriteriaProgress(criteria, referencePlayer.GetReputationMgr().GetExaltedFactionCount(), referencePlayer); break; case CriteriaTypes.LearnSkilllineSpells: case CriteriaTypes.LearnSkillLine: + { + uint spellCount = 0; + foreach (var spell in referencePlayer.GetSpellMap()) { - uint spellCount = 0; - foreach (var spell in referencePlayer.GetSpellMap()) + var bounds = Global.SpellMgr.GetSkillLineAbilityMapBounds(spell.Key); + foreach (var skill in bounds) { - var bounds = Global.SpellMgr.GetSkillLineAbilityMapBounds(spell.Key); - foreach (var skill in bounds) + if (skill.SkillLine == criteria.Entry.Asset) { - if (skill.SkillLine == criteria.Entry.Asset) - { - // do not add couter twice if by any chance skill is listed twice in dbc (eg. skill 777 and spell 22717) - ++spellCount; - break; - } + // do not add couter twice if by any chance skill is listed twice in dbc (eg. skill 777 and spell 22717) + ++spellCount; + break; } } - SetCriteriaProgress(criteria, spellCount, referencePlayer); - break; } + SetCriteriaProgress(criteria, spellCount, referencePlayer); + break; + } case CriteriaTypes.GainReveredReputation: SetCriteriaProgress(criteria, referencePlayer.GetReputationMgr().GetReveredFactionCount(), referencePlayer); break; @@ -319,39 +319,39 @@ namespace Game.Achievements SetCriteriaProgress(criteria, miscValue1, referencePlayer, ProgressType.Accumulate); break; case CriteriaTypes.HighestPersonalRating: - { - uint reqTeamType = criteria.Entry.Asset; + { + uint reqTeamType = criteria.Entry.Asset; - if (miscValue1 != 0) + if (miscValue1 != 0) + { + if (miscValue2 != reqTeamType) + continue; + + SetCriteriaProgress(criteria, miscValue1, referencePlayer, ProgressType.Highest); + } + else // login case + { + + for (byte arena_slot = 0; arena_slot < SharedConst.MaxArenaSlot; ++arena_slot) { - if (miscValue2 != reqTeamType) + uint teamId = referencePlayer.GetArenaTeamId(arena_slot); + if (teamId == 0) continue; - SetCriteriaProgress(criteria, miscValue1, referencePlayer, ProgressType.Highest); - } - else // login case - { + ArenaTeam team = Global.ArenaTeamMgr.GetArenaTeamById(teamId); + if (team == null || team.GetArenaType() != reqTeamType) + continue; - for (byte arena_slot = 0; arena_slot < SharedConst.MaxArenaSlot; ++arena_slot) + ArenaTeamMember member = team.GetMember(referencePlayer.GetGUID()); + if (member != null) { - uint teamId = referencePlayer.GetArenaTeamId(arena_slot); - if (teamId == 0) - continue; - - ArenaTeam team = Global.ArenaTeamMgr.GetArenaTeamById(teamId); - if (team == null || team.GetArenaType() != reqTeamType) - continue; - - ArenaTeamMember member = team.GetMember(referencePlayer.GetGUID()); - if (member != null) - { - SetCriteriaProgress(criteria, member.PersonalRating, referencePlayer, ProgressType.Highest); - break; - } + SetCriteriaProgress(criteria, member.PersonalRating, referencePlayer, ProgressType.Highest); + break; } } - break; } + break; + } case CriteriaTypes.OwnBattlePetCount: SetCriteriaProgress(criteria, referencePlayer.GetSession().GetBattlePetMgr().GetPetUniqueSpeciesCount(), referencePlayer); break; @@ -591,12 +591,12 @@ namespace Game.Achievements newValue = changeValue; break; case ProgressType.Accumulate: - { - // avoid overflow - ulong max_value = ulong.MaxValue; - newValue = max_value - progress.Counter > changeValue ? progress.Counter + changeValue : max_value; - break; - } + { + // avoid overflow + ulong max_value = ulong.MaxValue; + newValue = max_value - progress.Counter > changeValue ? progress.Counter + changeValue : max_value; + break; + } case ProgressType.Highest: newValue = progress.Counter < changeValue ? changeValue : progress.Counter; break; @@ -668,75 +668,75 @@ namespace Game.Achievements return false; return true; case CriteriaTreeOperator.Sum: + { + ulong progress = 0; + CriteriaManager.WalkCriteriaTree(tree, criteriaTree => { - ulong progress = 0; - CriteriaManager.WalkCriteriaTree(tree, criteriaTree => + if (criteriaTree.Criteria != null) { - if (criteriaTree.Criteria != null) - { - CriteriaProgress criteriaProgress = GetCriteriaProgress(criteriaTree.Criteria); - if (criteriaProgress != null) - progress += criteriaProgress.Counter; - } - }); - return progress >= requiredCount; - } - case CriteriaTreeOperator.Highest: - { - ulong progress = 0; - CriteriaManager.WalkCriteriaTree(tree, criteriaTree => - { - if (criteriaTree.Criteria != null) - { - CriteriaProgress criteriaProgress = GetCriteriaProgress(criteriaTree.Criteria); - if (criteriaProgress != null) - if (criteriaProgress.Counter > progress) - progress = criteriaProgress.Counter; - } - }); - return progress >= requiredCount; - } - case CriteriaTreeOperator.StartedAtLeast: - { - ulong progress = 0; - foreach (CriteriaTree node in tree.Children) - { - if (node.Criteria != null) - { - CriteriaProgress criteriaProgress = GetCriteriaProgress(node.Criteria); - if (criteriaProgress != null) - if (criteriaProgress.Counter >= 1) - if (++progress >= requiredCount) - return true; - } + CriteriaProgress criteriaProgress = GetCriteriaProgress(criteriaTree.Criteria); + if (criteriaProgress != null) + progress += criteriaProgress.Counter; } - - return false; - } - case CriteriaTreeOperator.CompleteAtLeast: + }); + return progress >= requiredCount; + } + case CriteriaTreeOperator.Highest: + { + ulong progress = 0; + CriteriaManager.WalkCriteriaTree(tree, criteriaTree => { - ulong progress = 0; - foreach (CriteriaTree node in tree.Children) - if (IsCompletedCriteriaTree(node)) - if (++progress >= requiredCount) - return true; - - return false; - } - case CriteriaTreeOperator.ProgressBar: - { - ulong progress = 0; - CriteriaManager.WalkCriteriaTree(tree, criteriaTree => + if (criteriaTree.Criteria != null) { - if (criteriaTree.Criteria != null) - { - CriteriaProgress criteriaProgress = GetCriteriaProgress(criteriaTree.Criteria); - if (criteriaProgress != null) - progress += criteriaProgress.Counter * criteriaTree.Entry.Amount; - } - }); - return progress >= requiredCount; + CriteriaProgress criteriaProgress = GetCriteriaProgress(criteriaTree.Criteria); + if (criteriaProgress != null) + if (criteriaProgress.Counter > progress) + progress = criteriaProgress.Counter; + } + }); + return progress >= requiredCount; + } + case CriteriaTreeOperator.StartedAtLeast: + { + ulong progress = 0; + foreach (CriteriaTree node in tree.Children) + { + if (node.Criteria != null) + { + CriteriaProgress criteriaProgress = GetCriteriaProgress(node.Criteria); + if (criteriaProgress != null) + if (criteriaProgress.Counter >= 1) + if (++progress >= requiredCount) + return true; + } } + + return false; + } + case CriteriaTreeOperator.CompleteAtLeast: + { + ulong progress = 0; + foreach (CriteriaTree node in tree.Children) + if (IsCompletedCriteriaTree(node)) + if (++progress >= requiredCount) + return true; + + return false; + } + case CriteriaTreeOperator.ProgressBar: + { + ulong progress = 0; + CriteriaManager.WalkCriteriaTree(tree, criteriaTree => + { + if (criteriaTree.Criteria != null) + { + CriteriaProgress criteriaProgress = GetCriteriaProgress(criteriaTree.Criteria); + if (criteriaProgress != null) + progress += criteriaProgress.Counter * criteriaTree.Entry.Amount; + } + }); + return progress >= requiredCount; + } default: break; } @@ -859,7 +859,7 @@ namespace Game.Achievements return false; } - bool CanUpdateCriteria(Criteria criteria, List trees, ulong miscValue1, ulong miscValue2, ulong miscValue3, Unit unit, Player referencePlayer) + bool CanUpdateCriteria(Criteria criteria, List trees, ulong miscValue1, ulong miscValue2, ulong miscValue3, WorldObject refe, Player referencePlayer) { if (Global.DisableMgr.IsDisabledFor(DisableType.Criteria, criteria.Id, null)) { @@ -880,13 +880,13 @@ namespace Game.Achievements if (!treeRequirementPassed) return false; - if (!RequirementsSatisfied(criteria, miscValue1, miscValue2, miscValue3, unit, referencePlayer)) + if (!RequirementsSatisfied(criteria, miscValue1, miscValue2, miscValue3, refe, referencePlayer)) { Log.outTrace(LogFilter.Achievement, "CanUpdateCriteria: (Id: {0} Type {1}) Requirements not satisfied", criteria.Id, criteria.Entry.Type); return false; } - if (criteria.Modifier != null && !ModifierTreeSatisfied(criteria.Modifier, miscValue1, miscValue2, unit, referencePlayer)) + if (criteria.Modifier != null && !ModifierTreeSatisfied(criteria.Modifier, miscValue1, miscValue2, refe, referencePlayer)) { Log.outTrace(LogFilter.Achievement, "CanUpdateCriteria: (Id: {0} Type {1}) Requirements have not been satisfied", criteria.Id, criteria.Entry.Type); return false; @@ -923,7 +923,7 @@ namespace Game.Achievements return true; } - bool RequirementsSatisfied(Criteria criteria, ulong miscValue1, ulong miscValue2, ulong miscValue3, Unit unit, Player referencePlayer) + bool RequirementsSatisfied(Criteria criteria, ulong miscValue1, ulong miscValue2, ulong miscValue3, WorldObject refe, Player referencePlayer) { switch (criteria.Entry.Type) { @@ -1014,27 +1014,27 @@ namespace Game.Achievements } break; case CriteriaTypes.Death: - { - if (miscValue1 == 0) - return false; - break; - } + { + if (miscValue1 == 0) + return false; + break; + } case CriteriaTypes.DeathInDungeon: - { - if (miscValue1 == 0) - return false; + { + if (miscValue1 == 0) + return false; - Map map = referencePlayer.IsInWorld ? referencePlayer.GetMap() : Global.MapMgr.FindMap(referencePlayer.GetMapId(), referencePlayer.GetInstanceId()); - if (!map || !map.IsDungeon()) - return false; + Map map = referencePlayer.IsInWorld ? referencePlayer.GetMap() : Global.MapMgr.FindMap(referencePlayer.GetMapId(), referencePlayer.GetInstanceId()); + if (!map || !map.IsDungeon()) + return false; - //FIXME: work only for instances where max == min for players - if (map.ToInstanceMap().GetMaxPlayers() != criteria.Entry.Asset) - return false; - break; - } + //FIXME: work only for instances where max == min for players + if (map.ToInstanceMap().GetMaxPlayers() != criteria.Entry.Asset) + return false; + break; + } case CriteriaTypes.KilledByPlayer: - if (miscValue1 == 0 || !unit || !unit.IsTypeId(TypeId.Player)) + if (miscValue1 == 0 || !refe || !refe.IsTypeId(TypeId.Player)) return false; break; case CriteriaTypes.DeathsFrom: @@ -1042,25 +1042,25 @@ namespace Game.Achievements return false; break; case CriteriaTypes.CompleteQuest: + { + // if miscValues != 0, it contains the questID. + if (miscValue1 != 0) { - // if miscValues != 0, it contains the questID. - if (miscValue1 != 0) - { - if (miscValue1 != criteria.Entry.Asset) - return false; - } - else - { - // login case. - if (!referencePlayer.GetQuestRewardStatus(criteria.Entry.Asset)) - return false; - } - CriteriaDataSet data = Global.CriteriaMgr.GetCriteriaDataSet(criteria); - if (data != null) - if (!data.Meets(referencePlayer, unit)) - return false; - break; + if (miscValue1 != criteria.Entry.Asset) + return false; } + else + { + // login case. + if (!referencePlayer.GetQuestRewardStatus(criteria.Entry.Asset)) + return false; + } + CriteriaDataSet data = Global.CriteriaMgr.GetCriteriaDataSet(criteria); + if (data != null) + if (!data.Meets(referencePlayer, refe)) + return false; + break; + } case CriteriaTypes.BeSpellTarget: case CriteriaTypes.BeSpellTarget2: case CriteriaTypes.CastSpell: @@ -1092,37 +1092,37 @@ namespace Game.Achievements return false; break; case CriteriaTypes.ExploreArea: + { + WorldMapOverlayRecord worldOverlayEntry = CliDB.WorldMapOverlayStorage.LookupByKey(criteria.Entry.Asset); + if (worldOverlayEntry == null) + break; + + bool matchFound = false; + for (int j = 0; j < SharedConst.MaxWorldMapOverlayArea; ++j) { - WorldMapOverlayRecord worldOverlayEntry = CliDB.WorldMapOverlayStorage.LookupByKey(criteria.Entry.Asset); - if (worldOverlayEntry == null) + AreaTableRecord area = CliDB.AreaTableStorage.LookupByKey(worldOverlayEntry.AreaID[j]); + if (area == null) break; - bool matchFound = false; - for (int j = 0; j < SharedConst.MaxWorldMapOverlayArea; ++j) + if (area.AreaBit < 0) + continue; + + int playerIndexOffset = (int)((uint)area.AreaBit / 64); + if (playerIndexOffset >= PlayerConst.ExploredZonesSize) + continue; + + ulong mask = 1ul << (int)((uint)area.AreaBit % 64); + if (Convert.ToBoolean(referencePlayer.m_activePlayerData.ExploredZones[playerIndexOffset] & mask)) { - AreaTableRecord area = CliDB.AreaTableStorage.LookupByKey(worldOverlayEntry.AreaID[j]); - if (area == null) - break; - - if (area.AreaBit < 0) - continue; - - int playerIndexOffset = (int)((uint)area.AreaBit / 64); - if (playerIndexOffset >= PlayerConst.ExploredZonesSize) - continue; - - ulong mask = 1ul << (int)((uint)area.AreaBit % 64); - if (Convert.ToBoolean(referencePlayer.m_activePlayerData.ExploredZones[playerIndexOffset] & mask)) - { - matchFound = true; - break; - } + matchFound = true; + break; } - - if (!matchFound) - return false; - break; } + + if (!matchFound) + return false; + break; + } case CriteriaTypes.GainReputation: if (miscValue1 != 0 && miscValue1 != criteria.Entry.Asset) return false; @@ -1135,16 +1135,16 @@ namespace Game.Achievements break; case CriteriaTypes.RollNeedOnLoot: case CriteriaTypes.RollGreedOnLoot: - { - // miscValue1 = itemid miscValue2 = diced value - if (miscValue1 == 0 || miscValue2 != criteria.Entry.Asset) - return false; + { + // miscValue1 = itemid miscValue2 = diced value + if (miscValue1 == 0 || miscValue2 != criteria.Entry.Asset) + return false; - ItemTemplate proto = Global.ObjectMgr.GetItemTemplate((uint)miscValue1); - if (proto == null) - return false; - break; - } + ItemTemplate proto = Global.ObjectMgr.GetItemTemplate((uint)miscValue1); + if (proto == null) + return false; + break; + } case CriteriaTypes.DoEmote: if (miscValue1 == 0 || miscValue1 != criteria.Entry.Asset) return false; @@ -1160,7 +1160,7 @@ namespace Game.Achievements return false; // map specific case (BG in fact) expected player targeted damage/heal - if (!unit || !unit.IsTypeId(TypeId.Player)) + if (!refe || !refe.IsTypeId(TypeId.Player)) return false; } break; @@ -1225,29 +1225,29 @@ namespace Game.Achievements return true; } - public bool ModifierTreeSatisfied(ModifierTreeNode tree, ulong miscValue1, ulong miscValue2, Unit unit, Player referencePlayer) + public bool ModifierTreeSatisfied(ModifierTreeNode tree, ulong miscValue1, ulong miscValue2, WorldObject refe, Player referencePlayer) { switch ((ModifierTreeOperator)tree.Entry.Operator) { case ModifierTreeOperator.SingleTrue: - return tree.Entry.Type != 0 && ModifierSatisfied(tree.Entry, miscValue1, miscValue2, unit, referencePlayer); + return tree.Entry.Type != 0 && ModifierSatisfied(tree.Entry, miscValue1, miscValue2, refe, referencePlayer); case ModifierTreeOperator.SingleFalse: - return tree.Entry.Type != 0 && !ModifierSatisfied(tree.Entry, miscValue1, miscValue2, unit, referencePlayer); + return tree.Entry.Type != 0 && !ModifierSatisfied(tree.Entry, miscValue1, miscValue2, refe, referencePlayer); case ModifierTreeOperator.All: foreach (ModifierTreeNode node in tree.Children) - if (!ModifierTreeSatisfied(node, miscValue1, miscValue2, unit, referencePlayer)) + if (!ModifierTreeSatisfied(node, miscValue1, miscValue2, refe, referencePlayer)) return false; return true; case ModifierTreeOperator.Some: - { - sbyte requiredAmount = Math.Max(tree.Entry.Amount, (sbyte)1); - foreach (ModifierTreeNode node in tree.Children) - if (ModifierTreeSatisfied(node, miscValue1, miscValue2, unit, referencePlayer)) - if (--requiredAmount == 0) - return true; + { + sbyte requiredAmount = Math.Max(tree.Entry.Amount, (sbyte)1); + foreach (ModifierTreeNode node in tree.Children) + if (ModifierTreeSatisfied(node, miscValue1, miscValue2, refe, referencePlayer)) + if (--requiredAmount == 0) + return true; - return false; - } + return false; + } default: break; } @@ -1255,7 +1255,7 @@ namespace Game.Achievements return false; } - bool ModifierSatisfied(ModifierTreeRecord modifier, ulong miscValue1, ulong miscValue2, Unit unit, Player referencePlayer) + bool ModifierSatisfied(ModifierTreeRecord modifier, ulong miscValue1, ulong miscValue2, WorldObject refe, Player referencePlayer) { uint reqValue = modifier.Asset; int secondaryAsset = modifier.SecondaryAsset; @@ -1264,41 +1264,41 @@ namespace Game.Achievements switch ((ModifierTreeType)modifier.Type) { case ModifierTreeType.PlayerInebriationLevelEqualOrGreaterThan: // 1 - { - uint inebriation = (uint)Math.Min(Math.Max(referencePlayer.GetDrunkValue(), referencePlayer.m_playerData.FakeInebriation), 100); - if (inebriation < reqValue) - return false; - break; - } + { + uint inebriation = (uint)Math.Min(Math.Max(referencePlayer.GetDrunkValue(), referencePlayer.m_playerData.FakeInebriation), 100); + if (inebriation < reqValue) + return false; + break; + } case ModifierTreeType.PlayerMeetsCondition: // 2 - { - PlayerConditionRecord playerCondition = CliDB.PlayerConditionStorage.LookupByKey(reqValue); - if (playerCondition == null || !ConditionManager.IsPlayerMeetingCondition(referencePlayer, playerCondition)) - return false; - break; - } + { + PlayerConditionRecord playerCondition = CliDB.PlayerConditionStorage.LookupByKey(reqValue); + if (playerCondition == null || !ConditionManager.IsPlayerMeetingCondition(referencePlayer, playerCondition)) + return false; + break; + } case ModifierTreeType.MinimumItemLevel: // 3 - { - // miscValue1 is itemid - ItemTemplate item = Global.ObjectMgr.GetItemTemplate((uint)miscValue1); - if (item == null || item.GetBaseItemLevel() < reqValue) - return false; - break; - } + { + // miscValue1 is itemid + ItemTemplate item = Global.ObjectMgr.GetItemTemplate((uint)miscValue1); + if (item == null || item.GetBaseItemLevel() < reqValue) + return false; + break; + } case ModifierTreeType.TargetCreatureId: // 4 - if (unit == null || unit.GetEntry() != reqValue) + if (refe == null || refe.GetEntry() != reqValue) return false; break; case ModifierTreeType.TargetIsPlayer: // 5 - if (unit == null || !unit.IsTypeId(TypeId.Player)) + if (refe == null || !refe.IsTypeId(TypeId.Player)) return false; break; case ModifierTreeType.TargetIsDead: // 6 - if (unit == null || unit.IsAlive()) + if (refe == null || !refe.IsUnit() || refe.ToUnit().IsAlive()) return false; break; case ModifierTreeType.TargetIsOppositeFaction: // 7 - if (unit == null || !referencePlayer.IsHostileTo(unit)) + if (refe == null || !referencePlayer.IsHostileTo(refe)) return false; break; case ModifierTreeType.PlayerHasAura: // 8 @@ -1310,15 +1310,15 @@ namespace Game.Achievements return false; break; case ModifierTreeType.TargetHasAura: // 10 - if (unit == null || !unit.HasAura(reqValue)) + if (refe == null || !refe.IsUnit() || !refe.ToUnit().HasAura(reqValue)) return false; break; case ModifierTreeType.TargetHasAuraEffect: // 11 - if (unit == null || !unit.HasAuraType((AuraType)reqValue)) + if (refe == null || !refe.IsUnit() || !refe.ToUnit().HasAuraType((AuraType)reqValue)) return false; break; case ModifierTreeType.TargetHasAuraState: // 12 - if (unit == null || !unit.HasAuraState((AuraStateType)reqValue)) + if (refe == null || !refe.IsUnit() || !refe.ToUnit().HasAuraState((AuraStateType)reqValue)) return false; break; case ModifierTreeType.PlayerHasAuraState: // 13 @@ -1326,73 +1326,73 @@ namespace Game.Achievements return false; break; case ModifierTreeType.ItemQualityIsAtLeast: // 14 - { - // miscValue1 is itemid - ItemTemplate item = Global.ObjectMgr.GetItemTemplate((uint)miscValue1); - if (item == null || (uint)item.GetQuality() < reqValue) - return false; - break; - } + { + // miscValue1 is itemid + ItemTemplate item = Global.ObjectMgr.GetItemTemplate((uint)miscValue1); + if (item == null || (uint)item.GetQuality() < reqValue) + return false; + break; + } case ModifierTreeType.ItemQualityIsExactly: // 15 - { - // miscValue1 is itemid - ItemTemplate item = Global.ObjectMgr.GetItemTemplate((uint)miscValue1); - if (item == null || (uint)item.GetQuality() != reqValue) - return false; - break; - } + { + // miscValue1 is itemid + ItemTemplate item = Global.ObjectMgr.GetItemTemplate((uint)miscValue1); + if (item == null || (uint)item.GetQuality() != reqValue) + return false; + break; + } case ModifierTreeType.PlayerIsAlive: // 16 if (referencePlayer.IsDead()) return false; break; case ModifierTreeType.PlayerIsInArea: // 17 - { - uint zoneId, areaId; - referencePlayer.GetZoneAndAreaId(out zoneId, out areaId); - if (zoneId != reqValue && areaId != reqValue) - return false; - break; - } + { + uint zoneId, areaId; + referencePlayer.GetZoneAndAreaId(out zoneId, out areaId); + if (zoneId != reqValue && areaId != reqValue) + return false; + break; + } case ModifierTreeType.TargetIsInArea: // 18 - { - if (unit == null) - return false; - uint zoneId, areaId; - unit.GetZoneAndAreaId(out zoneId, out areaId); - if (zoneId != reqValue && areaId != reqValue) - return false; - break; - } + { + if (refe == null) + return false; + uint zoneId, areaId; + refe.GetZoneAndAreaId(out zoneId, out areaId); + if (zoneId != reqValue && areaId != reqValue) + return false; + break; + } case ModifierTreeType.ItemId: // 19 if (miscValue1 != reqValue) return false; break; case ModifierTreeType.LegacyDungeonDifficulty: // 20 - { - DifficultyRecord difficulty = CliDB.DifficultyStorage.LookupByKey(referencePlayer.GetMap().GetDifficultyID()); - if (difficulty == null || difficulty.OldEnumValue == -1 || difficulty.OldEnumValue != reqValue) - return false; - break; - } + { + DifficultyRecord difficulty = CliDB.DifficultyStorage.LookupByKey(referencePlayer.GetMap().GetDifficultyID()); + if (difficulty == null || difficulty.OldEnumValue == -1 || difficulty.OldEnumValue != reqValue) + return false; + break; + } case ModifierTreeType.PlayerToTargetLevelDeltaGreaterThan: // 21 - if (unit == null || referencePlayer.GetLevel() < unit.GetLevel() + reqValue) + if (refe == null || !refe.IsUnit() || referencePlayer.GetLevel() < refe.ToUnit().GetLevel() + reqValue) return false; break; case ModifierTreeType.TargetToPlayerLevelDeltaGreaterThan: // 22 - if (!unit || referencePlayer.GetLevel() + reqValue < unit.GetLevel()) + if (!refe || !refe.IsUnit() || referencePlayer.GetLevel() + reqValue < refe.ToUnit().GetLevel()) return false; break; case ModifierTreeType.PlayerLevelEqualTargetLevel: // 23 - if (!unit || referencePlayer.GetLevel() != unit.GetLevel()) + if (!refe || !refe.IsUnit() || referencePlayer.GetLevel() != refe.ToUnit().GetLevel()) return false; break; case ModifierTreeType.PlayerInArenaWithTeamSize: // 24 - { - Battleground bg = referencePlayer.GetBattleground(); - if (!bg || !bg.IsArena() || bg.GetArenaType() != (ArenaTypes)reqValue) - return false; - break; - } + { + Battleground bg = referencePlayer.GetBattleground(); + if (!bg || !bg.IsArena() || bg.GetArenaType() != (ArenaTypes)reqValue) + return false; + break; + } case ModifierTreeType.PlayerRace: // 25 if ((uint)referencePlayer.GetRace() != reqValue) return false; @@ -1402,11 +1402,11 @@ namespace Game.Achievements return false; break; case ModifierTreeType.TargetRace: // 27 - if (unit == null || !unit.IsTypeId(TypeId.Player) || (uint)unit.GetRace() != reqValue) + if (refe == null || !refe.IsUnit() || refe.ToUnit().GetRace() != (Race)reqValue) return false; break; case ModifierTreeType.TargetClass: // 28 - if (unit == null || !unit.IsTypeId(TypeId.Player) || (uint)unit.GetClass() != reqValue) + if (refe == null || !refe.IsUnit() || refe.ToUnit().GetClass() != (Class)reqValue) return false; break; case ModifierTreeType.LessThanTappers: // 29 @@ -1414,22 +1414,22 @@ namespace Game.Achievements return false; break; case ModifierTreeType.CreatureType: // 30 - { - if (unit == null) - return false; + { + if (refe == null) + return false; - if (!unit.IsTypeId(TypeId.Unit) || (uint)unit.GetCreatureType() != reqValue) - return false; - break; - } + if (!refe.IsUnit() || refe.ToUnit().GetCreatureType() != (CreatureType)reqValue) + return false; + break; + } case ModifierTreeType.CreatureFamily: // 31 - { - if (!unit) - return false; - if (unit.GetTypeId() != TypeId.Unit || unit.ToCreature().GetCreatureTemplate().Family != (CreatureFamily)reqValue) - return false; - break; - } + { + if (!refe) + return false; + if (!refe.IsCreature() || refe.ToCreature().GetCreatureTemplate().Family != (CreatureFamily)reqValue) + return false; + break; + } case ModifierTreeType.PlayerMap: // 32 if (referencePlayer.GetMapId() != reqValue) return false; @@ -1464,33 +1464,33 @@ namespace Game.Achievements return false; break; case ModifierTreeType.TargetLevelEqual: // 40 - if (unit == null || unit.GetLevelForTarget(referencePlayer) != reqValue) + if (refe == null || refe.GetLevelForTarget(referencePlayer) != reqValue) return false; break; case ModifierTreeType.PlayerIsInZone: // 41 - { - uint zoneId = referencePlayer.GetAreaId(); - AreaTableRecord areaEntry = CliDB.AreaTableStorage.LookupByKey(zoneId); - if (areaEntry != null) - if (areaEntry.Flags.HasFlag(AreaFlags.Unk9)) - zoneId = areaEntry.ParentAreaID; - if (zoneId != reqValue) - return false; - break; - } + { + uint zoneId = referencePlayer.GetAreaId(); + AreaTableRecord areaEntry = CliDB.AreaTableStorage.LookupByKey(zoneId); + if (areaEntry != null) + if (areaEntry.Flags.HasFlag(AreaFlags.Unk9)) + zoneId = areaEntry.ParentAreaID; + if (zoneId != reqValue) + return false; + break; + } case ModifierTreeType.TargetIsInZone: // 42 - { - if (!unit) - return false; - uint zoneId = unit.GetAreaId(); - AreaTableRecord areaEntry = CliDB.AreaTableStorage.LookupByKey(zoneId); - if (areaEntry != null) - if (areaEntry.Flags.HasFlag(AreaFlags.Unk9)) - zoneId = areaEntry.ParentAreaID; - if (zoneId != reqValue) - return false; - break; - } + { + if (!refe) + return false; + uint zoneId = refe.GetAreaId(); + AreaTableRecord areaEntry = CliDB.AreaTableStorage.LookupByKey(zoneId); + if (areaEntry != null) + if (areaEntry.Flags.HasFlag(AreaFlags.Unk9)) + zoneId = areaEntry.ParentAreaID; + if (zoneId != reqValue) + return false; + break; + } case ModifierTreeType.PlayerHealthBelowPercent: // 43 if (referencePlayer.GetHealthPct() > (float)reqValue) return false; @@ -1504,15 +1504,15 @@ namespace Game.Achievements return false; break; case ModifierTreeType.TargetHealthBelowPercent: // 46 - if (unit == null || unit.GetHealthPct() >= (float)reqValue) + if (refe == null || !refe.IsUnit() || refe.ToUnit().GetHealthPct() > reqValue) return false; break; case ModifierTreeType.TargetHealthAbovePercent: // 47 - if (!unit || unit.GetHealthPct() < (float)reqValue) + if (!refe || !refe.IsUnit() || refe.ToUnit().GetHealthPct() < reqValue) return false; break; case ModifierTreeType.TargetHealthEqualsPercent: // 48 - if (!unit || unit.GetHealthPct() != (float)reqValue) + if (!refe || !refe.IsUnit() || refe.ToUnit().GetHealthPct() != reqValue) return false; break; case ModifierTreeType.PlayerHealthBelowValue: // 49 @@ -1528,27 +1528,27 @@ namespace Game.Achievements return false; break; case ModifierTreeType.TargetHealthBelowValue: // 52 - if (!unit || unit.GetHealth() > reqValue) + if (!refe || !refe.IsUnit() || refe.ToUnit().GetHealth() > reqValue) return false; break; case ModifierTreeType.TargetHealthAboveValue: // 53 - if (!unit || unit.GetHealth() < reqValue) + if (!refe || !refe.IsUnit() || refe.ToUnit().GetHealth() < reqValue) return false; break; case ModifierTreeType.TargetHealthEqualsValue: // 54 - if (!unit || unit.GetHealth() != reqValue) + if (!refe || !refe.IsUnit() || refe.ToUnit().GetHealth() != reqValue) return false; break; case ModifierTreeType.TargetIsPlayerAndMeetsCondition: // 55 - { - if (unit == null || !unit.IsPlayer()) - return false; + { + if (refe == null || !refe.IsPlayer()) + return false; - PlayerConditionRecord playerCondition = CliDB.PlayerConditionStorage.LookupByKey(reqValue); - if (playerCondition == null || !ConditionManager.IsPlayerMeetingCondition(unit.ToPlayer(), playerCondition)) - return false; - break; - } + PlayerConditionRecord playerCondition = CliDB.PlayerConditionStorage.LookupByKey(reqValue); + if (playerCondition == null || !ConditionManager.IsPlayerMeetingCondition(refe.ToPlayer(), playerCondition)) + return false; + break; + } case ModifierTreeType.PlayerHasMoreThanAchievementPoints: // 56 if (referencePlayer.GetAchievementPoints() <= reqValue) return false; @@ -1566,12 +1566,12 @@ namespace Game.Achievements return false; break; case ModifierTreeType.PlayerInRankedArenaMatch: // 60 - { - Battleground bg = referencePlayer.GetBattleground(); - if (bg == null || !bg.IsArena() || !bg.IsRated()) - return false; - break; - } + { + Battleground bg = referencePlayer.GetBattleground(); + if (bg == null || !bg.IsArena() || !bg.IsRated()) + return false; + break; + } case ModifierTreeType.PlayerInGuildParty: // 61 NYI return false; case ModifierTreeType.PlayerGuildReputationEqualOrGreaterThan: // 62 @@ -1579,12 +1579,12 @@ namespace Game.Achievements return false; break; case ModifierTreeType.PlayerInRatedBattleground: // 63 - { - Battleground bg = referencePlayer.GetBattleground(); - if (bg == null || !bg.IsBattleground() || !bg.IsRated()) - return false; - break; - } + { + Battleground bg = referencePlayer.GetBattleground(); + if (bg == null || !bg.IsBattleground() || !bg.IsRated()) + return false; + break; + } case ModifierTreeType.PlayerBattlegroundRatingEqualOrGreaterThan: // 64 if (referencePlayer.GetRBGPersonalRating() < reqValue) return false; @@ -1606,7 +1606,7 @@ namespace Game.Achievements return false; break; case ModifierTreeType.TargetLevelEqualOrGreaterThan: // 70 - if (!unit || unit.GetLevel() < reqValue) + if (!refe || !refe.IsUnit() || refe.ToUnit().GetLevel() < reqValue) return false; break; case ModifierTreeType.PlayerLevelEqualOrLessThan: // 71 @@ -1614,94 +1614,94 @@ namespace Game.Achievements return false; break; case ModifierTreeType.TargetLevelEqualOrLessThan: // 72 - if (!unit || unit.GetLevel() > reqValue) + if (!refe || !refe.IsUnit() || refe.ToUnit().GetLevel() > reqValue) return false; break; case ModifierTreeType.ModifierTree: // 73 ModifierTreeNode nextModifierTree = Global.CriteriaMgr.GetModifierTree(reqValue); if (nextModifierTree != null) - return ModifierTreeSatisfied(nextModifierTree, miscValue1, miscValue2, unit, referencePlayer); + return ModifierTreeSatisfied(nextModifierTree, miscValue1, miscValue2, refe, referencePlayer); return false; case ModifierTreeType.PlayerScenario: // 74 - { - Scenario scenario = referencePlayer.GetScenario(); - if (scenario == null || scenario.GetEntry().Id != reqValue) - return false; - break; - } + { + Scenario scenario = referencePlayer.GetScenario(); + if (scenario == null || scenario.GetEntry().Id != reqValue) + return false; + break; + } case ModifierTreeType.TillersReputationGreaterThan: // 75 if (referencePlayer.GetReputationMgr().GetReputation(1272) < reqValue) return false; break; case ModifierTreeType.BattlePetAchievementPointsEqualOrGreaterThan: // 76 + { + static short getRootAchievementCategory(AchievementRecord achievement) { - static short getRootAchievementCategory(AchievementRecord achievement) + short category = (short)achievement.Category; + do { - short category = (short)achievement.Category; - do - { - var categoryEntry = CliDB.AchievementCategoryStorage.LookupByKey(category); - if (categoryEntry?.Parent == -1) - break; + var categoryEntry = CliDB.AchievementCategoryStorage.LookupByKey(category); + if (categoryEntry?.Parent == -1) + break; - category = categoryEntry.Parent; - } while (true); + category = categoryEntry.Parent; + } while (true); - return category; - } - - uint petAchievementPoints = 0; - foreach (uint achievementId in referencePlayer.GetCompletedAchievementIds()) - { - var achievement = CliDB.AchievementStorage.LookupByKey(achievementId); - if (getRootAchievementCategory(achievement) == SharedConst.AchivementCategoryPetBattles) - petAchievementPoints += achievement.Points; - } - - if (petAchievementPoints < reqValue) - return false; - break; + return category; } + + uint petAchievementPoints = 0; + foreach (uint achievementId in referencePlayer.GetCompletedAchievementIds()) + { + var achievement = CliDB.AchievementStorage.LookupByKey(achievementId); + if (getRootAchievementCategory(achievement) == SharedConst.AchivementCategoryPetBattles) + petAchievementPoints += achievement.Points; + } + + if (petAchievementPoints < reqValue) + return false; + break; + } case ModifierTreeType.UniqueBattlePetsEqualOrGreaterThan: // 77 if (referencePlayer.GetSession().GetBattlePetMgr().GetPetUniqueSpeciesCount() < reqValue) return false; break; case ModifierTreeType.BattlePetType: // 78 - { - var speciesEntry = CliDB.BattlePetSpeciesStorage.LookupByKey(miscValue1); - if (speciesEntry?.PetTypeEnum != reqValue) - return false; - break; - } + { + var speciesEntry = CliDB.BattlePetSpeciesStorage.LookupByKey(miscValue1); + if (speciesEntry?.PetTypeEnum != reqValue) + return false; + break; + } case ModifierTreeType.BattlePetHealthPercentLessThan: // 79 NYI - use target battle pet here, the one we were just battling return false; case ModifierTreeType.GuildGroupMemberCountEqualOrGreaterThan: // 80 + { + uint guildMemberCount = 0; + var group = referencePlayer.GetGroup(); + if (group != null) { - uint guildMemberCount = 0; - var group = referencePlayer.GetGroup(); - if (group != null) - { - for (var itr = group.GetFirstMember(); itr != null; itr = itr.Next()) - if (itr.GetSource().GetGuildId() == referencePlayer.GetGuildId()) - ++guildMemberCount; - } - - if (guildMemberCount < reqValue) - return false; - break; + for (var itr = group.GetFirstMember(); itr != null; itr = itr.Next()) + if (itr.GetSource().GetGuildId() == referencePlayer.GetGuildId()) + ++guildMemberCount; } + + if (guildMemberCount < reqValue) + return false; + break; + } case ModifierTreeType.BattlePetOpponentCreatureId: // 81 NYI return false; case ModifierTreeType.PlayerScenarioStep: // 82 - { - Scenario scenario = referencePlayer.GetScenario(); - if (scenario == null) - return false; + { + Scenario scenario = referencePlayer.GetScenario(); + if (scenario == null) + return false; - if (scenario.GetStep().OrderIndex != (reqValue - 1)) - return false; - break; - } + if (scenario.GetStep().OrderIndex != (reqValue - 1)) + return false; + break; + } case ModifierTreeType.ChallengeModeMedal: // 83 return false; // OBSOLETE case ModifierTreeType.PlayerOnQuest: // 84 @@ -1737,30 +1737,30 @@ namespace Game.Achievements return false; break; case ModifierTreeType.FriendshipRepReactionIsMet: // 94 - { - var friendshipRepReaction = CliDB.FriendshipRepReactionStorage.LookupByKey(reqValue); - if (friendshipRepReaction == null) - return false; + { + var friendshipRepReaction = CliDB.FriendshipRepReactionStorage.LookupByKey(reqValue); + if (friendshipRepReaction == null) + return false; - var friendshipReputation = CliDB.FriendshipReputationStorage.LookupByKey(friendshipRepReaction.FriendshipRepID); - if (friendshipReputation == null) - return false; + var friendshipReputation = CliDB.FriendshipReputationStorage.LookupByKey(friendshipRepReaction.FriendshipRepID); + if (friendshipReputation == null) + return false; - if (referencePlayer.GetReputation((uint)friendshipReputation.FactionID) < friendshipRepReaction.ReactionThreshold) - return false; - break; - } + if (referencePlayer.GetReputation((uint)friendshipReputation.FactionID) < friendshipRepReaction.ReactionThreshold) + return false; + break; + } case ModifierTreeType.ReputationWithFactionIsEqualOrGreaterThan: // 95 if (referencePlayer.GetReputationMgr().GetReputation(reqValue) < reqValue) return false; break; case ModifierTreeType.ItemClassAndSubclass: // 96 - { - ItemTemplate item = Global.ObjectMgr.GetItemTemplate((uint)miscValue1); - if (item == null || item.GetClass() != (ItemClass)reqValue || item.GetSubClass() != secondaryAsset) - return false; - break; - } + { + ItemTemplate item = Global.ObjectMgr.GetItemTemplate((uint)miscValue1); + if (item == null || item.GetClass() != (ItemClass)reqValue || item.GetSubClass() != secondaryAsset) + return false; + break; + } case ModifierTreeType.PlayerGender: // 97 if ((int)referencePlayer.GetGender() != reqValue) return false; @@ -1774,12 +1774,12 @@ namespace Game.Achievements return false; break; case ModifierTreeType.PlayerLanguageSkillEqualOrGreaterThan: // 100 - { - var languageDescs = Global.LanguageMgr.GetLanguageDescById((Language)reqValue); - if (!languageDescs.Any(desc => referencePlayer.GetSkillValue((SkillType)desc.SkillId) >= secondaryAsset)) - return false; - break; - } + { + var languageDescs = Global.LanguageMgr.GetLanguageDescById((Language)reqValue); + if (!languageDescs.Any(desc => referencePlayer.GetSkillValue((SkillType)desc.SkillId) >= secondaryAsset)) + return false; + break; + } case ModifierTreeType.PlayerIsInNormalPhase: // 101 if (!PhasingHandler.InDbPhaseShift(referencePlayer, 0, 0, 0)) return false; @@ -1813,13 +1813,13 @@ namespace Game.Achievements return false; break; case ModifierTreeType.TimeBetween: // 109 - { - long from = Time.GetUnixTimeFromPackedTime(reqValue); - long to = Time.GetUnixTimeFromPackedTime((uint)secondaryAsset); - if (GameTime.GetGameTime() < from || GameTime.GetGameTime() > to) - return false; - break; - } + { + long from = Time.GetUnixTimeFromPackedTime(reqValue); + long to = Time.GetUnixTimeFromPackedTime((uint)secondaryAsset); + if (GameTime.GetGameTime() < from || GameTime.GetGameTime() > to) + return false; + break; + } case ModifierTreeType.PlayerHasCompletedQuest: // 110 uint questBit = Global.DB2Mgr.GetQuestUniqueBitFlag(reqValue); if (questBit != 0) @@ -1831,37 +1831,37 @@ namespace Game.Achievements return false; break; case ModifierTreeType.PlayerHasCompletedQuestObjective: // 112 - { - QuestObjective objective = Global.ObjectMgr.GetQuestObjective(reqValue); - if (objective == null) - return false; + { + QuestObjective objective = Global.ObjectMgr.GetQuestObjective(reqValue); + if (objective == null) + return false; - Quest quest = Global.ObjectMgr.GetQuestTemplate(objective.QuestID); - if (quest == null) - return false; + Quest quest = Global.ObjectMgr.GetQuestTemplate(objective.QuestID); + if (quest == null) + return false; - ushort slot = referencePlayer.FindQuestSlot(objective.QuestID); - if (slot >= SharedConst.MaxQuestLogSize || referencePlayer.GetQuestRewardStatus(objective.QuestID) || !referencePlayer.IsQuestObjectiveComplete(slot, quest, objective)) - return false; - break; - } + ushort slot = referencePlayer.FindQuestSlot(objective.QuestID); + if (slot >= SharedConst.MaxQuestLogSize || referencePlayer.GetQuestRewardStatus(objective.QuestID) || !referencePlayer.IsQuestObjectiveComplete(slot, quest, objective)) + return false; + break; + } case ModifierTreeType.PlayerHasExploredArea: // 113 - { - AreaTableRecord areaTable = CliDB.AreaTableStorage.LookupByKey(reqValue); - if (areaTable == null) - return false; + { + AreaTableRecord areaTable = CliDB.AreaTableStorage.LookupByKey(reqValue); + if (areaTable == null) + return false; - if (areaTable.AreaBit <= 0) - break; // success + if (areaTable.AreaBit <= 0) + break; // success - int playerIndexOffset = areaTable.AreaBit / 64; - if (playerIndexOffset >= PlayerConst.ExploredZonesSize) - break; - - if ((referencePlayer.m_activePlayerData.ExploredZones[playerIndexOffset] & (1ul << (areaTable.AreaBit % 64))) == 0) - return false; + int playerIndexOffset = areaTable.AreaBit / 64; + if (playerIndexOffset >= PlayerConst.ExploredZonesSize) break; - } + + if ((referencePlayer.m_activePlayerData.ExploredZones[playerIndexOffset] & (1ul << (areaTable.AreaBit % 64))) == 0) + return false; + break; + } case ModifierTreeType.PlayerHasItemQuantityIncludingBank: // 114 if (referencePlayer.GetItemCount(reqValue, true) < secondaryAsset) return false; @@ -1871,26 +1871,26 @@ namespace Game.Achievements return false; break; case ModifierTreeType.PlayerFaction: // 116 - { - ChrRacesRecord race = CliDB.ChrRacesStorage.LookupByKey(referencePlayer.GetRace()); - if (race == null) - return false; + { + ChrRacesRecord race = CliDB.ChrRacesStorage.LookupByKey(referencePlayer.GetRace()); + if (race == null) + return false; - FactionTemplateRecord faction = CliDB.FactionTemplateStorage.LookupByKey(race.FactionID); - if (faction == null) - return false; + FactionTemplateRecord faction = CliDB.FactionTemplateStorage.LookupByKey(race.FactionID); + if (faction == null) + return false; - int factionIndex = -1; - if (faction.FactionGroup.HasAnyFlag((byte)FactionMasks.Horde)) - factionIndex = 0; - else if (faction.FactionGroup.HasAnyFlag((byte)FactionMasks.Alliance)) - factionIndex = 1; - else if (faction.FactionGroup.HasAnyFlag((byte)FactionMasks.Player)) - factionIndex = 0; - if (factionIndex != reqValue) - return false; - break; - } + int factionIndex = -1; + if (faction.FactionGroup.HasAnyFlag((byte)FactionMasks.Horde)) + factionIndex = 0; + else if (faction.FactionGroup.HasAnyFlag((byte)FactionMasks.Alliance)) + factionIndex = 1; + else if (faction.FactionGroup.HasAnyFlag((byte)FactionMasks.Player)) + factionIndex = 0; + if (factionIndex != reqValue) + return false; + break; + } case ModifierTreeType.LfgStatusEqual: // 117 if (ConditionManager.GetPlayerConditionLfgValue(referencePlayer, (PlayerConditionLfgStatus)reqValue) != secondaryAsset) return false; @@ -1904,13 +1904,16 @@ namespace Game.Achievements return false; break; case ModifierTreeType.TargetThreatListSizeLessThan: // 120 - { - if (!unit || !unit.CanHaveThreatList()) - return false; - if (unit.GetThreatManager().GetThreatListSize() >= reqValue) - return false; - break; - } + { + if (refe == null) + return false; + Unit unitRef = refe.ToUnit(); + if (unitRef == null || !unitRef.CanHaveThreatList()) + return false; + if (unitRef.GetThreatManager().GetThreatListSize() >= reqValue) + return false; + break; + } case ModifierTreeType.PlayerHasTrackedCurrencyEqualOrGreaterThan: // 121 if (referencePlayer.GetTrackedCurrencyCount(reqValue) < secondaryAsset) return false; @@ -1932,185 +1935,185 @@ namespace Game.Achievements return false; break; case ModifierTreeType.GarrisonTierEqualOrGreaterThan: // 126 - { - Garrison garrison = referencePlayer.GetGarrison(); - if (garrison == null || garrison.GetGarrisonType() != (GarrisonType)secondaryAsset || garrison.GetSiteLevel().GarrLevel < reqValue) - return false; - break; - } - case ModifierTreeType.GarrisonFollowersWithLevelEqualOrGreaterThan: // 127 - { - Garrison garrison = referencePlayer.GetGarrison(); - if (garrison == null) - return false; - - uint followerCount = garrison.CountFollowers(follower => - { - var garrFollower = CliDB.GarrFollowerStorage.LookupByKey(follower.PacketInfo.GarrFollowerID); - return garrFollower?.GarrFollowerTypeID == tertiaryAsset && follower.PacketInfo.FollowerLevel >= secondaryAsset; - }); - - if (followerCount < reqValue) - return false; - break; - } - case ModifierTreeType.GarrisonFollowersWithQualityEqualOrGreaterThan: // 128 - { - Garrison garrison = referencePlayer.GetGarrison(); - if (garrison == null) - return false; - - uint followerCount = garrison.CountFollowers(follower => - { - var garrFollower = CliDB.GarrFollowerStorage.LookupByKey(follower.PacketInfo.GarrFollowerID); - return garrFollower?.GarrFollowerTypeID == tertiaryAsset && follower.PacketInfo.Quality >= secondaryAsset; - }); - - if (followerCount < reqValue) - return false; - break; - } - case ModifierTreeType.GarrisonFollowerWithAbilityAtLevelEqualOrGreaterThan: // 129 - { - Garrison garrison = referencePlayer.GetGarrison(); - if (garrison == null) - return false; - - uint followerCount = garrison.CountFollowers(follower => - { - var garrFollower = CliDB.GarrFollowerStorage.LookupByKey(follower.PacketInfo.GarrFollowerID); - return garrFollower?.GarrFollowerTypeID == tertiaryAsset && follower.PacketInfo.FollowerLevel >= reqValue && follower.HasAbility((uint)secondaryAsset); - }); - - if (followerCount < 1) - return false; - break; - } - case ModifierTreeType.GarrisonFollowerWithTraitAtLevelEqualOrGreaterThan: // 130 - { - Garrison garrison = referencePlayer.GetGarrison(); - if (garrison == null) - return false; - - GarrAbilityRecord traitEntry = CliDB.GarrAbilityStorage.LookupByKey(secondaryAsset); - if (traitEntry == null || !traitEntry.Flags.HasAnyFlag(GarrisonAbilityFlags.Trait)) - return false; - - uint followerCount = garrison.CountFollowers(follower => - { - var garrFollower = CliDB.GarrFollowerStorage.LookupByKey(follower.PacketInfo.GarrFollowerID); - return garrFollower?.GarrFollowerTypeID == tertiaryAsset && follower.PacketInfo.FollowerLevel >= reqValue && follower.HasAbility((uint)secondaryAsset); - }); - - if (followerCount < 1) - return false; - break; - } - case ModifierTreeType.GarrisonFollowerWithAbilityAssignedToBuilding: // 131 - { - Garrison garrison = referencePlayer.GetGarrison(); - if (garrison == null || garrison.GetGarrisonType() != (GarrisonType)tertiaryAsset) - return false; - - uint followerCount = garrison.CountFollowers(follower => - { - GarrBuildingRecord followerBuilding = CliDB.GarrBuildingStorage.LookupByKey(follower.PacketInfo.CurrentBuildingID); - if (followerBuilding == null) - return false; - - return followerBuilding.BuildingType == secondaryAsset && follower.HasAbility(reqValue); ; - }); - - if (followerCount < 1) - return false; - break; - } - case ModifierTreeType.GarrisonFollowerWithTraitAssignedToBuilding: // 132 - { - Garrison garrison = referencePlayer.GetGarrison(); - if (garrison == null || garrison.GetGarrisonType() != (GarrisonType)tertiaryAsset) - return false; - - GarrAbilityRecord traitEntry = CliDB.GarrAbilityStorage.LookupByKey(reqValue); - if (traitEntry == null || !traitEntry.Flags.HasAnyFlag(GarrisonAbilityFlags.Trait)) - return false; - - uint followerCount = garrison.CountFollowers(follower => - { - GarrBuildingRecord followerBuilding = CliDB.GarrBuildingStorage.LookupByKey(follower.PacketInfo.CurrentBuildingID); - if (followerBuilding == null) - return false; - - return followerBuilding.BuildingType == secondaryAsset && follower.HasAbility(reqValue); ; - }); - - if (followerCount < 1) - return false; - break; - } - case ModifierTreeType.GarrisonFollowerWithLevelAssignedToBuilding: // 133 - { - Garrison garrison = referencePlayer.GetGarrison(); - if (garrison == null || garrison.GetGarrisonType() != (GarrisonType)tertiaryAsset) - return false; - - uint followerCount = garrison.CountFollowers(follower => - { - if (follower.PacketInfo.FollowerLevel < reqValue) - return false; - - GarrBuildingRecord followerBuilding = CliDB.GarrBuildingStorage.LookupByKey(follower.PacketInfo.CurrentBuildingID); - if (followerBuilding == null) - return false; - - return followerBuilding.BuildingType == secondaryAsset; - }); - if (followerCount < 1) - return false; - break; - } - case ModifierTreeType.GarrisonBuildingWithLevelEqualOrGreaterThan: // 134 - { - Garrison garrison = referencePlayer.GetGarrison(); - if (garrison == null || garrison.GetGarrisonType() != (GarrisonType)tertiaryAsset) - return false; - - foreach (Garrison.Plot plot in garrison.GetPlots()) - { - if (!plot.BuildingInfo.PacketInfo.HasValue) - continue; - - GarrBuildingRecord building = CliDB.GarrBuildingStorage.LookupByKey(plot.BuildingInfo.PacketInfo.Value.GarrBuildingID); - if (building == null || building.UpgradeLevel < reqValue || building.BuildingType != secondaryAsset) - continue; - - return true; - } + { + Garrison garrison = referencePlayer.GetGarrison(); + if (garrison == null || garrison.GetGarrisonType() != (GarrisonType)secondaryAsset || garrison.GetSiteLevel().GarrLevel < reqValue) return false; - } - case ModifierTreeType.HasBlueprintForGarrisonBuilding: // 135 + break; + } + case ModifierTreeType.GarrisonFollowersWithLevelEqualOrGreaterThan: // 127 + { + Garrison garrison = referencePlayer.GetGarrison(); + if (garrison == null) + return false; + + uint followerCount = garrison.CountFollowers(follower => { - Garrison garrison = referencePlayer.GetGarrison(); - if (garrison == null || garrison.GetGarrisonType() != (GarrisonType)secondaryAsset) + var garrFollower = CliDB.GarrFollowerStorage.LookupByKey(follower.PacketInfo.GarrFollowerID); + return garrFollower?.GarrFollowerTypeID == tertiaryAsset && follower.PacketInfo.FollowerLevel >= secondaryAsset; + }); + + if (followerCount < reqValue) + return false; + break; + } + case ModifierTreeType.GarrisonFollowersWithQualityEqualOrGreaterThan: // 128 + { + Garrison garrison = referencePlayer.GetGarrison(); + if (garrison == null) + return false; + + uint followerCount = garrison.CountFollowers(follower => + { + var garrFollower = CliDB.GarrFollowerStorage.LookupByKey(follower.PacketInfo.GarrFollowerID); + return garrFollower?.GarrFollowerTypeID == tertiaryAsset && follower.PacketInfo.Quality >= secondaryAsset; + }); + + if (followerCount < reqValue) + return false; + break; + } + case ModifierTreeType.GarrisonFollowerWithAbilityAtLevelEqualOrGreaterThan: // 129 + { + Garrison garrison = referencePlayer.GetGarrison(); + if (garrison == null) + return false; + + uint followerCount = garrison.CountFollowers(follower => + { + var garrFollower = CliDB.GarrFollowerStorage.LookupByKey(follower.PacketInfo.GarrFollowerID); + return garrFollower?.GarrFollowerTypeID == tertiaryAsset && follower.PacketInfo.FollowerLevel >= reqValue && follower.HasAbility((uint)secondaryAsset); + }); + + if (followerCount < 1) + return false; + break; + } + case ModifierTreeType.GarrisonFollowerWithTraitAtLevelEqualOrGreaterThan: // 130 + { + Garrison garrison = referencePlayer.GetGarrison(); + if (garrison == null) + return false; + + GarrAbilityRecord traitEntry = CliDB.GarrAbilityStorage.LookupByKey(secondaryAsset); + if (traitEntry == null || !traitEntry.Flags.HasAnyFlag(GarrisonAbilityFlags.Trait)) + return false; + + uint followerCount = garrison.CountFollowers(follower => + { + var garrFollower = CliDB.GarrFollowerStorage.LookupByKey(follower.PacketInfo.GarrFollowerID); + return garrFollower?.GarrFollowerTypeID == tertiaryAsset && follower.PacketInfo.FollowerLevel >= reqValue && follower.HasAbility((uint)secondaryAsset); + }); + + if (followerCount < 1) + return false; + break; + } + case ModifierTreeType.GarrisonFollowerWithAbilityAssignedToBuilding: // 131 + { + Garrison garrison = referencePlayer.GetGarrison(); + if (garrison == null || garrison.GetGarrisonType() != (GarrisonType)tertiaryAsset) + return false; + + uint followerCount = garrison.CountFollowers(follower => + { + GarrBuildingRecord followerBuilding = CliDB.GarrBuildingStorage.LookupByKey(follower.PacketInfo.CurrentBuildingID); + if (followerBuilding == null) return false; - if (!garrison.HasBlueprint(reqValue)) + return followerBuilding.BuildingType == secondaryAsset && follower.HasAbility(reqValue); ; + }); + + if (followerCount < 1) + return false; + break; + } + case ModifierTreeType.GarrisonFollowerWithTraitAssignedToBuilding: // 132 + { + Garrison garrison = referencePlayer.GetGarrison(); + if (garrison == null || garrison.GetGarrisonType() != (GarrisonType)tertiaryAsset) + return false; + + GarrAbilityRecord traitEntry = CliDB.GarrAbilityStorage.LookupByKey(reqValue); + if (traitEntry == null || !traitEntry.Flags.HasAnyFlag(GarrisonAbilityFlags.Trait)) + return false; + + uint followerCount = garrison.CountFollowers(follower => + { + GarrBuildingRecord followerBuilding = CliDB.GarrBuildingStorage.LookupByKey(follower.PacketInfo.CurrentBuildingID); + if (followerBuilding == null) return false; - break; + + return followerBuilding.BuildingType == secondaryAsset && follower.HasAbility(reqValue); ; + }); + + if (followerCount < 1) + return false; + break; + } + case ModifierTreeType.GarrisonFollowerWithLevelAssignedToBuilding: // 133 + { + Garrison garrison = referencePlayer.GetGarrison(); + if (garrison == null || garrison.GetGarrisonType() != (GarrisonType)tertiaryAsset) + return false; + + uint followerCount = garrison.CountFollowers(follower => + { + if (follower.PacketInfo.FollowerLevel < reqValue) + return false; + + GarrBuildingRecord followerBuilding = CliDB.GarrBuildingStorage.LookupByKey(follower.PacketInfo.CurrentBuildingID); + if (followerBuilding == null) + return false; + + return followerBuilding.BuildingType == secondaryAsset; + }); + if (followerCount < 1) + return false; + break; + } + case ModifierTreeType.GarrisonBuildingWithLevelEqualOrGreaterThan: // 134 + { + Garrison garrison = referencePlayer.GetGarrison(); + if (garrison == null || garrison.GetGarrisonType() != (GarrisonType)tertiaryAsset) + return false; + + foreach (Garrison.Plot plot in garrison.GetPlots()) + { + if (!plot.BuildingInfo.PacketInfo.HasValue) + continue; + + GarrBuildingRecord building = CliDB.GarrBuildingStorage.LookupByKey(plot.BuildingInfo.PacketInfo.Value.GarrBuildingID); + if (building == null || building.UpgradeLevel < reqValue || building.BuildingType != secondaryAsset) + continue; + + return true; } + return false; + } + case ModifierTreeType.HasBlueprintForGarrisonBuilding: // 135 + { + Garrison garrison = referencePlayer.GetGarrison(); + if (garrison == null || garrison.GetGarrisonType() != (GarrisonType)secondaryAsset) + return false; + + if (!garrison.HasBlueprint(reqValue)) + return false; + break; + } case ModifierTreeType.HasGarrisonBuildingSpecialization: // 136 return false; // OBSOLETE case ModifierTreeType.AllGarrisonPlotsAreFull: // 137 - { - Garrison garrison = referencePlayer.GetGarrison(); - if (garrison == null || garrison.GetGarrisonType() != (GarrisonType)reqValue) - return false; + { + Garrison garrison = referencePlayer.GetGarrison(); + if (garrison == null || garrison.GetGarrisonType() != (GarrisonType)reqValue) + return false; - foreach (var plot in garrison.GetPlots()) - if (!plot.BuildingInfo.PacketInfo.HasValue) - return false; - break; - } + foreach (var plot in garrison.GetPlots()) + if (!plot.BuildingInfo.PacketInfo.HasValue) + return false; + break; + } case ModifierTreeType.PlayerIsInOwnGarrison: // 138 if (!referencePlayer.GetMap().IsGarrison() || referencePlayer.GetMap().GetInstanceId() != referencePlayer.GetGUID().GetCounter()) return false; @@ -2118,264 +2121,264 @@ namespace Game.Achievements case ModifierTreeType.GarrisonShipmentOfTypeIsPending: // 139 NYI return false; case ModifierTreeType.GarrisonBuildingIsUnderConstruction: // 140 - { - GarrBuildingRecord building = CliDB.GarrBuildingStorage.LookupByKey(reqValue); - if (building == null) - return false; - - Garrison garrison = referencePlayer.GetGarrison(); - if (garrison == null || garrison.GetGarrisonType() != (GarrisonType)tertiaryAsset) - return false; - - foreach (Garrison.Plot plot in garrison.GetPlots()) - { - if (!plot.BuildingInfo.PacketInfo.HasValue || plot.BuildingInfo.PacketInfo.Value.GarrBuildingID != reqValue) - continue; - - return !plot.BuildingInfo.PacketInfo.Value.Active; - } + { + GarrBuildingRecord building = CliDB.GarrBuildingStorage.LookupByKey(reqValue); + if (building == null) return false; + + Garrison garrison = referencePlayer.GetGarrison(); + if (garrison == null || garrison.GetGarrisonType() != (GarrisonType)tertiaryAsset) + return false; + + foreach (Garrison.Plot plot in garrison.GetPlots()) + { + if (!plot.BuildingInfo.PacketInfo.HasValue || plot.BuildingInfo.PacketInfo.Value.GarrBuildingID != reqValue) + continue; + + return !plot.BuildingInfo.PacketInfo.Value.Active; } + return false; + } case ModifierTreeType.GarrisonMissionHasBeenCompleted: // 141 NYI return false; case ModifierTreeType.GarrisonBuildingLevelEqual: // 142 - { - Garrison garrison = referencePlayer.GetGarrison(); - if (garrison == null || garrison.GetGarrisonType() != (GarrisonType)tertiaryAsset) - return false; - - foreach (Garrison.Plot plot in garrison.GetPlots()) - { - if (!plot.BuildingInfo.PacketInfo.HasValue) - continue; - - GarrBuildingRecord building = CliDB.GarrBuildingStorage.LookupByKey(plot.BuildingInfo.PacketInfo.Value.GarrBuildingID); - if (building == null || building.UpgradeLevel != secondaryAsset || building.BuildingType != reqValue) - continue; - - return true; - } + { + Garrison garrison = referencePlayer.GetGarrison(); + if (garrison == null || garrison.GetGarrisonType() != (GarrisonType)tertiaryAsset) return false; + + foreach (Garrison.Plot plot in garrison.GetPlots()) + { + if (!plot.BuildingInfo.PacketInfo.HasValue) + continue; + + GarrBuildingRecord building = CliDB.GarrBuildingStorage.LookupByKey(plot.BuildingInfo.PacketInfo.Value.GarrBuildingID); + if (building == null || building.UpgradeLevel != secondaryAsset || building.BuildingType != reqValue) + continue; + + return true; } + return false; + } case ModifierTreeType.GarrisonFollowerHasAbility: // 143 + { + Garrison garrison = referencePlayer.GetGarrison(); + if (garrison == null || garrison.GetGarrisonType() != (GarrisonType)secondaryAsset) + return false; + + if (miscValue1 != 0) { - Garrison garrison = referencePlayer.GetGarrison(); - if (garrison == null || garrison.GetGarrisonType() != (GarrisonType)secondaryAsset) + Garrison.Follower follower = garrison.GetFollower(miscValue1); + if (follower == null) return false; - if (miscValue1 != 0) - { - Garrison.Follower follower = garrison.GetFollower(miscValue1); - if (follower == null) - return false; - - if (!follower.HasAbility(reqValue)) - return false; - } - else - { - uint followerCount = garrison.CountFollowers(follower => - { - return follower.HasAbility(reqValue); - }); - - if (followerCount < 1) - return false; - } - break; + if (!follower.HasAbility(reqValue)) + return false; } - case ModifierTreeType.GarrisonFollowerHasTrait: // 144 + else { - GarrAbilityRecord traitEntry = CliDB.GarrAbilityStorage.LookupByKey(reqValue); - if (traitEntry == null || !traitEntry.Flags.HasAnyFlag(GarrisonAbilityFlags.Trait)) - return false; - - Garrison garrison = referencePlayer.GetGarrison(); - if (garrison == null || garrison.GetGarrisonType() != (GarrisonType)secondaryAsset) - return false; - - if (miscValue1 != 0) - { - Garrison.Follower follower = garrison.GetFollower(miscValue1); - if (follower == null || !follower.HasAbility(reqValue)) - return false; - } - else - { - uint followerCount = garrison.CountFollowers(follower => - { - return follower.HasAbility(reqValue); - }); - - if (followerCount < 1) - return false; - } - break; - } - case ModifierTreeType.GarrisonFollowerQualityEqual: // 145 - { - Garrison garrison = referencePlayer.GetGarrison(); - if (garrison == null || garrison.GetGarrisonType() != GarrisonType.Garrison) - return false; - - if (miscValue1 != 0) - { - Garrison.Follower follower = garrison.GetFollower(miscValue1); - if (follower == null || follower.PacketInfo.Quality < reqValue) - return false; - } - else - { - uint followerCount = garrison.CountFollowers(follower => - { - return follower.PacketInfo.Quality >= reqValue; - }); - - if (followerCount < 1) - return false; - } - break; - } - case ModifierTreeType.GarrisonFollowerLevelEqual: // 146 - { - Garrison garrison = referencePlayer.GetGarrison(); - if (garrison == null || garrison.GetGarrisonType() != (GarrisonType)secondaryAsset) - return false; - - if (miscValue1 != 0) - { - Garrison.Follower follower = garrison.GetFollower(miscValue1); - if (follower == null || follower.PacketInfo.FollowerLevel != reqValue) - return false; - } - else - { - uint followerCount = garrison.CountFollowers(follower => - { - return follower.PacketInfo.FollowerLevel == reqValue; - }); - - if (followerCount < 1) - return false; - } - break; - } - case ModifierTreeType.GarrisonMissionIsRare: // 147 NYI - case ModifierTreeType.GarrisonMissionIsElite: // 148 NYI - return false; - case ModifierTreeType.CurrentGarrisonBuildingLevelEqual: // 149 - { - if (miscValue1 == 0) - return false; - - Garrison garrison = referencePlayer.GetGarrison(); - if (garrison == null) - return false; - - foreach (var plot in garrison.GetPlots()) - { - if (!plot.BuildingInfo.PacketInfo.HasValue || plot.BuildingInfo.PacketInfo.Value.GarrBuildingID != miscValue1) - continue; - - var building = CliDB.GarrBuildingStorage.LookupByKey(plot.BuildingInfo.PacketInfo.Value.GarrBuildingID); - if (building == null || building.UpgradeLevel != reqValue) - continue; - - return true; - } - break; - } - case ModifierTreeType.GarrisonPlotInstanceHasBuildingThatIsReadyToActivate: // 150 - { - Garrison garrison = referencePlayer.GetGarrison(); - if (garrison == null) - return false; - - var plot = garrison.GetPlot(reqValue); - if (plot == null) - return false; - - if (!plot.BuildingInfo.CanActivate() || !plot.BuildingInfo.PacketInfo.HasValue || plot.BuildingInfo.PacketInfo.Value.Active) - return false; - break; - } - case ModifierTreeType.BattlePetTeamWithSpeciesEqualOrGreaterThan: // 151 - { - uint count = 0; - foreach (BattlePetSlot slot in referencePlayer.GetSession().GetBattlePetMgr().GetSlots()) - if (slot.Pet.Species == secondaryAsset) - ++count; - - if (count < reqValue) - return false; - break; - } - case ModifierTreeType.BattlePetTeamWithTypeEqualOrGreaterThan: // 152 - { - uint count = 0; - foreach (BattlePetSlot slot in referencePlayer.GetSession().GetBattlePetMgr().GetSlots()) - { - BattlePetSpeciesRecord species = CliDB.BattlePetSpeciesStorage.LookupByKey(slot.Pet.Species); - if (species != null) - if (species.PetTypeEnum == secondaryAsset) - ++count; - } - - if (count < reqValue) - return false; - break; - } - case ModifierTreeType.PetBattleLastAbility: // 153 NYI - case ModifierTreeType.PetBattleLastAbilityType: // 154 NYI - return false; - case ModifierTreeType.BattlePetTeamWithAliveEqualOrGreaterThan: // 155 - { - uint count = 0; - foreach (var slot in referencePlayer.GetSession().GetBattlePetMgr().GetSlots()) - if (slot.Pet.Health > 0) - ++count; - - if (count < reqValue) - return false; - break; - } - case ModifierTreeType.HasGarrisonBuildingActiveSpecialization: // 156 - return false; // OBSOLETE - case ModifierTreeType.HasGarrisonFollower: // 157 - { - Garrison garrison = referencePlayer.GetGarrison(); - if (garrison == null) - return false; - uint followerCount = garrison.CountFollowers(follower => { - return follower.PacketInfo.GarrFollowerID == reqValue; + return follower.HasAbility(reqValue); }); if (followerCount < 1) return false; - break; } + break; + } + case ModifierTreeType.GarrisonFollowerHasTrait: // 144 + { + GarrAbilityRecord traitEntry = CliDB.GarrAbilityStorage.LookupByKey(reqValue); + if (traitEntry == null || !traitEntry.Flags.HasAnyFlag(GarrisonAbilityFlags.Trait)) + return false; + + Garrison garrison = referencePlayer.GetGarrison(); + if (garrison == null || garrison.GetGarrisonType() != (GarrisonType)secondaryAsset) + return false; + + if (miscValue1 != 0) + { + Garrison.Follower follower = garrison.GetFollower(miscValue1); + if (follower == null || !follower.HasAbility(reqValue)) + return false; + } + else + { + uint followerCount = garrison.CountFollowers(follower => + { + return follower.HasAbility(reqValue); + }); + + if (followerCount < 1) + return false; + } + break; + } + case ModifierTreeType.GarrisonFollowerQualityEqual: // 145 + { + Garrison garrison = referencePlayer.GetGarrison(); + if (garrison == null || garrison.GetGarrisonType() != GarrisonType.Garrison) + return false; + + if (miscValue1 != 0) + { + Garrison.Follower follower = garrison.GetFollower(miscValue1); + if (follower == null || follower.PacketInfo.Quality < reqValue) + return false; + } + else + { + uint followerCount = garrison.CountFollowers(follower => + { + return follower.PacketInfo.Quality >= reqValue; + }); + + if (followerCount < 1) + return false; + } + break; + } + case ModifierTreeType.GarrisonFollowerLevelEqual: // 146 + { + Garrison garrison = referencePlayer.GetGarrison(); + if (garrison == null || garrison.GetGarrisonType() != (GarrisonType)secondaryAsset) + return false; + + if (miscValue1 != 0) + { + Garrison.Follower follower = garrison.GetFollower(miscValue1); + if (follower == null || follower.PacketInfo.FollowerLevel != reqValue) + return false; + } + else + { + uint followerCount = garrison.CountFollowers(follower => + { + return follower.PacketInfo.FollowerLevel == reqValue; + }); + + if (followerCount < 1) + return false; + } + break; + } + case ModifierTreeType.GarrisonMissionIsRare: // 147 NYI + case ModifierTreeType.GarrisonMissionIsElite: // 148 NYI + return false; + case ModifierTreeType.CurrentGarrisonBuildingLevelEqual: // 149 + { + if (miscValue1 == 0) + return false; + + Garrison garrison = referencePlayer.GetGarrison(); + if (garrison == null) + return false; + + foreach (var plot in garrison.GetPlots()) + { + if (!plot.BuildingInfo.PacketInfo.HasValue || plot.BuildingInfo.PacketInfo.Value.GarrBuildingID != miscValue1) + continue; + + var building = CliDB.GarrBuildingStorage.LookupByKey(plot.BuildingInfo.PacketInfo.Value.GarrBuildingID); + if (building == null || building.UpgradeLevel != reqValue) + continue; + + return true; + } + break; + } + case ModifierTreeType.GarrisonPlotInstanceHasBuildingThatIsReadyToActivate: // 150 + { + Garrison garrison = referencePlayer.GetGarrison(); + if (garrison == null) + return false; + + var plot = garrison.GetPlot(reqValue); + if (plot == null) + return false; + + if (!plot.BuildingInfo.CanActivate() || !plot.BuildingInfo.PacketInfo.HasValue || plot.BuildingInfo.PacketInfo.Value.Active) + return false; + break; + } + case ModifierTreeType.BattlePetTeamWithSpeciesEqualOrGreaterThan: // 151 + { + uint count = 0; + foreach (BattlePetSlot slot in referencePlayer.GetSession().GetBattlePetMgr().GetSlots()) + if (slot.Pet.Species == secondaryAsset) + ++count; + + if (count < reqValue) + return false; + break; + } + case ModifierTreeType.BattlePetTeamWithTypeEqualOrGreaterThan: // 152 + { + uint count = 0; + foreach (BattlePetSlot slot in referencePlayer.GetSession().GetBattlePetMgr().GetSlots()) + { + BattlePetSpeciesRecord species = CliDB.BattlePetSpeciesStorage.LookupByKey(slot.Pet.Species); + if (species != null) + if (species.PetTypeEnum == secondaryAsset) + ++count; + } + + if (count < reqValue) + return false; + break; + } + case ModifierTreeType.PetBattleLastAbility: // 153 NYI + case ModifierTreeType.PetBattleLastAbilityType: // 154 NYI + return false; + case ModifierTreeType.BattlePetTeamWithAliveEqualOrGreaterThan: // 155 + { + uint count = 0; + foreach (var slot in referencePlayer.GetSession().GetBattlePetMgr().GetSlots()) + if (slot.Pet.Health > 0) + ++count; + + if (count < reqValue) + return false; + break; + } + case ModifierTreeType.HasGarrisonBuildingActiveSpecialization: // 156 + return false; // OBSOLETE + case ModifierTreeType.HasGarrisonFollower: // 157 + { + Garrison garrison = referencePlayer.GetGarrison(); + if (garrison == null) + return false; + + uint followerCount = garrison.CountFollowers(follower => + { + return follower.PacketInfo.GarrFollowerID == reqValue; + }); + + if (followerCount < 1) + return false; + break; + } case ModifierTreeType.PlayerQuestObjectiveProgressEqual: // 158 - { - QuestObjective objective = Global.ObjectMgr.GetQuestObjective(reqValue); - if (objective == null) - return false; + { + QuestObjective objective = Global.ObjectMgr.GetQuestObjective(reqValue); + if (objective == null) + return false; - if (referencePlayer.GetQuestObjectiveData(objective) != secondaryAsset) - return false; - break; - } + if (referencePlayer.GetQuestObjectiveData(objective) != secondaryAsset) + return false; + break; + } case ModifierTreeType.PlayerQuestObjectiveProgressEqualOrGreaterThan: // 159 - { - QuestObjective objective = Global.ObjectMgr.GetQuestObjective(reqValue); - if (objective == null) - return false; + { + QuestObjective objective = Global.ObjectMgr.GetQuestObjective(reqValue); + if (objective == null) + return false; - if (referencePlayer.GetQuestObjectiveData(objective) < secondaryAsset) - return false; - break; - } + if (referencePlayer.GetQuestObjectiveData(objective) < secondaryAsset) + return false; + break; + } case ModifierTreeType.IsPTRRealm: // 160 case ModifierTreeType.IsBetaRealm: // 161 case ModifierTreeType.IsQARealm: // 162 @@ -2389,66 +2392,66 @@ namespace Game.Achievements return false; break; case ModifierTreeType.AllGarrisonPlotsFilledWithBuildingsWithLevelEqualOrGreater: // 166 + { + Garrison garrison = referencePlayer.GetGarrison(); + if (garrison == null || garrison.GetGarrisonType() != (GarrisonType)reqValue) + return false; + + foreach (var plot in garrison.GetPlots()) { - Garrison garrison = referencePlayer.GetGarrison(); - if (garrison == null || garrison.GetGarrisonType() != (GarrisonType)reqValue) + if (!plot.BuildingInfo.PacketInfo.HasValue) return false; - foreach (var plot in garrison.GetPlots()) - { - if (!plot.BuildingInfo.PacketInfo.HasValue) - return false; - - var building = CliDB.GarrBuildingStorage.LookupByKey(plot.BuildingInfo.PacketInfo.Value.GarrBuildingID); - if (building == null || building.UpgradeLevel != reqValue) - return false; - } - break; + var building = CliDB.GarrBuildingStorage.LookupByKey(plot.BuildingInfo.PacketInfo.Value.GarrBuildingID); + if (building == null || building.UpgradeLevel != reqValue) + return false; } + break; + } case ModifierTreeType.GarrisonMissionType: // 167 NYI return false; case ModifierTreeType.GarrisonFollowerItemLevelEqualOrGreaterThan: // 168 + { + if (miscValue1 == 0) + return false; + + Garrison garrison = referencePlayer.GetGarrison(); + if (garrison == null) + return false; + + uint followerCount = garrison.CountFollowers(follower => { - if (miscValue1 == 0) - return false; + return follower.PacketInfo.GarrFollowerID == miscValue1 && follower.GetItemLevel() >= reqValue; + }); - Garrison garrison = referencePlayer.GetGarrison(); - if (garrison == null) - return false; - - uint followerCount = garrison.CountFollowers(follower => - { - return follower.PacketInfo.GarrFollowerID == miscValue1 && follower.GetItemLevel() >= reqValue; - }); - - if (followerCount < 1) - return false; - break; - } + if (followerCount < 1) + return false; + break; + } case ModifierTreeType.GarrisonFollowerCountWithItemLevelEqualOrGreaterThan: // 169 + { + Garrison garrison = referencePlayer.GetGarrison(); + if (garrison == null) + return false; + + uint followerCount = garrison.CountFollowers(follower => { - Garrison garrison = referencePlayer.GetGarrison(); - if (garrison == null) - return false; + var garrFollower = CliDB.GarrFollowerStorage.LookupByKey(follower.PacketInfo.GarrFollowerID); + return garrFollower?.GarrFollowerTypeID == tertiaryAsset && follower.GetItemLevel() >= secondaryAsset; + }); - uint followerCount = garrison.CountFollowers(follower => - { - var garrFollower = CliDB.GarrFollowerStorage.LookupByKey(follower.PacketInfo.GarrFollowerID); - return garrFollower?.GarrFollowerTypeID == tertiaryAsset && follower.GetItemLevel() >= secondaryAsset; - }); + if (followerCount < reqValue) + return false; - if (followerCount < reqValue) - return false; - - break; - } + break; + } case ModifierTreeType.GarrisonTierEqual: // 170 - { - Garrison garrison = referencePlayer.GetGarrison(); - if (garrison == null || garrison.GetGarrisonType() != (GarrisonType)secondaryAsset || garrison.GetSiteLevel().GarrLevel != reqValue) - return false; - break; - } + { + Garrison garrison = referencePlayer.GetGarrison(); + if (garrison == null || garrison.GetGarrisonType() != (GarrisonType)secondaryAsset || garrison.GetSiteLevel().GarrLevel != reqValue) + return false; + break; + } case ModifierTreeType.InstancePlayerCountEqual: // 171 if (referencePlayer.GetMap().GetPlayers().Count != reqValue) return false; @@ -2462,68 +2465,68 @@ namespace Game.Achievements return false; break; case ModifierTreeType.PlayerCanAcceptQuest: // 174 - { - Quest quest = Global.ObjectMgr.GetQuestTemplate(reqValue); - if (quest == null) - return false; + { + Quest quest = Global.ObjectMgr.GetQuestTemplate(reqValue); + if (quest == null) + return false; - if (!referencePlayer.CanTakeQuest(quest, false)) - return false; - break; - } + if (!referencePlayer.CanTakeQuest(quest, false)) + return false; + break; + } case ModifierTreeType.GarrisonFollowerCountWithLevelEqualOrGreaterThan: // 175 + { + Garrison garrison = referencePlayer.GetGarrison(); + if (garrison == null || garrison.GetGarrisonType() != (GarrisonType)tertiaryAsset) + return false; + + uint followerCount = garrison.CountFollowers(follower => { - Garrison garrison = referencePlayer.GetGarrison(); - if (garrison == null || garrison.GetGarrisonType() != (GarrisonType)tertiaryAsset) - return false; + var garrFollower = CliDB.GarrFollowerStorage.LookupByKey(follower.PacketInfo.GarrFollowerID); + return garrFollower?.GarrFollowerTypeID == tertiaryAsset && follower.PacketInfo.FollowerLevel == secondaryAsset; + }); - uint followerCount = garrison.CountFollowers(follower => - { - var garrFollower = CliDB.GarrFollowerStorage.LookupByKey(follower.PacketInfo.GarrFollowerID); - return garrFollower?.GarrFollowerTypeID == tertiaryAsset && follower.PacketInfo.FollowerLevel == secondaryAsset; - }); - - if (followerCount < reqValue) - return false; - break; - } + if (followerCount < reqValue) + return false; + break; + } case ModifierTreeType.GarrisonFollowerIsInBuilding: // 176 + { + Garrison garrison = referencePlayer.GetGarrison(); + if (garrison == null) + return false; + + uint followerCount = garrison.CountFollowers(follower => { - Garrison garrison = referencePlayer.GetGarrison(); - if (garrison == null) - return false; + return follower.PacketInfo.GarrFollowerID == reqValue && follower.PacketInfo.CurrentBuildingID == secondaryAsset; + }); - uint followerCount = garrison.CountFollowers(follower => - { - return follower.PacketInfo.GarrFollowerID == reqValue && follower.PacketInfo.CurrentBuildingID == secondaryAsset; - }); - - if (followerCount < 1) - return false; - break; - } + if (followerCount < 1) + return false; + break; + } case ModifierTreeType.GarrisonMissionCountLessThan: // 177 NYI return false; case ModifierTreeType.GarrisonPlotInstanceCountEqualOrGreaterThan: // 178 + { + Garrison garrison = referencePlayer.GetGarrison(); + if (garrison == null || garrison.GetGarrisonType() != (GarrisonType)reqValue) + return false; + + uint plotCount = 0; + foreach (var plot in garrison.GetPlots()) { - Garrison garrison = referencePlayer.GetGarrison(); - if (garrison == null || garrison.GetGarrisonType() != (GarrisonType)reqValue) - return false; + var garrPlotInstance = CliDB.GarrPlotInstanceStorage.LookupByKey(plot.PacketInfo.GarrPlotInstanceID); + if (garrPlotInstance == null || garrPlotInstance.GarrPlotID != secondaryAsset) + continue; - uint plotCount = 0; - foreach (var plot in garrison.GetPlots()) - { - var garrPlotInstance = CliDB.GarrPlotInstanceStorage.LookupByKey(plot.PacketInfo.GarrPlotInstanceID); - if (garrPlotInstance == null || garrPlotInstance.GarrPlotID != secondaryAsset) - continue; - - ++plotCount; - } - - if (plotCount < reqValue) - return false; - break; + ++plotCount; } + + if (plotCount < reqValue) + return false; + break; + } case ModifierTreeType.CurrencySource: // 179 NYI return false; case ModifierTreeType.PlayerIsInNotOwnGarrison: // 180 @@ -2531,70 +2534,70 @@ namespace Game.Achievements return false; break; case ModifierTreeType.HasActiveGarrisonFollower: // 181 - { - Garrison garrison = referencePlayer.GetGarrison(); - if (garrison == null) - return false; + { + Garrison garrison = referencePlayer.GetGarrison(); + if (garrison == null) + return false; - uint followerCount = garrison.CountFollowers(follower => follower.PacketInfo.GarrFollowerID == reqValue && (follower.PacketInfo.FollowerStatus & (byte)GarrisonFollowerStatus.Inactive) == 0); - if (followerCount < 1) - return false; - break; - } + uint followerCount = garrison.CountFollowers(follower => follower.PacketInfo.GarrFollowerID == reqValue && (follower.PacketInfo.FollowerStatus & (byte)GarrisonFollowerStatus.Inactive) == 0); + if (followerCount < 1) + return false; + break; + } case ModifierTreeType.PlayerDailyRandomValueMod_X_Equals: // 182 NYI return false; case ModifierTreeType.PlayerHasMount: // 183 + { + foreach (var pair in referencePlayer.GetSession().GetCollectionMgr().GetAccountMounts()) { - foreach (var pair in referencePlayer.GetSession().GetCollectionMgr().GetAccountMounts()) - { - var mount = Global.DB2Mgr.GetMount(pair.Key); - if (mount == null) - continue; + var mount = Global.DB2Mgr.GetMount(pair.Key); + if (mount == null) + continue; - if (mount.Id == reqValue) - return true; - } - return false; + if (mount.Id == reqValue) + return true; } + return false; + } case ModifierTreeType.GarrisonFollowerCountWithInactiveWithItemLevelEqualOrGreaterThan: // 184 + { + Garrison garrison = referencePlayer.GetGarrison(); + if (garrison == null) + return false; + + uint followerCount = garrison.CountFollowers(follower => { - Garrison garrison = referencePlayer.GetGarrison(); - if (garrison == null) + GarrFollowerRecord garrFollower = CliDB.GarrFollowerStorage.LookupByKey(follower.PacketInfo.GarrFollowerID); + if (garrFollower == null) return false; - uint followerCount = garrison.CountFollowers(follower => - { - GarrFollowerRecord garrFollower = CliDB.GarrFollowerStorage.LookupByKey(follower.PacketInfo.GarrFollowerID); - if (garrFollower == null) - return false; + return follower.GetItemLevel() >= secondaryAsset && garrFollower.GarrFollowerTypeID == tertiaryAsset; + }); - return follower.GetItemLevel() >= secondaryAsset && garrFollower.GarrFollowerTypeID == tertiaryAsset; - }); - - if (followerCount < reqValue) - return false; - break; - } + if (followerCount < reqValue) + return false; + break; + } case ModifierTreeType.GarrisonFollowerIsOnAMission: // 185 - { - Garrison garrison = referencePlayer.GetGarrison(); - if (garrison == null) - return false; + { + Garrison garrison = referencePlayer.GetGarrison(); + if (garrison == null) + return false; - uint followerCount = garrison.CountFollowers(follower => follower.PacketInfo.GarrFollowerID == reqValue && follower.PacketInfo.CurrentMissionID != 0); - if (followerCount < 1) - return false; - break; - } + uint followerCount = garrison.CountFollowers(follower => follower.PacketInfo.GarrFollowerID == reqValue && follower.PacketInfo.CurrentMissionID != 0); + if (followerCount < 1) + return false; + break; + } case ModifierTreeType.GarrisonMissionCountInSetLessThan: // 186 NYI return false; case ModifierTreeType.GarrisonFollowerType: // 187 - { - var garrFollower = CliDB.GarrFollowerStorage.LookupByKey(miscValue1); - if (garrFollower == null || garrFollower.GarrFollowerTypeID != reqValue) - return false; - break; - } + { + var garrFollower = CliDB.GarrFollowerStorage.LookupByKey(miscValue1); + if (garrFollower == null || garrFollower.GarrFollowerTypeID != reqValue) + return false; + break; + } case ModifierTreeType.PlayerUsedBoostLessThanHoursAgoRealTime: // 188 NYI case ModifierTreeType.PlayerUsedBoostLessThanHoursAgoGameTime: // 189 NYI return false; @@ -2625,22 +2628,22 @@ namespace Game.Achievements return false; break; case ModifierTreeType.PlayerHasTransmog: // 200 - { - var (PermAppearance, TempAppearance) = referencePlayer.GetSession().GetCollectionMgr().HasItemAppearance(reqValue); - if (!PermAppearance || TempAppearance) - return false; - break; - } + { + var (PermAppearance, TempAppearance) = referencePlayer.GetSession().GetCollectionMgr().HasItemAppearance(reqValue); + if (!PermAppearance || TempAppearance) + return false; + break; + } case ModifierTreeType.GarrisonTalentSelected: // 201 NYI case ModifierTreeType.GarrisonTalentResearched: // 202 NYI return false; case ModifierTreeType.PlayerHasRestriction: // 203 - { - int restrictionIndex = referencePlayer.m_activePlayerData.CharacterRestrictions.FindIndexIf(restriction => restriction.Type == reqValue); - if (restrictionIndex < 0) - return false; - break; - } + { + int restrictionIndex = referencePlayer.m_activePlayerData.CharacterRestrictions.FindIndexIf(restriction => restriction.Type == reqValue); + if (restrictionIndex < 0) + return false; + break; + } case ModifierTreeType.PlayerCreatedCharacterLessThanHoursAgoRealTime: // 204 NYI return false; case ModifierTreeType.PlayerCreatedCharacterLessThanHoursAgoGameTime: // 205 @@ -2648,30 +2651,30 @@ namespace Game.Achievements return false; break; case ModifierTreeType.QuestHasQuestInfoId: // 206 - { - Quest quest = Global.ObjectMgr.GetQuestTemplate((uint)miscValue1); - if (quest == null || quest.Id != reqValue) - return false; - break; - } + { + Quest quest = Global.ObjectMgr.GetQuestTemplate((uint)miscValue1); + if (quest == null || quest.Id != reqValue) + return false; + break; + } case ModifierTreeType.GarrisonTalentResearchInProgress: // 207 NYI return false; case ModifierTreeType.PlayerEquippedArtifactAppearanceSet: // 208 + { + Aura artifactAura = referencePlayer.GetAura(PlayerConst.ArtifactsAllWeaponsGeneralWeaponEquippedPassive); + if (artifactAura != null) { - Aura artifactAura = referencePlayer.GetAura(PlayerConst.ArtifactsAllWeaponsGeneralWeaponEquippedPassive); - if (artifactAura != null) + Item artifact = referencePlayer.GetItemByGuid(artifactAura.GetCastItemGUID()); + if (artifact != null) { - Item artifact = referencePlayer.GetItemByGuid(artifactAura.GetCastItemGUID()); - if (artifact != null) - { - ArtifactAppearanceRecord artifactAppearance = CliDB.ArtifactAppearanceStorage.LookupByKey(artifact.GetModifier(ItemModifier.ArtifactAppearanceId)); - if (artifactAppearance != null) - if (artifactAppearance.ArtifactAppearanceSetID == reqValue) - break; - } + ArtifactAppearanceRecord artifactAppearance = CliDB.ArtifactAppearanceStorage.LookupByKey(artifact.GetModifier(ItemModifier.ArtifactAppearanceId)); + if (artifactAppearance != null) + if (artifactAppearance.ArtifactAppearanceSetID == reqValue) + break; } - return false; } + return false; + } case ModifierTreeType.PlayerHasCurrencyEqual: // 209 if (referencePlayer.GetCurrency(reqValue) != secondaryAsset) return false; @@ -2679,15 +2682,15 @@ namespace Game.Achievements case ModifierTreeType.MinimumAverageItemHighWaterMarkForSpec: // 210 NYI return false; case ModifierTreeType.PlayerScenarioType: // 211 - { - Scenario scenario = referencePlayer.GetScenario(); - if (scenario == null) - return false; + { + Scenario scenario = referencePlayer.GetScenario(); + if (scenario == null) + return false; - if (scenario.GetEntry().Type != reqValue) - return false; - break; - } + if (scenario.GetEntry().Type != reqValue) + return false; + break; + } case ModifierTreeType.PlayersAuthExpansionLevelEqualOrGreaterThan: // 212 if (referencePlayer.GetSession().GetAccountExpansion() < (Expansion)reqValue) return false; @@ -2697,30 +2700,30 @@ namespace Game.Achievements case ModifierTreeType.PlayerLastWeekRBGRating: // 215 NYI return false; case ModifierTreeType.GroupMemberCountFromConnectedRealmEqualOrGreaterThan: // 216 + { + uint memberCount = 0; + var group = referencePlayer.GetGroup(); + if (group != null) { - uint memberCount = 0; - var group = referencePlayer.GetGroup(); - if (group != null) - { - for (var itr = group.GetFirstMember(); itr != null; itr = itr.Next()) - if (itr.GetSource() != referencePlayer && referencePlayer.m_playerData.VirtualPlayerRealm == itr.GetSource().m_playerData.VirtualPlayerRealm) - ++memberCount; - } - - if (memberCount < reqValue) - return false; - break; + for (var itr = group.GetFirstMember(); itr != null; itr = itr.Next()) + if (itr.GetSource() != referencePlayer && referencePlayer.m_playerData.VirtualPlayerRealm == itr.GetSource().m_playerData.VirtualPlayerRealm) + ++memberCount; } + + if (memberCount < reqValue) + return false; + break; + } case ModifierTreeType.ArtifactTraitUnlockedCountEqualOrGreaterThan: // 217 - { - Item artifact = referencePlayer.GetItemByEntry((uint)secondaryAsset, ItemSearchLocation.Everywhere); - if (artifact == null) - return false; + { + Item artifact = referencePlayer.GetItemByEntry((uint)secondaryAsset, ItemSearchLocation.Everywhere); + if (artifact == null) + return false; - if (artifact.GetTotalUnlockedArtifactPowers() < reqValue) - return false; - break; - } + if (artifact.GetTotalUnlockedArtifactPowers() < reqValue) + return false; + break; + } case ModifierTreeType.ParagonReputationLevelEqualOrGreaterThan: // 218 if (referencePlayer.GetReputationMgr().GetParagonLevel((uint)miscValue1) < reqValue) return false; @@ -2728,38 +2731,38 @@ namespace Game.Achievements case ModifierTreeType.GarrisonShipmentIsReady: // 219 NYI return false; case ModifierTreeType.PlayerIsInPvpBrawl: // 220 - { - var bg = CliDB.BattlemasterListStorage.LookupByKey(referencePlayer.GetBattlegroundTypeId()); - if (bg == null || !bg.Flags.HasFlag(BattlemasterListFlags.Brawl)) - return false; - break; - } + { + var bg = CliDB.BattlemasterListStorage.LookupByKey(referencePlayer.GetBattlegroundTypeId()); + if (bg == null || !bg.Flags.HasFlag(BattlemasterListFlags.Brawl)) + return false; + break; + } case ModifierTreeType.ParagonReputationLevelWithFactionEqualOrGreaterThan: // 221 - { - var faction = CliDB.FactionStorage.LookupByKey(secondaryAsset); - if (faction == null) - return false; + { + var faction = CliDB.FactionStorage.LookupByKey(secondaryAsset); + if (faction == null) + return false; - if (referencePlayer.GetReputationMgr().GetParagonLevel(faction.ParagonFactionID) < reqValue) - return false; - break; - } + if (referencePlayer.GetReputationMgr().GetParagonLevel(faction.ParagonFactionID) < reqValue) + return false; + break; + } case ModifierTreeType.PlayerHasItemWithBonusListFromTreeAndQuality: // 222 + { + var bonusListIDs = Global.DB2Mgr.GetAllItemBonusTreeBonuses(reqValue); + if (bonusListIDs.Empty()) + return false; + + bool bagScanReachedEnd = referencePlayer.ForEachItem(ItemSearchLocation.Everywhere, item => { - var bonusListIDs = Global.DB2Mgr.GetAllItemBonusTreeBonuses(reqValue); - if (bonusListIDs.Empty()) - return false; + bool hasBonus = item.m_itemData.BonusListIDs._value.Any(bonusListID => bonusListIDs.Contains(bonusListID)); + return !hasBonus; + }); - bool bagScanReachedEnd = referencePlayer.ForEachItem(ItemSearchLocation.Everywhere, item => - { - bool hasBonus = item.m_itemData.BonusListIDs._value.Any(bonusListID => bonusListIDs.Contains(bonusListID)); - return !hasBonus; - }); - - if (bagScanReachedEnd) - return false; - break; - } + if (bagScanReachedEnd) + return false; + break; + } case ModifierTreeType.PlayerHasEmptyInventorySlotCountEqualOrGreaterThan: // 223 if (referencePlayer.GetFreeInventorySlotCount(ItemSearchLocation.Inventory) < reqValue) return false; @@ -2767,23 +2770,23 @@ namespace Game.Achievements case ModifierTreeType.PlayerHasItemInHistoryOfProgressiveEvent: // 224 NYI return false; case ModifierTreeType.PlayerHasArtifactPowerRankCountPurchasedEqualOrGreaterThan: // 225 - { - Aura artifactAura = referencePlayer.GetAura(PlayerConst.ArtifactsAllWeaponsGeneralWeaponEquippedPassive); - if (artifactAura == null) - return false; + { + Aura artifactAura = referencePlayer.GetAura(PlayerConst.ArtifactsAllWeaponsGeneralWeaponEquippedPassive); + if (artifactAura == null) + return false; - Item artifact = referencePlayer.GetItemByGuid(artifactAura.GetCastItemGUID()); - if (!artifact) - return false; + Item artifact = referencePlayer.GetItemByGuid(artifactAura.GetCastItemGUID()); + if (!artifact) + return false; - var artifactPower = artifact.GetArtifactPower((uint)secondaryAsset); - if (artifactPower == null) - return false; + var artifactPower = artifact.GetArtifactPower((uint)secondaryAsset); + if (artifactPower == null) + return false; - if (artifactPower.PurchasedRank < reqValue) - return false; - break; - } + if (artifactPower.PurchasedRank < reqValue) + return false; + break; + } case ModifierTreeType.PlayerHasBoosted: // 226 if (referencePlayer.HasLevelBoosted()) return false; @@ -2803,187 +2806,187 @@ namespace Game.Achievements return false; break; case ModifierTreeType.GroupMemberCountWithAchievementEqualOrLessThan: // 231 + { + var group = referencePlayer.GetGroup(); + if (group != null) { - var group = referencePlayer.GetGroup(); - if (group != null) - { - uint membersWithAchievement = 0; - for (var itr = group.GetFirstMember(); itr != null; itr = itr.Next()) - if (itr.GetSource().HasAchieved((uint)secondaryAsset)) - ++membersWithAchievement; + uint membersWithAchievement = 0; + for (var itr = group.GetFirstMember(); itr != null; itr = itr.Next()) + if (itr.GetSource().HasAchieved((uint)secondaryAsset)) + ++membersWithAchievement; - if (membersWithAchievement > reqValue) - return false; - } - // true if no group - break; + if (membersWithAchievement > reqValue) + return false; } + // true if no group + break; + } case ModifierTreeType.PlayerMainhandWeaponType: // 232 + { + var visibleItem = referencePlayer.m_playerData.VisibleItems[EquipmentSlot.MainHand]; + uint itemSubclass = (uint)ItemSubClassWeapon.Fist; + ItemTemplate itemTemplate = Global.ObjectMgr.GetItemTemplate(visibleItem.ItemID); + if (itemTemplate != null) { - var visibleItem = referencePlayer.m_playerData.VisibleItems[EquipmentSlot.MainHand]; - uint itemSubclass = (uint)ItemSubClassWeapon.Fist; - ItemTemplate itemTemplate = Global.ObjectMgr.GetItemTemplate(visibleItem.ItemID); - if (itemTemplate != null) + if (itemTemplate.GetClass() == ItemClass.Weapon) { - if (itemTemplate.GetClass() == ItemClass.Weapon) - { - itemSubclass = itemTemplate.GetSubClass(); + itemSubclass = itemTemplate.GetSubClass(); - var itemModifiedAppearance = Global.DB2Mgr.GetItemModifiedAppearance(visibleItem.ItemID, visibleItem.ItemAppearanceModID); - if (itemModifiedAppearance != null) - { - var itemModifiedAppearaceExtra = CliDB.ItemModifiedAppearanceExtraStorage.LookupByKey(itemModifiedAppearance.Id); - if (itemModifiedAppearaceExtra != null) - if (itemModifiedAppearaceExtra.DisplayWeaponSubclassID > 0) - itemSubclass = (uint)itemModifiedAppearaceExtra.DisplayWeaponSubclassID; - } + var itemModifiedAppearance = Global.DB2Mgr.GetItemModifiedAppearance(visibleItem.ItemID, visibleItem.ItemAppearanceModID); + if (itemModifiedAppearance != null) + { + var itemModifiedAppearaceExtra = CliDB.ItemModifiedAppearanceExtraStorage.LookupByKey(itemModifiedAppearance.Id); + if (itemModifiedAppearaceExtra != null) + if (itemModifiedAppearaceExtra.DisplayWeaponSubclassID > 0) + itemSubclass = (uint)itemModifiedAppearaceExtra.DisplayWeaponSubclassID; } } - if (itemSubclass != reqValue) - return false; - break; } + if (itemSubclass != reqValue) + return false; + break; + } case ModifierTreeType.PlayerOffhandWeaponType: // 233 + { + var visibleItem = referencePlayer.m_playerData.VisibleItems[EquipmentSlot.OffHand]; + uint itemSubclass = (uint)ItemSubClassWeapon.Fist; + ItemTemplate itemTemplate = Global.ObjectMgr.GetItemTemplate(visibleItem.ItemID); + if (itemTemplate != null) { - var visibleItem = referencePlayer.m_playerData.VisibleItems[EquipmentSlot.OffHand]; - uint itemSubclass = (uint)ItemSubClassWeapon.Fist; - ItemTemplate itemTemplate = Global.ObjectMgr.GetItemTemplate(visibleItem.ItemID); - if (itemTemplate != null) + if (itemTemplate.GetClass() == ItemClass.Weapon) { - if (itemTemplate.GetClass() == ItemClass.Weapon) - { - itemSubclass = itemTemplate.GetSubClass(); + itemSubclass = itemTemplate.GetSubClass(); - var itemModifiedAppearance = Global.DB2Mgr.GetItemModifiedAppearance(visibleItem.ItemID, visibleItem.ItemAppearanceModID); - if (itemModifiedAppearance != null) - { - var itemModifiedAppearaceExtra = CliDB.ItemModifiedAppearanceExtraStorage.LookupByKey(itemModifiedAppearance.Id); - if (itemModifiedAppearaceExtra != null) - if (itemModifiedAppearaceExtra.DisplayWeaponSubclassID > 0) - itemSubclass = (uint)itemModifiedAppearaceExtra.DisplayWeaponSubclassID; - } + var itemModifiedAppearance = Global.DB2Mgr.GetItemModifiedAppearance(visibleItem.ItemID, visibleItem.ItemAppearanceModID); + if (itemModifiedAppearance != null) + { + var itemModifiedAppearaceExtra = CliDB.ItemModifiedAppearanceExtraStorage.LookupByKey(itemModifiedAppearance.Id); + if (itemModifiedAppearaceExtra != null) + if (itemModifiedAppearaceExtra.DisplayWeaponSubclassID > 0) + itemSubclass = (uint)itemModifiedAppearaceExtra.DisplayWeaponSubclassID; } } - if (itemSubclass != reqValue) - return false; - break; } + if (itemSubclass != reqValue) + return false; + break; + } case ModifierTreeType.PlayerPvpTier: // 234 - { - var pvpTier = CliDB.PvpTierStorage.LookupByKey(reqValue); - if (pvpTier == null) - return false; + { + var pvpTier = CliDB.PvpTierStorage.LookupByKey(reqValue); + if (pvpTier == null) + return false; - if (pvpTier.BracketID >= referencePlayer.m_activePlayerData.PvpInfo.GetSize()) - return false; + if (pvpTier.BracketID >= referencePlayer.m_activePlayerData.PvpInfo.GetSize()) + return false; - var pvpInfo = referencePlayer.m_activePlayerData.PvpInfo[pvpTier.BracketID]; - if (pvpTier.Id != pvpInfo.PvpTierID || pvpInfo.Disqualified) - return false; - break; - } + var pvpInfo = referencePlayer.m_activePlayerData.PvpInfo[pvpTier.BracketID]; + if (pvpTier.Id != pvpInfo.PvpTierID || pvpInfo.Disqualified) + return false; + break; + } case ModifierTreeType.PlayerAzeriteLevelEqualOrGreaterThan: // 235 - { - Item heartOfAzeroth = referencePlayer.GetItemByEntry(PlayerConst.ItemIdHeartOfAzeroth, ItemSearchLocation.Everywhere); - if (!heartOfAzeroth || heartOfAzeroth.ToAzeriteItem().GetLevel() < reqValue) - return false; - break; - } + { + Item heartOfAzeroth = referencePlayer.GetItemByEntry(PlayerConst.ItemIdHeartOfAzeroth, ItemSearchLocation.Everywhere); + if (!heartOfAzeroth || heartOfAzeroth.ToAzeriteItem().GetLevel() < reqValue) + return false; + break; + } case ModifierTreeType.PlayerIsOnQuestInQuestline: // 236 - { - bool isOnQuest = false; - var questLineQuests = Global.DB2Mgr.GetQuestsForQuestLine(reqValue); - if (!questLineQuests.Empty()) - isOnQuest = questLineQuests.Any(questLineQuest => referencePlayer.FindQuestSlot(questLineQuest.QuestID) < SharedConst.MaxQuestLogSize); + { + bool isOnQuest = false; + var questLineQuests = Global.DB2Mgr.GetQuestsForQuestLine(reqValue); + if (!questLineQuests.Empty()) + isOnQuest = questLineQuests.Any(questLineQuest => referencePlayer.FindQuestSlot(questLineQuest.QuestID) < SharedConst.MaxQuestLogSize); - if (!isOnQuest) - return false; - break; - } + if (!isOnQuest) + return false; + break; + } case ModifierTreeType.PlayerIsQnQuestLinkedToScheduledWorldStateGroup: // 237 return false; // OBSOLETE (db2 removed) case ModifierTreeType.PlayerIsInRaidGroup: // 238 - { - var group = referencePlayer.GetGroup(); - if (group == null || !group.IsRaidGroup()) - return false; + { + var group = referencePlayer.GetGroup(); + if (group == null || !group.IsRaidGroup()) + return false; - break; - } + break; + } case ModifierTreeType.PlayerPvpTierInBracketEqualOrGreaterThan: // 239 - { - if (secondaryAsset >= referencePlayer.m_activePlayerData.PvpInfo.GetSize()) - return false; + { + if (secondaryAsset >= referencePlayer.m_activePlayerData.PvpInfo.GetSize()) + return false; - var pvpInfo = referencePlayer.m_activePlayerData.PvpInfo[secondaryAsset]; - var pvpTier = CliDB.PvpTierStorage.LookupByKey(pvpInfo.PvpTierID); - if (pvpTier == null) - return false; + var pvpInfo = referencePlayer.m_activePlayerData.PvpInfo[secondaryAsset]; + var pvpTier = CliDB.PvpTierStorage.LookupByKey(pvpInfo.PvpTierID); + if (pvpTier == null) + return false; - if (pvpTier.Rank < reqValue) - return false; - break; - } + if (pvpTier.Rank < reqValue) + return false; + break; + } case ModifierTreeType.PlayerCanAcceptQuestInQuestline: // 240 + { + var questLineQuests = Global.DB2Mgr.GetQuestsForQuestLine(reqValue); + if (questLineQuests.Empty()) + return false; + + bool canTakeQuest = questLineQuests.Any(questLineQuest => { - var questLineQuests = Global.DB2Mgr.GetQuestsForQuestLine(reqValue); - if (questLineQuests.Empty()) - return false; + Quest quest = Global.ObjectMgr.GetQuestTemplate(questLineQuest.QuestID); + if (quest != null) + return referencePlayer.CanTakeQuest(quest, false); - bool canTakeQuest = questLineQuests.Any(questLineQuest => - { - Quest quest = Global.ObjectMgr.GetQuestTemplate(questLineQuest.QuestID); - if (quest != null) - return referencePlayer.CanTakeQuest(quest, false); + return false; + }); - return false; - }); - - if (!canTakeQuest) - return false; - break; - } + if (!canTakeQuest) + return false; + break; + } case ModifierTreeType.PlayerHasCompletedQuestline: // 241 - { - var questLineQuests = Global.DB2Mgr.GetQuestsForQuestLine(reqValue); - if (questLineQuests.Empty()) - return false; + { + var questLineQuests = Global.DB2Mgr.GetQuestsForQuestLine(reqValue); + if (questLineQuests.Empty()) + return false; - foreach (var questLineQuest in questLineQuests) - if (!referencePlayer.GetQuestRewardStatus(questLineQuest.QuestID)) - return false; - break; - } + foreach (var questLineQuest in questLineQuests) + if (!referencePlayer.GetQuestRewardStatus(questLineQuest.QuestID)) + return false; + break; + } case ModifierTreeType.PlayerHasCompletedQuestlineQuestCount: // 242 - { - var questLineQuests = Global.DB2Mgr.GetQuestsForQuestLine(reqValue); - if (questLineQuests.Empty()) - return false; + { + var questLineQuests = Global.DB2Mgr.GetQuestsForQuestLine(reqValue); + if (questLineQuests.Empty()) + return false; - uint completedQuests = 0; - foreach (var questLineQuest in questLineQuests) - if (referencePlayer.GetQuestRewardStatus(questLineQuest.QuestID)) - ++completedQuests; + uint completedQuests = 0; + foreach (var questLineQuest in questLineQuests) + if (referencePlayer.GetQuestRewardStatus(questLineQuest.QuestID)) + ++completedQuests; - if (completedQuests < reqValue) - return false; - break; - } + if (completedQuests < reqValue) + return false; + break; + } case ModifierTreeType.PlayerHasCompletedPercentageOfQuestline: // 243 - { - var questLineQuests = Global.DB2Mgr.GetQuestsForQuestLine(reqValue); - if (questLineQuests.Empty()) - return false; + { + var questLineQuests = Global.DB2Mgr.GetQuestsForQuestLine(reqValue); + if (questLineQuests.Empty()) + return false; - int completedQuests = 0; - foreach (var questLineQuest in questLineQuests) - if (referencePlayer.GetQuestRewardStatus(questLineQuest.QuestID)) - ++completedQuests; + int completedQuests = 0; + foreach (var questLineQuest in questLineQuests) + if (referencePlayer.GetQuestRewardStatus(questLineQuest.QuestID)) + ++completedQuests; - if (MathFunctions.GetPctOf(completedQuests, questLineQuests.Count) < reqValue) - return false; - break; - } + if (MathFunctions.GetPctOf(completedQuests, questLineQuests.Count) < reqValue) + return false; + break; + } case ModifierTreeType.PlayerHasWarModeEnabled: // 244 if (!referencePlayer.HasPlayerLocalFlag(PlayerLocalFlags.WarMode)) return false; @@ -3003,63 +3006,63 @@ namespace Game.Achievements case ModifierTreeType.MythicPlusMilestoneSeason: // 251 NYI return false; case ModifierTreeType.PlayerVisibleRace: // 252 - { - CreatureDisplayInfoRecord creatureDisplayInfo = CliDB.CreatureDisplayInfoStorage.LookupByKey(referencePlayer.GetDisplayId()); - if (creatureDisplayInfo == null) - return false; + { + CreatureDisplayInfoRecord creatureDisplayInfo = CliDB.CreatureDisplayInfoStorage.LookupByKey(referencePlayer.GetDisplayId()); + if (creatureDisplayInfo == null) + return false; - CreatureDisplayInfoExtraRecord creatureDisplayInfoExtra = CliDB.CreatureDisplayInfoExtraStorage.LookupByKey(creatureDisplayInfo.ExtendedDisplayInfoID); - if (creatureDisplayInfoExtra == null) - return false; + CreatureDisplayInfoExtraRecord creatureDisplayInfoExtra = CliDB.CreatureDisplayInfoExtraStorage.LookupByKey(creatureDisplayInfo.ExtendedDisplayInfoID); + if (creatureDisplayInfoExtra == null) + return false; - if (creatureDisplayInfoExtra.DisplayRaceID != reqValue) - return false; - break; - } + if (creatureDisplayInfoExtra.DisplayRaceID != reqValue) + return false; + break; + } case ModifierTreeType.TargetVisibleRace: // 253 - { - if (!unit) - return false; - CreatureDisplayInfoRecord creatureDisplayInfo = CliDB.CreatureDisplayInfoStorage.LookupByKey(unit.GetDisplayId()); - if (creatureDisplayInfo == null) - return false; + { + if (refe == null || !refe.IsUnit()) + return false; + CreatureDisplayInfoRecord creatureDisplayInfo = CliDB.CreatureDisplayInfoStorage.LookupByKey(refe.ToUnit().GetDisplayId()); + if (creatureDisplayInfo == null) + return false; - CreatureDisplayInfoExtraRecord creatureDisplayInfoExtra = CliDB.CreatureDisplayInfoExtraStorage.LookupByKey(creatureDisplayInfo.ExtendedDisplayInfoID); - if (creatureDisplayInfoExtra == null) - return false; + CreatureDisplayInfoExtraRecord creatureDisplayInfoExtra = CliDB.CreatureDisplayInfoExtraStorage.LookupByKey(creatureDisplayInfo.ExtendedDisplayInfoID); + if (creatureDisplayInfoExtra == null) + return false; - if (creatureDisplayInfoExtra.DisplayRaceID != reqValue) - return false; - break; - } + if (creatureDisplayInfoExtra.DisplayRaceID != reqValue) + return false; + break; + } case ModifierTreeType.FriendshipRepReactionEqual: // 254 - { - var friendshipRepReaction = CliDB.FriendshipRepReactionStorage.LookupByKey(reqValue); - if (friendshipRepReaction == null) - return false; + { + var friendshipRepReaction = CliDB.FriendshipRepReactionStorage.LookupByKey(reqValue); + if (friendshipRepReaction == null) + return false; - var friendshipReputation = CliDB.FriendshipReputationStorage.LookupByKey(friendshipRepReaction.FriendshipRepID); - if (friendshipReputation == null) - return false; + var friendshipReputation = CliDB.FriendshipReputationStorage.LookupByKey(friendshipRepReaction.FriendshipRepID); + if (friendshipReputation == null) + return false; - var friendshipReactions = Global.DB2Mgr.GetFriendshipRepReactions(reqValue); - if (friendshipReactions == null) - return false; + var friendshipReactions = Global.DB2Mgr.GetFriendshipRepReactions(reqValue); + if (friendshipReactions == null) + return false; - int rank = (int)referencePlayer.GetReputationRank((uint)friendshipReputation.FactionID); - if (rank >= friendshipReactions.Count) - return false; + int rank = (int)referencePlayer.GetReputationRank((uint)friendshipReputation.FactionID); + if (rank >= friendshipReactions.Count) + return false; - if (friendshipReactions[rank].Id != reqValue) - return false; - break; - } + if (friendshipReactions[rank].Id != reqValue) + return false; + break; + } case ModifierTreeType.PlayerAuraStackCountEqual: // 255 if (referencePlayer.GetAuraCount((uint)secondaryAsset) != reqValue) return false; break; case ModifierTreeType.TargetAuraStackCountEqual: // 256 - if (!unit || unit.GetAuraCount((uint)secondaryAsset) != reqValue) + if (!refe || !refe.IsUnit() || refe.ToUnit().GetAuraCount((uint)secondaryAsset) != reqValue) return false; break; case ModifierTreeType.PlayerAuraStackCountEqualOrGreaterThan: // 257 @@ -3067,172 +3070,172 @@ namespace Game.Achievements return false; break; case ModifierTreeType.TargetAuraStackCountEqualOrGreaterThan: // 258 - if (!unit || unit.GetAuraCount((uint)secondaryAsset) < reqValue) + if (!refe || !refe.IsUnit() || refe.ToUnit().GetAuraCount((uint)secondaryAsset) < reqValue) return false; break; case ModifierTreeType.PlayerHasAzeriteEssenceRankLessThan: // 259 + { + Item heartOfAzeroth = referencePlayer.GetItemByEntry(PlayerConst.ItemIdHeartOfAzeroth, ItemSearchLocation.Everywhere); + if (heartOfAzeroth != null) { - Item heartOfAzeroth = referencePlayer.GetItemByEntry(PlayerConst.ItemIdHeartOfAzeroth, ItemSearchLocation.Everywhere); - if (heartOfAzeroth != null) + AzeriteItem azeriteItem = heartOfAzeroth.ToAzeriteItem(); + if (azeriteItem != null) { - AzeriteItem azeriteItem = heartOfAzeroth.ToAzeriteItem(); - if (azeriteItem != null) - { - foreach (UnlockedAzeriteEssence essence in azeriteItem.m_azeriteItemData.UnlockedEssences) - if (essence.AzeriteEssenceID == reqValue && essence.Rank < secondaryAsset) - return true; - } + foreach (UnlockedAzeriteEssence essence in azeriteItem.m_azeriteItemData.UnlockedEssences) + if (essence.AzeriteEssenceID == reqValue && essence.Rank < secondaryAsset) + return true; } - return false; } + return false; + } case ModifierTreeType.PlayerHasAzeriteEssenceRankEqual: // 260 + { + Item heartOfAzeroth = referencePlayer.GetItemByEntry(PlayerConst.ItemIdHeartOfAzeroth, ItemSearchLocation.Everywhere); + if (heartOfAzeroth != null) { - Item heartOfAzeroth = referencePlayer.GetItemByEntry(PlayerConst.ItemIdHeartOfAzeroth, ItemSearchLocation.Everywhere); - if (heartOfAzeroth != null) + AzeriteItem azeriteItem = heartOfAzeroth.ToAzeriteItem(); + if (azeriteItem != null) { - AzeriteItem azeriteItem = heartOfAzeroth.ToAzeriteItem(); - if (azeriteItem != null) - { - foreach (UnlockedAzeriteEssence essence in azeriteItem.m_azeriteItemData.UnlockedEssences) - if (essence.AzeriteEssenceID == reqValue && essence.Rank == secondaryAsset) - return true; - } + foreach (UnlockedAzeriteEssence essence in azeriteItem.m_azeriteItemData.UnlockedEssences) + if (essence.AzeriteEssenceID == reqValue && essence.Rank == secondaryAsset) + return true; } - return false; } + return false; + } case ModifierTreeType.PlayerHasAzeriteEssenceRankGreaterThan: // 261 + { + Item heartOfAzeroth = referencePlayer.GetItemByEntry(PlayerConst.ItemIdHeartOfAzeroth, ItemSearchLocation.Everywhere); + if (heartOfAzeroth != null) { - Item heartOfAzeroth = referencePlayer.GetItemByEntry(PlayerConst.ItemIdHeartOfAzeroth, ItemSearchLocation.Everywhere); - if (heartOfAzeroth != null) + AzeriteItem azeriteItem = heartOfAzeroth.ToAzeriteItem(); + if (azeriteItem != null) { - AzeriteItem azeriteItem = heartOfAzeroth.ToAzeriteItem(); - if (azeriteItem != null) - { - foreach (UnlockedAzeriteEssence essence in azeriteItem.m_azeriteItemData.UnlockedEssences) - if (essence.AzeriteEssenceID == reqValue && essence.Rank > secondaryAsset) - return true; - } + foreach (UnlockedAzeriteEssence essence in azeriteItem.m_azeriteItemData.UnlockedEssences) + if (essence.AzeriteEssenceID == reqValue && essence.Rank > secondaryAsset) + return true; } - return false; } + return false; + } case ModifierTreeType.PlayerHasAuraWithEffectIndex: // 262 if (referencePlayer.GetAuraEffect(reqValue, (uint)secondaryAsset) == null) return false; break; case ModifierTreeType.PlayerLootSpecializationMatchesRole: // 263 - { - ChrSpecializationRecord spec = CliDB.ChrSpecializationStorage.LookupByKey(referencePlayer.GetPrimarySpecialization()); - if (spec == null || spec.Role != reqValue) - return false; - break; - } + { + ChrSpecializationRecord spec = CliDB.ChrSpecializationStorage.LookupByKey(referencePlayer.GetPrimarySpecialization()); + if (spec == null || spec.Role != reqValue) + return false; + break; + } case ModifierTreeType.PlayerIsAtMaxExpansionLevel: // 264 if (referencePlayer.GetLevel() != Global.ObjectMgr.GetMaxLevelForExpansion((Expansion)WorldConfig.GetIntValue(WorldCfg.Expansion))) return false; break; case ModifierTreeType.TransmogSource: // 265 - { - var itemModifiedAppearance = CliDB.ItemModifiedAppearanceStorage.LookupByKey(miscValue2); - if (itemModifiedAppearance == null) - return false; + { + var itemModifiedAppearance = CliDB.ItemModifiedAppearanceStorage.LookupByKey(miscValue2); + if (itemModifiedAppearance == null) + return false; - if (itemModifiedAppearance.TransmogSourceTypeEnum != reqValue) - return false; - break; - } + if (itemModifiedAppearance.TransmogSourceTypeEnum != reqValue) + return false; + break; + } case ModifierTreeType.PlayerHasAzeriteEssenceInSlotAtRankLessThan: // 266 + { + Item heartOfAzeroth = referencePlayer.GetItemByEntry(PlayerConst.ItemIdHeartOfAzeroth, ItemSearchLocation.Everywhere); + if (heartOfAzeroth != null) { - Item heartOfAzeroth = referencePlayer.GetItemByEntry(PlayerConst.ItemIdHeartOfAzeroth, ItemSearchLocation.Everywhere); - if (heartOfAzeroth != null) + AzeriteItem azeriteItem = heartOfAzeroth.ToAzeriteItem(); + if (azeriteItem != null) { - AzeriteItem azeriteItem = heartOfAzeroth.ToAzeriteItem(); - if (azeriteItem != null) + SelectedAzeriteEssences selectedEssences = azeriteItem.GetSelectedAzeriteEssences(); + if (selectedEssences != null) { - SelectedAzeriteEssences selectedEssences = azeriteItem.GetSelectedAzeriteEssences(); - if (selectedEssences != null) - { - foreach (UnlockedAzeriteEssence essence in azeriteItem.m_azeriteItemData.UnlockedEssences) - if (essence.AzeriteEssenceID == selectedEssences.AzeriteEssenceID[(int)reqValue] && essence.Rank < secondaryAsset) - return true; - } + foreach (UnlockedAzeriteEssence essence in azeriteItem.m_azeriteItemData.UnlockedEssences) + if (essence.AzeriteEssenceID == selectedEssences.AzeriteEssenceID[(int)reqValue] && essence.Rank < secondaryAsset) + return true; } } - return false; } + return false; + } case ModifierTreeType.PlayerHasAzeriteEssenceInSlotAtRankGreaterThan: // 267 + { + Item heartOfAzeroth = referencePlayer.GetItemByEntry(PlayerConst.ItemIdHeartOfAzeroth, ItemSearchLocation.Everywhere); + if (heartOfAzeroth != null) { - Item heartOfAzeroth = referencePlayer.GetItemByEntry(PlayerConst.ItemIdHeartOfAzeroth, ItemSearchLocation.Everywhere); - if (heartOfAzeroth != null) + AzeriteItem azeriteItem = heartOfAzeroth.ToAzeriteItem(); + if (azeriteItem != null) { - AzeriteItem azeriteItem = heartOfAzeroth.ToAzeriteItem(); - if (azeriteItem != null) + SelectedAzeriteEssences selectedEssences = azeriteItem.GetSelectedAzeriteEssences(); + if (selectedEssences != null) { - SelectedAzeriteEssences selectedEssences = azeriteItem.GetSelectedAzeriteEssences(); - if (selectedEssences != null) - { - foreach (UnlockedAzeriteEssence essence in azeriteItem.m_azeriteItemData.UnlockedEssences) - if (essence.AzeriteEssenceID == selectedEssences.AzeriteEssenceID[(int)reqValue] && essence.Rank > secondaryAsset) - return true; - } + foreach (UnlockedAzeriteEssence essence in azeriteItem.m_azeriteItemData.UnlockedEssences) + if (essence.AzeriteEssenceID == selectedEssences.AzeriteEssenceID[(int)reqValue] && essence.Rank > secondaryAsset) + return true; } } - return false; } + return false; + } case ModifierTreeType.PlayerLevelWithinContentTuning: // 268 + { + uint level = referencePlayer.GetLevel(); + var levels = Global.DB2Mgr.GetContentTuningData(reqValue, 0); + if (levels.HasValue) { - uint level = referencePlayer.GetLevel(); - var levels = Global.DB2Mgr.GetContentTuningData(reqValue, 0); - if (levels.HasValue) - { - if (secondaryAsset != 0) - return level >= levels.Value.MinLevelWithDelta && level <= levels.Value.MaxLevelWithDelta; - return level >= levels.Value.MinLevel && level <= levels.Value.MaxLevel; - } - return false; + if (secondaryAsset != 0) + return level >= levels.Value.MinLevelWithDelta && level <= levels.Value.MaxLevelWithDelta; + return level >= levels.Value.MinLevel && level <= levels.Value.MaxLevel; } + return false; + } case ModifierTreeType.TargetLevelWithinContentTuning: // 269 - { - if (!unit) - return false; - - uint level = unit.GetLevel(); - var levels = Global.DB2Mgr.GetContentTuningData(reqValue, 0); - if (levels.HasValue) - { - if (secondaryAsset != 0) - return level >= levels.Value.MinLevelWithDelta && level <= levels.Value.MaxLevelWithDelta; - return level >= levels.Value.MinLevel && level <= levels.Value.MaxLevel; - } + { + if (!refe || !refe.IsUnit()) return false; + + uint level = refe.ToUnit().GetLevel(); + var levels = Global.DB2Mgr.GetContentTuningData(reqValue, 0); + if (levels.HasValue) + { + if (secondaryAsset != 0) + return level >= levels.Value.MinLevelWithDelta && level <= levels.Value.MaxLevelWithDelta; + return level >= levels.Value.MinLevel && level <= levels.Value.MaxLevel; } + return false; + } case ModifierTreeType.PlayerIsScenarioInitiator: // 270 NYI return false; case ModifierTreeType.PlayerHasCompletedQuestOrIsOnQuest: // 271 - { - QuestStatus status = referencePlayer.GetQuestStatus(reqValue); - if (status == QuestStatus.None || status == QuestStatus.Failed) - return false; - break; - } + { + QuestStatus status = referencePlayer.GetQuestStatus(reqValue); + if (status == QuestStatus.None || status == QuestStatus.Failed) + return false; + break; + } case ModifierTreeType.PlayerLevelWithinOrAboveContentTuning: // 272 - { - uint level = referencePlayer.GetLevel(); - var levels = Global.DB2Mgr.GetContentTuningData(reqValue, 0); - if (levels.HasValue) - return secondaryAsset != 0 ? level >= levels.Value.MinLevelWithDelta : level >= levels.Value.MinLevel; - return false; - } + { + uint level = referencePlayer.GetLevel(); + var levels = Global.DB2Mgr.GetContentTuningData(reqValue, 0); + if (levels.HasValue) + return secondaryAsset != 0 ? level >= levels.Value.MinLevelWithDelta : level >= levels.Value.MinLevel; + return false; + } case ModifierTreeType.TargetLevelWithinOrAboveContentTuning: // 273 - { - if (!unit) - return false; - - uint level = unit.GetLevel(); - var levels = Global.DB2Mgr.GetContentTuningData(reqValue, 0); - if (levels.HasValue) - return secondaryAsset != 0 ? level >= levels.Value.MinLevelWithDelta : level >= levels.Value.MinLevel; + { + if (!refe || !refe.IsUnit()) return false; - } + + uint level = refe.ToUnit().GetLevel(); + var levels = Global.DB2Mgr.GetContentTuningData(reqValue, 0); + if (levels.HasValue) + return secondaryAsset != 0 ? level >= levels.Value.MinLevelWithDelta : level >= levels.Value.MinLevel; + return false; + } case ModifierTreeType.PlayerLevelWithinOrAboveLevelRange: // 274 NYI case ModifierTreeType.TargetLevelWithinOrAboveLevelRange: // 275 NYI return false; @@ -3241,40 +3244,40 @@ namespace Game.Achievements return false; break; case ModifierTreeType.GroupedWithRaFRecruit: // 277 - { - var group = referencePlayer.GetGroup(); - if (group == null) - return false; - - for (var itr = group.GetFirstMember(); itr != null; itr = itr.Next()) - if (itr.GetSource().GetSession().GetRecruiterId() == referencePlayer.GetSession().GetAccountId()) - return true; - + { + var group = referencePlayer.GetGroup(); + if (group == null) return false; - } + + for (var itr = group.GetFirstMember(); itr != null; itr = itr.Next()) + if (itr.GetSource().GetSession().GetRecruiterId() == referencePlayer.GetSession().GetAccountId()) + return true; + + return false; + } case ModifierTreeType.GroupedWithRaFRecruiter: // 278 - { - var group = referencePlayer.GetGroup(); - if (group == null) - return false; - - for (var itr = group.GetFirstMember(); itr != null; itr = itr.Next()) - if (itr.GetSource().GetSession().GetAccountId() == referencePlayer.GetSession().GetRecruiterId()) - return true; - + { + var group = referencePlayer.GetGroup(); + if (group == null) return false; - } + + for (var itr = group.GetFirstMember(); itr != null; itr = itr.Next()) + if (itr.GetSource().GetSession().GetAccountId() == referencePlayer.GetSession().GetRecruiterId()) + return true; + + return false; + } case ModifierTreeType.PlayerSpecialization: // 279 if (referencePlayer.GetPrimarySpecialization() != reqValue) return false; break; case ModifierTreeType.PlayerMapOrCosmeticChildMap: // 280 - { - MapRecord map = referencePlayer.GetMap().GetEntry(); - if (map.Id != reqValue && map.CosmeticParentMapID != reqValue) - return false; - break; - } + { + MapRecord map = referencePlayer.GetMap().GetEntry(); + if (map.Id != reqValue && map.CosmeticParentMapID != reqValue) + return false; + break; + } case ModifierTreeType.PlayerCanAccessShadowlandsPrepurchaseContent: // 281 if (referencePlayer.GetSession().GetAccountExpansion() < Expansion.ShadowLands) return false; @@ -3291,32 +3294,32 @@ namespace Game.Achievements return false; break; case ModifierTreeType.HasTimeEventPassed: // 289 + { + long eventTimestamp = GameTime.GetGameTime(); + switch (reqValue) { - long eventTimestamp = GameTime.GetGameTime(); - switch (reqValue) - { - case 111: // Battle for Azeroth Season 4 Start - eventTimestamp = 1579618800L; // January 21, 2020 8:00 - break; - case 120: // Patch 9.0.1 - eventTimestamp = 1602601200L; // October 13, 2020 8:00 - break; - case 121: // Shadowlands Season 1 Start - eventTimestamp = 1607439600L; // December 8, 2020 8:00 - break; - case 123: // Shadowlands Season 1 End - // timestamp = unknown - break; ; - case 149: // Shadowlands Season 2 End - // timestamp = unknown - break; - default: - break; - } - if (GameTime.GetGameTime() < eventTimestamp) - return false; - break; + case 111: // Battle for Azeroth Season 4 Start + eventTimestamp = 1579618800L; // January 21, 2020 8:00 + break; + case 120: // Patch 9.0.1 + eventTimestamp = 1602601200L; // October 13, 2020 8:00 + break; + case 121: // Shadowlands Season 1 Start + eventTimestamp = 1607439600L; // December 8, 2020 8:00 + break; + case 123: // Shadowlands Season 1 End + // timestamp = unknown + break; ; + case 149: // Shadowlands Season 2 End + // timestamp = unknown + break; + default: + break; } + if (GameTime.GetGameTime() < eventTimestamp) + return false; + break; + } case ModifierTreeType.GarrisonHasPermanentTalent: // 290 NYI return false; case ModifierTreeType.HasActiveSoulbind: // 291 @@ -3332,28 +3335,28 @@ namespace Game.Achievements case ModifierTreeType.PlayerHasImpInABallToySubscriptionReward: // 297 return false; case ModifierTreeType.PlayerIsInAreaGroup: // 298 - { - var areas = Global.DB2Mgr.GetAreasForGroup(reqValue); - AreaTableRecord area = CliDB.AreaTableStorage.LookupByKey(referencePlayer.GetAreaId()); - if (area != null) - foreach (uint areaInGroup in areas) - if (areaInGroup == area.Id || areaInGroup == area.ParentAreaID) - return true; - return false; - } + { + var areas = Global.DB2Mgr.GetAreasForGroup(reqValue); + AreaTableRecord area = CliDB.AreaTableStorage.LookupByKey(referencePlayer.GetAreaId()); + if (area != null) + foreach (uint areaInGroup in areas) + if (areaInGroup == area.Id || areaInGroup == area.ParentAreaID) + return true; + return false; + } case ModifierTreeType.TargetIsInAreaGroup: // 299 - { - if (!unit) - return false; - - var areas = Global.DB2Mgr.GetAreasForGroup(reqValue); - var area = CliDB.AreaTableStorage.LookupByKey(unit.GetAreaId()); - if (area != null) - foreach (uint areaInGroup in areas) - if (areaInGroup == area.Id || areaInGroup == area.ParentAreaID) - return true; + { + if (!refe) return false; - } + + var areas = Global.DB2Mgr.GetAreasForGroup(reqValue); + var area = CliDB.AreaTableStorage.LookupByKey(refe.GetAreaId()); + if (area != null) + foreach (uint areaInGroup in areas) + if (areaInGroup == area.Id || areaInGroup == area.ParentAreaID) + return true; + return false; + } case ModifierTreeType.PlayerIsInChromieTime: // 300 if (referencePlayer.m_activePlayerData.UiChromieTimeExpansionID != reqValue) return false; @@ -3367,14 +3370,14 @@ namespace Game.Achievements return false; break; case ModifierTreeType.PlayerHasRuneforgePower: // 303 - { - int block = (int)reqValue / 32; - if (block >= referencePlayer.m_activePlayerData.RuneforgePowers.Size()) - return false; + { + int block = (int)reqValue / 32; + if (block >= referencePlayer.m_activePlayerData.RuneforgePowers.Size()) + return false; - uint bit = reqValue % 32; - return (referencePlayer.m_activePlayerData.RuneforgePowers[block] & (1u << (int)bit)) != 0; - } + uint bit = reqValue % 32; + return (referencePlayer.m_activePlayerData.RuneforgePowers[block] & (1u << (int)bit)) != 0; + } case ModifierTreeType.PlayerInChromieTimeForScaling: // 304 if ((referencePlayer.m_playerData.CtrOptions._value.ContentTuningConditionMask & 1) == 0) return false; @@ -3384,35 +3387,35 @@ namespace Game.Achievements return false; break; case ModifierTreeType.AllPlayersInGroupHaveAchievement: // 306 + { + var group = referencePlayer.GetGroup(); + if (group != null) { - var group = referencePlayer.GetGroup(); - if (group != null) - { - for (var itr = group.GetFirstMember(); itr != null; itr = itr.Next()) - if (!itr.GetSource().HasAchieved(reqValue)) - return false; - } - else if (!referencePlayer.HasAchieved(reqValue)) - return false; - break; + for (var itr = group.GetFirstMember(); itr != null; itr = itr.Next()) + if (!itr.GetSource().HasAchieved(reqValue)) + return false; } + else if (!referencePlayer.HasAchieved(reqValue)) + return false; + break; + } case ModifierTreeType.PlayerHasSoulbindConduitRankEqualOrGreaterThan: // 307 NYI return false; case ModifierTreeType.PlayerSpellShapeshiftFormCreatureDisplayInfoSelection: // 308 - { - ShapeshiftFormModelData formModelData = Global.DB2Mgr.GetShapeshiftFormModelData(referencePlayer.GetRace(), referencePlayer.GetNativeSex(), (ShapeShiftForm)secondaryAsset); - if (formModelData == null) - return false; + { + ShapeshiftFormModelData formModelData = Global.DB2Mgr.GetShapeshiftFormModelData(referencePlayer.GetRace(), referencePlayer.GetNativeSex(), (ShapeShiftForm)secondaryAsset); + if (formModelData == null) + return false; - uint formChoice = referencePlayer.GetCustomizationChoice(formModelData.OptionID); - var choiceIndex = formModelData.Choices.FindIndex(choice => { return choice.Id == formChoice; }); - if (choiceIndex == -1) - return false; + uint formChoice = referencePlayer.GetCustomizationChoice(formModelData.OptionID); + var choiceIndex = formModelData.Choices.FindIndex(choice => { return choice.Id == formChoice; }); + if (choiceIndex == -1) + return false; - if (reqValue != formModelData.Displays[choiceIndex].DisplayID) - return false; - break; - } + if (reqValue != formModelData.Displays[choiceIndex].DisplayID) + return false; + break; + } case ModifierTreeType.PlayerSoulbindConduitCountAtRankEqualOrGreaterThan: // 309 NYI return false; case ModifierTreeType.PlayerIsRestrictedAccount: // 310 @@ -3422,23 +3425,23 @@ namespace Game.Achievements return false; break; case ModifierTreeType.PlayerScenarioIsLastStep: // 312 - { - Scenario scenario = referencePlayer.GetScenario(); - if (scenario == null) - return false; + { + Scenario scenario = referencePlayer.GetScenario(); + if (scenario == null) + return false; - if (scenario.GetStep() != scenario.GetLastStep()) - return false; - break; - } + if (scenario.GetStep() != scenario.GetLastStep()) + return false; + break; + } case ModifierTreeType.PlayerHasWeeklyRewardsAvailable: // 313 if (referencePlayer.m_activePlayerData.WeeklyRewardsPeriodSinceOrigin == 0) return false; break; case ModifierTreeType.TargetCovenant: // 314 - if (!unit || !unit.IsPlayer()) + if (!refe || !refe.IsPlayer()) return false; - if (unit.ToPlayer().m_playerData.CovenantID != reqValue) + if (refe.ToPlayer().m_playerData.CovenantID != reqValue) return false; break; case ModifierTreeType.PlayerHasTBCCollectorsEdition: // 315 @@ -3452,66 +3455,66 @@ namespace Game.Achievements case ModifierTreeType.PlayerMythicPlusRunCountInCurrentExpansionEqualOrGreaterThan: // 322 NYI return false; case ModifierTreeType.PlayerHasCustomizationChoice: // 323 + { + int customizationChoiceIndex = referencePlayer.m_playerData.Customizations.FindIndexIf(choice => { - int customizationChoiceIndex = referencePlayer.m_playerData.Customizations.FindIndexIf(choice => - { - return choice.ChrCustomizationChoiceID == reqValue; - }); + return choice.ChrCustomizationChoiceID == reqValue; + }); - if (customizationChoiceIndex < 0) - return false; + if (customizationChoiceIndex < 0) + return false; - break; - } + break; + } case ModifierTreeType.PlayerBestWeeklyWinPvpTier: // 324 - { - var pvpTier = CliDB.PvpTierStorage.LookupByKey(reqValue); - if (pvpTier == null) - return false; + { + var pvpTier = CliDB.PvpTierStorage.LookupByKey(reqValue); + if (pvpTier == null) + return false; - if (pvpTier.BracketID >= referencePlayer.m_activePlayerData.PvpInfo.GetSize()) - return false; + if (pvpTier.BracketID >= referencePlayer.m_activePlayerData.PvpInfo.GetSize()) + return false; - var pvpInfo = referencePlayer.m_activePlayerData.PvpInfo[pvpTier.BracketID]; - if (pvpTier.Id != pvpInfo.WeeklyBestWinPvpTierID || pvpInfo.Disqualified) - return false; + var pvpInfo = referencePlayer.m_activePlayerData.PvpInfo[pvpTier.BracketID]; + if (pvpTier.Id != pvpInfo.WeeklyBestWinPvpTierID || pvpInfo.Disqualified) + return false; - break; - } + break; + } case ModifierTreeType.PlayerBestWeeklyWinPvpTierInBracketEqualOrGreaterThan: // 325 - { - if (secondaryAsset >= referencePlayer.m_activePlayerData.PvpInfo.GetSize()) - return false; + { + if (secondaryAsset >= referencePlayer.m_activePlayerData.PvpInfo.GetSize()) + return false; - var pvpInfo = referencePlayer.m_activePlayerData.PvpInfo[secondaryAsset]; - var pvpTier = CliDB.PvpTierStorage.LookupByKey(pvpInfo.WeeklyBestWinPvpTierID); - if (pvpTier == null) - return false; + var pvpInfo = referencePlayer.m_activePlayerData.PvpInfo[secondaryAsset]; + var pvpTier = CliDB.PvpTierStorage.LookupByKey(pvpInfo.WeeklyBestWinPvpTierID); + if (pvpTier == null) + return false; - if (pvpTier.Rank < reqValue) - return false; + if (pvpTier.Rank < reqValue) + return false; - break; - } + break; + } case ModifierTreeType.PlayerHasVanillaCollectorsEdition: // 326 return false; case ModifierTreeType.PlayerHasItemWithKeystoneLevelModifierEqualOrGreaterThan: // 327 + { + bool bagScanReachedEnd = referencePlayer.ForEachItem(ItemSearchLocation.Inventory, item => { - bool bagScanReachedEnd = referencePlayer.ForEachItem(ItemSearchLocation.Inventory, item => - { - if (item.GetEntry() != reqValue) - return true; + if (item.GetEntry() != reqValue) + return true; - if (item.GetModifier(ItemModifier.ChallengeKeystoneLevel) < secondaryAsset) - return true; + if (item.GetModifier(ItemModifier.ChallengeKeystoneLevel) < secondaryAsset) + return true; - return false; - }); - if (bagScanReachedEnd) - return false; + return false; + }); + if (bagScanReachedEnd) + return false; - break; - } + break; + } default: return false; } @@ -3996,7 +3999,7 @@ namespace Game.Achievements [StructLayout(LayoutKind.Explicit)] public class CriteriaData - { + { [FieldOffset(0)] public CriteriaDataType DataType; @@ -4164,29 +4167,29 @@ namespace Game.Achievements return true; case CriteriaDataType.SAura: case CriteriaDataType.TAura: + { + SpellInfo spellEntry = Global.SpellMgr.GetSpellInfo(Aura.SpellId, Difficulty.None); + if (spellEntry == null) { - SpellInfo spellEntry = Global.SpellMgr.GetSpellInfo(Aura.SpellId, Difficulty.None); - if (spellEntry == null) - { - Log.outError(LogFilter.Sql, "Table `criteria_data` (Entry: {0} Type: {1}) for data type {2} has wrong spell id in value1 ({3}), ignored.", - criteria.Id, criteria.Entry.Type, DataType, Aura.SpellId); - return false; - } - SpellEffectInfo effect = spellEntry.GetEffect(Aura.EffectIndex); - if (effect == null) - { - Log.outError(LogFilter.Sql, "Table `criteria_data` (Entry: {0} Type: {1}) for data type {2} has wrong spell effect index in value2 ({3}), ignored.", - criteria.Id, criteria.Entry.Type, DataType, Aura.EffectIndex); - return false; - } - if (effect.ApplyAuraName == 0) - { - Log.outError(LogFilter.Sql, "Table `criteria_data` (Entry: {0} Type: {1}) for data type {2} has non-aura spell effect (ID: {3} Effect: {4}), ignores.", - criteria.Id, criteria.Entry.Type, DataType, Aura.SpellId, Aura.EffectIndex); - return false; - } - return true; + Log.outError(LogFilter.Sql, "Table `criteria_data` (Entry: {0} Type: {1}) for data type {2} has wrong spell id in value1 ({3}), ignored.", + criteria.Id, criteria.Entry.Type, DataType, Aura.SpellId); + return false; } + SpellEffectInfo effect = spellEntry.GetEffect(Aura.EffectIndex); + if (effect == null) + { + Log.outError(LogFilter.Sql, "Table `criteria_data` (Entry: {0} Type: {1}) for data type {2} has wrong spell effect index in value2 ({3}), ignored.", + criteria.Id, criteria.Entry.Type, DataType, Aura.EffectIndex); + return false; + } + if (effect.ApplyAuraName == 0) + { + Log.outError(LogFilter.Sql, "Table `criteria_data` (Entry: {0} Type: {1}) for data type {2} has non-aura spell effect (ID: {3} Effect: {4}), ignores.", + criteria.Id, criteria.Entry.Type, DataType, Aura.SpellId, Aura.EffectIndex); + return false; + } + return true; + } case CriteriaDataType.Value: if (Value.ComparisonType >= (int)ComparisionType.Max) { @@ -4252,16 +4255,16 @@ namespace Game.Achievements } return true; case CriteriaDataType.GameEvent: + { + var events = Global.GameEventMgr.GetEventMap(); + if (GameEvent.Id < 1 || GameEvent.Id >= events.Length) { - var events = Global.GameEventMgr.GetEventMap(); - if (GameEvent.Id < 1 || GameEvent.Id >= events.Length) - { - Log.outError(LogFilter.Sql, "Table `criteria_data` (Entry: {0} Type: {1}) for data type CRITERIA_DATA_TYPE_GAME_EVENT ({2}) has unknown game_event in value1 ({3}), ignored.", - criteria.Id, criteria.Entry.Type, DataType, GameEvent.Id); - return false; - } - return true; + Log.outError(LogFilter.Sql, "Table `criteria_data` (Entry: {0} Type: {1}) for data type CRITERIA_DATA_TYPE_GAME_EVENT ({2}) has unknown game_event in value1 ({3}), ignored.", + criteria.Id, criteria.Entry.Type, DataType, GameEvent.Id); + return false; } + return true; + } case CriteriaDataType.BgLossTeamScore: return true; // not check correctness node indexes case CriteriaDataType.SEquippedItem: @@ -4322,7 +4325,7 @@ namespace Game.Achievements } } - public bool Meets(uint criteriaId, Player source, Unit target, uint miscValue1 = 0, uint miscValue2 = 0) + public bool Meets(uint criteriaId, Player source, WorldObject target, uint miscValue1 = 0, uint miscValue2 = 0) { switch (DataType) { @@ -4351,11 +4354,18 @@ namespace Game.Achievements case CriteriaDataType.TPlayerLessHealth: if (target == null || !target.IsTypeId(TypeId.Player)) return false; - return !target.HealthAbovePct((int)Health.Percent); + return !target.ToPlayer().HealthAbovePct((int)Health.Percent); case CriteriaDataType.SAura: return source.HasAuraEffect(Aura.SpellId, (byte)Aura.EffectIndex); case CriteriaDataType.TAura: - return target != null && target.HasAuraEffect(Aura.SpellId, (byte)Aura.EffectIndex); + { + if (target == null) + return false; + Unit unitTarget = target.ToUnit(); + if (unitTarget == null) + return false; + return unitTarget.HasAuraEffect(Aura.SpellId, Aura.EffectIndex); + } case CriteriaDataType.Value: return MathFunctions.CompareValues((ComparisionType)Value.ComparisonType, miscValue1, Value.Value); case CriteriaDataType.TLevel: @@ -4363,11 +4373,21 @@ namespace Game.Achievements return false; return target.GetLevelForTarget(source) >= Level.Min; case CriteriaDataType.TGender: + { if (target == null) return false; - return (uint)target.GetGender() == Gender.Gender; + Unit unitTarget = target.ToUnit(); + if (unitTarget == null) + return false; + return unitTarget.GetGender() == (Gender)Gender.Gender; + } case CriteriaDataType.Script: - return Global.ScriptMgr.OnCriteriaCheck(ScriptId, source, target); + { + Unit unitTarget = null; + if (target) + unitTarget = target.ToUnit(); + return Global.ScriptMgr.OnCriteriaCheck(ScriptId, source.ToPlayer(), unitTarget.ToUnit()); + } case CriteriaDataType.MapPlayerCount: return source.GetMap().GetPlayersCountExceptGMs() <= MapPlayers.MaxCount; case CriteriaDataType.TTeam: @@ -4381,61 +4401,65 @@ namespace Game.Achievements case CriteriaDataType.GameEvent: return Global.GameEventMgr.IsEventActive((ushort)GameEvent.Id); case CriteriaDataType.BgLossTeamScore: - { - Battleground bg = source.GetBattleground(); - if (!bg) - return false; + { + Battleground bg = source.GetBattleground(); + if (!bg) + return false; - int score = (int)bg.GetTeamScore(source.GetTeam() == Team.Alliance ? Framework.Constants.TeamId.Horde : Framework.Constants.TeamId.Alliance); - return score >= BattlegroundScore.Min && score <= BattlegroundScore.Max; - } + int score = (int)bg.GetTeamScore(source.GetTeam() == Team.Alliance ? Framework.Constants.TeamId.Horde : Framework.Constants.TeamId.Alliance); + return score >= BattlegroundScore.Min && score <= BattlegroundScore.Max; + } case CriteriaDataType.InstanceScript: + { + if (!source.IsInWorld) + return false; + Map map = source.GetMap(); + if (!map.IsDungeon()) { - if (!source.IsInWorld) - return false; - Map map = source.GetMap(); - if (!map.IsDungeon()) - { - Log.outError(LogFilter.Achievement, "Achievement system call AchievementCriteriaDataType.InstanceScript ({0}) for achievement criteria {1} for non-dungeon/non-raid map {2}", - CriteriaDataType.InstanceScript, criteriaId, map.GetId()); - return false; - } - InstanceScript instance = ((InstanceMap)map).GetInstanceScript(); - if (instance == null) - { - Log.outError(LogFilter.Achievement, "Achievement system call criteria_data_INSTANCE_SCRIPT ({0}) for achievement criteria {1} for map {2} but map does not have a instance script", - CriteriaDataType.InstanceScript, criteriaId, map.GetId()); - return false; - } - return instance.CheckAchievementCriteriaMeet(criteriaId, source, target, miscValue1); + Log.outError(LogFilter.Achievement, "Achievement system call AchievementCriteriaDataType.InstanceScript ({0}) for achievement criteria {1} for non-dungeon/non-raid map {2}", + CriteriaDataType.InstanceScript, criteriaId, map.GetId()); + return false; } - case CriteriaDataType.SEquippedItem: + InstanceScript instance = ((InstanceMap)map).GetInstanceScript(); + if (instance == null) { - Criteria entry = Global.CriteriaMgr.GetCriteria(criteriaId); + Log.outError(LogFilter.Achievement, "Achievement system call criteria_data_INSTANCE_SCRIPT ({0}) for achievement criteria {1} for map {2} but map does not have a instance script", + CriteriaDataType.InstanceScript, criteriaId, map.GetId()); + return false; + } - uint itemId = entry.Entry.Type == CriteriaTypes.EquipItemInSlot ? miscValue2 : miscValue1; - ItemTemplate itemTemplate = Global.ObjectMgr.GetItemTemplate(itemId); - if (itemTemplate == null) - return false; - return itemTemplate.GetBaseItemLevel() >= EquippedItem.ItemLevel && (uint)itemTemplate.GetQuality() >= EquippedItem.ItemQuality; - } + Unit unitTarget = null; + if (target != null) + unitTarget = target.ToUnit(); + return instance.CheckAchievementCriteriaMeet(criteriaId, source, unitTarget, miscValue1); + } + case CriteriaDataType.SEquippedItem: + { + Criteria entry = Global.CriteriaMgr.GetCriteria(criteriaId); + + uint itemId = entry.Entry.Type == CriteriaTypes.EquipItemInSlot ? miscValue2 : miscValue1; + ItemTemplate itemTemplate = Global.ObjectMgr.GetItemTemplate(itemId); + if (itemTemplate == null) + return false; + return itemTemplate.GetBaseItemLevel() >= EquippedItem.ItemLevel && (uint)itemTemplate.GetQuality() >= EquippedItem.ItemQuality; + } case CriteriaDataType.MapId: return source.GetMapId() == MapId.Id; case CriteriaDataType.SKnownTitle: - { - CharTitlesRecord titleInfo = CliDB.CharTitlesStorage.LookupByKey(KnownTitle.Id); - if (titleInfo != null) - return source && source.HasTitle(titleInfo.MaskID); + { + CharTitlesRecord titleInfo = CliDB.CharTitlesStorage.LookupByKey(KnownTitle.Id); + if (titleInfo != null) + return source && source.HasTitle(titleInfo.MaskID); - return false; - } + return false; + } case CriteriaDataType.SItemQuality: - { - ItemTemplate pProto = Global.ObjectMgr.GetItemTemplate(miscValue1); - if (pProto == null) - return false; - return (uint)pProto.GetQuality() == itemQuality.Quality; - } + { + ItemTemplate pProto = Global.ObjectMgr.GetItemTemplate(miscValue1); + if (pProto == null) + return false; + return (uint)pProto.GetQuality() == itemQuality.Quality; + } default: break; } @@ -4548,13 +4572,13 @@ namespace Game.Achievements } public class CriteriaDataSet - { + { uint _criteriaId; List _storage = new(); public void Add(CriteriaData data) { _storage.Add(data); } - public bool Meets(Player source, Unit target, uint miscValue = 0, uint miscValue2 = 0) + public bool Meets(Player source, WorldObject target, uint miscValue = 0, uint miscValue2 = 0) { foreach (var data in _storage) if (!data.Meets(_criteriaId, source, target, miscValue, miscValue2)) diff --git a/Source/Game/Collision/Management/VMapManager.cs b/Source/Game/Collision/Management/VMapManager.cs index 826c22727..ff8a7e929 100644 --- a/Source/Game/Collision/Management/VMapManager.cs +++ b/Source/Game/Collision/Management/VMapManager.cs @@ -143,7 +143,7 @@ namespace Game.Collision public bool IsInLineOfSight(uint mapId, float x1, float y1, float z1, float x2, float y2, float z2, ModelIgnoreFlags ignoreFlags) { - if (!IsLineOfSightCalcEnabled() || Global.DisableMgr.IsDisabledFor(DisableType.VMAP, mapId, null, DisableFlags.VmapLOS)) + if (!IsLineOfSightCalcEnabled() || Global.DisableMgr.IsDisabledFor(DisableType.VMAP, mapId, null, (byte)DisableFlags.VmapLOS)) return true; var instanceTree = iInstanceMapTrees.LookupByKey(mapId); @@ -160,7 +160,7 @@ namespace Game.Collision public bool GetObjectHitPos(uint mapId, float x1, float y1, float z1, float x2, float y2, float z2, out float rx, out float ry, out float rz, float modifyDist) { - if (IsLineOfSightCalcEnabled() && !Global.DisableMgr.IsDisabledFor(DisableType.VMAP, mapId, null, DisableFlags.VmapLOS)) + if (IsLineOfSightCalcEnabled() && !Global.DisableMgr.IsDisabledFor(DisableType.VMAP, mapId, null, (byte)DisableFlags.VmapLOS)) { var instanceTree = iInstanceMapTrees.LookupByKey(mapId); if (instanceTree != null) @@ -186,7 +186,7 @@ namespace Game.Collision public float GetHeight(uint mapId, float x, float y, float z, float maxSearchDist) { - if (IsHeightCalcEnabled() && !Global.DisableMgr.IsDisabledFor(DisableType.VMAP, mapId, null, DisableFlags.VmapHeight)) + if (IsHeightCalcEnabled() && !Global.DisableMgr.IsDisabledFor(DisableType.VMAP, mapId, null, (byte)DisableFlags.VmapHeight)) { var instanceTree = iInstanceMapTrees.LookupByKey(mapId); if (instanceTree != null) @@ -209,7 +209,7 @@ namespace Game.Collision adtId = 0; rootId = 0; groupId = 0; - if (!Global.DisableMgr.IsDisabledFor(DisableType.VMAP, mapId, null, DisableFlags.VmapAreaFlag)) + if (!Global.DisableMgr.IsDisabledFor(DisableType.VMAP, mapId, null, (byte)DisableFlags.VmapAreaFlag)) { var instanceTree = iInstanceMapTrees.LookupByKey(mapId); if (instanceTree != null) @@ -227,7 +227,7 @@ namespace Game.Collision public bool GetLiquidLevel(uint mapId, float x, float y, float z, uint reqLiquidType, ref float level, ref float floor, ref uint type) { - if (!Global.DisableMgr.IsDisabledFor(DisableType.VMAP, mapId, null, DisableFlags.VmapLiquidStatus)) + if (!Global.DisableMgr.IsDisabledFor(DisableType.VMAP, mapId, null, (byte)DisableFlags.VmapLiquidStatus)) { var instanceTree = iInstanceMapTrees.LookupByKey(mapId); if (instanceTree != null) @@ -253,7 +253,7 @@ namespace Game.Collision { var data = new AreaAndLiquidData(); - if (!Global.DisableMgr.IsDisabledFor(DisableType.VMAP, mapId, null, DisableFlags.VmapLiquidStatus)) + if (!Global.DisableMgr.IsDisabledFor(DisableType.VMAP, mapId, null, (byte)DisableFlags.VmapLiquidStatus)) { data.floorZ = z; int adtId, rootId, groupId; @@ -276,7 +276,7 @@ namespace Game.Collision if (info.hitInstance.GetLiquidLevel(pos, info, ref liquidLevel)) data.liquidInfo.Set(new AreaAndLiquidData.LiquidInfo(liquidType, liquidLevel)); - if (!Global.DisableMgr.IsDisabledFor(DisableType.VMAP, mapId, null, DisableFlags.VmapLiquidStatus)) + if (!Global.DisableMgr.IsDisabledFor(DisableType.VMAP, mapId, null, (byte)DisableFlags.VmapLiquidStatus)) data.areaInfo.Set(new AreaAndLiquidData.AreaInfo(info.hitInstance.adtId, info.rootId, (int)info.hitModel.GetWmoID(), info.hitModel.GetMogpFlags())); } } diff --git a/Source/Game/Conditions/DisableManager.cs b/Source/Game/Conditions/DisableManager.cs index 8f9e8df2f..d754d68ee 100644 --- a/Source/Game/Conditions/DisableManager.cs +++ b/Source/Game/Conditions/DisableManager.cs @@ -63,17 +63,17 @@ namespace Game } uint entry = result.Read(1); - byte flags = result.Read(2); + DisableFlags flags = (DisableFlags)result.Read(2); string params_0 = result.Read(3); string params_1 = result.Read(4); DisableData data = new(); - data.flags = flags; + data.flags = (byte)flags; switch (type) { case DisableType.Spell: - if (!(Global.SpellMgr.HasSpellInfo(entry, Difficulty.None) || flags.HasAnyFlag(DisableFlags.SpellDeprecatedSpell))) + if (!(Global.SpellMgr.HasSpellInfo(entry, Difficulty.None) || flags.HasFlag(DisableFlags.SpellDeprecatedSpell))) { Log.outError(LogFilter.Sql, "Spell entry {0} from `disables` doesn't exist in dbc, skipped.", entry); continue; @@ -85,7 +85,7 @@ namespace Game continue; } - if (flags.HasAnyFlag(DisableFlags.SpellMap)) + if (flags.HasFlag(DisableFlags.SpellMap)) { var array = new StringArray(params_0, ','); for (byte i = 0; i < array.Length;) @@ -95,7 +95,7 @@ namespace Game } } - if (flags.HasAnyFlag(DisableFlags.SpellArea)) + if (flags.HasFlag(DisableFlags.SpellArea)) { var array = new StringArray(params_1, ','); for (byte i = 0; i < array.Length;) @@ -111,43 +111,43 @@ namespace Game break; case DisableType.Map: case DisableType.LFGMap: + { + MapRecord mapEntry = CliDB.MapStorage.LookupByKey(entry); + if (mapEntry == null) { - MapRecord mapEntry = CliDB.MapStorage.LookupByKey(entry); - if (mapEntry == null) - { - Log.outError(LogFilter.Sql, "Map entry {0} from `disables` doesn't exist in dbc, skipped.", entry); - continue; - } - bool isFlagInvalid = false; - switch (mapEntry.InstanceType) - { - case MapTypes.Common: - if (flags != 0) - isFlagInvalid = true; - break; - case MapTypes.Instance: - case MapTypes.Raid: - if (flags.HasAnyFlag(DisableFlags.DungeonStatusHeroic) && Global.DB2Mgr.GetMapDifficultyData(entry, Difficulty.Heroic) == null) - flags -= DisableFlags.DungeonStatusHeroic; - if (flags.HasAnyFlag(DisableFlags.DungeonStatusHeroic10Man) && Global.DB2Mgr.GetMapDifficultyData(entry, Difficulty.Raid10HC) == null) - flags -= DisableFlags.DungeonStatusHeroic10Man; - if (flags.HasAnyFlag(DisableFlags.DungeonStatusHeroic25Man) && Global.DB2Mgr.GetMapDifficultyData(entry, Difficulty.Raid25HC) == null) - flags -= DisableFlags.DungeonStatusHeroic25Man; - if (flags == 0) - isFlagInvalid = true; - break; - case MapTypes.Battleground: - case MapTypes.Arena: - Log.outError(LogFilter.Sql, "Battlegroundmap {0} specified to be disabled in map case, skipped.", entry); - continue; - } - if (isFlagInvalid) - { - Log.outError(LogFilter.Sql, "Disable flags for map {0} are invalid, skipped.", entry); - continue; - } - break; + Log.outError(LogFilter.Sql, "Map entry {0} from `disables` doesn't exist in dbc, skipped.", entry); + continue; } + bool isFlagInvalid = false; + switch (mapEntry.InstanceType) + { + case MapTypes.Common: + if (flags != 0) + isFlagInvalid = true; + break; + case MapTypes.Instance: + case MapTypes.Raid: + if (flags.HasFlag(DisableFlags.DungeonStatusHeroic) && Global.DB2Mgr.GetMapDifficultyData(entry, Difficulty.Heroic) == null) + flags &= ~DisableFlags.DungeonStatusHeroic; + if (flags.HasFlag(DisableFlags.DungeonStatusHeroic10Man) && Global.DB2Mgr.GetMapDifficultyData(entry, Difficulty.Raid10HC) == null) + flags &= ~DisableFlags.DungeonStatusHeroic10Man; + if (flags.HasFlag(DisableFlags.DungeonStatusHeroic25Man) && Global.DB2Mgr.GetMapDifficultyData(entry, Difficulty.Raid25HC) == null) + flags &= ~DisableFlags.DungeonStatusHeroic25Man; + if (flags == 0) + isFlagInvalid = true; + break; + case MapTypes.Battleground: + case MapTypes.Arena: + Log.outError(LogFilter.Sql, "Battlegroundmap {0} specified to be disabled in map case, skipped.", entry); + continue; + } + if (isFlagInvalid) + { + Log.outError(LogFilter.Sql, "Disable flags for map {0} are invalid, skipped.", entry); + continue; + } + break; + } case DisableType.Battleground: if (!CliDB.BattlemasterListStorage.ContainsKey(entry)) { @@ -176,73 +176,73 @@ namespace Game Log.outError(LogFilter.Sql, "Disable flags specified for Criteria {0}, useless data.", entry); break; case DisableType.VMAP: + { + MapRecord mapEntry = CliDB.MapStorage.LookupByKey(entry); + if (mapEntry == null) { - MapRecord mapEntry = CliDB.MapStorage.LookupByKey(entry); - if (mapEntry == null) - { - Log.outError(LogFilter.Sql, "Map entry {0} from `disables` doesn't exist in dbc, skipped.", entry); - continue; - } - switch (mapEntry.InstanceType) - { - case MapTypes.Common: - if (flags.HasAnyFlag(DisableFlags.VmapAreaFlag)) - Log.outInfo(LogFilter.Server, "Areaflag disabled for world map {0}.", entry); - if (flags.HasAnyFlag(DisableFlags.VmapLiquidStatus)) - Log.outInfo(LogFilter.Server, "Liquid status disabled for world map {0}.", entry); - break; - case MapTypes.Instance: - case MapTypes.Raid: - if (flags.HasAnyFlag(DisableFlags.VmapHeight)) - Log.outInfo(LogFilter.Server, "Height disabled for instance map {0}.", entry); - if (flags.HasAnyFlag(DisableFlags.VmapLOS)) - Log.outInfo(LogFilter.Server, "LoS disabled for instance map {0}.", entry); - break; - case MapTypes.Battleground: - if (flags.HasAnyFlag(DisableFlags.VmapHeight)) - Log.outInfo(LogFilter.Server, "Height disabled for Battlegroundmap {0}.", entry); - if (flags.HasAnyFlag(DisableFlags.VmapLOS)) - Log.outInfo(LogFilter.Server, "LoS disabled for Battlegroundmap {0}.", entry); - break; - case MapTypes.Arena: - if (flags.HasAnyFlag(DisableFlags.VmapHeight)) - Log.outInfo(LogFilter.Server, "Height disabled for arena map {0}.", entry); - if (flags.HasAnyFlag(DisableFlags.VmapLOS)) - Log.outInfo(LogFilter.Server, "LoS disabled for arena map {0}.", entry); - break; - default: - break; - } - break; + Log.outError(LogFilter.Sql, "Map entry {0} from `disables` doesn't exist in dbc, skipped.", entry); + continue; } + switch (mapEntry.InstanceType) + { + case MapTypes.Common: + if (flags.HasFlag(DisableFlags.VmapAreaFlag)) + Log.outInfo(LogFilter.Server, "Areaflag disabled for world map {0}.", entry); + if (flags.HasFlag(DisableFlags.VmapLiquidStatus)) + Log.outInfo(LogFilter.Server, "Liquid status disabled for world map {0}.", entry); + break; + case MapTypes.Instance: + case MapTypes.Raid: + if (flags.HasFlag(DisableFlags.VmapHeight)) + Log.outInfo(LogFilter.Server, "Height disabled for instance map {0}.", entry); + if (flags.HasFlag(DisableFlags.VmapLOS)) + Log.outInfo(LogFilter.Server, "LoS disabled for instance map {0}.", entry); + break; + case MapTypes.Battleground: + if (flags.HasFlag(DisableFlags.VmapHeight)) + Log.outInfo(LogFilter.Server, "Height disabled for Battlegroundmap {0}.", entry); + if (flags.HasFlag(DisableFlags.VmapLOS)) + Log.outInfo(LogFilter.Server, "LoS disabled for Battlegroundmap {0}.", entry); + break; + case MapTypes.Arena: + if (flags.HasFlag(DisableFlags.VmapHeight)) + Log.outInfo(LogFilter.Server, "Height disabled for arena map {0}.", entry); + if (flags.HasFlag(DisableFlags.VmapLOS)) + Log.outInfo(LogFilter.Server, "LoS disabled for arena map {0}.", entry); + break; + default: + break; + } + break; + } case DisableType.MMAP: + { + MapRecord mapEntry = CliDB.MapStorage.LookupByKey(entry); + if (mapEntry == null) { - MapRecord mapEntry = CliDB.MapStorage.LookupByKey(entry); - if (mapEntry == null) - { - Log.outError(LogFilter.Sql, "Map entry {0} from `disables` doesn't exist in dbc, skipped.", entry); - continue; - } - switch (mapEntry.InstanceType) - { - case MapTypes.Common: - Log.outInfo(LogFilter.Server, "Pathfinding disabled for world map {0}.", entry); - break; - case MapTypes.Instance: - case MapTypes.Raid: - Log.outInfo(LogFilter.Server, "Pathfinding disabled for instance map {0}.", entry); - break; - case MapTypes.Battleground: - Log.outInfo(LogFilter.Server, "Pathfinding disabled for Battlegroundmap {0}.", entry); - break; - case MapTypes.Arena: - Log.outInfo(LogFilter.Server, "Pathfinding disabled for arena map {0}.", entry); - break; - default: - break; - } - break; + Log.outError(LogFilter.Sql, "Map entry {0} from `disables` doesn't exist in dbc, skipped.", entry); + continue; } + switch (mapEntry.InstanceType) + { + case MapTypes.Common: + Log.outInfo(LogFilter.Server, "Pathfinding disabled for world map {0}.", entry); + break; + case MapTypes.Instance: + case MapTypes.Raid: + Log.outInfo(LogFilter.Server, "Pathfinding disabled for instance map {0}.", entry); + break; + case MapTypes.Battleground: + Log.outInfo(LogFilter.Server, "Pathfinding disabled for Battlegroundmap {0}.", entry); + break; + case MapTypes.Arena: + Log.outInfo(LogFilter.Server, "Pathfinding disabled for arena map {0}.", entry); + break; + default: + break; + } + break; + } default: break; } @@ -284,7 +284,7 @@ namespace Game Log.outInfo(LogFilter.ServerLoading, "Checked {0} quest disables in {1} ms", m_DisableMap[DisableType.Quest].Count, Time.GetMSTimeDiffToNow(oldMSTime)); } - public bool IsDisabledFor(DisableType type, uint entry, Unit unit, byte flags = 0) + public bool IsDisabledFor(DisableType type, uint entry, WorldObject refe, byte flags = 0) { Cypher.Assert(type < DisableType.Max); if (!m_DisableMap.ContainsKey(type) || m_DisableMap[type].Empty()) @@ -297,66 +297,67 @@ namespace Game switch (type) { case DisableType.Spell: + { + DisableFlags spellFlags = (DisableFlags)data.flags; + if (refe != null) { - byte spellFlags = data.flags; - if (unit != null) + if ((refe.IsPlayer() && spellFlags.HasFlag(DisableFlags.SpellPlayer)) || + (refe.IsCreature() && (spellFlags.HasFlag(DisableFlags.SpellCreature) || (refe.ToUnit().IsPet() && spellFlags.HasFlag(DisableFlags.SpellPet)))) || + (refe.IsGameObject() && spellFlags.HasFlag(DisableFlags.SpellGameobject))) { - if ((spellFlags.HasAnyFlag(DisableFlags.SpellPlayer) && unit.IsTypeId(TypeId.Player)) || - (unit.IsTypeId(TypeId.Unit) && ((unit.IsPet() && spellFlags.HasAnyFlag(DisableFlags.SpellPet)) || spellFlags.HasAnyFlag(DisableFlags.SpellCreature)))) + if (spellFlags.HasFlag(DisableFlags.SpellMap)) { - if (spellFlags.HasAnyFlag(DisableFlags.SpellMap)) - { - List mapIds = data.param0; - if (mapIds.Contains(unit.GetMapId())) - return true; // Spell is disabled on current map + List mapIds = data.param0; + if (mapIds.Contains(refe.GetMapId())) + return true; // Spell is disabled on current map - if (!spellFlags.HasAnyFlag(DisableFlags.SpellArea)) - return false; // Spell is disabled on another map, but not this one, return false + if (!spellFlags.HasFlag(DisableFlags.SpellArea)) + return false; // Spell is disabled on another map, but not this one, return false - // Spell is disabled in an area, but not explicitly our current mapId. Continue processing. - } - - if (spellFlags.HasAnyFlag(DisableFlags.SpellArea)) - { - var areaIds = data.param1; - if (areaIds.Contains(unit.GetAreaId())) - return true; // Spell is disabled in this area - return false; // Spell is disabled in another area, but not this one, return false - } - else - return true; // Spell disabled for all maps + // Spell is disabled in an area, but not explicitly our current mapId. Continue processing. } - return false; + if (spellFlags.HasFlag(DisableFlags.SpellArea)) + { + var areaIds = data.param1; + if (areaIds.Contains(refe.GetAreaId())) + return true; // Spell is disabled in this area + return false; // Spell is disabled in another area, but not this one, return false + } + else + return true; // Spell disabled for all maps } - else if (spellFlags.HasAnyFlag(DisableFlags.SpellDeprecatedSpell)) // call not from spellcast - return true; - else if (flags.HasAnyFlag(DisableFlags.SpellLOS)) - return spellFlags.HasAnyFlag(DisableFlags.SpellLOS); - break; + return false; } + else if (spellFlags.HasFlag(DisableFlags.SpellDeprecatedSpell)) // call not from spellcast + return true; + else if (flags.HasAnyFlag((byte)DisableFlags.SpellLOS)) + return spellFlags.HasFlag(DisableFlags.SpellLOS); + + break; + } case DisableType.Map: case DisableType.LFGMap: - Player player = unit.ToPlayer(); + Player player = refe.ToPlayer(); if (player != null) { MapRecord mapEntry = CliDB.MapStorage.LookupByKey(entry); if (mapEntry.IsDungeon()) { - byte disabledModes = data.flags; + DisableFlags disabledModes = (DisableFlags)data.flags; Difficulty targetDifficulty = player.GetDifficultyID(mapEntry); Global.DB2Mgr.GetDownscaledMapDifficultyData(entry, ref targetDifficulty); switch (targetDifficulty) { case Difficulty.Normal: - return disabledModes.HasAnyFlag(DisableFlags.DungeonStatusNormal); + return disabledModes.HasFlag(DisableFlags.DungeonStatusNormal); case Difficulty.Heroic: - return disabledModes.HasAnyFlag(DisableFlags.DungeonStatusHeroic); + return disabledModes.HasFlag(DisableFlags.DungeonStatusHeroic); case Difficulty.Raid10HC: - return disabledModes.HasAnyFlag(DisableFlags.DungeonStatusHeroic10Man); + return disabledModes.HasFlag(DisableFlags.DungeonStatusHeroic10Man); case Difficulty.Raid25HC: - return disabledModes.HasAnyFlag(DisableFlags.DungeonStatusHeroic25Man); + return disabledModes.HasFlag(DisableFlags.DungeonStatusHeroic25Man); default: return false; } @@ -366,9 +367,9 @@ namespace Game } return false; case DisableType.Quest: - if (unit == null) + if (refe == null) return true; - Player player1 = unit.ToPlayer(); + Player player1 = refe.ToPlayer(); if (player1 != null) if (player1.IsGameMaster()) return false; @@ -401,7 +402,7 @@ namespace Game Spell = 0, Quest = 1, Map = 2, - Battleground= 3, + Battleground = 3, Criteria = 4, OutdoorPVP = 5, VMAP = 6, @@ -410,31 +411,32 @@ namespace Game Max = 9 } - public struct DisableFlags + [Flags] + public enum DisableFlags { - public const byte SpellPlayer = 0x1; - public const byte SpellCreature = 0x2; - public const byte SpellPet = 0x4; - public const byte SpellDeprecatedSpell = 0x8; - public const byte SpellMap = 0x10; - public const byte SpellArea = 0x20; - public const byte SpellLOS = 0x40; - public const byte MaxSpell = (SpellPlayer | SpellCreature | SpellPet | SpellDeprecatedSpell | SpellMap | SpellArea | SpellLOS); + SpellPlayer = 0x01, + SpellCreature = 0x02, + SpellPet = 0x04, + SpellDeprecatedSpell = 0x08, + SpellMap = 0x10, + SpellArea = 0x20, + SpellLOS = 0x40, + SpellGameobject = 0x80, + MaxSpell = SpellPlayer | SpellCreature | SpellPet | SpellDeprecatedSpell | SpellMap | SpellArea | SpellLOS | SpellGameobject, - public const byte VmapAreaFlag = 0x1; - public const byte VmapHeight = 0x2; - public const byte VmapLOS = 0x4; - public const byte VmapLiquidStatus = 0x8; + VmapAreaFlag = 0x01, + VmapHeight = 0x02, + VmapLOS = 0x04, + VmapLiquidStatus = 0x08, - public const byte MMapPathFinding = 0x0; + MMapPathFinding = 0x00, - public const byte DungeonStatusNormal = 0x01; - public const byte DungeonStatusHeroic = 0x02; - - public const byte DungeonStatusNormal10Man = 0x01; - public const byte DungeonStatusNormal25Man = 0x02; - public const byte DungeonStatusHeroic10Man = 0x04; - public const byte DungeonStatusHeroic25Man = 0x08; + DungeonStatusNormal = 0x01, + DungeonStatusHeroic = 0x02, + DungeonStatusNormal10Man = 0x01, + DungeonStatusNormal25Man = 0x02, + DungeonStatusHeroic10Man = 0x04, + DungeonStatusHeroic25Man = 0x08 } -} +} \ No newline at end of file diff --git a/Source/Game/Entities/AreaTrigger/AreaTrigger.cs b/Source/Game/Entities/AreaTrigger/AreaTrigger.cs index 3c76d574f..e603c5a8e 100644 --- a/Source/Game/Entities/AreaTrigger/AreaTrigger.cs +++ b/Source/Game/Entities/AreaTrigger/AreaTrigger.cs @@ -491,6 +491,15 @@ namespace Game.Entities return Global.ObjAccessor.GetUnit(this, _targetGuid); } + public override uint GetFaction() + { + Unit caster = GetCaster(); + if (caster) + return caster.GetFaction(); + + return 0; + } + void UpdatePolygonOrientation() { float newOrientation = GetOrientation(); @@ -1002,6 +1011,7 @@ namespace Game.Entities public AreaTriggerMiscTemplate GetMiscTemplate() { return _areaTriggerMiscTemplate; } + public override ObjectGuid GetOwnerGUID() { return GetCasterGuid(); } public ObjectGuid GetCasterGuid() { return m_areaTriggerData.Caster; } public Vector3 GetRollPitchYaw() { return _rollPitchYaw; } diff --git a/Source/Game/Entities/Conversation.cs b/Source/Game/Entities/Conversation.cs index 0030a14ad..c93201d70 100644 --- a/Source/Game/Entities/Conversation.cs +++ b/Source/Game/Entities/Conversation.cs @@ -288,7 +288,9 @@ namespace Game.Entities public uint GetTextureKitId() { return _textureKitId; } public ObjectGuid GetCreatorGuid() { return _creatorGuid; } - + public override ObjectGuid GetOwnerGUID() { return GetCreatorGuid(); } + public override uint GetFaction() { return 0; } + public override float GetStationaryX() { return _stationaryPosition.GetPositionX(); } public override float GetStationaryY() { return _stationaryPosition.GetPositionY(); } public override float GetStationaryZ() { return _stationaryPosition.GetPositionZ(); } diff --git a/Source/Game/Entities/Corpse.cs b/Source/Game/Entities/Corpse.cs index 8ca5d39bb..f52023e62 100644 --- a/Source/Game/Entities/Corpse.cs +++ b/Source/Game/Entities/Corpse.cs @@ -287,7 +287,7 @@ namespace Game.Entities public void AddCorpseDynamicFlag(CorpseDynFlags dynamicFlags) { SetUpdateFieldFlagValue(m_values.ModifyValue(m_corpseData).ModifyValue(m_corpseData.DynamicFlags), (uint)dynamicFlags); } public void RemoveCorpseDynamicFlag(CorpseDynFlags dynamicFlags) { RemoveUpdateFieldFlagValue(m_values.ModifyValue(m_corpseData).ModifyValue(m_corpseData.DynamicFlags), (uint)dynamicFlags); } public void SetCorpseDynamicFlags(CorpseDynFlags dynamicFlags) { SetUpdateFieldValue(m_values.ModifyValue(m_corpseData).ModifyValue(m_corpseData.DynamicFlags), (uint)dynamicFlags); } - public ObjectGuid GetOwnerGUID() { return m_corpseData.Owner; } + public override ObjectGuid GetOwnerGUID() { return m_corpseData.Owner; } public void SetOwnerGUID(ObjectGuid owner) { SetUpdateFieldValue(m_values.ModifyValue(m_corpseData).ModifyValue(m_corpseData.Owner), owner); } public void SetPartyGUID(ObjectGuid partyGuid) { SetUpdateFieldValue(m_values.ModifyValue(m_corpseData).ModifyValue(m_corpseData.PartyGUID), partyGuid); } public void SetGuildGUID(ObjectGuid guildGuid) { SetUpdateFieldValue(m_values.ModifyValue(m_corpseData).ModifyValue(m_corpseData.GuildGUID), guildGuid); } @@ -297,6 +297,8 @@ namespace Game.Entities public void SetSex(byte sex) { SetUpdateFieldValue(m_values.ModifyValue(m_corpseData).ModifyValue(m_corpseData.Sex), sex); } public void SetFlags(CorpseFlags flags) { SetUpdateFieldValue(m_values.ModifyValue(m_corpseData).ModifyValue(m_corpseData.Flags), (uint)flags); } public void SetFactionTemplate(int factionTemplate) { SetUpdateFieldValue(m_values.ModifyValue(m_corpseData).ModifyValue(m_corpseData.FactionTemplate), factionTemplate); } + public override uint GetFaction() { return (uint)(int)m_corpseData.FactionTemplate; } + public override void SetFaction(uint faction) { SetFactionTemplate((int)faction); } public void SetItem(uint slot, uint item) { SetUpdateFieldValue(ref m_values.ModifyValue(m_corpseData).ModifyValue(m_corpseData.Items, (int)slot), item); } public void SetCustomizations(List customizations) diff --git a/Source/Game/Entities/Creature/Creature.cs b/Source/Game/Entities/Creature/Creature.cs index 55eaad79a..a77e3ea57 100644 --- a/Source/Game/Entities/Creature/Creature.cs +++ b/Source/Game/Entities/Creature/Creature.cs @@ -2039,7 +2039,7 @@ namespace Game.Entities ApplySpellImmune(placeholderSpellId, SpellImmunity.School, 1u << i, true); } - public override bool IsImmunedToSpell(SpellInfo spellInfo, Unit caster) + public override bool IsImmunedToSpell(SpellInfo spellInfo, WorldObject caster) { if (spellInfo == null) return false; @@ -2063,7 +2063,7 @@ namespace Game.Entities return base.IsImmunedToSpell(spellInfo, caster); } - public override bool IsImmunedToSpellEffect(SpellInfo spellInfo, uint index, Unit caster) + public override bool IsImmunedToSpellEffect(SpellInfo spellInfo, uint index, WorldObject caster) { SpellEffectInfo effect = spellInfo.GetEffect(index); if (effect == null) diff --git a/Source/Game/Entities/DynamicObject.cs b/Source/Game/Entities/DynamicObject.cs index a6dc42185..dd3a5b36e 100644 --- a/Source/Game/Entities/DynamicObject.cs +++ b/Source/Game/Entities/DynamicObject.cs @@ -226,6 +226,12 @@ namespace Game.Entities } } + public override uint GetFaction() + { + Cypher.Assert(_caster != null); + return _caster.GetFaction(); + } + void BindToCaster() { Cypher.Assert(_caster == null); @@ -312,6 +318,7 @@ namespace Game.Entities public Unit GetCaster() { return _caster; } public uint GetSpellId() { return m_dynamicObjectData.SpellID; } public ObjectGuid GetCasterGUID() { return m_dynamicObjectData.Caster; } + public override ObjectGuid GetOwnerGUID() { return GetCasterGUID(); } public float GetRadius() { return m_dynamicObjectData.Radius; } DynamicObjectData m_dynamicObjectData; diff --git a/Source/Game/Entities/GameObject/GameObject.cs b/Source/Game/Entities/GameObject/GameObject.cs index de0de6fd7..0d96d6dfe 100644 --- a/Source/Game/Entities/GameObject/GameObject.cs +++ b/Source/Game/Entities/GameObject/GameObject.cs @@ -423,6 +423,8 @@ namespace Game.Entities public override void Update(uint diff) { + m_Events.Update(diff); + if (GetAI() != null) GetAI().UpdateAI(diff); else if (!AIM_Initialize()) @@ -730,8 +732,9 @@ namespace Game.Entities else if (target) { // Some traps do not have a spell but should be triggered + CastSpellExtraArgs args = new(GetOwnerGUID()); if (goInfo.Trap.spell != 0) - CastSpell(target, goInfo.Trap.spell); + CastSpell(target, goInfo.Trap.spell, args); // Template value or 4 seconds m_cooldownTime = (GameTime.GetGameTimeMS() + (goInfo.Trap.cooldown != 0 ? goInfo.Trap.cooldown : 4u)) * Time.InMilliseconds; @@ -1202,11 +1205,6 @@ namespace Game.Entities public Transport ToTransport() { return GetGoInfo().type == GameObjectTypes.MapObjTransport ? (this as Transport) : null; } - public Unit GetOwner() - { - return Global.ObjAccessor.GetUnit(this, GetOwnerGUID()); - } - public override void SaveRespawnTime(uint forceDelay = 0, bool savetodb = true) { if (m_goData != null && (forceDelay != 0 || m_respawnTime > GameTime.GetGameTime()) && m_spawnedByDefault) @@ -2089,69 +2087,6 @@ namespace Game.Entities CastSpell(user, spellId); } - public void CastSpell(Unit target, uint spellId, bool triggered = true) - { - CastSpell(target, spellId, triggered ? TriggerCastFlags.FullMask : TriggerCastFlags.None); - } - - public void CastSpell(Unit target, uint spellId, TriggerCastFlags triggered) - { - SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo(spellId, GetMap().GetDifficultyID()); - if (spellInfo == null) - return; - - bool self = false; - foreach (SpellEffectInfo effect in spellInfo.GetEffects()) - { - if (effect != null && effect.TargetA.GetTarget() == Targets.UnitCaster) - { - self = true; - break; - } - } - - if (self) - { - if (target != null) - target.CastSpell(target, spellInfo.Id, new CastSpellExtraArgs(triggered)); - return; - } - - //summon world trigger - Creature trigger = SummonTrigger(GetPositionX(), GetPositionY(), GetPositionZ(), 0, (uint)(spellInfo.CalcCastTime() + 100)); - if (!trigger) - return; - - // remove immunity flags, to allow spell to target anything - trigger.SetImmuneToAll(false); - - PhasingHandler.InheritPhaseShift(trigger, this); - - CastSpellExtraArgs args = new(triggered); - Unit owner = GetOwner(); - if (owner) - { - trigger.SetFaction(owner.GetFaction()); - if (owner.HasUnitFlag(UnitFlags.PvpAttackable)) - trigger.AddUnitFlag(UnitFlags.PvpAttackable); - // copy pvp state flags from owner - trigger.SetPvpFlags(owner.GetPvpFlags()); - // needed for GO casts for proper target validation checks - trigger.SetOwnerGUID(owner.GetGUID()); - - args.OriginalCaster = owner.GetGUID(); - trigger.CastSpell(target ?? trigger, spellInfo.Id, args); - } - else - { - trigger.SetFaction(spellInfo.IsPositive() ? 35 : 14u); - // Set owner guid for target if no owner available - needed by trigger auras - // - trigger gets despawned and there's no caster avalible (see AuraEffect.TriggerSpell()) - args.OriginalCaster = target ? target.GetGUID() : ObjectGuid.Empty; - trigger.CastSpell(target ?? trigger, spellInfo.Id, args); - } - } - public void SendCustomAnim(uint anim) { GameObjectCustomAnim customAnim = new(); @@ -2266,7 +2201,7 @@ namespace Game.Entities SetWorldRotation(quat.X, quat.Y, quat.Z, quat.W); } - public void ModifyHealth(int change, Unit attackerOrHealer = null, uint spellId = 0) + public void ModifyHealth(int change, WorldObject attackerOrHealer = null, uint spellId = 0) { if (m_goValue.Building.MaxHealth == 0 || change == 0) return; @@ -2285,9 +2220,8 @@ namespace Game.Entities // Set the health bar, value = 255 * healthPct; SetGoAnimProgress(m_goValue.Building.Health * 255 / m_goValue.Building.MaxHealth); - Player player = attackerOrHealer.GetCharmerOrOwnerPlayerOrPlayerItself(); - // dealing damage, send packet + Player player = attackerOrHealer != null ? attackerOrHealer.GetCharmerOrOwnerPlayerOrPlayerItself() : null; if (player != null) { DestructibleBuildingDamage packet = new(); @@ -2311,10 +2245,10 @@ namespace Game.Entities if (newState == GetDestructibleState()) return; - SetDestructibleState(newState, player, false); + SetDestructibleState(newState, attackerOrHealer, false); } - public void SetDestructibleState(GameObjectDestructibleState state, Player eventInvoker = null, bool setHealth = false) + public void SetDestructibleState(GameObjectDestructibleState state, WorldObject attackerOrHealer = null, bool setHealth = false) { // the user calling this must know he is already operating on destructible gameobject Cypher.Assert(GetGoType() == GameObjectTypes.DestructibleBuilding); @@ -2333,8 +2267,8 @@ namespace Game.Entities break; case GameObjectDestructibleState.Damaged: { - EventInform(m_goInfo.DestructibleBuilding.DamagedEvent, eventInvoker); - GetAI().Damaged(eventInvoker, m_goInfo.DestructibleBuilding.DamagedEvent); + EventInform(m_goInfo.DestructibleBuilding.DamagedEvent, attackerOrHealer); + GetAI().Damaged(attackerOrHealer, m_goInfo.DestructibleBuilding.DamagedEvent); RemoveFlag(GameObjectFlags.Destroyed); AddFlag(GameObjectFlags.Damaged); @@ -2359,13 +2293,14 @@ namespace Game.Entities } case GameObjectDestructibleState.Destroyed: { - EventInform(m_goInfo.DestructibleBuilding.DestroyedEvent, eventInvoker); - GetAI().Destroyed(eventInvoker, m_goInfo.DestructibleBuilding.DestroyedEvent); - if (eventInvoker != null) + EventInform(m_goInfo.DestructibleBuilding.DestroyedEvent, attackerOrHealer); + GetAI().Destroyed(attackerOrHealer, m_goInfo.DestructibleBuilding.DestroyedEvent); + + if (attackerOrHealer != null && attackerOrHealer.IsPlayer()) { - Battleground bg = eventInvoker.GetBattleground(); - if (bg) - bg.DestroyGate(eventInvoker, this); + var bg = attackerOrHealer.ToPlayer().GetBattleground(); + if (bg != null) + bg.DestroyGate(attackerOrHealer.ToPlayer(), this); } RemoveFlag(GameObjectFlags.Damaged); @@ -2388,7 +2323,7 @@ namespace Game.Entities } case GameObjectDestructibleState.Rebuilding: { - EventInform(m_goInfo.DestructibleBuilding.RebuildingEvent, eventInvoker); + EventInform(m_goInfo.DestructibleBuilding.RebuildingEvent, attackerOrHealer); RemoveFlag(GameObjectFlags.Damaged | GameObjectFlags.Destroyed); uint modelId = m_goInfo.displayId; @@ -2769,7 +2704,7 @@ namespace Game.Entities m_spawnedByDefault = false; // all object with owner is despawned after delay SetUpdateFieldValue(m_values.ModifyValue(m_gameObjectData).ModifyValue(m_gameObjectData.CreatedBy), owner); } - public ObjectGuid GetOwnerGUID() { return m_gameObjectData.CreatedBy; } + public override ObjectGuid GetOwnerGUID() { return m_gameObjectData.CreatedBy; } public void SetSpellId(uint id) { @@ -2871,8 +2806,8 @@ namespace Game.Entities public uint GetDisplayId() { return m_gameObjectData.DisplayID; } - public uint GetFaction() { return m_gameObjectData.FactionTemplate; } - public void SetFaction(uint faction) { SetUpdateFieldValue(m_values.ModifyValue(m_gameObjectData).ModifyValue(m_gameObjectData.FactionTemplate), faction); } + public override uint GetFaction() { return m_gameObjectData.FactionTemplate; } + public override void SetFaction(uint faction) { SetUpdateFieldValue(m_values.ModifyValue(m_gameObjectData).ModifyValue(m_gameObjectData.FactionTemplate), faction); } public override float GetStationaryX() { return StationaryPosition.GetPositionX(); } public override float GetStationaryY() { return StationaryPosition.GetPositionY(); } diff --git a/Source/Game/Entities/Object/WorldObject.cs b/Source/Game/Entities/Object/WorldObject.cs index 7c11f3da1..65aec8fef 100644 --- a/Source/Game/Entities/Object/WorldObject.cs +++ b/Source/Game/Entities/Object/WorldObject.cs @@ -27,6 +27,7 @@ using Game.Scenarios; using System; using System.Collections.Generic; using Game.DataStorage; +using Game.Spells; namespace Game.Entities { @@ -573,7 +574,7 @@ namespace Game.Entities } //if (hasMovementScript) - // *data << *areaTrigger->GetMovementScript(); // AreaTriggerMovementScriptInfo + // *data << *areaTrigger.GetMovementScript(); // AreaTriggerMovementScriptInfo if (hasOrbit) areaTrigger.GetCircularMovementInfo().Value.Write(data); @@ -647,9 +648,9 @@ namespace Game.Entities // *data << uint16(Players[i].Pets[j].StatusFlags); // *data << int8(Players[i].Pets[j].Slot); - // *data << uint32(Players[i].Pets[j].Abilities.size()); - // *data << uint32(Players[i].Pets[j].Auras.size()); - // *data << uint32(Players[i].Pets[j].States.size()); + // *data << uint(Players[i].Pets[j].Abilities.size()); + // *data << uint(Players[i].Pets[j].Auras.size()); + // *data << uint(Players[i].Pets[j].States.size()); // for (std::size_t k = 0; k < Players[i].Pets[j].Abilities.size(); ++k) // { // *data << int32(Players[i].Pets[j].Abilities[k].AbilityID); @@ -662,7 +663,7 @@ namespace Game.Entities // for (std::size_t k = 0; k < Players[i].Pets[j].Auras.size(); ++k) // { // *data << int32(Players[i].Pets[j].Auras[k].AbilityID); - // *data << uint32(Players[i].Pets[j].Auras[k].InstanceID); + // *data << uint(Players[i].Pets[j].Auras[k].InstanceID); // *data << int32(Players[i].Pets[j].Auras[k].RoundsRemaining); // *data << int32(Players[i].Pets[j].Auras[k].CurrentRound); // *data << uint8(Players[i].Pets[j].Auras[k].CasterPBOID); @@ -670,7 +671,7 @@ namespace Game.Entities // for (std::size_t k = 0; k < Players[i].Pets[j].States.size(); ++k) // { - // *data << uint32(Players[i].Pets[j].States[k].StateID); + // *data << uint(Players[i].Pets[j].States[k].StateID); // *data << int32(Players[i].Pets[j].States[k].StateValue); // } @@ -682,12 +683,12 @@ namespace Game.Entities // for (std::size_t i = 0; i < 3; ++i) // { - // *data << uint32(Enviros[j].Auras.size()); - // *data << uint32(Enviros[j].States.size()); + // *data << uint(Enviros[j].Auras.size()); + // *data << uint(Enviros[j].States.size()); // for (std::size_t j = 0; j < Enviros[j].Auras.size(); ++j) // { // *data << int32(Enviros[j].Auras[j].AbilityID); - // *data << uint32(Enviros[j].Auras[j].InstanceID); + // *data << uint(Enviros[j].Auras[j].InstanceID); // *data << int32(Enviros[j].Auras[j].RoundsRemaining); // *data << int32(Enviros[j].Auras[j].CurrentRound); // *data << uint8(Enviros[j].Auras[j].CasterPBOID); @@ -695,7 +696,7 @@ namespace Game.Entities // for (std::size_t j = 0; j < Enviros[j].States.size(); ++j) // { - // *data << uint32(Enviros[i].States[j].StateID); + // *data << uint(Enviros[i].States[j].StateID); // *data << int32(Enviros[i].States[j].StateValue); // } // } @@ -703,8 +704,8 @@ namespace Game.Entities // *data << uint16(WaitingForFrontPetsMaxSecs); // *data << uint16(PvpMaxRoundTime); // *data << int32(CurRound); - // *data << uint32(NpcCreatureID); - // *data << uint32(NpcDisplayID); + // *data << uint(NpcCreatureID); + // *data << uint(NpcDisplayID); // *data << int8(CurPetBattleState); // *data << uint8(ForfeitPenalty); // *data << ObjectGuid(InitialWildPetGUID); @@ -724,9 +725,9 @@ namespace Game.Entities data.FlushBits(); //if (HasSceneInstanceIDs) //{ - // *data << uint32(SceneInstanceIDs.size()); + // *data << uint(SceneInstanceIDs.size()); // for (std::size_t i = 0; i < SceneInstanceIDs.size(); ++i) - // *data << uint32(SceneInstanceIDs[i]); + // *data << uint(SceneInstanceIDs[i]); //} if (HasRuneState) { @@ -1028,7 +1029,7 @@ namespace Game.Entities public void GetZoneAndAreaId(out uint zoneid, out uint areaid) { zoneid = m_zoneId; areaid = m_areaId; } public bool IsOutdoors() { return m_outdoors; } - + public bool IsInWorldPvpZone() { switch (GetZoneId()) @@ -1369,6 +1370,18 @@ namespace Game.Entities Cell.VisitWorldObjects(this, notifier, GetVisibilityRange()); } + public void SendCombatLogMessage(CombatLogServerPacket combatLog) + { + CombatLogSender combatLogSender = new(combatLog); + + Player self = ToPlayer(); + if (self != null) + combatLogSender.Invoke(self); + + MessageDistDeliverer notifier = new(this, combatLogSender, GetVisibilityRange()); + Cell.VisitWorldObjects(this, notifier, GetVisibilityRange()); + } + public virtual void SetMap(Map map) { Cypher.Assert(map != null); @@ -1585,6 +1598,1006 @@ namespace Game.Entities return searcher.GetTarget(); } + public ObjectGuid GetCharmerOrOwnerOrOwnGUID() + { + ObjectGuid guid = GetCharmerOrOwnerGUID(); + if (!guid.IsEmpty()) + return guid; + return GetGUID(); + } + + public virtual Unit GetOwner() + { + return Global.ObjAccessor.GetUnit(this, GetOwnerGUID()); + } + + public Unit GetCharmerOrOwner() + { + Unit unit = ToUnit(); + if (unit != null) + return unit.GetCharmerOrOwner(); + else + { + GameObject go = ToGameObject(); + if (go != null) + return go.GetOwner(); + } + + return null; + } + + public Unit GetCharmerOrOwnerOrSelf() + { + Unit u = GetCharmerOrOwner(); + if (u != null) + return u; + + return ToUnit(); + } + + public Player GetCharmerOrOwnerPlayerOrPlayerItself() + { + ObjectGuid guid = GetCharmerOrOwnerGUID(); + if (guid.IsPlayer()) + return Global.ObjAccessor.GetPlayer(this, guid); + + return ToPlayer(); + } + + public Player GetAffectingPlayer() + { + if (GetCharmerOrOwnerGUID().IsEmpty()) + return ToPlayer(); + + Unit owner = GetCharmerOrOwner(); + if (owner != null) + return owner.GetCharmerOrOwnerPlayerOrPlayerItself(); + + return null; + } + + public Player GetSpellModOwner() + { + Player player = ToPlayer(); + if (player != null) + return player; + + if (IsCreature()) + { + Creature creature = ToCreature(); + if (creature.IsPet() || creature.IsTotem()) + { + Unit owner = creature.GetOwner(); + if (owner != null) + return owner.ToPlayer(); + } + } + else if (IsGameObject()) + { + GameObject go = ToGameObject(); + Unit owner = go.GetOwner(); + if (owner != null) + return owner.ToPlayer(); + } + + return null; + } + public int CalculateSpellDamage(Unit target, SpellInfo spellProto, uint effIndex, int? basePoints = null, uint castItemId = 0, int itemLevel = -1) + { + return CalculateSpellDamage(out _, target, spellProto, effIndex, basePoints, castItemId, itemLevel); + } + + // function uses real base points (typically value - 1) + public int CalculateSpellDamage(out float variance, Unit target, SpellInfo spellProto, uint effIndex, int? basePoints = null, uint castItemId = 0, int itemLevel = -1) + { + SpellEffectInfo effect = spellProto.GetEffect(effIndex); + variance = 0.0f; + + return effect != null ? effect.CalcValue(out variance, this, basePoints, target, castItemId, itemLevel) : 0; + } + + public float GetSpellMaxRangeForTarget(Unit target, SpellInfo spellInfo) + { + if (spellInfo.RangeEntry == null) + return 0.0f; + + if (spellInfo.RangeEntry.RangeMax[0] == spellInfo.RangeEntry.RangeMax[1]) + return spellInfo.GetMaxRange(); + + if (!target) + return spellInfo.GetMaxRange(true); + + return spellInfo.GetMaxRange(!IsHostileTo(target)); + } + + public float GetSpellMinRangeForTarget(Unit target, SpellInfo spellInfo) + { + if (spellInfo.RangeEntry == null) + return 0.0f; + + if (spellInfo.RangeEntry.RangeMin[0] == spellInfo.RangeEntry.RangeMin[1]) + return spellInfo.GetMinRange(); + + if (!target) + return spellInfo.GetMinRange(true); + + return spellInfo.GetMinRange(!IsHostileTo(target)); + } + + public float ApplyEffectModifiers(SpellInfo spellInfo, uint effIndex, float value) + { + Player modOwner = GetSpellModOwner(); + if (modOwner != null) + { + modOwner.ApplySpellMod(spellInfo, SpellModOp.Points, ref value); + switch (effIndex) + { + case 0: + modOwner.ApplySpellMod(spellInfo, SpellModOp.PointsIndex0, ref value); + break; + case 1: + modOwner.ApplySpellMod(spellInfo, SpellModOp.PointsIndex1, ref value); + break; + case 2: + modOwner.ApplySpellMod(spellInfo, SpellModOp.PointsIndex2, ref value); + break; + case 3: + modOwner.ApplySpellMod(spellInfo, SpellModOp.PointsIndex3, ref value); + break; + case 4: + modOwner.ApplySpellMod(spellInfo, SpellModOp.PointsIndex4, ref value); + break; + } + } + return value; + } + + public int CalcSpellDuration(SpellInfo spellInfo) + { + int comboPoints = 0; + Unit unit = ToUnit(); + if (unit != null) + comboPoints = unit.GetPower(PowerType.ComboPoints); + + int minduration = spellInfo.GetDuration(); + int maxduration = spellInfo.GetMaxDuration(); + + int duration; + if (comboPoints != 0 && minduration != -1 && minduration != maxduration) + duration = minduration + ((maxduration - minduration) * comboPoints / 5); + else + duration = minduration; + + return duration; + } + + public int ModSpellDuration(SpellInfo spellInfo, WorldObject target, int duration, bool positive, uint effectMask) + { + // don't mod permanent auras duration + if (duration < 0) + return duration; + + // some auras are not affected by duration modifiers + if (spellInfo.HasAttribute(SpellAttr7.IgnoreDurationMods)) + return duration; + + // cut duration only of negative effects + Unit unitTarget = target.ToUnit(); + if (!unitTarget) + return duration; + + if (!positive) + { + uint mechanicMask = spellInfo.GetSpellMechanicMaskByEffectMask(effectMask); + bool mechanicCheck(AuraEffect aurEff) + { + if ((mechanicMask & (1 << aurEff.GetMiscValue())) != 0) + return true; + return false; + } + + // Find total mod value (negative bonus) + int durationMod_always = unitTarget.GetTotalAuraModifier(AuraType.MechanicDurationMod, mechanicCheck); + // Find max mod (negative bonus) + int durationMod_not_stack = unitTarget.GetMaxNegativeAuraModifier(AuraType.MechanicDurationModNotStack, mechanicCheck); + + // Select strongest negative mod + int durationMod = Math.Min(durationMod_always, durationMod_not_stack); + if (durationMod != 0) + MathFunctions.AddPct(ref duration, durationMod); + + // there are only negative mods currently + durationMod_always = unitTarget.GetTotalAuraModifierByMiscValue(AuraType.ModAuraDurationByDispel, (int)spellInfo.Dispel); + durationMod_not_stack = unitTarget.GetMaxNegativeAuraModifierByMiscValue(AuraType.ModAuraDurationByDispelNotStack, (int)spellInfo.Dispel); + + durationMod = Math.Min(durationMod_always, durationMod_not_stack); + if (durationMod != 0) + MathFunctions.AddPct(ref duration, durationMod); + } + else + { + // else positive mods here, there are no currently + // when there will be, change GetTotalAuraModifierByMiscValue to GetMaxPositiveAuraModifierByMiscValue + + // Mixology - duration boost + if (unitTarget.IsPlayer()) + { + if (spellInfo.SpellFamilyName == SpellFamilyNames.Potion && ( + Global.SpellMgr.IsSpellMemberOfSpellGroup(spellInfo.Id, SpellGroup.ElixirBattle) || + Global.SpellMgr.IsSpellMemberOfSpellGroup(spellInfo.Id, SpellGroup.ElixirGuardian))) + { + SpellEffectInfo effect = spellInfo.GetEffect(0); + if (unitTarget.HasAura(53042) && effect != null && unitTarget.HasSpell(effect.TriggerSpell)) + duration *= 2; + } + } + } + + return Math.Max(duration, 0); + } + + public void ModSpellCastTime(SpellInfo spellInfo, ref int castTime, Spell spell = null) + { + if (spellInfo == null || castTime < 0) + return; + + // called from caster + Player modOwner = GetSpellModOwner(); + if (modOwner != null) + modOwner.ApplySpellMod(spellInfo, SpellModOp.ChangeCastTime, ref castTime, spell); + + Unit unitCaster = ToUnit(); + if (!unitCaster) + return; + + if (!(spellInfo.HasAttribute(SpellAttr0.Ability) || spellInfo.HasAttribute(SpellAttr0.Tradespell) || spellInfo.HasAttribute(SpellAttr3.NoDoneBonus)) && + ((IsPlayer() && spellInfo.SpellFamilyName != 0) || IsCreature())) + castTime = unitCaster.CanInstantCast() ? 0 : (int)(castTime * unitCaster.m_unitData.ModCastingSpeed); + else if (spellInfo.HasAttribute(SpellAttr0.ReqAmmo) && !spellInfo.HasAttribute(SpellAttr2.AutorepeatFlag)) + castTime = (int)(castTime * unitCaster.m_modAttackSpeedPct[(int)WeaponAttackType.RangedAttack]); + else if (Global.SpellMgr.IsPartOfSkillLine(SkillType.Cooking, spellInfo.Id) && unitCaster.HasAura(67556)) // cooking with Chef Hat. + castTime = 500; + } + + public void ModSpellDurationTime(SpellInfo spellInfo, ref int duration, Spell spell = null) + { + if (spellInfo == null || duration < 0) + return; + + if (spellInfo.IsChanneled() && !spellInfo.HasAttribute(SpellAttr5.HasteAffectDuration)) + return; + + // called from caster + Player modOwner = GetSpellModOwner(); + if (modOwner != null) + modOwner.ApplySpellMod(spellInfo, SpellModOp.ChangeCastTime, ref duration, spell); + + Unit unitCaster = ToUnit(); + if (!unitCaster) + return; + + if (!(spellInfo.HasAttribute(SpellAttr0.Ability) || spellInfo.HasAttribute(SpellAttr0.Tradespell) || spellInfo.HasAttribute(SpellAttr3.NoDoneBonus)) && + ((IsPlayer() && spellInfo.SpellFamilyName != 0) || IsCreature())) + duration = (int)(duration * unitCaster.m_unitData.ModCastingSpeed); + else if (spellInfo.HasAttribute(SpellAttr0.ReqAmmo) && !spellInfo.HasAttribute(SpellAttr2.AutorepeatFlag)) + duration = (int)(duration * unitCaster.m_modAttackSpeedPct[(int)WeaponAttackType.RangedAttack]); + } + + public virtual float MeleeSpellMissChance(Unit victim, WeaponAttackType attType, SpellInfo spellInfo) + { + return 0.0f; + } + + public virtual SpellMissInfo MeleeSpellHitResult(Unit victim, SpellInfo spellInfo) + { + return SpellMissInfo.None; + } + + SpellMissInfo MagicSpellHitResult(Unit victim, SpellInfo spellInfo) + { + // Can`t miss on dead target (on skinning for example) + if (!victim.IsAlive() && !victim.IsPlayer()) + return SpellMissInfo.None; + + SpellSchoolMask schoolMask = spellInfo.GetSchoolMask(); + // PvP - PvE spell misschances per leveldif > 2 + int lchance = victim.IsPlayer() ? 7 : 11; + uint thisLevel = GetLevelForTarget(victim); + if (IsCreature() && ToCreature().IsTrigger()) + thisLevel = Math.Max(thisLevel, spellInfo.SpellLevel); + int leveldif = (int)(victim.GetLevelForTarget(this) - thisLevel); + int levelBasedHitDiff = leveldif; + + // Base hit chance from attacker and victim levels + int modHitChance = 100; + if (levelBasedHitDiff >= 0) + { + if (!victim.IsPlayer()) + { + modHitChance = 94 - 3 * Math.Min(levelBasedHitDiff, 3); + levelBasedHitDiff -= 3; + } + else + { + modHitChance = 96 - Math.Min(levelBasedHitDiff, 2); + levelBasedHitDiff -= 2; + } + if (levelBasedHitDiff > 0) + modHitChance -= lchance * Math.Min(levelBasedHitDiff, 7); + } + else + modHitChance = 97 - levelBasedHitDiff; + + // Spellmod from SpellModOp::HitChance + Player modOwner = GetSpellModOwner(); + if (modOwner != null) + modOwner.ApplySpellMod(spellInfo, SpellModOp.HitChance, ref modHitChance); + + // Spells with SPELL_ATTR3_IGNORE_HIT_RESULT will ignore target's avoidance effects + if (!spellInfo.HasAttribute(SpellAttr3.IgnoreHitResult)) + { + // Chance hit from victim SPELL_AURA_MOD_ATTACKER_SPELL_HIT_CHANCE auras + modHitChance += victim.GetTotalAuraModifierByMiscMask(AuraType.ModAttackerSpellHitChance, (int)schoolMask); + } + + int HitChance = modHitChance * 100; + // Increase hit chance from attacker SPELL_AURA_MOD_SPELL_HIT_CHANCE and attacker ratings + Unit unit = ToUnit(); + if (unit != null) + HitChance += (int)(unit.ModSpellHitChance * 100.0f); + + MathFunctions.RoundToInterval(ref HitChance, 0, 10000); + + int tmp = 10000 - HitChance; + + int rand = RandomHelper.IRand(0, 9999); + if (tmp > 0 && rand < tmp) + return SpellMissInfo.Miss; + + // Chance resist mechanic (select max value from every mechanic spell effect) + int resist_chance = victim.GetMechanicResistChance(spellInfo) * 100; + + // Roll chance + if (resist_chance > 0 && rand < (tmp += resist_chance)) + return SpellMissInfo.Resist; + + // cast by caster in front of victim + if (!victim.HasUnitState(UnitState.Controlled) && (victim.HasInArc(MathF.PI, this) || victim.HasAuraType(AuraType.IgnoreHitDirection))) + { + int deflect_chance = victim.GetTotalAuraModifier(AuraType.DeflectSpells) * 100; + if (deflect_chance > 0 && rand < (tmp += deflect_chance)) + return SpellMissInfo.Deflect; + } + + return SpellMissInfo.None; + } + + // Calculate spell hit result can be: + // Every spell can: Evade/Immune/Reflect/Sucesful hit + // For melee based spells: + // Miss + // Dodge + // Parry + // For spells + // Resist + public SpellMissInfo SpellHitResult(Unit victim, SpellInfo spellInfo, bool canReflect = false) + { + // All positive spells can`t miss + /// @todo client not show miss log for this spells - so need find info for this in dbc and use it! + if (spellInfo.IsPositive() && !IsHostileTo(victim)) // prevent from affecting enemy by "positive" spell + return SpellMissInfo.None; + + if (this == victim) + return SpellMissInfo.None; + + // Return evade for units in evade mode + if (victim.IsCreature() && victim.ToCreature().IsEvadingAttacks()) + return SpellMissInfo.Evade; + + // Try victim reflect spell + if (canReflect) + { + int reflectchance = victim.GetTotalAuraModifier(AuraType.ReflectSpells); + reflectchance += victim.GetTotalAuraModifierByMiscMask(AuraType.ReflectSpellsSchool, (int)spellInfo.GetSchoolMask()); + + if (reflectchance > 0 && RandomHelper.randChance(reflectchance)) + return SpellMissInfo.Reflect; + } + + if (spellInfo.HasAttribute(SpellAttr3.IgnoreHitResult)) + return SpellMissInfo.None; + + // Check for immune + if (victim.IsImmunedToSpell(spellInfo, this)) + return SpellMissInfo.Immune; + + // Damage immunity is only checked if the spell has damage effects, this immunity must not prevent aura apply + // returns SPELL_MISS_IMMUNE in that case, for other spells, the SMSG_SPELL_GO must show hit + if (spellInfo.HasOnlyDamageEffects() && victim.IsImmunedToDamage(spellInfo)) + return SpellMissInfo.Immune; + + switch (spellInfo.DmgClass) + { + case SpellDmgClass.Ranged: + case SpellDmgClass.Melee: + return MeleeSpellHitResult(victim, spellInfo); + case SpellDmgClass.None: + return SpellMissInfo.None; + case SpellDmgClass.Magic: + return MagicSpellHitResult(victim, spellInfo); + } + return SpellMissInfo.None; + } + + public FactionTemplateRecord GetFactionTemplateEntry() + { + var entry = CliDB.FactionTemplateStorage.LookupByKey(GetFaction()); + if (entry == null) + Log.outError(LogFilter.Server, $"{GetGUID()} has invalid faction {GetFaction()}"); + + return entry; + } + + // function based on function Unit::UnitReaction from 13850 client + public ReputationRank GetReactionTo(WorldObject target) + { + // always friendly to self + if (this == target) + return ReputationRank.Friendly; + + // always friendly to charmer or owner + if (GetCharmerOrOwnerOrSelf() == target.GetCharmerOrOwnerOrSelf()) + return ReputationRank.Friendly; + + Player selfPlayerOwner = GetAffectingPlayer(); + Player targetPlayerOwner = target.GetAffectingPlayer(); + + // check forced reputation to support SPELL_AURA_FORCE_REACTION + if (selfPlayerOwner) + { + var targetFactionTemplateEntry = target.GetFactionTemplateEntry(); + if (targetFactionTemplateEntry != null) + { + var repRank = selfPlayerOwner.GetReputationMgr().GetForcedRankIfAny(targetFactionTemplateEntry); + if (repRank != ReputationRank.None) + return repRank; + } + } + else if (targetPlayerOwner) + { + var selfFactionTemplateEntry = GetFactionTemplateEntry(); + if (selfFactionTemplateEntry != null) + { + ReputationRank repRank = targetPlayerOwner.GetReputationMgr().GetForcedRankIfAny(selfFactionTemplateEntry); + if (repRank != ReputationRank.None) + return repRank; + } + } + + Unit unit = ToUnit(); + Unit targetUnit = target.ToUnit(); + if (unit && unit.HasUnitFlag(UnitFlags.PvpAttackable)) + { + if (targetUnit && targetUnit.HasUnitFlag(UnitFlags.PvpAttackable)) + { + if (selfPlayerOwner && targetPlayerOwner) + { + // always friendly to other unit controlled by player, or to the player himself + if (selfPlayerOwner == targetPlayerOwner) + return ReputationRank.Friendly; + + // duel - always hostile to opponent + if (selfPlayerOwner.duel != null && selfPlayerOwner.duel.opponent == targetPlayerOwner && selfPlayerOwner.duel.startTime != 0) + return ReputationRank.Hostile; + + // same group - checks dependant only on our faction - skip FFA_PVP for example + if (selfPlayerOwner.IsInRaidWith(targetPlayerOwner)) + return ReputationRank.Friendly; // return true to allow config option AllowTwoSide.Interaction.Group to work + // however client seems to allow mixed group parties, because in 13850 client it works like: + // return GetFactionReactionTo(GetFactionTemplateEntry(), target); + } + + // check FFA_PVP + if (unit.IsFFAPvP() && targetUnit.IsFFAPvP()) + return ReputationRank.Hostile; + + if (selfPlayerOwner) + { + var targetFactionTemplateEntry = targetUnit.GetFactionTemplateEntry(); + if (targetFactionTemplateEntry != null) + { + ReputationRank repRank = selfPlayerOwner.GetReputationMgr().GetForcedRankIfAny(targetFactionTemplateEntry); + if (repRank != ReputationRank.None) + return repRank; + + if (!selfPlayerOwner.HasUnitFlag2(UnitFlags2.IgnoreReputation)) + { + var targetFactionEntry = CliDB.FactionStorage.LookupByKey(targetFactionTemplateEntry.Faction); + if (targetFactionEntry != null) + { + if (targetFactionEntry.CanHaveReputation()) + { + // check contested flags + if ((targetFactionTemplateEntry.Flags & (ushort)FactionTemplateFlags.ContestedGuard) != 0 && selfPlayerOwner.HasPlayerFlag(PlayerFlags.ContestedPVP)) + return ReputationRank.Hostile; + + // if faction has reputation, hostile state depends only from AtWar state + if (selfPlayerOwner.GetReputationMgr().IsAtWar(targetFactionEntry)) + return ReputationRank.Hostile; + return ReputationRank.Friendly; + } + } + } + } + } + } + } + + // do checks dependant only on our faction + return GetFactionReactionTo(GetFactionTemplateEntry(), target); + } + + public static ReputationRank GetFactionReactionTo(FactionTemplateRecord factionTemplateEntry, WorldObject target) + { + // always neutral when no template entry found + if (factionTemplateEntry == null) + return ReputationRank.Neutral; + + var targetFactionTemplateEntry = target.GetFactionTemplateEntry(); + if (targetFactionTemplateEntry == null) + return ReputationRank.Neutral; + + Player targetPlayerOwner = target.GetAffectingPlayer(); + if (targetPlayerOwner != null) + { + // check contested flags + if ((factionTemplateEntry.Flags & (ushort)FactionTemplateFlags.ContestedGuard) != 0 && targetPlayerOwner.HasPlayerFlag(PlayerFlags.ContestedPVP)) + return ReputationRank.Hostile; + + var repRank = targetPlayerOwner.GetReputationMgr().GetForcedRankIfAny(factionTemplateEntry); + if (repRank != ReputationRank.None) + return repRank; + + if (target.IsUnit() && !target.ToUnit().HasUnitFlag2(UnitFlags2.IgnoreReputation)) + { + var factionEntry = CliDB.FactionStorage.LookupByKey(factionTemplateEntry.Faction); + if (factionEntry != null) + { + if (factionEntry.CanHaveReputation()) + { + // CvP case - check reputation, don't allow state higher than neutral when at war + ReputationRank repRank1 = targetPlayerOwner.GetReputationMgr().GetRank(factionEntry); + if (targetPlayerOwner.GetReputationMgr().IsAtWar(factionEntry)) + repRank1 = (ReputationRank)Math.Min((int)ReputationRank.Neutral, (int)repRank1); + return repRank1; + } + } + } + } + + // common faction based check + if (factionTemplateEntry.IsHostileTo(targetFactionTemplateEntry)) + return ReputationRank.Hostile; + if (factionTemplateEntry.IsFriendlyTo(targetFactionTemplateEntry)) + return ReputationRank.Friendly; + if (targetFactionTemplateEntry.IsFriendlyTo(factionTemplateEntry)) + return ReputationRank.Friendly; + if ((factionTemplateEntry.Flags & (ushort)FactionTemplateFlags.HostileByDefault) != 0) + return ReputationRank.Hostile; + // neutral by default + return ReputationRank.Neutral; + } + + public bool IsHostileTo(WorldObject target) + { + return GetReactionTo(target) <= ReputationRank.Hostile; + } + + public bool IsFriendlyTo(WorldObject target) + { + return GetReactionTo(target) >= ReputationRank.Friendly; + } + + public bool IsHostileToPlayers() + { + var my_faction = GetFactionTemplateEntry(); + if (my_faction.Faction == 0) + return false; + + var raw_faction = CliDB.FactionStorage.LookupByKey(my_faction.Faction); + if (raw_faction != null && raw_faction.ReputationIndex >= 0) + return false; + + return my_faction.IsHostileToPlayers(); + } + + public bool IsNeutralToAll() + { + var my_faction = GetFactionTemplateEntry(); + if (my_faction.Faction == 0) + return true; + + var raw_faction = CliDB.FactionStorage.LookupByKey(my_faction.Faction); + if (raw_faction != null && raw_faction.ReputationIndex >= 0) + return false; + + return my_faction.IsNeutralToAll(); + } + + public void CastSpell(WorldObject target, uint spellId, bool triggered = false) + { + CastSpellExtraArgs args = new(triggered); + CastSpell(target, spellId, args); + } + + public void CastSpell(SpellCastTargets targets, uint spellId, CastSpellExtraArgs args) + { + SpellInfo info = Global.SpellMgr.GetSpellInfo(spellId, args.CastDifficulty != Difficulty.None ? args.CastDifficulty : GetMap().GetDifficultyID()); + if (info == null) + { + Log.outError(LogFilter.Unit, $"CastSpell: unknown spell {spellId} by caster {GetGUID()}"); + return; + } + + Spell spell = new Spell(this, info, args.TriggerFlags, args.OriginalCaster); + foreach (var pair in args.SpellValueOverrides) + spell.SetSpellValue(pair.Key, pair.Value); + + spell.m_CastItem = args.CastItem; + spell.Prepare(targets, args.TriggeringAura); + } + + public void CastSpell(WorldObject target, uint spellId, CastSpellExtraArgs args) + { + SpellCastTargets targets = new(); + if (target) + { + Unit unitTarget = target.ToUnit(); + if (unitTarget != null) + targets.SetUnitTarget(unitTarget); + else + { + GameObject goTarget = target.ToGameObject(); + if (goTarget != null) + targets.SetGOTarget(goTarget); + else + { + Log.outError(LogFilter.Unit, $"CastSpell: Invalid target {target.GetGUID()} passed to spell cast by {GetGUID()}"); + return; + } + } + } + CastSpell(targets, spellId, args); + } + + public void CastSpell(Position dest, uint spellId, CastSpellExtraArgs args) + { + SpellCastTargets targets = new(); + targets.SetDst(dest); + CastSpell(targets, spellId, args); + } + + // function based on function Unit::CanAttack from 13850 client + public bool IsValidAttackTarget(WorldObject target, SpellInfo bySpell = null, bool spellCheck = true) + { + Cypher.Assert(target != null); + + // can't attack self + if (this == target) + return false; + + // can't attack GMs + if (target.IsPlayer() && target.ToPlayer().IsGameMaster()) + return false; + + Unit unit = ToUnit(); + Unit targetUnit = target.ToUnit(); + + // CvC case - can attack each other only when one of them is hostile + if (unit && !unit.HasUnitFlag(UnitFlags.PvpAttackable) && targetUnit && !targetUnit.HasUnitFlag(UnitFlags.PvpAttackable)) + return IsHostileTo(target) || target.IsHostileTo(this); + + // PvP, PvC, CvP case + // can't attack friendly targets + if (IsFriendlyTo(target) || target.IsFriendlyTo(this)) + return false; + + Player playerAffectingAttacker = unit && unit.HasUnitFlag(UnitFlags.PvpAttackable) ? GetAffectingPlayer() : null; + Player playerAffectingTarget = targetUnit && targetUnit.HasUnitFlag(UnitFlags.PvpAttackable) ? target.GetAffectingPlayer() : null; + + // Not all neutral creatures can be attacked (even some unfriendly faction does not react aggresive to you, like Sporaggar) + if ((playerAffectingAttacker && !playerAffectingTarget) || (!playerAffectingAttacker && playerAffectingTarget)) + { + Player player = playerAffectingAttacker ? playerAffectingAttacker : playerAffectingTarget; + Unit creature = playerAffectingAttacker ? targetUnit : unit; + if (creature != null) + { + if (creature.IsContestedGuard() && player.HasPlayerFlag(PlayerFlags.ContestedPVP)) + return true; + + var factionTemplate = creature.GetFactionTemplateEntry(); + if (factionTemplate != null) + { + if (player.GetReputationMgr().GetForcedRankIfAny(factionTemplate) == ReputationRank.None) + { + var factionEntry = CliDB.FactionStorage.LookupByKey(factionTemplate.Faction); + if (factionEntry != null) + { + var repState = player.GetReputationMgr().GetState(factionEntry); + if (repState != null) + if (!repState.Flags.HasFlag(ReputationFlags.AtWar)) + return false; + } + } + + } + } + } + + Creature creatureAttacker = ToCreature(); + if (creatureAttacker && creatureAttacker.GetCreatureTemplate().TypeFlags.HasFlag(CreatureTypeFlags.TreatAsRaidUnit)) + return false; + + if (bySpell == null) + spellCheck = false; + + if (spellCheck && !IsValidSpellAttackTarget(target, bySpell)) + return false; + + return true; + } + + public bool IsValidSpellAttackTarget(WorldObject target, SpellInfo bySpell) + { + Cypher.Assert(target != null); + Cypher.Assert(bySpell != null); + + // can't attack unattackable units + Unit unitTarget = target.ToUnit(); + if (unitTarget && unitTarget.HasUnitState(UnitState.Unattackable)) + return false; + + Unit unit = ToUnit(); + // visibility checks (only units) + if (unit) + { + // can't attack invisible + if (!bySpell.HasAttribute(SpellAttr6.CanTargetInvisible)) + { + if (!unit.CanSeeOrDetect(target, bySpell.IsAffectingArea())) + return false; + + /* + else if (!obj) + { + // ignore stealth for aoe spells. Ignore stealth if target is player and unit in combat with same player + bool const ignoreStealthCheck = (bySpell && bySpell.IsAffectingArea()) || + (target.GetTypeId() == TYPEID_PLAYER && target.HasStealthAura() && IsInCombatWith(target)); + + if (!CanSeeOrDetect(target, ignoreStealthCheck)) + return false; + } + */ + } + } + + // can't attack dead + if (!bySpell.IsAllowingDeadTarget() && unitTarget && !unitTarget.IsAlive()) + return false; + + // can't attack untargetable + if (!bySpell.HasAttribute(SpellAttr6.CanTargetInvisible) && unitTarget && unitTarget.HasUnitFlag(UnitFlags.NotSelectable)) + return false; + + Player playerAttacker = ToPlayer(); + if (playerAttacker != null) + { + if (playerAttacker.HasPlayerFlag(PlayerFlags.Uber)) + return false; + } + + // check flags + if (unitTarget != null && unitTarget.HasUnitFlag(UnitFlags.NonAttackable | UnitFlags.TaxiFlight | UnitFlags.NotAttackable1 | UnitFlags.Unk16)) + return false; + + if (unit != null && !unit.HasUnitFlag(UnitFlags.PvpAttackable) && unitTarget && unitTarget.IsImmuneToNPC()) + return false; + + if (unitTarget != null && !unitTarget.HasUnitFlag(UnitFlags.PvpAttackable) && unit && unit.IsImmuneToNPC()) + return false; + + if (!bySpell.HasAttribute(SpellAttr8.AttackIgnoreImmuneToPCFlag)) + { + if (unit && unit.HasUnitFlag(UnitFlags.PvpAttackable) && unitTarget && unitTarget.IsImmuneToPC()) + return false; + + if (unitTarget && unitTarget.HasUnitFlag(UnitFlags.PvpAttackable) && unit && unit.IsImmuneToPC()) + return false; + } + + // check duel - before sanctuary checks + Player playerAffectingAttacker = unit && unit.HasUnitFlag(UnitFlags.PvpAttackable) ? GetAffectingPlayer() : null; + Player playerAffectingTarget = unitTarget && unitTarget.HasUnitFlag(UnitFlags.PvpAttackable) ? target.GetAffectingPlayer() : null; + if (playerAffectingAttacker && playerAffectingTarget) + if (playerAffectingAttacker.duel != null && playerAffectingAttacker.duel.opponent == playerAffectingTarget && playerAffectingAttacker.duel.startTime != 0) + return true; + + // PvP case - can't attack when attacker or target are in sanctuary + // however, 13850 client doesn't allow to attack when one of the unit's has sanctuary flag and is pvp + if (unitTarget && unitTarget.HasUnitFlag(UnitFlags.PvpAttackable) && unit && unit.HasUnitFlag(UnitFlags.PvpAttackable) && (unitTarget.IsInSanctuary() || unit.IsInSanctuary())) + return false; + + // additional checks - only PvP case + if (playerAffectingAttacker && playerAffectingTarget) + { + if (unitTarget.IsPvP()) + return true; + + if (unit.IsFFAPvP() && unitTarget.IsFFAPvP()) + return true; + + return unit.HasPvpFlag(UnitPVPStateFlags.Unk1) || + unitTarget.HasPvpFlag(UnitPVPStateFlags.Unk1); + } + + return true; + } + + // function based on function Unit::CanAssist from 13850 client + public bool IsValidAssistTarget(WorldObject target, SpellInfo bySpell = null, bool spellCheck = true) + { + Cypher.Assert(target); + + // can assist to self + if (this == target) + return true; + + // can't assist GMs + if (target.IsPlayer() && target.ToPlayer().IsGameMaster()) + return false; + + // can't assist non-friendly targets + if (GetReactionTo(target) < ReputationRank.Neutral && target.GetReactionTo(this) < ReputationRank.Neutral && (!ToCreature() || !ToCreature().GetCreatureTemplate().TypeFlags.HasFlag(CreatureTypeFlags.TreatAsRaidUnit))) + return false; + + if (bySpell == null) + spellCheck = false; + + if (spellCheck && !IsValidSpellAssistTarget(target, bySpell)) + return false; + + return true; + } + + public bool IsValidSpellAssistTarget(WorldObject target, SpellInfo bySpell) + { + Cypher.Assert(target != null); + Cypher.Assert(bySpell != null); + + // can't assist unattackable units + Unit unitTarget = target.ToUnit(); + if (unitTarget && unitTarget.HasUnitState(UnitState.Unattackable)) + return false; + + // can't assist own vehicle or passenger + Unit unit = ToUnit(); + if (unit && unitTarget && unit.GetVehicle()) + { + if (unit.IsOnVehicle(unitTarget)) + return false; + + if (unit.GetVehicleBase().IsOnVehicle(unitTarget)) + return false; + } + + // can't assist invisible + if (!bySpell.HasAttribute(SpellAttr6.CanTargetInvisible) && !CanSeeOrDetect(target, bySpell.IsAffectingArea())) + return false; + + // can't assist dead + if (!bySpell.IsAllowingDeadTarget() && unitTarget && !unitTarget.IsAlive()) + return false; + + // can't assist untargetable + if (!bySpell.HasAttribute(SpellAttr6.CanTargetUntargetable) && unitTarget && unitTarget.HasUnitFlag(UnitFlags.NotSelectable)) + return false; + + if (!bySpell.HasAttribute(SpellAttr6.AssistIgnoreImmuneFlag)) + { + if (unit && unit.HasUnitFlag(UnitFlags.PvpAttackable)) + { + if (unitTarget && unitTarget.IsImmuneToPC()) + return false; + } + else + { + if (unitTarget && unitTarget.IsImmuneToNPC()) + return false; + } + } + + // PvP case + if (unitTarget && unitTarget.HasUnitFlag(UnitFlags.PvpAttackable)) + { + Player targetPlayerOwner = target.GetAffectingPlayer(); + if (unit && unit.HasUnitFlag(UnitFlags.PvpAttackable)) + { + Player selfPlayerOwner = GetAffectingPlayer(); + if (selfPlayerOwner && targetPlayerOwner) + { + // can't assist player which is dueling someone + if (selfPlayerOwner != targetPlayerOwner && targetPlayerOwner.duel != null) + return false; + } + // can't assist player in ffa_pvp zone from outside + if (unitTarget.IsFFAPvP() && unit && !unit.IsFFAPvP()) + return false; + + // can't assist player out of sanctuary from sanctuary if has pvp enabled + if (unitTarget.IsPvP()) + if (unit && unit.IsInSanctuary() && !unitTarget.IsInSanctuary()) + return false; + } + } + // PvC case - player can assist creature only if has specific type flags + // !target.HasFlag(UNIT_FIELD_FLAGS, UnitFlags.PvpAttackable) && + else if (unit && unit.HasUnitFlag(UnitFlags.PvpAttackable)) + { + if (!bySpell.HasAttribute(SpellAttr6.AssistIgnoreImmuneFlag)) + if (unitTarget && !unitTarget.IsPvP()) + { + Creature creatureTarget = target.ToCreature(); + if (creatureTarget != null) + return (creatureTarget.GetCreatureTemplate().TypeFlags.HasFlag(CreatureTypeFlags.TreatAsRaidUnit) || creatureTarget.GetCreatureTemplate().TypeFlags.HasFlag(CreatureTypeFlags.CanAssist)); + } + } + + return true; + } + + public Unit GetMagicHitRedirectTarget(Unit victim, SpellInfo spellInfo) + { + // Patch 1.2 notes: Spell Reflection no longer reflects abilities + if (spellInfo.HasAttribute(SpellAttr0.Ability) || spellInfo.HasAttribute(SpellAttr1.CantBeRedirected) || spellInfo.HasAttribute(SpellAttr0.UnaffectedByInvulnerability)) + return victim; + + var magnetAuras = victim.GetAuraEffectsByType(AuraType.SpellMagnet); + foreach (AuraEffect aurEff in magnetAuras) + { + Unit magnet = aurEff.GetBase().GetCaster(); + if (magnet != null) + { + if (spellInfo.CheckExplicitTarget(this, magnet) == SpellCastResult.SpellCastOk && IsValidAttackTarget(magnet, spellInfo)) + { + /// @todo handle this charge drop by proc in cast phase on explicit target + if (spellInfo.HasHitDelay()) + { + // Set up missile speed based delay + float hitDelay = spellInfo.LaunchDelay; + if (spellInfo.HasAttribute(SpellAttr9.SpecialDelayCalculation)) + hitDelay += spellInfo.Speed; + else if (spellInfo.Speed > 0.0f) + hitDelay += Math.Max(victim.GetDistance(this), 5.0f) / spellInfo.Speed; + + uint delay = (uint)Math.Floor(hitDelay * 1000.0f); + // Schedule charge drop + aurEff.GetBase().DropChargeDelayed(delay, AuraRemoveMode.Expire); + } + else + aurEff.GetBase().DropCharge(AuraRemoveMode.Expire); + + return magnet; + } + } + } + return victim; + } + + public virtual uint GetCastSpellXSpellVisualId(SpellInfo spellInfo) + { + return spellInfo.GetSpellXSpellVisualId(this); + } + public void GetGameObjectListWithEntryInGrid(List gameobjectList, uint entry = 0, float maxSearchRange = 250.0f) { var check = new AllGameObjectsWithEntryInRange(this, entry, maxSearchRange); @@ -1814,6 +2827,12 @@ namespace Game.Entities public virtual bool LoadFromDB(ulong spawnId, Map map, bool addToMap, bool allowDuplicate) { return true; } + public virtual ObjectGuid GetOwnerGUID() { return default; } + public virtual ObjectGuid GetCharmerOrOwnerGUID() { return GetOwnerGUID(); } + + public virtual uint GetFaction() { return 0; } + public virtual void SetFaction(uint faction) { } + //Position public float GetDistanceZ(WorldObject obj) @@ -2375,6 +3394,9 @@ namespace Game.Entities float m_staticFloorZ; bool m_outdoors; + // Event handler + public EventSystem m_Events = new(); + public MovementInfo m_movementInfo; string _name; protected bool m_isActive; @@ -2609,4 +3631,22 @@ namespace Game.Entities Conversation = false; } } + + class CombatLogSender : IDoWork + { + CombatLogServerPacket i_message; + + public CombatLogSender(CombatLogServerPacket msg) + { + i_message = msg; + } + + public void Invoke(Player player) + { + i_message.Clear(); + i_message.SetAdvancedCombatLogging(player.IsAdvancedCombatLoggingEnabled()); + + player.SendPacket(i_message); + } + } } \ No newline at end of file diff --git a/Source/Game/Entities/Player/Player.Achievement.cs b/Source/Game/Entities/Player/Player.Achievement.cs index 843f66631..5ae531307 100644 --- a/Source/Game/Entities/Player/Player.Achievement.cs +++ b/Source/Game/Entities/Player/Player.Achievement.cs @@ -66,10 +66,10 @@ namespace Game.Entities m_questObjectiveCriteriaMgr.ResetCriteria(failEvent, failAsset, evenIfCriteriaComplete); } - public void UpdateCriteria(CriteriaTypes type, ulong miscValue1 = 0, ulong miscValue2 = 0, ulong miscValue3 = 0, Unit unit = null) + public void UpdateCriteria(CriteriaTypes type, ulong miscValue1 = 0, ulong miscValue2 = 0, ulong miscValue3 = 0, WorldObject refe = null) { - m_achievementSys.UpdateCriteria(type, miscValue1, miscValue2, miscValue3, unit, this); - m_questObjectiveCriteriaMgr.UpdateCriteria(type, miscValue1, miscValue2, miscValue3, unit, this); + m_achievementSys.UpdateCriteria(type, miscValue1, miscValue2, miscValue3, refe, this); + m_questObjectiveCriteriaMgr.UpdateCriteria(type, miscValue1, miscValue2, miscValue3, refe, this); // Update only individual achievement criteria here, otherwise we may get multiple updates // from a single boss kill @@ -78,11 +78,11 @@ namespace Game.Entities Scenario scenario = GetScenario(); if (scenario != null) - scenario.UpdateCriteria(type, miscValue1, miscValue2, miscValue3, unit, this); + scenario.UpdateCriteria(type, miscValue1, miscValue2, miscValue3, refe, this); Guild guild = Global.GuildMgr.GetGuildById(GetGuildId()); if (guild) - guild.UpdateCriteria(type, miscValue1, miscValue2, miscValue3, unit, this); + guild.UpdateCriteria(type, miscValue1, miscValue2, miscValue3, refe, this); } public void CompletedAchievement(AchievementRecord entry) diff --git a/Source/Game/Entities/Player/Player.cs b/Source/Game/Entities/Player/Player.cs index a9f1c9d8e..b8b62bfe2 100644 --- a/Source/Game/Entities/Player/Player.cs +++ b/Source/Game/Entities/Player/Player.cs @@ -3482,7 +3482,7 @@ namespace Game.Entities SetFaction(rEntry != null ? (uint)rEntry.FactionID : 0); } - public void SetResurrectRequestData(Unit caster, uint health, uint mana, uint appliedAura) + public void SetResurrectRequestData(WorldObject caster, uint health, uint mana, uint appliedAura) { Cypher.Assert(!IsResurrectRequested()); _resurrectionData = new ResurrectionData(); @@ -3633,7 +3633,7 @@ namespace Game.Entities return false; } - public override bool IsImmunedToSpellEffect(SpellInfo spellInfo, uint index, Unit caster) + public override bool IsImmunedToSpellEffect(SpellInfo spellInfo, uint index, WorldObject caster) { SpellEffectInfo effect = spellInfo.GetEffect(index); if (effect == null || !effect.IsEffect()) @@ -6637,46 +6637,6 @@ namespace Game.Entities return m_auraBaseFlatMod[(int)modGroup] * m_auraBasePctMod[(int)modGroup]; } - public void AddComboPoints(sbyte count, Spell spell = null) - { - if (count == 0) - return; - - sbyte comboPoints = spell != null ? spell.m_comboPointGain : (sbyte)GetPower(PowerType.ComboPoints); - - comboPoints += count; - - if (comboPoints > 5) - comboPoints = 5; - else if (comboPoints < 0) - comboPoints = 0; - - if (spell == null) - SetPower(PowerType.ComboPoints, comboPoints); - else - spell.m_comboPointGain = comboPoints; - } - public void GainSpellComboPoints(sbyte count) - { - if (count == 0) - return; - - sbyte cp = (sbyte)GetPower(PowerType.ComboPoints); - cp += count; - - if (cp > 5) - cp = 5; - else if (cp < 0) - cp = 0; - - SetPower(PowerType.ComboPoints, cp); - } - public void ClearComboPoints() - { - SetPower(PowerType.ComboPoints, 0); - } - public byte GetComboPoints() { return (byte)GetPower(PowerType.ComboPoints); } - public byte GetDrunkValue() { return m_playerData.Inebriation; } public void SetDrunkValue(byte newDrunkValue, uint itemId = 0) { diff --git a/Source/Game/Entities/StatSystem.cs b/Source/Game/Entities/StatSystem.cs index abf8ee968..50bb66958 100644 --- a/Source/Game/Entities/StatSystem.cs +++ b/Source/Game/Entities/StatSystem.cs @@ -788,7 +788,7 @@ namespace Game.Entities public void SetRangedWeaponAttackPower(int attackPower) { SetUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.RangedWeaponAttackPower), attackPower); } //Chances - float MeleeSpellMissChance(Unit victim, WeaponAttackType attType, SpellInfo spellInfo) + public override float MeleeSpellMissChance(Unit victim, WeaponAttackType attType, SpellInfo spellInfo) { //calculate miss chance float missChance = victim.GetUnitMissChance(); diff --git a/Source/Game/Entities/Totem.cs b/Source/Game/Entities/Totem.cs index ddc0ff289..4357f4e44 100644 --- a/Source/Game/Entities/Totem.cs +++ b/Source/Game/Entities/Totem.cs @@ -143,7 +143,7 @@ namespace Game.Entities AddObjectToRemoveList(); } - public override bool IsImmunedToSpellEffect(SpellInfo spellInfo, uint index, Unit caster) + public override bool IsImmunedToSpellEffect(SpellInfo spellInfo, uint index, WorldObject caster) { // @todo possibly all negative auras immune? if (GetEntry() == 5925) diff --git a/Source/Game/Entities/Unit/Unit.Combat.cs b/Source/Game/Entities/Unit/Unit.Combat.cs index fd87b6986..ad1dec479 100644 --- a/Source/Game/Entities/Unit/Unit.Combat.cs +++ b/Source/Game/Entities/Unit/Unit.Combat.cs @@ -631,43 +631,6 @@ namespace Game.Entities public void SetBaseWeaponDamage(WeaponAttackType attType, WeaponDamageRange damageRange, float value) { m_weaponDamage[(int)attType][(int)damageRange] = value; } - public Unit GetMagicHitRedirectTarget(Unit victim, SpellInfo spellInfo) - { - // Patch 1.2 notes: Spell Reflection no longer reflects abilities - if (spellInfo.HasAttribute(SpellAttr0.Ability) || spellInfo.HasAttribute(SpellAttr1.CantBeRedirected) || spellInfo.HasAttribute(SpellAttr0.UnaffectedByInvulnerability)) - return victim; - - var magnetAuras = victim.GetAuraEffectsByType(AuraType.SpellMagnet); - foreach (var eff in magnetAuras) - { - Unit magnet = eff.GetBase().GetCaster(); - if (magnet != null) - { - if (spellInfo.CheckExplicitTarget(this, magnet) == SpellCastResult.SpellCastOk && IsValidAttackTarget(magnet, spellInfo)) - { - // @todo handle this charge drop by proc in cast phase on explicit target - if (spellInfo.HasHitDelay()) - { - // Set up missile speed based delay - float hitDelay = spellInfo.LaunchDelay; - if (spellInfo.HasAttribute(SpellAttr9.SpecialDelayCalculation)) - hitDelay += spellInfo.Speed; - else if (spellInfo.Speed > 0.0f) - hitDelay += Math.Max(victim.GetDistance(this), 5.0f) / spellInfo.Speed; - - uint delay = (uint)Math.Floor(hitDelay * 1000.0f); - // Schedule charge drop - eff.GetBase().DropChargeDelayed(delay, AuraRemoveMode.Expire); - } - else - eff.GetBase().DropCharge(AuraRemoveMode.Expire); - return magnet; - } - } - } - return victim; - } - public Unit GetMeleeHitRedirectTarget(Unit victim, SpellInfo spellInfo = null) { var interceptAuras = victim.GetAuraEffectsByType(AuraType.InterceptMeleeRangedAttacks); @@ -753,17 +716,6 @@ namespace Game.Entities } } - internal void SendCombatLogMessage(CombatLogServerPacket combatLog) - { - CombatLogSender combatLogSender = new(combatLog); - - if (IsPlayer()) - combatLogSender.Invoke(ToPlayer()); - - var notifier = new MessageDistDeliverer(this, combatLogSender, GetVisibilityRange()); - Cell.VisitWorldObjects(this, notifier, GetVisibilityRange()); - } - bool IsThreatened() { return !m_threatManager.IsThreatListEmpty(); @@ -1562,286 +1514,6 @@ namespace Game.Entities } } - // function based on function Unit::CanAttack from 13850 client - public bool IsValidAttackTarget(WorldObject target, SpellInfo bySpell = null, bool spellCheck = true) - { - Cypher.Assert(target != null); - - // can't attack self - if (this == target) - return false; - - // can't attack GMs - if (target.IsPlayer() && target.ToPlayer().IsGameMaster()) - return false; - - Unit unit = ToUnit(); - Unit targetUnit = target.ToUnit(); - - // CvC case - can attack each other only when one of them is hostile - if (unit && !unit.HasUnitFlag(UnitFlags.PvpAttackable) && targetUnit && !targetUnit.HasUnitFlag(UnitFlags.PvpAttackable)) - return IsHostileTo(target) || target.IsHostileTo(this); - - // PvP, PvC, CvP case - // can't attack friendly targets - if (IsFriendlyTo(target) || target.IsFriendlyTo(this)) - return false; - - Player playerAffectingAttacker = unit && unit.HasUnitFlag(UnitFlags.PvpAttackable) ? GetAffectingPlayer() : null; - Player playerAffectingTarget = targetUnit && targetUnit.HasUnitFlag(UnitFlags.PvpAttackable) ? target.GetAffectingPlayer() : null; - - // Not all neutral creatures can be attacked (even some unfriendly faction does not react aggresive to you, like Sporaggar) - if ((playerAffectingAttacker && !playerAffectingTarget) || (!playerAffectingAttacker && playerAffectingTarget)) - { - Player player = playerAffectingAttacker ? playerAffectingAttacker : playerAffectingTarget; - Unit creature = playerAffectingAttacker ? targetUnit : unit; - if (creature != null) - { - if (creature.IsContestedGuard() && player.HasPlayerFlag(PlayerFlags.ContestedPVP)) - return true; - - var factionTemplate = creature.GetFactionTemplateEntry(); - if (factionTemplate != null) - { - if (player.GetReputationMgr().GetForcedRankIfAny(factionTemplate) == 0) - { - var factionEntry = CliDB.FactionStorage.LookupByKey(factionTemplate.Faction); - if (factionEntry != null) - { - var repState = player.GetReputationMgr().GetState(factionEntry); - if (repState != null) - if (!repState.Flags.HasFlag(ReputationFlags.AtWar)) - return false; - } - } - - } - } - } - - Creature creatureAttacker = ToCreature(); - if (creatureAttacker && creatureAttacker.GetCreatureTemplate().TypeFlags.HasFlag(CreatureTypeFlags.TreatAsRaidUnit)) - return false; - - if (bySpell == null) - spellCheck = false; - - if (spellCheck && !IsValidSpellAttackTarget(target, bySpell)) - return false; - - return true; - } - - public bool IsValidSpellAttackTarget(WorldObject target, SpellInfo bySpell) - { - Cypher.Assert(target != null); - Cypher.Assert(bySpell != null); - - // can't attack unattackable units - Unit unitTarget = target.ToUnit(); - if (unitTarget && unitTarget.HasUnitState(UnitState.Unattackable)) - return false; - - Unit unit = ToUnit(); - // visibility checks (only units) - if (unit) - { - // can't attack invisible - if (!bySpell.HasAttribute(SpellAttr6.CanTargetInvisible)) - { - if (!unit.CanSeeOrDetect(target, bySpell.IsAffectingArea())) - return false; - - /* - else if (!obj) - { - // ignore stealth for aoe spells. Ignore stealth if target is player and unit in combat with same player - bool const ignoreStealthCheck = (bySpell && bySpell.IsAffectingArea()) || - (target.GetTypeId() == TYPEID_PLAYER && target.HasStealthAura() && IsInCombatWith(target)); - - if (!CanSeeOrDetect(target, ignoreStealthCheck)) - return false; - } - */ - } - } - - // can't attack dead - if (!bySpell.IsAllowingDeadTarget() && unitTarget && !unitTarget.IsAlive()) - return false; - - // can't attack untargetable - if (!bySpell.HasAttribute(SpellAttr6.CanTargetUntargetable) && unitTarget && unitTarget.HasUnitFlag(UnitFlags.NotSelectable)) - return false; - - Player playerAttacker = ToPlayer(); - if (playerAttacker != null) - { - if (playerAttacker.HasPlayerFlag(PlayerFlags.Uber)) - return false; - } - - // check flags - if (unitTarget != null && unitTarget.HasUnitFlag(UnitFlags.NonAttackable | UnitFlags.TaxiFlight | UnitFlags.NotAttackable1 | UnitFlags.Unk16)) - return false; - - if (unit != null && !unit.HasUnitFlag(UnitFlags.PvpAttackable) && unitTarget && unitTarget.IsImmuneToNPC()) - return false; - - if (unitTarget != null && !unitTarget.HasUnitFlag(UnitFlags.PvpAttackable) && unit && unit.IsImmuneToNPC()) - return false; - - if (!bySpell.HasAttribute(SpellAttr8.AttackIgnoreImmuneToPCFlag)) - { - if (unit && unit.HasUnitFlag(UnitFlags.PvpAttackable) && unitTarget && unitTarget.IsImmuneToPC()) - return false; - - if (unitTarget && unitTarget.HasUnitFlag(UnitFlags.PvpAttackable) && unit && unit.IsImmuneToPC()) - return false; - } - - // check duel - before sanctuary checks - Player playerAffectingAttacker = unit && unit.HasUnitFlag(UnitFlags.PvpAttackable) ? GetAffectingPlayer() : null; - Player playerAffectingTarget = unitTarget && unitTarget.HasUnitFlag(UnitFlags.PvpAttackable) ? target.GetAffectingPlayer() : null; - if (playerAffectingAttacker && playerAffectingTarget) - if (playerAffectingAttacker.duel != null && playerAffectingAttacker.duel.opponent == playerAffectingTarget && playerAffectingAttacker.duel.startTime != 0) - return true; - - // PvP case - can't attack when attacker or target are in sanctuary - // however, 13850 client doesn't allow to attack when one of the unit's has sanctuary flag and is pvp - if (unitTarget && unitTarget.HasUnitFlag(UnitFlags.PvpAttackable) && unit && unit.HasUnitFlag(UnitFlags.PvpAttackable) && (unitTarget.IsInSanctuary() || unit.IsInSanctuary())) - return false; - - // additional checks - only PvP case - if (playerAffectingAttacker && playerAffectingTarget) - { - if (unitTarget.IsPvP()) - return true; - - if (unit.IsFFAPvP() && unitTarget.IsFFAPvP()) - return true; - - return unit.HasPvpFlag(UnitPVPStateFlags.Unk1) || - unitTarget.HasPvpFlag(UnitPVPStateFlags.Unk1); - } - - return true; - } - - // function based on function Unit::CanAssist from 13850 client - public bool IsValidAssistTarget(WorldObject target, SpellInfo bySpell = null, bool spellCheck = true) - { - Cypher.Assert(target != null); - - // can assist to self - if (this == target) - return true; - - // can't assist GMs - if (target.IsPlayer() && target.ToPlayer().IsGameMaster()) - return false; - - // can't assist non-friendly targets - if (GetReactionTo(target) < ReputationRank.Neutral && target.GetReactionTo(this) < ReputationRank.Neutral && (!ToCreature() || !ToCreature().GetCreatureTemplate().TypeFlags.HasFlag(CreatureTypeFlags.TreatAsRaidUnit))) - return false; - - if (bySpell == null) - spellCheck = false; - - if (spellCheck && !IsValidSpellAssistTarget(target, bySpell)) - return false; - - return true; - } - - public bool IsValidSpellAssistTarget(WorldObject target, SpellInfo bySpell) - { - Cypher.Assert(target != null); - Cypher.Assert(bySpell != null); - - // can't assist unattackable units - Unit unitTarget = target.ToUnit(); - if (unitTarget && unitTarget.HasUnitState(UnitState.Unattackable)) - return false; - - // can't assist own vehicle or passenger - Unit unit = ToUnit(); - if (unit && unitTarget && unit.GetVehicle()) - { - if (unit.IsOnVehicle(unitTarget)) - return false; - - if (unit.GetVehicleBase().IsOnVehicle(unitTarget)) - return false; - } - - // can't assist invisible - if (!bySpell.HasAttribute(SpellAttr6.CanTargetInvisible) && !CanSeeOrDetect(target, bySpell.IsAffectingArea())) - return false; - - // can't assist dead - if (!bySpell.IsAllowingDeadTarget() && unitTarget && !unitTarget.IsAlive()) - return false; - - // can't assist untargetable - if (!bySpell.HasAttribute(SpellAttr6.CanTargetUntargetable) && unitTarget && unitTarget.HasUnitFlag(UnitFlags.NotSelectable)) - return false; - - if (!bySpell.HasAttribute(SpellAttr6.AssistIgnoreImmuneFlag)) - { - if (unit && unit.HasUnitFlag(UnitFlags.PvpAttackable)) - { - if (unitTarget && unitTarget.IsImmuneToPC()) - return false; - } - else - { - if (unitTarget && unitTarget.IsImmuneToNPC()) - return false; - } - } - - // PvP case - if (unitTarget && unitTarget.HasUnitFlag(UnitFlags.PvpAttackable)) - { - Player targetPlayerOwner = target.GetAffectingPlayer(); - if (unit && unit.HasUnitFlag(UnitFlags.PvpAttackable)) - { - Player selfPlayerOwner = GetAffectingPlayer(); - if (selfPlayerOwner && targetPlayerOwner) - { - // can't assist player which is dueling someone - if (selfPlayerOwner != targetPlayerOwner && targetPlayerOwner.duel != null) - return false; - } - // can't assist player in ffa_pvp zone from outside - if (unitTarget.IsFFAPvP() && unit && !unit.IsFFAPvP()) - return false; - - // can't assist player out of sanctuary from sanctuary if has pvp enabled - if (unitTarget.IsPvP()) - if (unit && unit.IsInSanctuary() && !unitTarget.IsInSanctuary()) - return false; - } - } - // PvC case - player can assist creature only if has specific type flags - // !target.HasFlag(UNIT_FIELD_FLAGS, UnitFlags.PvpAttackable) && - else if (unit && unit.HasUnitFlag(UnitFlags.PvpAttackable)) - { - if (!bySpell.HasAttribute(SpellAttr6.AssistIgnoreImmuneFlag)) - { - if (unitTarget && !unitTarget.IsPvP()) - { - Creature creatureTarget = target.ToCreature(); - if (creatureTarget != null) - return creatureTarget.GetCreatureTemplate().TypeFlags.HasFlag(CreatureTypeFlags.TreatAsRaidUnit) || creatureTarget.GetCreatureTemplate().TypeFlags.HasFlag(CreatureTypeFlags.CanAssist); - } - } - } - - return true; - } - public virtual bool CheckAttackFitToAuraRequirement(WeaponAttackType attackType, AuraEffect aurEff) { return true; } public void ApplyAttackTimePercentMod(WeaponAttackType att, float val, bool apply) diff --git a/Source/Game/Entities/Unit/Unit.Fields.cs b/Source/Game/Entities/Unit/Unit.Fields.cs index 4ca7ab61d..e1b79b4d5 100644 --- a/Source/Game/Entities/Unit/Unit.Fields.cs +++ b/Source/Game/Entities/Unit/Unit.Fields.cs @@ -55,7 +55,7 @@ namespace Game.Entities protected float[][] m_weaponDamage = new float[(int)WeaponAttackType.Max][]; uint[] m_baseAttackSpeed = new uint[(int)WeaponAttackType.Max]; - float[] m_modAttackSpeedPct = new float[(int)WeaponAttackType.Max]; + internal float[] m_modAttackSpeedPct = new float[(int)WeaponAttackType.Max]; protected uint[] m_attackTimer = new uint[(int)WeaponAttackType.Max]; CombatManager m_combatManager; @@ -117,7 +117,6 @@ namespace Game.Entities float[] m_floatStatNegBuff = new float[(int)Stats.Max]; public ObjectGuid[] m_SummonSlot = new ObjectGuid[7]; public ObjectGuid[] m_ObjectSlot = new ObjectGuid[4]; - public EventSystem m_Events = new(); public UnitTypeMask UnitTypeMask { get; set; } UnitState m_state; protected LiquidTypeRecord _lastLiquid; @@ -498,14 +497,14 @@ namespace Game.Entities public class DispelInfo { - public DispelInfo(Unit dispeller, uint dispellerSpellId, byte chargesRemoved) + public DispelInfo(WorldObject dispeller, uint dispellerSpellId, byte chargesRemoved) { - _dispellerUnit = dispeller; + _dispeller = dispeller; _dispellerSpell = dispellerSpellId; _chargesRemoved = chargesRemoved; } - public Unit GetDispeller() { return _dispellerUnit; } + public WorldObject GetDispeller() { return _dispeller; } uint GetDispellerSpellId() { return _dispellerSpell; } public byte GetRemovedCharges() { return _chargesRemoved; } public void SetRemovedCharges(byte amount) @@ -513,7 +512,7 @@ namespace Game.Entities _chargesRemoved = amount; } - Unit _dispellerUnit; + WorldObject _dispeller; uint _dispellerSpell; byte _chargesRemoved; } @@ -554,22 +553,4 @@ namespace Game.Entities { public StringArray name = new(SharedConst.MaxDeclinedNameCases); } - - class CombatLogSender : IDoWork - { - CombatLogServerPacket i_message; - - public CombatLogSender(CombatLogServerPacket msg) - { - i_message = msg; - } - - public void Invoke(Player player) - { - i_message.Clear(); - i_message.SetAdvancedCombatLogging(player.IsAdvancedCombatLoggingEnabled()); - - player.SendPacket(i_message); - } - } } diff --git a/Source/Game/Entities/Unit/Unit.Spells.cs b/Source/Game/Entities/Unit/Unit.Spells.cs index bf4f59793..77400c5c1 100644 --- a/Source/Game/Entities/Unit/Unit.Spells.cs +++ b/Source/Game/Entities/Unit/Unit.Spells.cs @@ -32,19 +32,6 @@ namespace Game.Entities public void SetInstantCast(bool set) { _instantCast = set; } public bool CanInstantCast() { return _instantCast; } - - // function uses real base points (typically value - 1) - public int CalculateSpellDamage(Unit target, SpellInfo spellProto, uint effect_index, int? basePoints = null, uint castItemId = 0, int itemLevel = -1) - { - SpellEffectInfo effect = spellProto.GetEffect(effect_index); - return effect != null ? effect.CalcValue(this, basePoints, target, castItemId, itemLevel) : 0; - } - public int CalculateSpellDamage(Unit target, SpellInfo spellProto, uint effect_index, out float variance, int? basePoints = null, uint castItemId = 0, int itemLevel = -1) - { - SpellEffectInfo effect = spellProto.GetEffect(effect_index); - variance = 0.0f; - return effect != null ? effect.CalcValue(out variance, this, basePoints, target, castItemId, itemLevel) : 0; - } public int SpellBaseDamageBonusDone(SpellSchoolMask schoolMask) { @@ -754,66 +741,8 @@ namespace Game.Entities return Math.Max(crit_chance, 0.0f); } - // Calculate spell hit result can be: - // Every spell can: Evade/Immune/Reflect/Sucesful hit - // For melee based spells: - // Miss - // Dodge - // Parry - // For spells - // Resist - public SpellMissInfo SpellHitResult(Unit victim, SpellInfo spellInfo, bool canReflect = false) - { - // All positive spells can`t miss - // @todo client not show miss log for this spells - so need find info for this in dbc and use it! - if (spellInfo.IsPositive() - && (!IsHostileTo(victim))) // prevent from affecting enemy by "positive" spell - return SpellMissInfo.None; - - if (this == victim) - return SpellMissInfo.None; - - // Return evade for units in evade mode - if (victim.IsTypeId(TypeId.Unit) && victim.ToCreature().IsEvadingAttacks()) - return SpellMissInfo.Evade; - - // Try victim reflect spell - if (canReflect) - { - int reflectchance = victim.GetTotalAuraModifier(AuraType.ReflectSpells); - reflectchance += victim.GetTotalAuraModifierByMiscMask(AuraType.ReflectSpellsSchool, (int)spellInfo.GetSchoolMask()); - - if (reflectchance > 0 && RandomHelper.randChance(reflectchance)) - return SpellMissInfo.Reflect; - } - - if (spellInfo.HasAttribute(SpellAttr3.IgnoreHitResult)) - return SpellMissInfo.None; - - // Check for immune - if (victim.IsImmunedToSpell(spellInfo, this)) - return SpellMissInfo.Immune; - - // Damage immunity is only checked if the spell has damage effects, this immunity must not prevent aura apply - // returns SPELL_MISS_IMMUNE in that case, for other spells, the SMSG_SPELL_GO must show hit - if (spellInfo.HasOnlyDamageEffects() && victim.IsImmunedToDamage(spellInfo)) - return SpellMissInfo.Immune; - - switch (spellInfo.DmgClass) - { - case SpellDmgClass.Ranged: - case SpellDmgClass.Melee: - return MeleeSpellHitResult(victim, spellInfo); - case SpellDmgClass.None: - return SpellMissInfo.None; - case SpellDmgClass.Magic: - return MagicSpellHitResult(victim, spellInfo); - } - return SpellMissInfo.None; - } - // Melee based spells hit result calculations - SpellMissInfo MeleeSpellHitResult(Unit victim, SpellInfo spellInfo) + public override SpellMissInfo MeleeSpellHitResult(Unit victim, SpellInfo spellInfo) { WeaponAttackType attType = WeaponAttackType.BaseAttack; @@ -949,142 +878,6 @@ namespace Game.Entities return SpellMissInfo.None; } - SpellMissInfo MagicSpellHitResult(Unit victim, SpellInfo spell) - { - // Can`t miss on dead target (on skinning for example) - if (!victim.IsAlive() && !victim.IsTypeId(TypeId.Player)) - return SpellMissInfo.None; - - SpellSchoolMask schoolMask = spell.GetSchoolMask(); - // PvP - PvE spell misschances per leveldif > 2 - int lchance = victim.IsTypeId(TypeId.Player) ? 7 : 11; - int thisLevel = (int)GetLevelForTarget(victim); - if (IsTypeId(TypeId.Unit) && ToCreature().IsTrigger()) - thisLevel = (int)Math.Max(thisLevel, spell.SpellLevel); - int leveldif = (int)(victim.GetLevelForTarget(this)) - thisLevel; - int levelBasedHitDiff = leveldif; - - // Base hit chance from attacker and victim levels - int modHitChance; - if (levelBasedHitDiff >= 0) - { - if (!victim.IsTypeId(TypeId.Player)) - { - modHitChance = 94 - 3 * Math.Min(levelBasedHitDiff, 3); - levelBasedHitDiff -= 3; - } - else - { - modHitChance = 96 - Math.Min(levelBasedHitDiff, 2); - levelBasedHitDiff -= 2; - } - if (levelBasedHitDiff > 0) - modHitChance -= lchance * Math.Min(levelBasedHitDiff, 7); - } - else - modHitChance = 97 - levelBasedHitDiff; - - // Spellmod from SpellModOp.HitChance - Player modOwner = GetSpellModOwner(); - if (modOwner != null) - modOwner.ApplySpellMod(spell, SpellModOp.HitChance, ref modHitChance); - - // Spells with SPELL_ATTR3_IGNORE_HIT_RESULT will ignore target's avoidance effects - if (!spell.HasAttribute(SpellAttr3.IgnoreHitResult)) - { - // Chance hit from victim SPELL_AURA_MOD_ATTACKER_SPELL_HIT_CHANCE auras - modHitChance += victim.GetTotalAuraModifierByMiscMask(AuraType.ModAttackerSpellHitChance, (int)schoolMask); - } - - int HitChance = modHitChance * 100; - // Increase hit chance from attacker SPELL_AURA_MOD_SPELL_HIT_CHANCE and attacker ratings - HitChance += (int)(modHitChance * 100.0f); - - MathFunctions.RoundToInterval(ref HitChance, 0, 10000); - - int tmp = 10000 - HitChance; - - int rand = RandomHelper.IRand(0, 9999); - if (tmp > 0 && rand < tmp) - return SpellMissInfo.Miss; - - // Spells with SPELL_ATTR3_IGNORE_HIT_RESULT will additionally fully ignore - // resist and deflect chances - if (spell.HasAttribute(SpellAttr3.IgnoreHitResult)) - return SpellMissInfo.None; - - // Chance resist mechanic (select max value from every mechanic spell effect) - int resist_chance = victim.GetMechanicResistChance(spell) * 100; - - // Roll chance - if (resist_chance > 0 && rand < (tmp += resist_chance)) - return SpellMissInfo.Resist; - - // cast by caster in front of victim - if (!victim.HasUnitState(UnitState.Controlled) && (victim.HasInArc(MathFunctions.PI, this) || victim.HasAuraType(AuraType.IgnoreHitDirection))) - { - int deflect_chance = victim.GetTotalAuraModifier(AuraType.DeflectSpells) * 100; - if (deflect_chance > 0 && rand < (tmp += deflect_chance)) - return SpellMissInfo.Deflect; - } - - return SpellMissInfo.None; - } - - public void CastSpell(SpellCastTargets targets, uint spellId, CastSpellExtraArgs args) - { - if (args == null) - args = new CastSpellExtraArgs(); - - SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo(spellId, args.CastDifficulty != Difficulty.None ? args.CastDifficulty : GetMap().GetDifficultyID()); - if (spellInfo == null) - { - Log.outError(LogFilter.Unit, $"CastSpell: unknown spell {spellId} by caster: {GetGUID()}"); - return; - } - - Spell spell = new(this, spellInfo, args.TriggerFlags, args.OriginalCaster); - foreach (var pair in args.SpellValueOverrides) - spell.SetSpellValue(pair.Key, pair.Value); - - spell.m_CastItem = args.CastItem; - spell.Prepare(targets, args.TriggeringAura); - } - - public void CastSpell(WorldObject target, uint spellId, bool triggered) - { - CastSpell(target, spellId, new CastSpellExtraArgs(triggered)); - } - - public void CastSpell(WorldObject target, uint spellId, CastSpellExtraArgs args = null) - { - SpellCastTargets targets = new(); - if (target) - { - Unit unitTarget = target.ToUnit(); - GameObject goTarget = target.ToGameObject(); - if (unitTarget != null) - targets.SetUnitTarget(unitTarget); - else if (goTarget != null) - targets.SetGOTarget(goTarget); - else - { - Log.outError(LogFilter.Unit, $"CastSpell: Invalid target {target.GetGUID()} passed to spell cast by {GetGUID()}"); - return; - } - } - - CastSpell(targets, spellId, args); - } - - public void CastSpell(Position dest, uint spellId, CastSpellExtraArgs args = null) - { - SpellCastTargets targets = new(); - targets.SetDst(dest); - - CastSpell(targets, spellId, args); - } - public void FinishSpell(CurrentSpellTypes spellType, bool ok = true) { Spell spell = GetCurrentSpell(spellType); @@ -1223,7 +1016,7 @@ namespace Game.Entities return spellInfo; } - public uint GetCastSpellXSpellVisualId(SpellInfo spellInfo) + public override uint GetCastSpellXSpellVisualId(SpellInfo spellInfo) { var visualOverrides = GetAuraEffectsByType(AuraType.OverrideSpellVisual); foreach (AuraEffect effect in visualOverrides) @@ -1239,7 +1032,7 @@ namespace Game.Entities } } - return spellInfo.GetSpellXSpellVisualId(this); + return base.GetCastSpellXSpellVisualId(spellInfo); } public SpellHistory GetSpellHistory() { return _spellHistory; } @@ -1520,7 +1313,7 @@ namespace Game.Entities } } } - public virtual bool IsImmunedToSpell(SpellInfo spellInfo, Unit caster) + public virtual bool IsImmunedToSpell(SpellInfo spellInfo, WorldObject caster) { if (spellInfo == null) return false; @@ -1617,7 +1410,7 @@ namespace Game.Entities return mask; } - public virtual bool IsImmunedToSpellEffect(SpellInfo spellInfo, uint index, Unit caster) + public virtual bool IsImmunedToSpellEffect(SpellInfo spellInfo, uint index, WorldObject caster) { if (spellInfo == null) return false; @@ -1887,88 +1680,11 @@ namespace Game.Entities if (GetCurrentSpell(i) != null && GetCurrentSpell(i).m_spellInfo.Id != except_spellid) InterruptSpell(i, false); } - public void ModSpellCastTime(SpellInfo spellInfo, ref int castTime, Spell spell = null) - { - if (spellInfo == null || castTime < 0) - return; - - // called from caster - Player modOwner = GetSpellModOwner(); - if (modOwner != null) - modOwner.ApplySpellMod(spellInfo, SpellModOp.ChangeCastTime, ref castTime, spell); - - if (!(spellInfo.HasAttribute(SpellAttr0.Ability | SpellAttr0.Tradespell) || spellInfo.HasAttribute(SpellAttr3.NoDoneBonus)) - && (IsTypeId(TypeId.Player) && spellInfo.SpellFamilyName != 0) || IsTypeId(TypeId.Unit)) - castTime = CanInstantCast() ? 0 : (int)(castTime * m_unitData.ModCastingSpeed); - else if (spellInfo.HasAttribute(SpellAttr0.ReqAmmo) && !spellInfo.HasAttribute(SpellAttr2.AutorepeatFlag)) - castTime = (int)(castTime * m_modAttackSpeedPct[(int)WeaponAttackType.RangedAttack]); - else if (Global.SpellMgr.IsPartOfSkillLine(SkillType.Cooking, spellInfo.Id) && HasAura(67556)) // cooking with Chef Hat. - castTime = 500; - } - public void ModSpellDurationTime(SpellInfo spellInfo, ref int duration, Spell spell = null) - { - if (spellInfo == null || duration < 0) - return; - - if (spellInfo.IsChanneled() && !spellInfo.HasAttribute(SpellAttr5.HasteAffectDuration)) - return; - - // called from caster - Player modOwner = GetSpellModOwner(); - if (modOwner) - modOwner.ApplySpellMod(spellInfo, SpellModOp.ChangeCastTime, ref duration, spell); - - if (!(spellInfo.HasAttribute(SpellAttr0.Ability) || spellInfo.HasAttribute(SpellAttr0.Tradespell) || spellInfo.HasAttribute(SpellAttr3.NoDoneBonus)) && - (IsTypeId(TypeId.Player) && spellInfo.SpellFamilyName != 0) || IsTypeId(TypeId.Unit)) - duration = (int)(duration * m_unitData.ModCastingSpeed); - else if (spellInfo.HasAttribute(SpellAttr0.ReqAmmo) && !spellInfo.HasAttribute(SpellAttr2.AutorepeatFlag)) - duration = (int)(duration * m_modAttackSpeedPct[(int)WeaponAttackType.RangedAttack]); - } - public float ApplyEffectModifiers(SpellInfo spellProto, uint effect_index, float value) - { - Player modOwner = GetSpellModOwner(); - if (modOwner != null) - { - modOwner.ApplySpellMod(spellProto, SpellModOp.Points, ref value); - switch (effect_index) - { - case 0: - modOwner.ApplySpellMod(spellProto, SpellModOp.PointsIndex0, ref value); - break; - case 1: - modOwner.ApplySpellMod(spellProto, SpellModOp.PointsIndex1, ref value); - break; - case 2: - modOwner.ApplySpellMod(spellProto, SpellModOp.PointsIndex2, ref value); - break; - case 3: - modOwner.ApplySpellMod(spellProto, SpellModOp.PointsIndex3, ref value); - break; - case 4: - modOwner.ApplySpellMod(spellProto, SpellModOp.PointsIndex4, ref value); - break; - } - } - return value; - } public ushort GetMaxSkillValueForLevel(Unit target = null) { return (ushort)(target != null ? GetLevelForTarget(target) : GetLevel() * 5); } - public Player GetSpellModOwner() - { - if (IsTypeId(TypeId.Player)) - return ToPlayer(); - - if (HasUnitTypeMask(UnitTypeMask.Pet | UnitTypeMask.Totem | UnitTypeMask.Guardian)) - { - Unit owner = GetOwner(); - if (owner != null && owner.IsTypeId(TypeId.Player)) - return owner.ToPlayer(); - } - return null; - } public Spell GetCurrentSpell(CurrentSpellTypes spellType) { @@ -2283,7 +1999,6 @@ namespace Game.Entities // update for out of range group members (on 1 slot use) aura.SetNeedClientUpdateForTargets(); - Log.outDebug(LogFilter.Spells, "Aura {0} partially interrupted on {1}, new duration: {2} ms", aura.GetId(), GetGUID().ToString(), aura.GetDuration()); } } } @@ -2641,7 +2356,7 @@ namespace Game.Entities diminish.HitCount = currentLevel + 1; } - public bool ApplyDiminishingToDuration(SpellInfo auraSpellInfo, ref int duration, Unit caster, DiminishingLevels previousLevel) + public bool ApplyDiminishingToDuration(SpellInfo auraSpellInfo, ref int duration, WorldObject caster, DiminishingLevels previousLevel) { DiminishingGroup group = auraSpellInfo.GetDiminishingReturnsGroupForSpell(); if (duration == -1 || group == DiminishingGroup.None) @@ -2656,7 +2371,7 @@ namespace Game.Entities if (limitDuration > 0 && duration > limitDuration) { Unit target = targetOwner ?? this; - Unit source = casterOwner ?? caster; + WorldObject source = casterOwner ?? caster; if (target.IsAffectedByDiminishingReturns() && source.IsPlayer()) duration = limitDuration; @@ -2863,9 +2578,6 @@ namespace Game.Entities if (spellInfo == null) return null; - if (!target.IsAlive() && !spellInfo.IsPassive() && !spellInfo.HasAttribute(SpellAttr2.CanTargetDead)) - return null; - return AddAura(spellInfo, SpellConst.MaxEffectMask, target); } @@ -2874,6 +2586,9 @@ namespace Game.Entities if (spellInfo == null) return null; + if (!target.IsAlive() && !spellInfo.IsPassive() && !spellInfo.HasAttribute(SpellAttr2.CanTargetDead)) + return null; + if (target.IsImmunedToSpell(spellInfo, this)) return null; @@ -3037,29 +2752,6 @@ namespace Game.Entities return false; } - // target dependent range checks - public float GetSpellMaxRangeForTarget(Unit target, SpellInfo spellInfo) - { - if (spellInfo.RangeEntry == null) - return 0; - if (spellInfo.RangeEntry.RangeMax[0] == spellInfo.RangeEntry.RangeMax[1]) - return spellInfo.GetMaxRange(); - if (!target) - return spellInfo.GetMaxRange(true); - return spellInfo.GetMaxRange(!IsHostileTo(target)); - } - - public float GetSpellMinRangeForTarget(Unit target, SpellInfo spellInfo) - { - if (spellInfo.RangeEntry == null) - return 0; - if (spellInfo.RangeEntry.RangeMin[0] == spellInfo.RangeEntry.RangeMin[1]) - return spellInfo.GetMinRange(); - if (!target) - return spellInfo.GetMinRange(true); - return spellInfo.GetMinRange(!IsHostileTo(target)); - } - public bool HasAuraType(AuraType auraType) { return !m_modAuras.LookupByKey(auraType).Empty(); @@ -3177,7 +2869,7 @@ namespace Game.Entities return null; } - public List GetDispellableAuraList(Unit caster, uint dispelMask, bool isReflect = false) + public List GetDispellableAuraList(WorldObject caster, uint dispelMask, bool isReflect = false) { List dispelList = new(); @@ -3311,7 +3003,7 @@ namespace Game.Entities } } } - public void RemoveAurasDueToSpellBySteal(uint spellId, ObjectGuid casterGUID, Unit stealer) + public void RemoveAurasDueToSpellBySteal(uint spellId, ObjectGuid casterGUID, WorldObject stealer) { var range = m_ownedAuras.LookupByKey(spellId); foreach (var aura in range) @@ -3344,39 +3036,43 @@ namespace Game.Entities // Cast duration to unsigned to prevent permanent aura's such as Righteous Fury being permanently added to caster uint dur = (uint)Math.Min(2u * Time.Minute * Time.InMilliseconds, aura.GetDuration()); - Aura oldAura = stealer.GetAura(aura.GetId(), aura.GetCasterGUID()); - if (oldAura != null) + Unit unitStealer = stealer.ToUnit(); + if (unitStealer != null) { - if (stealCharge) - oldAura.ModCharges(1); - else - oldAura.ModStackAmount(1); - oldAura.SetDuration((int)dur); - } - else - { - // single target state must be removed before aura creation to preserve existing single target aura - if (aura.IsSingleTarget()) - aura.UnregisterSingleTarget(); - - AuraCreateInfo createInfo = new(aura.GetCastGUID(), aura.GetSpellInfo(), aura.GetCastDifficulty(), effMask, stealer); - createInfo.SetCasterGUID(aura.GetCasterGUID()); - createInfo.SetBaseAmount(baseDamage); - - Aura newAura = Aura.TryRefreshStackOrCreate(createInfo); - if (newAura != null) + Aura oldAura = unitStealer.GetAura(aura.GetId(), aura.GetCasterGUID()); + if (oldAura != null) { - // created aura must not be single target aura, so stealer won't loose it on recast - if (newAura.IsSingleTarget()) + if (stealCharge) + oldAura.ModCharges(1); + else + oldAura.ModStackAmount(1); + oldAura.SetDuration((int)dur); + } + else + { + // single target state must be removed before aura creation to preserve existing single target aura + if (aura.IsSingleTarget()) + aura.UnregisterSingleTarget(); + + AuraCreateInfo createInfo = new(aura.GetCastGUID(), aura.GetSpellInfo(), aura.GetCastDifficulty(), effMask, stealer); + createInfo.SetCasterGUID(aura.GetCasterGUID()); + createInfo.SetBaseAmount(baseDamage); + + Aura newAura = Aura.TryRefreshStackOrCreate(createInfo); + if (newAura != null) { - newAura.UnregisterSingleTarget(); - // bring back single target aura status to the old aura - aura.SetIsSingleTarget(true); - caster.GetSingleCastAuras().Add(aura); + // created aura must not be single target aura, so stealer won't loose it on recast + if (newAura.IsSingleTarget()) + { + newAura.UnregisterSingleTarget(); + // bring back single target aura status to the old aura + aura.SetIsSingleTarget(true); + caster.GetSingleCastAuras().Add(aura); + } + // FIXME: using aura.GetMaxDuration() maybe not blizzlike but it fixes stealing of spells like Innervate + newAura.SetLoadedState(aura.GetMaxDuration(), (int)dur, stealCharge ? 1 : aura.GetCharges(), 1, recalculateMask, damage); + newAura.ApplyForTargets(); } - // FIXME: using aura.GetMaxDuration() maybe not blizzlike but it fixes stealing of spells like Innervate - newAura.SetLoadedState(aura.GetMaxDuration(), (int)dur, stealCharge ? 1 : aura.GetCharges(), 1, recalculateMask, damage); - newAura.ApplyForTargets(); } } @@ -3524,7 +3220,7 @@ namespace Game.Entities } } } - public void RemoveAurasDueToSpellByDispel(uint spellId, uint dispellerSpellId, ObjectGuid casterGUID, Unit dispeller, byte chargesRemoved = 1) + public void RemoveAurasDueToSpellByDispel(uint spellId, uint dispellerSpellId, ObjectGuid casterGUID, WorldObject dispeller, byte chargesRemoved = 1) { foreach (var pair in GetOwnedAuras()) { @@ -4270,6 +3966,14 @@ namespace Game.Entities if (createInfo.CasterGUID.IsEmpty() && !createInfo.GetSpellInfo().IsStackableOnOneSlotWithDifferentCasters()) createInfo.CasterGUID = createInfo.Caster.GetGUID(); + // world gameobjects can't own auras and they send empty casterguid + // checked on sniffs with spell 22247 + if (createInfo.CasterGUID.IsGameObject()) + { + createInfo.Caster = null; + createInfo.CasterGUID.Clear(); + } + // passive and Incanter's Absorption and auras with different type can stack with themselves any number of times if (!createInfo.GetSpellInfo().IsMultiSlotAura()) { @@ -4612,7 +4316,7 @@ namespace Game.Entities }); } - int GetMaxNegativeAuraModifierByMiscValue(AuraType auratype, int miscValue) + public int GetMaxNegativeAuraModifierByMiscValue(AuraType auratype, int miscValue) { return GetMaxNegativeAuraModifier(auratype, aurEff => { diff --git a/Source/Game/Entities/Unit/Unit.cs b/Source/Game/Entities/Unit/Unit.cs index 5fa099c5e..2d483cacb 100644 --- a/Source/Game/Entities/Unit/Unit.cs +++ b/Source/Game/Entities/Unit/Unit.cs @@ -1394,101 +1394,6 @@ namespace Game.Entities SetUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.ShapeshiftForm), (byte)form); } - public int CalcSpellDuration(SpellInfo spellProto) - { - sbyte comboPoints = (sbyte)(m_playerMovingMe != null ? m_playerMovingMe.GetComboPoints() : 0); - - int minduration = spellProto.GetDuration(); - int maxduration = spellProto.GetMaxDuration(); - - int duration; - - if (comboPoints != 0 && minduration != -1 && minduration != maxduration) - duration = minduration + (maxduration - minduration) * comboPoints / 5; - else - duration = minduration; - - return duration; - } - - public int ModSpellDuration(SpellInfo spellProto, Unit target, int duration, bool positive, uint effectMask) - { - // don't mod permanent auras duration - if (duration < 0) - return duration; - - // some auras are not affected by duration modifiers - if (spellProto.HasAttribute(SpellAttr7.IgnoreDurationMods)) - return duration; - - // cut duration only of negative effects - if (!positive) - { - uint mechanic = spellProto.GetSpellMechanicMaskByEffectMask(effectMask); - - int durationMod; - int durationMod_always = 0; - int durationMod_not_stack = 0; - - for (byte i = 1; i <= (int)Mechanics.Enraged; ++i) - { - if (!Convert.ToBoolean(mechanic & 1 << i)) - continue; - // Find total mod value (negative bonus) - int new_durationMod_always = target.GetTotalAuraModifierByMiscValue(AuraType.MechanicDurationMod, i); - // Find max mod (negative bonus) - int new_durationMod_not_stack = target.GetMaxNegativeAuraModifierByMiscValue(AuraType.MechanicDurationModNotStack, i); - // Check if mods applied before were weaker - if (new_durationMod_always < durationMod_always) - durationMod_always = new_durationMod_always; - if (new_durationMod_not_stack < durationMod_not_stack) - durationMod_not_stack = new_durationMod_not_stack; - } - - // Select strongest negative mod - if (durationMod_always > durationMod_not_stack) - durationMod = durationMod_not_stack; - else - durationMod = durationMod_always; - - if (durationMod != 0) - MathFunctions.AddPct(ref duration, durationMod); - - // there are only negative mods currently - durationMod_always = target.GetTotalAuraModifierByMiscValue(AuraType.ModAuraDurationByDispel, (int)spellProto.Dispel); - durationMod_not_stack = target.GetMaxNegativeAuraModifierByMiscValue(AuraType.ModAuraDurationByDispelNotStack, (int)spellProto.Dispel); - - durationMod = 0; - if (durationMod_always > durationMod_not_stack) - durationMod += durationMod_not_stack; - else - durationMod += durationMod_always; - - if (durationMod != 0) - MathFunctions.AddPct(ref duration, durationMod); - } - else - { - // else positive mods here, there are no currently - // when there will be, change GetTotalAuraModifierByMiscValue to GetTotalPositiveAuraModifierByMiscValue - - // Mixology - duration boost - if (target.IsTypeId(TypeId.Player)) - { - if (spellProto.SpellFamilyName == SpellFamilyNames.Potion && ( - Global.SpellMgr.IsSpellMemberOfSpellGroup(spellProto.Id, SpellGroup.ElixirBattle) || - Global.SpellMgr.IsSpellMemberOfSpellGroup(spellProto.Id, SpellGroup.ElixirGuardian))) - { - SpellEffectInfo effect = spellProto.GetEffect(0); - if (target.HasAura(53042) && effect != null && target.HasSpell(effect.TriggerSpell)) - duration *= 2; - } - } - } - - return Math.Max(duration, 0); - } - // creates aura application instance and registers it in lists // aura application effects are handled separately to prevent aura list corruption public AuraApplication _CreateAuraApplication(Aura aura, uint effMask) @@ -1528,8 +1433,8 @@ namespace Game.Entities return aurApp; } - bool HasInterruptFlag(SpellAuraInterruptFlags flags) { return m_interruptMask.HasFlag(flags); } - bool HasInterruptFlag(SpellAuraInterruptFlags2 flags) { return m_interruptMask2.HasFlag(flags); } + bool HasInterruptFlag(SpellAuraInterruptFlags flags) { return m_interruptMask.HasAnyFlag(flags); } + bool HasInterruptFlag(SpellAuraInterruptFlags2 flags) { return m_interruptMask2.HasAnyFlag(flags); } public void AddInterruptMask(SpellAuraInterruptFlags flags, SpellAuraInterruptFlags2 flags2) { @@ -1649,26 +1554,6 @@ namespace Game.Entities RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags.Sheathing); } - public FactionTemplateRecord GetFactionTemplateEntry() - { - FactionTemplateRecord entry = CliDB.FactionTemplateStorage.LookupByKey(GetFaction()); - if (entry == null) - { - Player player = ToPlayer(); - if (player != null) - Log.outError(LogFilter.Unit, "Player {0} has invalid faction (faction template id) #{1}", player.GetName(), GetFaction()); - else - { - Creature creature = ToCreature(); - if (creature != null) - Log.outError(LogFilter.Unit, "Creature (template id: {0}) has invalid faction (faction template id) #{1}", creature.GetCreatureTemplate().Entry, GetFaction()); - else - Log.outError(LogFilter.Unit, "Unit (name={0}, type={1}) has invalid faction (faction template id) #{2}", GetName(), GetTypeId(), GetFaction()); - } - } - return entry; - } - public bool IsInFeralForm() { ShapeShiftForm form = GetShapeshiftForm(); @@ -1902,17 +1787,9 @@ namespace Game.Entities public uint GetMountDisplayId() { return m_unitData.MountDisplayID; } public void SetMountDisplayId(uint mountDisplayId) { SetUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.MountDisplayID), mountDisplayId); } - public virtual Unit GetOwner() - { - ObjectGuid ownerid = GetOwnerGUID(); - if (!ownerid.IsEmpty()) - return Global.ObjAccessor.GetUnit(this, ownerid); - - return null; - } public virtual float GetFollowAngle() { return MathFunctions.PiOver2; } - public ObjectGuid GetOwnerGUID() { return m_unitData.SummonedBy; } + public override ObjectGuid GetOwnerGUID() { return m_unitData.SummonedBy; } public void SetOwnerGUID(ObjectGuid owner) { if (GetOwnerGUID() == owner) @@ -1947,18 +1824,10 @@ namespace Game.Entities public void SetCritterGUID(ObjectGuid guid) { SetUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.Critter), guid); } public ObjectGuid GetBattlePetCompanionGUID() { return m_unitData.BattlePetCompanionGUID; } public void SetBattlePetCompanionGUID(ObjectGuid guid) { SetUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.BattlePetCompanionGUID), guid); } - public ObjectGuid GetCharmerOrOwnerGUID() + public override ObjectGuid GetCharmerOrOwnerGUID() { return !GetCharmerGUID().IsEmpty() ? GetCharmerGUID() : GetOwnerGUID(); } - public ObjectGuid GetCharmerOrOwnerOrOwnGUID() - { - ObjectGuid guid = GetCharmerOrOwnerGUID(); - if (!guid.IsEmpty()) - return guid; - - return GetGUID(); - } public Unit GetCharmer() { ObjectGuid charmerid = GetCharmerGUID(); @@ -1966,22 +1835,6 @@ namespace Game.Entities return Global.ObjAccessor.GetUnit(this, charmerid); return null; } - public Unit GetCharmerOrOwnerOrSelf() - { - Unit u = GetCharmerOrOwner(); - if (u != null) - return u; - - return this; - } - public Player GetCharmerOrOwnerPlayerOrPlayerItself() - { - ObjectGuid guid = GetCharmerOrOwnerGUID(); - if (guid.IsPlayer()) - return Global.ObjAccessor.FindPlayer(guid); - - return IsTypeId(TypeId.Player) ? ToPlayer() : null; - } public Unit GetCharmerOrOwner() { return !GetCharmerGUID().IsEmpty() ? GetCharmer() : GetOwner(); @@ -2044,16 +1897,6 @@ namespace Game.Entities else return ToCreature().GetCreatureTemplate().CreatureType; } - public Player GetAffectingPlayer() - { - if (GetCharmerOrOwnerGUID().IsEmpty()) - return IsTypeId(TypeId.Player) ? ToPlayer() : null; - - Unit owner = GetCharmerOrOwner(); - if (owner != null) - return owner.GetCharmerOrOwnerPlayerOrPlayerItself(); - return null; - } public void DeMorph() { @@ -2079,7 +1922,7 @@ namespace Game.Entities } public bool HasUnitState(UnitState f) { - return m_state.HasFlag(f); + return m_state.HasAnyFlag(f); } public void ClearUnitState(UnitState f) { @@ -2113,146 +1956,8 @@ namespace Game.Entities return false; } - //Faction - public bool IsNeutralToAll() - { - var my_faction = GetFactionTemplateEntry(); - if (my_faction == null || my_faction.Faction == 0) - return true; - - var raw_faction = CliDB.FactionStorage.LookupByKey(my_faction.Faction); - if (raw_faction != null && raw_faction.ReputationIndex >= 0) - return false; - - return my_faction.IsNeutralToAll(); - } - public bool IsHostileTo(Unit unit) - { - return GetReactionTo(unit) <= ReputationRank.Hostile; - } - public bool IsFriendlyTo(Unit unit) - { - return GetReactionTo(unit) >= ReputationRank.Friendly; - } - public ReputationRank GetReactionTo(Unit target) - { - // always friendly to self - if (this == target) - return ReputationRank.Friendly; - - // always friendly to charmer or owner - if (GetCharmerOrOwnerOrSelf() == target.GetCharmerOrOwnerOrSelf()) - return ReputationRank.Friendly; - - if (HasUnitFlag(UnitFlags.PvpAttackable)) - { - if (target.HasUnitFlag(UnitFlags.PvpAttackable)) - { - Player selfPlayerOwner = GetAffectingPlayer(); - Player targetPlayerOwner = target.GetAffectingPlayer(); - - if (selfPlayerOwner != null && targetPlayerOwner != null) - { - // always friendly to other unit controlled by player, or to the player himself - if (selfPlayerOwner == targetPlayerOwner) - return ReputationRank.Friendly; - - // duel - always hostile to opponent - if (selfPlayerOwner.duel != null && selfPlayerOwner.duel.opponent == targetPlayerOwner && selfPlayerOwner.duel.startTime != 0) - return ReputationRank.Hostile; - - // same group - checks dependant only on our faction - skip FFA_PVP for example - if (selfPlayerOwner.IsInRaidWith(targetPlayerOwner)) - return ReputationRank.Friendly; // return true to allow config option AllowTwoSide.Interaction.Group to work - } - - // check FFA_PVP - if (IsFFAPvP() && target.IsFFAPvP()) - return ReputationRank.Hostile; - - if (selfPlayerOwner != null) - { - var targetFactionTemplateEntry = target.GetFactionTemplateEntry(); - if (targetFactionTemplateEntry != null) - { - if (!selfPlayerOwner.HasUnitFlag2(UnitFlags2.IgnoreReputation)) - { - var targetFactionEntry = CliDB.FactionStorage.LookupByKey(targetFactionTemplateEntry.Faction); - if (targetFactionEntry != null) - { - if (targetFactionEntry.CanHaveReputation()) - { - // check contested flags - if (Convert.ToBoolean(targetFactionTemplateEntry.Flags & (uint)FactionTemplateFlags.ContestedGuard) - && selfPlayerOwner.HasPlayerFlag(PlayerFlags.ContestedPVP)) - return ReputationRank.Hostile; - - // if faction has reputation, hostile state depends only from AtWar state - if (selfPlayerOwner.GetReputationMgr().IsAtWar(targetFactionEntry)) - return ReputationRank.Hostile; - return ReputationRank.Friendly; - } - } - } - } - } - } - } - // do checks dependant only on our faction - return GetFactionReactionTo(GetFactionTemplateEntry(), target); - } - ReputationRank GetFactionReactionTo(FactionTemplateRecord factionTemplateEntry, Unit target) - { - // always neutral when no template entry found - if (factionTemplateEntry == null) - return ReputationRank.Neutral; - - var targetFactionTemplateEntry = target.GetFactionTemplateEntry(); - if (targetFactionTemplateEntry == null) - return ReputationRank.Neutral; - - Player targetPlayerOwner = target.GetAffectingPlayer(); - if (targetPlayerOwner != null) - { - // check contested flags - if (Convert.ToBoolean(factionTemplateEntry.Flags & (uint)FactionTemplateFlags.ContestedGuard) - && targetPlayerOwner.HasPlayerFlag(PlayerFlags.ContestedPVP)) - return ReputationRank.Hostile; - ReputationRank repRank = targetPlayerOwner.GetReputationMgr().GetForcedRankIfAny(factionTemplateEntry); - if (repRank != ReputationRank.None) - return repRank; - if (!target.HasUnitFlag2(UnitFlags2.IgnoreReputation)) - { - var factionEntry = CliDB.FactionStorage.LookupByKey(factionTemplateEntry.Faction); - if (factionEntry != null) - { - if (factionEntry.CanHaveReputation()) - { - // CvP case - check reputation, don't allow state higher than neutral when at war - repRank = targetPlayerOwner.GetReputationMgr().GetRank(factionEntry); - if (targetPlayerOwner.GetReputationMgr().IsAtWar(factionEntry)) - repRank = (ReputationRank)Math.Min((int)ReputationRank.Neutral, (int)repRank); - return repRank; - } - } - } - } - - // common faction based check - if (factionTemplateEntry.IsHostileTo(targetFactionTemplateEntry)) - return ReputationRank.Hostile; - if (factionTemplateEntry.IsFriendlyTo(targetFactionTemplateEntry)) - return ReputationRank.Friendly; - if (targetFactionTemplateEntry.IsFriendlyTo(factionTemplateEntry)) - return ReputationRank.Friendly; - if (Convert.ToBoolean(factionTemplateEntry.Flags & (uint)FactionTemplateFlags.HostileByDefault)) - return ReputationRank.Hostile; - // neutral by default - return ReputationRank.Neutral; - } - - public uint GetFaction() { return m_unitData.FactionTemplate; } - public void SetFaction(uint faction) { SetUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.FactionTemplate), faction); } + public override uint GetFaction() { return m_unitData.FactionTemplate; } + public override void SetFaction(uint faction) { SetUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.FactionTemplate), faction); } public void RestoreFaction() { @@ -2834,60 +2539,58 @@ namespace Game.Entities } } - if (damagetype != DamageEffectType.NoDamage) + if (damagetype != DamageEffectType.NoDamage && damagetype != DamageEffectType.DOT) { if (victim != attacker && (spellProto == null || !(spellProto.HasAttribute(SpellAttr7.NoPushbackOnDamage) || spellProto.HasAttribute(SpellAttr3.TreatAsPeriodic)))) { - if (damagetype != DamageEffectType.DOT) + Spell spell = victim.GetCurrentSpell(CurrentSpellTypes.Generic); + if (spell != null) { - Spell spell = victim.GetCurrentSpell(CurrentSpellTypes.Generic); - if (spell != null) + if (spell.GetState() == SpellState.Preparing) { - if (spell.GetState() == SpellState.Preparing) + bool isCastInterrupted() { - bool isCastInterrupted() - { - if (damage == 0) - return spell.m_spellInfo.InterruptFlags.HasAnyFlag(SpellInterruptFlags.ZeroDamageCancels); + if (damage == 0) + return spell.m_spellInfo.InterruptFlags.HasAnyFlag(SpellInterruptFlags.ZeroDamageCancels); - if (victim.IsPlayer() && spell.m_spellInfo.InterruptFlags.HasAnyFlag(SpellInterruptFlags.DamageCancelsPlayerOnly)) - return true; + if (victim.IsPlayer() && spell.m_spellInfo.InterruptFlags.HasAnyFlag(SpellInterruptFlags.DamageCancelsPlayerOnly)) + return true; - if (spell.m_spellInfo.InterruptFlags.HasAnyFlag(SpellInterruptFlags.DamageCancels)) - return true; + if (spell.m_spellInfo.InterruptFlags.HasAnyFlag(SpellInterruptFlags.DamageCancels)) + return true; + return false; + }; + + bool isCastDelayed() + { + if (damage == 0) return false; - }; - bool isCastDelayed() - { - if (damage == 0) - return false; + if (victim.IsPlayer() && spell.m_spellInfo.InterruptFlags.HasAnyFlag(SpellInterruptFlags.DamagePushbackPlayerOnly)) + return true; - if (victim.IsPlayer() && spell.m_spellInfo.InterruptFlags.HasAnyFlag(SpellInterruptFlags.DamagePushbackPlayerOnly)) - return true; + if (spell.m_spellInfo.InterruptFlags.HasAnyFlag(SpellInterruptFlags.DamagePushback)) + return true; - if (spell.m_spellInfo.InterruptFlags.HasAnyFlag(SpellInterruptFlags.DamagePushback)) - return true; - - return false; - } - - if (isCastInterrupted()) - victim.InterruptNonMeleeSpells(false); - else if (isCastDelayed()) - spell.Delayed(); + return false; } - } - if (damage != 0 && victim.IsPlayer()) - { - Spell spell1 = victim.GetCurrentSpell(CurrentSpellTypes.Channeled); - if (spell1 != null) - if (spell1.GetState() == SpellState.Casting && spell1.m_spellInfo.HasChannelInterruptFlag(SpellAuraInterruptFlags.DamageChannelDuration)) - spell1.DelayedChannel(); + if (isCastInterrupted()) + victim.InterruptNonMeleeSpells(false); + else if (isCastDelayed()) + spell.Delayed(); } } + + if (damage != 0 && victim.IsPlayer()) + { + Spell spell1 = victim.GetCurrentSpell(CurrentSpellTypes.Channeled); + if (spell1 != null) + if (spell1.GetState() == SpellState.Casting && spell1.m_spellInfo.HasChannelInterruptFlag(SpellAuraInterruptFlags.DamageChannelDuration)) + spell1.DelayedChannel(); + } + } } @@ -3322,6 +3025,47 @@ namespace Game.Entities return nearMembers[randTarget]; } + public uint GetComboPoints() { return (uint)GetPower(PowerType.ComboPoints); } + + public void AddComboPoints(sbyte count, Spell spell = null) + { + if (count == 0) + return; + + sbyte comboPoints = (sbyte)(spell != null ? spell.m_comboPointGain : GetPower(PowerType.ComboPoints)); + + comboPoints += count; + + if (comboPoints > 5) + comboPoints = 5; + else if (comboPoints < 0) + comboPoints = 0; + + if (!spell) + SetPower(PowerType.ComboPoints, comboPoints); + else + spell.m_comboPointGain = comboPoints; + } + + void GainSpellComboPoints(sbyte count) + { + if (count == 0) + return; + + sbyte cp = (sbyte)GetPower(PowerType.ComboPoints); + + cp += count; + if (cp > 5) cp = 5; + else if (cp < 0) cp = 0; + + SetPower(PowerType.ComboPoints, cp); + } + + public void ClearComboPoints() + { + SetPower(PowerType.ComboPoints, 0); + } + public void ClearAllReactives() { for (ReactiveType i = 0; i < ReactiveType.Max; ++i) @@ -3411,22 +3155,25 @@ namespace Game.Entities return (uint)damageResisted; } - static float CalculateAverageResistReduction(Unit attacker, SpellSchoolMask schoolMask, Unit victim, SpellInfo spellInfo = null) + static float CalculateAverageResistReduction(WorldObject caster, SpellSchoolMask schoolMask, Unit victim, SpellInfo spellInfo = null) { float victimResistance = victim.GetResistance(schoolMask); - if (attacker != null) + if (caster != null) { // pets inherit 100% of masters penetration - // excluding traps - Player player = attacker.GetSpellModOwner(); - if (player != null && attacker.GetEntry() != SharedConst.WorldTrigger) + Player player = caster.GetSpellModOwner(); + if (player != null) { victimResistance += player.GetTotalAuraModifierByMiscMask(AuraType.ModTargetResistance, (int)schoolMask); victimResistance -= player.GetSpellPenetrationItemMod(); } else - victimResistance += attacker.GetTotalAuraModifierByMiscMask(AuraType.ModTargetResistance, (int)schoolMask); + { + Unit unitCaster = caster.ToUnit(); + if (unitCaster != null) + victimResistance += unitCaster.GetTotalAuraModifierByMiscMask(AuraType.ModTargetResistance, (int)schoolMask); + } } // holy resistance exists in pve and comes from level difference, ignore template values @@ -3440,12 +3187,13 @@ namespace Game.Entities victimResistance = Math.Max(victimResistance, 0.0f); // level-based resistance does not apply to binary spells, and cannot be overcome by spell penetration - if (attacker != null && (spellInfo == null || !spellInfo.HasAttribute(SpellCustomAttributes.BinarySpell))) - victimResistance += Math.Max(((float)victim.GetLevelForTarget(attacker) - (float)attacker.GetLevelForTarget(victim)) * 5.0f, 0.0f); + // gameobject caster -- should it have level based resistance? + if (caster != null && !caster.IsGameObject() && (spellInfo == null || !spellInfo.HasAttribute(SpellCustomAttributes.BinarySpell))) + victimResistance += Math.Max(((float)victim.GetLevelForTarget(caster) - (float)caster.GetLevelForTarget(victim)) * 5.0f, 0.0f); uint bossLevel = 83; float bossResistanceConstant = 510.0f; - uint level = attacker != null ? victim.GetLevelForTarget(attacker) : attacker.GetLevel(); + uint level = caster ? victim.GetLevelForTarget(caster) : victim.GetLevel(); float resistanceConstant; if (level == bossLevel) diff --git a/Source/Game/Guilds/Guild.cs b/Source/Game/Guilds/Guild.cs index 66fceba87..00b4e683e 100644 --- a/Source/Game/Guilds/Guild.cs +++ b/Source/Game/Guilds/Guild.cs @@ -2331,9 +2331,9 @@ namespace Game.Guilds return m_achievementSys.HasAchieved(achievementId); } - public void UpdateCriteria(CriteriaTypes type, ulong miscValue1, ulong miscValue2, ulong miscValue3, Unit unit, Player player) + public void UpdateCriteria(CriteriaTypes type, ulong miscValue1, ulong miscValue2, ulong miscValue3, WorldObject refe, Player player) { - m_achievementSys.UpdateCriteria(type, miscValue1, miscValue2, miscValue3, unit, player); + m_achievementSys.UpdateCriteria(type, miscValue1, miscValue2, miscValue3, refe, player); } public void HandleNewsSetSticky(WorldSession session, uint newsId, bool sticky) diff --git a/Source/Game/Maps/GridNotifiers.cs b/Source/Game/Maps/GridNotifiers.cs index e2b0bc835..d383bd936 100644 --- a/Source/Game/Maps/GridNotifiers.cs +++ b/Source/Game/Maps/GridNotifiers.cs @@ -2577,27 +2577,26 @@ namespace Game.Maps class GameObjectFocusCheck : ICheck { - public GameObjectFocusCheck(Unit unit, uint focusId) + public GameObjectFocusCheck(WorldObject caster, uint focusId) { - i_unit = unit; - i_focusId = focusId; + _caster = caster; + _focusId = focusId; } public bool Invoke(GameObject go) { - if (go.GetGoInfo().GetSpellFocusType() != i_focusId) + if (go.GetGoInfo().GetSpellFocusType() != _focusId) return false; if (!go.IsSpawned()) return false; float dist = go.GetGoInfo().GetSpellFocusRadius() / 2.0f; - - return go.IsWithinDistInMap(i_unit, dist); + return go.IsWithinDistInMap(_caster, dist); } - Unit i_unit; - uint i_focusId; + WorldObject _caster; + uint _focusId; } // Find the nearest Fishing hole and return true only if source object is in range of hole @@ -2697,7 +2696,7 @@ namespace Game.Maps public class AnyDeadUnitObjectInRangeCheck : ICheck where T : WorldObject { - public AnyDeadUnitObjectInRangeCheck(Unit searchObj, float range) + public AnyDeadUnitObjectInRangeCheck(WorldObject searchObj, float range) { i_searchObj = searchObj; i_range = range; @@ -2720,16 +2719,14 @@ namespace Game.Maps return false; } - Unit i_searchObj; + WorldObject i_searchObj; float i_range; } public class AnyDeadUnitSpellTargetInRangeCheck : AnyDeadUnitObjectInRangeCheck where T : WorldObject { - public AnyDeadUnitSpellTargetInRangeCheck(Unit searchObj, float range, SpellInfo spellInfo, SpellTargetCheckTypes check, SpellTargetObjectTypes objectType) - : base(searchObj, range) + public AnyDeadUnitSpellTargetInRangeCheck(WorldObject searchObj, float range, SpellInfo spellInfo, SpellTargetCheckTypes check, SpellTargetObjectTypes objectType) : base(searchObj, range) { - i_spellInfo = spellInfo; i_check = new WorldObjectSpellTargetCheck(searchObj, searchObj, spellInfo, check, null, objectType); } @@ -2738,7 +2735,6 @@ namespace Game.Maps return base.Invoke(obj) && i_check.Invoke(obj); } - SpellInfo i_spellInfo; WorldObjectSpellTargetCheck i_check; } diff --git a/Source/Game/Maps/Map.cs b/Source/Game/Maps/Map.cs index a7ab7da6a..4fe17826f 100644 --- a/Source/Game/Maps/Map.cs +++ b/Source/Game/Maps/Map.cs @@ -4994,7 +4994,7 @@ namespace Game.Maps List _updateObjects = new(); - delegate void FarSpellCallback(Map map); + public delegate void FarSpellCallback(Map map); Queue _farSpellCallbacks = new(); #endregion } diff --git a/Source/Game/Networking/Packets/CombatLogPackets.cs b/Source/Game/Networking/Packets/CombatLogPackets.cs index 8478e6aab..b57bb4616 100644 --- a/Source/Game/Networking/Packets/CombatLogPackets.cs +++ b/Source/Game/Networking/Packets/CombatLogPackets.cs @@ -23,7 +23,7 @@ using System.Collections.Generic; namespace Game.Networking.Packets { - class CombatLogServerPacket : ServerPacket + public class CombatLogServerPacket : ServerPacket { public CombatLogServerPacket(ServerOpcodes opcode, ConnectionType connection = ConnectionType.Realm) : base(opcode, connection) { diff --git a/Source/Game/Networking/Packets/SpellPackets.cs b/Source/Game/Networking/Packets/SpellPackets.cs index 75e8de2fe..52777fa3a 100644 --- a/Source/Game/Networking/Packets/SpellPackets.cs +++ b/Source/Game/Networking/Packets/SpellPackets.cs @@ -1181,21 +1181,25 @@ namespace Game.Networking.Packets public void Initialize(Spell spell) { - Health = (long)spell.GetCaster().GetHealth(); - AttackPower = (int)spell.GetCaster().GetTotalAttackPowerValue(spell.GetCaster().GetClass() == Class.Hunter ? WeaponAttackType.RangedAttack : WeaponAttackType.BaseAttack); - SpellPower = spell.GetCaster().SpellBaseDamageBonusDone(SpellSchoolMask.Spell); - Armor = spell.GetCaster().GetArmor(); - PowerType primaryPowerType = spell.GetCaster().GetPowerType(); - bool primaryPowerAdded = false; - foreach (SpellPowerCost cost in spell.GetPowerCost()) + Unit unitCaster = spell.GetCaster().ToUnit(); + if (unitCaster != null) { - PowerData.Add(new SpellLogPowerData((int)cost.Power, spell.GetCaster().GetPower(cost.Power), cost.Amount)); - if (cost.Power == primaryPowerType) - primaryPowerAdded = true; - } + Health = (long)unitCaster.GetHealth(); + AttackPower = (int)unitCaster.GetTotalAttackPowerValue(unitCaster.GetClass() == Class.Hunter ? WeaponAttackType.RangedAttack : WeaponAttackType.BaseAttack); + SpellPower = unitCaster.SpellBaseDamageBonusDone(SpellSchoolMask.Spell); + Armor = unitCaster.GetArmor(); + PowerType primaryPowerType = unitCaster.GetPowerType(); + bool primaryPowerAdded = false; + foreach (SpellPowerCost cost in spell.GetPowerCost()) + { + PowerData.Add(new SpellLogPowerData((int)cost.Power, unitCaster.GetPower(cost.Power), (int)cost.Amount)); + if (cost.Power == primaryPowerType) + primaryPowerAdded = true; + } - if (!primaryPowerAdded) - PowerData.Insert(0, new SpellLogPowerData((int)primaryPowerType, spell.GetCaster().GetPower(primaryPowerType), 0)); + if (!primaryPowerAdded) + PowerData.Insert(0, new SpellLogPowerData((int)primaryPowerType, unitCaster.GetPower(primaryPowerType), 0)); + } } public void Write(WorldPacket data) diff --git a/Source/Game/Reputation/ReputationManager.cs b/Source/Game/Reputation/ReputationManager.cs index 40f0e144f..beb231e88 100644 --- a/Source/Game/Reputation/ReputationManager.cs +++ b/Source/Game/Reputation/ReputationManager.cs @@ -540,7 +540,7 @@ namespace Game return; // always invisible or hidden faction can't change war state - if (factionState.Flags.HasFlag(ReputationFlags.Hidden | ReputationFlags.Header)) + if (factionState.Flags.HasAnyFlag(ReputationFlags.Hidden | ReputationFlags.Header)) return; SetAtWar(factionState, on); @@ -577,7 +577,7 @@ namespace Game void SetInactive(FactionState faction, bool inactive) { // always invisible or hidden faction can't be inactive - if (faction.Flags.HasFlag(ReputationFlags.Hidden | ReputationFlags.Header) || !faction.Flags.HasFlag(ReputationFlags.Visible)) + if (faction.Flags.HasAnyFlag(ReputationFlags.Hidden | ReputationFlags.Header) || !faction.Flags.HasFlag(ReputationFlags.Visible)) return; // already set diff --git a/Source/Game/Scripting/SpellScript.cs b/Source/Game/Scripting/SpellScript.cs index ad5803485..0e9cbb687 100644 --- a/Source/Game/Scripting/SpellScript.cs +++ b/Source/Game/Scripting/SpellScript.cs @@ -536,7 +536,8 @@ namespace Game.Scripting // methods allowing interaction with Spell object // // methods useable during all spell handling phases - public Unit GetCaster() { return m_spell.GetCaster(); } + public Unit GetCaster() { return m_spell.GetCaster().ToUnit(); } + public GameObject GetGObjCaster() { return m_spell.GetCaster().ToGameObject(); } public Unit GetOriginalCaster() { return m_spell.GetOriginalCaster(); } public SpellInfo GetSpellInfo() { return m_spell.GetSpellInfo(); } public Difficulty GetCastDifficulty() { return m_spell.GetCastDifficulty(); } @@ -1453,7 +1454,23 @@ namespace Game.Scripting // returns guid of object which casted the aura (m_originalCaster of the Spell class) public ObjectGuid GetCasterGUID() { return m_aura.GetCasterGUID(); } // returns unit which casted the aura or null if not avalible (caster logged out for example) - public Unit GetCaster() { return m_aura.GetCaster(); } + public Unit GetCaster() + { + WorldObject caster = m_aura.GetCaster(); + if (caster != null) + return caster.ToUnit(); + + return null; + } + // returns gameobject which cast the aura or NULL if not available + public GameObject GetGObjCaster() + { + WorldObject caster = m_aura.GetCaster(); + if (caster != null) + return caster.ToGameObject(); + + return null; + } // returns object on which aura was casted, target for non-area auras, area aura source for area auras public WorldObject GetOwner() { return m_aura.GetOwner(); } // returns owner if it's unit or unit derived object, null otherwise (only for persistent area auras null is returned) diff --git a/Source/Game/Spells/Auras/Aura.cs b/Source/Game/Spells/Auras/Aura.cs index c17f80e2f..623a88cc6 100644 --- a/Source/Game/Spells/Auras/Aura.cs +++ b/Source/Game/Spells/Auras/Aura.cs @@ -330,7 +330,7 @@ namespace Game.Spells m_spellInfo = createInfo._spellInfo; m_castDifficulty = createInfo._castDifficulty; m_castGuid = createInfo._castId; - m_casterGuid = createInfo.CasterGUID.IsEmpty() ? createInfo.Caster.GetGUID() : createInfo.CasterGUID; + m_casterGuid = createInfo.CasterGUID; m_castItemGuid = createInfo.CastItemGUID; m_castItemId = createInfo.CastItemId; m_castItemLevel = createInfo.CastItemLevel; @@ -2457,8 +2457,6 @@ namespace Game.Spells public static Aura TryCreate(AuraCreateInfo createInfo) { - Cypher.Assert(createInfo.Caster != null || !createInfo.CasterGUID.IsEmpty()); - uint effMask = createInfo._auraEffectMask; if (createInfo._targetEffectMask != 0) effMask = createInfo._targetEffectMask; @@ -2472,17 +2470,25 @@ namespace Game.Spells public static Aura Create(AuraCreateInfo createInfo) { - Cypher.Assert(createInfo.Caster != null || !createInfo.CasterGUID.IsEmpty()); - // try to get caster of aura if (!createInfo.CasterGUID.IsEmpty()) { - if (createInfo.GetOwner().GetGUID() == createInfo.CasterGUID) - createInfo.Caster = createInfo.GetOwner().ToUnit(); + // world gameobjects can't own auras and they send empty casterguid + // checked on sniffs with spell 22247 + if (createInfo.CasterGUID.IsGameObject()) + { + createInfo.Caster = null; + createInfo.CasterGUID.Clear(); + } else - createInfo.Caster = Global.ObjAccessor.GetUnit(createInfo.GetOwner(), createInfo.CasterGUID); + { + if (createInfo._owner.GetGUID() == createInfo.CasterGUID) + createInfo.Caster = createInfo._owner.ToUnit(); + else + createInfo.Caster = Global.ObjAccessor.GetUnit(createInfo._owner, createInfo.CasterGUID); + } } - else + else if (createInfo.Caster != null) createInfo.CasterGUID = createInfo.Caster.GetGUID(); // check if aura can be owned by owner diff --git a/Source/Game/Spells/Spell.cs b/Source/Game/Spells/Spell.cs index 5e7212d21..999e2aa8e 100644 --- a/Source/Game/Spells/Spell.cs +++ b/Source/Game/Spells/Spell.cs @@ -36,12 +36,11 @@ namespace Game.Spells { public partial class Spell : IDisposable { - public Spell(Unit caster, SpellInfo info, TriggerCastFlags triggerFlags, ObjectGuid originalCasterGUID = default) + public Spell(WorldObject caster, SpellInfo info, TriggerCastFlags triggerFlags, ObjectGuid originalCasterGUID = default) { m_spellInfo = info; m_caster = (info.HasAttribute(SpellAttr6.CastByCharmer) && caster.GetCharmerOrOwner() != null ? caster.GetCharmerOrOwner() : caster); m_spellValue = new SpellValue(m_spellInfo, caster); - m_preGeneratedPath = new PathGenerator(m_caster); m_castItemLevel = -1; m_castId = ObjectGuid.Create(HighGuid.Cast, SpellCastSource.Normal, m_caster.GetMapId(), m_spellInfo.Id, m_caster.GetMap().GenerateLowGuid(HighGuid.Cast)); m_SpellVisual.SpellXSpellVisualID = caster.GetCastSpellXSpellVisualId(m_spellInfo); @@ -55,13 +54,18 @@ namespace Game.Spells m_spellSchoolMask = m_spellInfo.GetSchoolMask(); // Can be override for some spell (wand shoot for example) - if (m_attackType == WeaponAttackType.RangedAttack) + Player playerCaster = m_caster.ToPlayer(); + if (playerCaster != null) { - if ((m_caster.GetClassMask() & (uint)Class.ClassMaskWandUsers) != 0 && m_caster.IsTypeId(TypeId.Player)) + // wand case + if (m_attackType == WeaponAttackType.RangedAttack) { - Item pItem = m_caster.ToPlayer().GetWeaponForAttack(WeaponAttackType.RangedAttack); - if (pItem != null) - m_spellSchoolMask = (SpellSchoolMask)(1 << (int)pItem.GetTemplate().GetDamageType()); + if ((playerCaster.GetClassMask() & (uint)Class.ClassMaskWandUsers) != 0) + { + Item pItem = playerCaster.GetWeaponForAttack(WeaponAttackType.RangedAttack); + if (pItem != null) + m_spellSchoolMask = (SpellSchoolMask)(1 << (int)pItem.GetTemplate().GetDamageType()); + } } } @@ -75,7 +79,7 @@ namespace Game.Spells m_originalCasterGUID = m_caster.GetGUID(); if (m_originalCasterGUID == m_caster.GetGUID()) - m_originalCaster = m_caster; + m_originalCaster = m_caster.ToUnit(); else { m_originalCaster = Global.ObjAccessor.GetUnit(m_caster, m_originalCasterGUID); @@ -95,7 +99,7 @@ namespace Game.Spells // Determine if spell can be reflected back to the caster // Patch 1.2 notes: Spell Reflection no longer reflects abilities - m_canReflect = m_spellInfo.DmgClass == SpellDmgClass.Magic && !m_spellInfo.HasAttribute(SpellAttr0.Ability) + m_canReflect = caster.IsUnit() && m_spellInfo.DmgClass == SpellDmgClass.Magic && !m_spellInfo.HasAttribute(SpellAttr0.Ability) && !m_spellInfo.HasAttribute(SpellAttr1.CantBeReflected) && !m_spellInfo.HasAttribute(SpellAttr0.UnaffectedByInvulnerability) && !m_spellInfo.IsPassive(); @@ -141,15 +145,15 @@ namespace Game.Spells { // check if object target is valid with needed target flags // for unit case allow corpse target mask because player with not released corpse is a unit target - if ((target.ToUnit() && !Convert.ToBoolean(neededTargets & (SpellCastTargetFlags.UnitMask | SpellCastTargetFlags.CorpseMask))) - || (target.IsTypeId(TypeId.GameObject) && !Convert.ToBoolean(neededTargets & SpellCastTargetFlags.GameobjectMask)) - || (target.IsTypeId(TypeId.Corpse) && !Convert.ToBoolean(neededTargets & SpellCastTargetFlags.CorpseMask))) + if ((target.ToUnit() && !neededTargets.HasAnyFlag(SpellCastTargetFlags.UnitMask | SpellCastTargetFlags.CorpseMask)) + || (target.IsTypeId(TypeId.GameObject) && !neededTargets.HasFlag(SpellCastTargetFlags.GameobjectMask)) + || (target.IsTypeId(TypeId.Corpse) && !neededTargets.HasFlag(SpellCastTargetFlags.CorpseMask))) m_targets.RemoveObjectTarget(); } else { // try to select correct unit target if not provided by client or by serverside cast - if (Convert.ToBoolean(neededTargets & SpellCastTargetFlags.UnitMask)) + if (neededTargets.HasAnyFlag(SpellCastTargetFlags.UnitMask)) { Unit unit = null; // try to use player selection as a target @@ -163,19 +167,19 @@ namespace Game.Spells unit = selectedUnit; } // try to use attacked unit as a target - else if ((m_caster.IsTypeId(TypeId.Unit)) && Convert.ToBoolean(neededTargets & (SpellCastTargetFlags.UnitEnemy | SpellCastTargetFlags.Unit))) - unit = m_caster.GetVictim(); + else if (m_caster.IsTypeId(TypeId.Unit) && neededTargets.HasAnyFlag(SpellCastTargetFlags.UnitEnemy | SpellCastTargetFlags.Unit)) + unit = m_caster.ToUnit().GetVictim(); // didn't find anything - let's use self as target - if (unit == null && Convert.ToBoolean(neededTargets & (SpellCastTargetFlags.UnitRaid | SpellCastTargetFlags.UnitParty | SpellCastTargetFlags.UnitAlly))) - unit = m_caster; + if (unit == null && neededTargets.HasAnyFlag(SpellCastTargetFlags.UnitRaid | SpellCastTargetFlags.UnitParty | SpellCastTargetFlags.UnitAlly)) + unit = m_caster.ToUnit(); m_targets.SetUnitTarget(unit); } } // check if spell needs dst target - if (Convert.ToBoolean(neededTargets & SpellCastTargetFlags.DestLocation)) + if (neededTargets.HasFlag(SpellCastTargetFlags.DestLocation)) { // and target isn't set if (!m_targets.HasDst()) @@ -192,7 +196,7 @@ namespace Game.Spells else m_targets.RemoveDst(); - if (Convert.ToBoolean(neededTargets & SpellCastTargetFlags.SourceLocation)) + if (neededTargets.HasFlag(SpellCastTargetFlags.SourceLocation)) { if (!targets.HasSrc()) m_targets.SetSrc(m_caster); @@ -211,7 +215,7 @@ namespace Game.Spells if (m_spellInfo.GetExplicitTargetMask().HasAnyFlag(SpellCastTargetFlags.UnitEnemy) || (m_spellInfo.GetExplicitTargetMask().HasAnyFlag(SpellCastTargetFlags.Unit) && !m_caster.IsFriendlyTo(target))) { - Unit redirect; + Unit redirect = null; switch (m_spellInfo.DmgClass) { case SpellDmgClass.Magic: @@ -219,10 +223,10 @@ namespace Game.Spells break; case SpellDmgClass.Melee: case SpellDmgClass.Ranged: - redirect = m_caster.GetMeleeHitRedirectTarget(target, m_spellInfo); + // should gameobjects cast damagetype melee/ranged spells this needs to be changed + redirect = m_caster.ToUnit().GetMeleeHitRedirectTarget(target, m_spellInfo); break; default: - redirect = null; break; } if (redirect != null && (redirect != target)) @@ -280,7 +284,7 @@ namespace Game.Spells uint mask = (1u << (int)effect.EffectIndex); foreach (var ihit in m_UniqueTargetInfo) { - if (Convert.ToBoolean(ihit.effectMask & mask)) + if (Convert.ToBoolean(ihit.EffectMask & mask)) { m_channelTargetEffectMask |= mask; break; @@ -696,7 +700,7 @@ namespace Game.Spells void SelectImplicitAreaTargets(uint effIndex, SpellImplicitTargetInfo targetType, uint effMask) { - Unit referer = null; + WorldObject referer = null; switch (targetType.GetReferenceType()) { case SpellTargetReferenceTypes.Src: @@ -712,9 +716,9 @@ namespace Game.Spells // find last added target for this effect foreach (var target in m_UniqueTargetInfo) { - if (Convert.ToBoolean(target.effectMask & (1 << (int)effIndex))) + if (Convert.ToBoolean(target.EffectMask & (1 << (int)effIndex))) { - referer = Global.ObjAccessor.GetUnit(m_caster, target.targetGUID); + referer = Global.ObjAccessor.GetUnit(m_caster, target.TargetGUID); break; } } @@ -756,7 +760,7 @@ namespace Game.Spells Unit targetedUnit = m_targets.GetUnitTarget(); if (targetedUnit != null) { - if (!m_caster.IsInRaidWith(targetedUnit)) + if (!m_caster.IsUnit() || !m_caster.ToUnit().IsInRaidWith(targetedUnit)) { targets.Add(m_targets.GetUnitTarget()); @@ -888,21 +892,25 @@ namespace Game.Spells } case Targets.DestCasterFrontLeap: { - float dist = m_spellInfo.GetEffect(effIndex).CalcRadius(m_caster); + Unit unitCaster = m_caster.ToUnit(); + if (unitCaster == null) + break; + + float dist = m_spellInfo.GetEffect(effIndex).CalcRadius(unitCaster); float angle = targetType.CalcDirectionAngle(); Position pos = new(dest.Position); - m_caster.MovePositionToFirstCollision(pos, dist, angle); + unitCaster.MovePositionToFirstCollision(pos, dist, angle); // Generate path to that point. if (m_preGeneratedPath == null) - m_preGeneratedPath = new(m_caster); + m_preGeneratedPath = new(unitCaster); m_preGeneratedPath.SetPathLengthLimit(dist); // Should we use straightline here ? What do we do when we don't have a full path ? bool pathResult = m_preGeneratedPath.CalculatePath(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), false, true); - if (pathResult && m_preGeneratedPath.GetPathType().HasFlag(PathType.Normal | PathType.Shortcut)) + if (pathResult && m_preGeneratedPath.GetPathType().HasAnyFlag(PathType.Normal | PathType.Shortcut)) { pos.posX = m_preGeneratedPath.GetActualEndPosition().X; pos.posY = m_preGeneratedPath.GetActualEndPosition().Y; @@ -916,14 +924,20 @@ namespace Game.Spells m_caster.UpdateAllowedPositionZ(dest.Position.GetPositionX(), dest.Position.GetPositionY(), ref dest.Position.posZ); break; case Targets.DestSummoner: - TempSummon casterSummon = m_caster.ToTempSummon(); - if (casterSummon != null) + { + Unit unitCaster = m_caster.ToUnit(); + if (unitCaster != null) { - Unit summoner = casterSummon.GetSummoner(); - if (summoner != null) - dest = new SpellDestination(summoner); + TempSummon casterSummon = unitCaster.ToTempSummon(); + if (casterSummon != null) + { + Unit summoner = casterSummon.GetSummoner(); + if (summoner != null) + dest = new SpellDestination(summoner); + } } break; + } default: SpellEffectInfo effect = m_spellInfo.GetEffect(effIndex); if (effect != null) @@ -1055,15 +1069,27 @@ namespace Game.Spells target = m_caster.GetCharmerOrOwner(); break; case Targets.UnitPet: - target = m_caster.GetGuardianPet(); + { + Unit unitCaster = m_caster.ToUnit(); + if (unitCaster != null) + target = unitCaster.GetGuardianPet(); break; + } case Targets.UnitSummoner: - if (m_caster.IsSummon()) - target = m_caster.ToTempSummon().GetSummoner(); + { + Unit unitCaster = m_caster.ToUnit(); + if (unitCaster != null) + if (unitCaster.IsSummon()) + target = unitCaster.ToTempSummon().GetSummoner(); break; + } case Targets.UnitVehicle: - target = m_caster.GetVehicleBase(); + { + Unit unitCaster = m_caster.ToUnit(); + if (unitCaster != null) + target = unitCaster.GetVehicleBase(); break; + } case Targets.UnitPassenger0: case Targets.UnitPassenger1: case Targets.UnitPassenger2: @@ -1072,20 +1098,35 @@ namespace Game.Spells case Targets.UnitPassenger5: case Targets.UnitPassenger6: case Targets.UnitPassenger7: - if (m_caster.IsTypeId(TypeId.Unit) && m_caster.ToCreature().IsVehicle()) - target = m_caster.GetVehicleKit().GetPassenger((sbyte)(targetType.GetTarget() - Targets.UnitPassenger0)); + Creature vehicleBase = m_caster.ToCreature(); + if (vehicleBase != null && vehicleBase.IsVehicle()) + target = vehicleBase.GetVehicleKit().GetPassenger((sbyte)(targetType.GetTarget() - Targets.UnitPassenger0)); break; case Targets.UnitOwnCritter: - target = ObjectAccessor.GetCreatureOrPetOrVehicle(m_caster, m_caster.GetCritterGUID()); + { + Unit unitCaster = m_caster.ToUnit(); + if (unitCaster != null) + target = ObjectAccessor.GetCreatureOrPetOrVehicle(m_caster, unitCaster.GetCritterGUID()); break; + } default: break; } CallScriptObjectTargetSelectHandlers(ref target, effIndex, targetType); - if (target != null && target.ToUnit()) - AddUnitTarget(target.ToUnit(), (uint)(1 << (int)effIndex), checkIfValid); + if (target) + { + Unit unit = target.ToUnit(); + if (unit != null) + AddUnitTarget(unit, 1u << (int)effIndex, checkIfValid); + else + { + GameObject go = target.ToGameObject(); + if (go != null) + AddGOTarget(go, 1u << (int)effIndex); + } + } } void SelectImplicitTargetObjectTargets(uint effIndex, SpellImplicitTargetInfo targetType) @@ -1178,7 +1219,7 @@ namespace Game.Spells float b = Tangent(m_targets.GetPitch()); float a = (srcToDestDelta - dist2d * b) / (dist2d * dist2d); if (a > -0.0001f) - a = 0; + a = 0f; // We should check if triggered spell has greater range (which is true in many cases, and initial spell has too short max range) // limit max range to 300 yards, sometimes triggered spells can have 50000yds @@ -1187,15 +1228,17 @@ namespace Game.Spells if (triggerSpellInfo != null) bestDist = Math.Min(Math.Max(bestDist, triggerSpellInfo.GetMaxRange(false)), Math.Min(dist2d, 300.0f)); + // GameObjects don't cast traj + Unit unitCaster = m_caster.ToUnit(); foreach (var obj in targets) { - if (m_spellInfo.CheckTarget(m_caster, obj, true) != SpellCastResult.SpellCastOk) + if (m_spellInfo.CheckTarget(unitCaster, obj, true) != SpellCastResult.SpellCastOk) continue; Unit unitTarget = obj.ToUnit(); if (unitTarget) { - if (m_caster == obj || m_caster.IsOnVehicle(unitTarget) || unitTarget.GetVehicle()) + if (unitCaster == obj || unitCaster.IsOnVehicle(unitTarget) || unitTarget.GetVehicle()) continue; Creature creatureTarget = unitTarget.ToCreature(); @@ -1228,18 +1271,14 @@ namespace Game.Spells if (dist2d > bestDist) { - float x = (float)(m_targets.GetSrcPos().posX + Math.Cos(m_caster.GetOrientation()) * bestDist); - float y = (float)(m_targets.GetSrcPos().posY + Math.Sin(m_caster.GetOrientation()) * bestDist); + float x = (float)(m_targets.GetSrcPos().posX + Math.Cos(unitCaster.GetOrientation()) * bestDist); + float y = (float)(m_targets.GetSrcPos().posY + Math.Sin(unitCaster.GetOrientation()) * bestDist); float z = m_targets.GetSrcPos().posZ + bestDist * (a * bestDist + b); - SpellDestination dest = new(x, y, z, m_caster.GetOrientation()); + SpellDestination dest = new(x, y, z, unitCaster.GetOrientation()); CallScriptDestinationTargetSelectHandlers(ref dest, effIndex, targetType); m_targets.ModDst(dest); } - - Vehicle veh = m_caster.GetVehicleKit(); - if (veh != null) - veh.SetLastShootPos(m_targets.GetDstPos()); } void SelectImplicitLineTargets(uint effIndex, SpellImplicitTargetInfo targetType, uint effMask) @@ -1322,9 +1361,9 @@ namespace Game.Spells { case SpellEffectName.SummonRafFriend: case SpellEffectName.SummonPlayer: - if (m_caster.IsTypeId(TypeId.Player) && !m_caster.GetTarget().IsEmpty()) + if (m_caster.IsTypeId(TypeId.Player) && !m_caster.ToPlayer().GetTarget().IsEmpty()) { - WorldObject rafTarget = Global.ObjAccessor.FindPlayer(m_caster.GetTarget()); + WorldObject rafTarget = Global.ObjAccessor.FindPlayer(m_caster.ToPlayer().GetTarget()); CallScriptObjectTargetSelectHandlers(ref rafTarget, effIndex, new SpellImplicitTargetInfo()); @@ -1456,7 +1495,7 @@ namespace Game.Spells return retMask; } - void SearchTargets(Notifier notifier, GridMapTypeMask containerMask, Unit referer, Position pos, float radius) + void SearchTargets(Notifier notifier, GridMapTypeMask containerMask, WorldObject referer, Position pos, float radius) { if (containerMask == 0) return; @@ -1493,7 +1532,7 @@ namespace Game.Spells return searcher.GetTarget(); } - void SearchAreaTargets(List targets, float range, Position position, Unit referer, SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectionType, List condList) + void SearchAreaTargets(List targets, float range, Position position, WorldObject referer, SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectionType, List condList) { var containerTypeMask = GetSearcherTypeMask(objectType, condList); if (containerTypeMask == 0) @@ -1699,7 +1738,7 @@ namespace Game.Spells return; if (checkIfValid) - if (m_spellInfo.CheckTarget(m_caster, target, Implicit || m_caster.GetEntry() == SharedConst.WorldTrigger) != SpellCastResult.SpellCastOk) // skip stealth checks for GO casts + if (m_spellInfo.CheckTarget(m_caster, target, Implicit) != SpellCastResult.SpellCastOk) // skip stealth checks for AOE return; // Check for effect immune skip if immuned @@ -1727,10 +1766,8 @@ namespace Game.Spells targetInfo.IsAlive = target.IsAlive(); // Calculate hit result - if (m_originalCaster != null) - targetInfo.MissCondition = m_originalCaster.SpellHitResult(target, m_spellInfo, m_canReflect && !(IsPositive() && m_caster.IsFriendlyTo(target))); - else - targetInfo.MissCondition = SpellMissInfo.Evade; + WorldObject caster = m_originalCaster ? m_originalCaster : m_caster; + targetInfo.MissCondition = caster.SpellHitResult(target, m_spellInfo, m_canReflect && !(IsPositive() && m_caster.IsFriendlyTo(target))); // Spell have speed - need calculate incoming time // Incoming time is zero for self casts. At least I think so. @@ -1755,8 +1792,9 @@ namespace Game.Spells // If target reflect spell back to caster if (targetInfo.MissCondition == SpellMissInfo.Reflect) { - // Calculate reflected spell result on caster - targetInfo.ReflectResult = m_caster.SpellHitResult(m_caster, m_spellInfo, false); + // Calculate reflected spell result on caster (shouldn't be able to reflect gameobject spells) + Unit unitCaster = m_caster.ToUnit(); + targetInfo.ReflectResult = unitCaster.SpellHitResult(unitCaster, m_spellInfo, false); // can't reflect twice // Proc spell reflect aura when missile hits the original target target.m_Events.AddEvent(new ProcReflectDelayed(target, m_originalCasterGUID), target.m_Events.CalculateTime(targetInfo.TimeDelay)); @@ -1868,7 +1906,7 @@ namespace Game.Spells public long GetUnitTargetCountForEffect(uint effect) { - return m_UniqueTargetInfo.Count(targetInfo => (targetInfo.EffectMask & (1 << (int)effect)) != 0); + return m_UniqueTargetInfo.Count(targetInfo => targetInfo.MissCondition == SpellMissInfo.Miss && (targetInfo.EffectMask & (1 << (int)effect)) != 0); } public long GetGameObjectTargetCountForEffect(uint effect) @@ -1881,7 +1919,7 @@ namespace Game.Spells return m_UniqueItemInfo.Count(targetInfo => (targetInfo.EffectMask & (1 << (int)effect)) != 0); } - SpellMissInfo PreprocessSpellHit(Unit unit, TargetInfo hitInfo) + public SpellMissInfo PreprocessSpellHit(Unit unit, TargetInfo hitInfo) { if (unit == null) return SpellMissInfo.Evade; @@ -2058,7 +2096,7 @@ namespace Game.Spells // Haste modifies duration of channeled spells if (m_spellInfo.IsChanneled()) - caster.ModSpellDurationTime(m_spellInfo, hitInfo.AuraDuration, this); + caster.ModSpellDurationTime(m_spellInfo, ref hitInfo.AuraDuration, this); else if (m_spellInfo.HasAttribute(SpellAttr5.HasteAffectDuration)) { int origDuration = hitInfo.AuraDuration; @@ -2172,41 +2210,45 @@ namespace Game.Spells range += Math.Min(3.0f, range * 0.1f); // 10% but no more than 3.0f } - foreach (var ihit in m_UniqueTargetInfo) + foreach (var targetInfo in m_UniqueTargetInfo) { - if (ihit.missCondition == SpellMissInfo.None && Convert.ToBoolean(channelTargetEffectMask & ihit.effectMask)) + if (targetInfo.MissCondition == SpellMissInfo.None && Convert.ToBoolean(channelTargetEffectMask & targetInfo.EffectMask)) { - Unit unit = m_caster.GetGUID() == ihit.targetGUID ? m_caster : Global.ObjAccessor.GetUnit(m_caster, ihit.targetGUID); - + Unit unit = m_caster.GetGUID() == targetInfo.TargetGUID ? m_caster.ToUnit() : Global.ObjAccessor.GetUnit(m_caster, targetInfo.TargetGUID); if (unit == null) { - m_caster.RemoveChannelObject(ihit.targetGUID); + Unit unitCaster = m_caster.ToUnit(); + if (unitCaster != null) + unitCaster.RemoveChannelObject(targetInfo.TargetGUID); continue; } if (IsValidDeadOrAliveTarget(unit)) { - if (Convert.ToBoolean(channelAuraMask & ihit.effectMask)) + if (Convert.ToBoolean(channelAuraMask & targetInfo.EffectMask)) { AuraApplication aurApp = unit.GetAuraApplication(m_spellInfo.Id, m_originalCasterGUID); if (aurApp != null) { if (m_caster != unit && !m_caster.IsWithinDistInMap(unit, range)) { - ihit.effectMask &= ~aurApp.GetEffectMask(); + targetInfo.EffectMask &= ~aurApp.GetEffectMask(); unit.RemoveAura(aurApp); - m_caster.RemoveChannelObject(ihit.targetGUID); + Unit unitCaster = m_caster.ToUnit(); + if (unitCaster != null) + unitCaster.RemoveChannelObject(targetInfo.TargetGUID); continue; } } else // aura is dispelled { - m_caster.RemoveChannelObject(ihit.targetGUID); + if (unitCaster != null) + unitCaster.RemoveChannelObject(targetInfo.TargetGUID); continue; } } - channelTargetEffectMask &= ~ihit.effectMask; // remove from need alive mask effect that have alive target + channelTargetEffectMask &= ~targetInfo.EffectMask; // remove from need alive mask effect that have alive target } } } @@ -2249,20 +2291,22 @@ namespace Game.Spells _spellEvent = new SpellEvent(this); m_caster.m_Events.AddEvent(_spellEvent, m_caster.m_Events.CalculateTime(1)); - //Prevent casting at cast another spell (ServerSide check) - if (!Convert.ToBoolean(_triggeredCastFlags & TriggerCastFlags.IgnoreCastInProgress) && m_caster.IsNonMeleeSpellCast(false, true, true, m_spellInfo.Id == 75) && !m_castId.IsEmpty()) - { - SendCastResult(SpellCastResult.SpellInProgress); - Finish(false); - return; - } - + // check disables if (Global.DisableMgr.IsDisabledFor(DisableType.Spell, m_spellInfo.Id, m_caster)) { SendCastResult(SpellCastResult.SpellUnavailable); Finish(false); return; } + + // Prevent casting at cast another spell (ServerSide check) + if (!_triggeredCastFlags.HasFlag(TriggerCastFlags.IgnoreCastInProgress) && m_caster.ToUnit() != null && m_caster.ToUnit().IsNonMeleeSpellCast(false, true, true, m_spellInfo.Id == 75) && !m_castId.IsEmpty()) + { + SendCastResult(SpellCastResult.SpellInProgress); + Finish(false); + return; + } + LoadScripts(); // Fill cost data (not use power for item casts @@ -2270,7 +2314,7 @@ namespace Game.Spells m_powerCost = m_spellInfo.CalcPowerCost(m_caster, m_spellSchoolMask, this); // Set combo point requirement - if (Convert.ToBoolean(_triggeredCastFlags & TriggerCastFlags.IgnoreComboPoints) || m_CastItem != null || m_caster.m_playerMovingMe == null) + if (Convert.ToBoolean(_triggeredCastFlags & TriggerCastFlags.IgnoreComboPoints) || m_CastItem != null) m_needComboPoints = false; uint param1 = 0, param2 = 0; @@ -2320,8 +2364,8 @@ namespace Game.Spells // exception are only channeled spells that have no casttime and SPELL_ATTR5_CAN_CHANNEL_WHEN_MOVING // (even if they are interrupted on moving, spells with almost immediate effect get to have their effect processed before movement interrupter kicks in) // don't cancel spells which are affected by a SPELL_AURA_CAST_WHILE_WALKING effect - if (((m_spellInfo.IsChanneled() || m_casttime != 0) && m_caster.IsTypeId(TypeId.Player) && !(m_caster.IsCharmed() && m_caster.GetCharmerGUID().IsCreature()) && m_caster.IsMoving() && - m_spellInfo.InterruptFlags.HasAnyFlag(SpellInterruptFlags.Movement) && !m_caster.HasAuraTypeWithAffectMask(AuraType.CastWhileWalking, m_spellInfo))) + if (((m_spellInfo.IsChanneled() || m_casttime != 0) && m_caster.IsPlayer() && !(m_caster.ToPlayer().IsCharmed() && m_caster.ToPlayer().GetCharmerGUID().IsCreature()) && m_caster.ToPlayer().IsMoving() && + m_spellInfo.InterruptFlags.HasFlag(SpellInterruptFlags.Movement)) && !m_caster.ToPlayer().HasAuraTypeWithAffectMask(AuraType.CastWhileWalking, m_spellInfo)) { // 1. Has casttime, 2. Or doesn't have flag to allow movement during channel if (m_casttime != 0 || !m_spellInfo.IsMoveAllowedChannel()) @@ -2333,7 +2377,7 @@ namespace Game.Spells } // focus if not controlled creature - if (m_caster.GetTypeId() == TypeId.Unit && !m_caster.HasUnitFlag(UnitFlags.PlayerControlled)) + if (m_caster.GetTypeId() == TypeId.Unit && !m_caster.ToUnit().HasUnitFlag(UnitFlags.PlayerControlled)) { if (!(m_spellInfo.IsNextMeleeSwingSpell() || IsAutoRepeat())) { @@ -2359,12 +2403,16 @@ namespace Game.Spells Cast(true); else { - // stealth must be removed at cast starting (at show channel bar) - // skip triggered spell (item equip spell casting and other not explicit character casts/item uses) - if (!_triggeredCastFlags.HasAnyFlag(TriggerCastFlags.IgnoreAuraInterruptFlags) && m_spellInfo.IsBreakingStealth() && !m_spellInfo.HasAttribute(SpellAttr2.IgnoreActionAuraInterruptFlags)) - m_caster.RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags.Action); + Unit unitCaster = m_caster.ToUnit(); + if (unitCaster != null) + { + // stealth must be removed at cast starting (at show channel bar) + // skip triggered spell (item equip spell casting and other not explicit character casts/item uses) + if (!_triggeredCastFlags.HasAnyFlag(TriggerCastFlags.IgnoreAuraInterruptFlags) && m_spellInfo.IsBreakingStealth() && !m_spellInfo.HasAttribute(SpellAttr2.IgnoreActionAuraInterruptFlags)) + unitCaster.RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags.Action); - m_caster.SetCurrentCastSpell(this); + unitCaster.SetCurrentCastSpell(this); + } SendSpellStart(); @@ -2402,9 +2450,9 @@ namespace Game.Spells case SpellState.Casting: foreach (var ihit in m_UniqueTargetInfo) { - if (ihit.missCondition == SpellMissInfo.None) + if (ihit.MissCondition == SpellMissInfo.None) { - Unit unit = m_caster.GetGUID() == ihit.targetGUID ? m_caster : Global.ObjAccessor.GetUnit(m_caster, ihit.targetGUID); + Unit unit = m_caster.GetGUID() == ihit.TargetGUID ? m_caster.ToUnit() : Global.ObjAccessor.GetUnit(m_caster, ihit.TargetGUID); if (unit != null) unit.RemoveOwnedAura(m_spellInfo.Id, m_originalCasterGUID, 0, AuraRemoveMode.Cancel); } @@ -2425,9 +2473,13 @@ namespace Game.Spells if (m_selfContainer != null && m_selfContainer == this) m_selfContainer = null; - m_caster.RemoveDynObject(m_spellInfo.Id); - if (m_spellInfo.IsChanneled()) // if not channeled then the object for the current cast wasn't summoned yet - m_caster.RemoveGameObject(m_spellInfo.Id, true); + // originalcaster handles gameobjects/dynobjects for gob caster + if (m_originalCaster != null) + { + m_originalCaster.RemoveDynObject(m_spellInfo.Id); + if (m_spellInfo.IsChanneled()) // if not channeled then the object for the current cast wasn't summoned yet + m_originalCaster.RemoveGameObject(m_spellInfo.Id, true); + } //set state back so finish will be processed m_spellState = oldState; @@ -2498,7 +2550,7 @@ namespace Game.Spells if (!Convert.ToBoolean(_triggeredCastFlags & TriggerCastFlags.IgnoreSetFacing)) if (m_caster.IsTypeId(TypeId.Unit) && m_targets.GetObjectTarget() != null && m_caster != m_targets.GetObjectTarget()) - m_caster.SetInFront(m_targets.GetObjectTarget()); + m_caster.ToCreature().SetInFront(m_targets.GetObjectTarget()); // Should this be done for original caster? Player modOwner = m_caster.GetSpellModOwner(); @@ -2571,10 +2623,14 @@ namespace Game.Spells DiminishingReturnsType type = m_spellInfo.GetDiminishingReturnsGroupType(); if (type == DiminishingReturnsType.All || (type == DiminishingReturnsType.Player && target.IsAffectedByDiminishingReturns())) { - if (target.HasStrongerAuraWithDR(m_spellInfo, m_originalCaster ?? m_caster)) + Unit caster1 = m_originalCaster ? m_originalCaster : m_caster.ToUnit(); + if (caster1 != null) { - cleanupSpell(SpellCastResult.AuraBounced); - return; + if (target.HasStrongerAuraWithDR(m_spellInfo, caster1)) + { + cleanupSpell(SpellCastResult.AuraBounced); + return; + } } } } @@ -2584,13 +2640,13 @@ namespace Game.Spells // if the spell allows the creature to turn while casting, then adjust server-side orientation to face the target now // client-side orientation is handled by the client itself, as the cast target is targeted due to Creature::FocusTarget - if (m_caster.IsTypeId(TypeId.Unit) && !m_caster.HasUnitFlag(UnitFlags.PlayerControlled)) + if (m_caster.IsTypeId(TypeId.Unit) && !m_caster.ToUnit().HasUnitFlag(UnitFlags.PlayerControlled)) { if (!m_spellInfo.HasAttribute(SpellAttr5.DontTurnDuringCast)) { WorldObject objTarget = m_targets.GetObjectTarget(); if (objTarget != null) - m_caster.SetInFront(objTarget); + m_caster.ToCreature().SetInFront(objTarget); } } @@ -2608,12 +2664,16 @@ namespace Game.Spells SetExecutedCurrently(false); return; } - - if (m_spellInfo.HasAttribute(SpellAttr1.DismissPet)) + + Unit unitCaster = m_caster.ToUnit(); + if (unitCaster != null) { - Creature pet = ObjectAccessor.GetCreature(m_caster, m_caster.GetPetGUID()); - if (pet) - pet.DespawnOrUnsummon(); + if (m_spellInfo.HasAttribute(SpellAttr1.DismissPet)) + { + Creature pet = ObjectAccessor.GetCreature(m_caster, unitCaster.GetPetGUID()); + if (pet != null) + pet.DespawnOrUnsummon(); + } } PrepareTriggersExecutedOnHit(); @@ -2682,8 +2742,10 @@ namespace Game.Spells m_spellState = SpellState.Delayed; SetDelayStart(0); - if (m_caster.HasUnitState(UnitState.Casting) && !m_caster.IsNonMeleeSpellCast(false, false, true)) - m_caster.ClearUnitState(UnitState.Casting); + unitCaster = m_caster.ToUnit(); + if (unitCaster != null) + if (unitCaster.HasUnitState(UnitState.Casting) && !unitCaster.IsNonMeleeSpellCast(false, false, true)) + unitCaster.ClearUnitState(UnitState.Casting); } else { @@ -2699,7 +2761,11 @@ namespace Game.Spells foreach (var spellId in spell_triggered) { if (spellId < 0) - m_caster.RemoveAurasDueToSpell((uint)-spellId); + { + unitCaster = m_caster.ToUnit(); + if (unitCaster != null) + unitCaster.RemoveAurasDueToSpell((uint)-spellId); + } else m_caster.CastSpell(m_targets.GetUnitTarget() ?? m_caster, (uint)spellId, new CastSpellExtraArgs(true)); } @@ -2710,10 +2776,10 @@ namespace Game.Spells modOwner.SetSpellModTakingSpell(this, false); //Clear spell cooldowns after every spell is cast if .cheat cooldown is enabled. - if (modOwner.GetCommandStatus(PlayerCommandStates.Cooldown)) + if (m_originalCaster != null && modOwner.GetCommandStatus(PlayerCommandStates.Cooldown)) { - m_caster.GetSpellHistory().ResetCooldown(m_spellInfo.Id, true); - m_caster.GetSpellHistory().RestoreCharge(m_spellInfo.ChargeCategoryId); + m_originalCaster.GetSpellHistory().ResetCooldown(m_spellInfo.Id, true); + m_originalCaster.GetSpellHistory().RestoreCharge(m_spellInfo.ChargeCategoryId); } } @@ -2781,16 +2847,15 @@ namespace Game.Spells // Apply haste mods m_caster.ModSpellDurationTime(m_spellInfo, ref duration, this); - m_spellState = SpellState.Casting; - m_caster.AddInterruptMask(m_spellInfo.ChannelInterruptFlags, m_spellInfo.ChannelInterruptFlags2); SendChannelStart((uint)duration); } else if (duration == -1) - { - m_spellState = SpellState.Casting; - m_caster.AddInterruptMask(m_spellInfo.ChannelInterruptFlags, m_spellInfo.ChannelInterruptFlags2); SendChannelStart((uint)duration); - } + + m_spellState = SpellState.Casting; + + // GameObjects shouldn't cast channeled spells + m_caster.ToUnit().AddInterruptMask(m_spellInfo.ChannelInterruptFlags, m_spellInfo.ChannelInterruptFlags2); } PrepareTargetProcessing(); @@ -2844,7 +2909,7 @@ namespace Game.Spells return m_delayMoment; next_time = m_delayMoment; - if ((m_UniqueTargetInfo.Count > 2 || (m_UniqueTargetInfo.Count == 1 && m_UniqueTargetInfo[0].targetGUID == m_caster.GetGUID())) || !m_UniqueGOTargetInfo.Empty()) + if ((m_UniqueTargetInfo.Count > 2 || (m_UniqueTargetInfo.Count == 1 && m_UniqueTargetInfo[0].TargetGUID == m_caster.GetGUID())) || !m_UniqueGOTargetInfo.Empty()) { offset = 0; // if LaunchDelay was present then the only target that has timeDelay = 0 is m_caster - and that is the only target we want to process now } @@ -2951,24 +3016,25 @@ namespace Game.Spells void _handle_finish_phase() { - if (m_caster.m_playerMovingMe != null) + Unit unitCaster = m_caster.ToUnit(); + if (unitCaster != null) { // Take for real after all targets are processed if (m_needComboPoints) - m_caster.m_playerMovingMe.ClearComboPoints(); + unitCaster.ClearComboPoints(); // Real add combo points from effects if (m_comboPointGain != 0) - m_caster.m_playerMovingMe.GainSpellComboPoints(m_comboPointGain); - } + unitCaster.AddComboPoints(m_comboPointGain); - if (m_caster.ExtraAttacks != 0 && m_spellInfo.HasEffect(SpellEffectName.AddExtraAttacks)) - { - Unit victim = Global.ObjAccessor.GetUnit(m_caster, m_targets.GetOrigUnitTargetGUID()); - if (victim) - m_caster.HandleProcExtraAttackFor(victim); - else - m_caster.ExtraAttacks = 0; + if (unitCaster.ExtraAttacks != 0 && m_spellInfo.HasEffect(SpellEffectName.AddExtraAttacks)) + { + Unit victim = Global.ObjAccessor.GetUnit(unitCaster, m_targets.GetOrigUnitTargetGUID()); + if (victim) + unitCaster.HandleProcExtraAttackFor(victim); + else + unitCaster.ExtraAttacks = 0; + } } // Handle procs on finish @@ -2989,10 +3055,13 @@ namespace Game.Spells void SendSpellCooldown() { + if (!m_caster.IsUnit()) + return; + if (m_CastItem) - m_caster.GetSpellHistory().HandleCooldowns(m_spellInfo, m_CastItem, this); + m_caster.ToUnit().GetSpellHistory().HandleCooldowns(m_spellInfo, m_CastItem, this); else - m_caster.GetSpellHistory().HandleCooldowns(m_spellInfo, m_castItemEntry, this); + m_caster.ToUnit().GetSpellHistory().HandleCooldowns(m_spellInfo, m_castItemEntry, this); } public void Update(uint difftime) @@ -3015,9 +3084,9 @@ namespace Game.Spells // with the exception of spells affected with SPELL_AURA_CAST_WHILE_WALKING effect SpellEffectInfo effect = m_spellInfo.GetEffect(0); if ((m_caster.IsTypeId(TypeId.Player) && m_timer != 0) && - m_caster.IsMoving() && m_spellInfo.InterruptFlags.HasAnyFlag(SpellInterruptFlags.Movement) && - ((effect != null && effect.Effect != SpellEffectName.Stuck) || !m_caster.HasUnitMovementFlag(MovementFlag.FallingFar)) && - !m_caster.HasAuraTypeWithAffectMask(AuraType.CastWhileWalking, m_spellInfo)) + m_caster.ToPlayer().IsMoving() && (m_spellInfo.InterruptFlags.HasFlag(SpellInterruptFlags.Movement)) && + ((effect != null && effect.Effect != SpellEffectName.Stuck) || !m_caster.ToPlayer().HasUnitMovementFlag(MovementFlag.FallingFar)) && + !m_caster.ToPlayer().HasAuraTypeWithAffectMask(AuraType.CastWhileWalking, m_spellInfo)) { // don't cancel for melee, autorepeat, triggered and instant spells if (!m_spellInfo.IsNextMeleeSwingSpell() && !IsAutoRepeat() && !IsTriggered() && !(IsChannelActive() && m_spellInfo.IsMoveAllowedChannel())) @@ -3025,7 +3094,7 @@ namespace Game.Spells // if charmed by creature, trust the AI not to cheat and allow the cast to proceed // @todo this is a hack, "creature" movesplines don't differentiate turning/moving right now // however, checking what type of movement the spline is for every single spline would be really expensive - if (!m_caster.GetCharmerGUID().IsCreature()) + if (!m_caster.ToPlayer().GetCharmerGUID().IsCreature()) Cancel(); } } @@ -3060,7 +3129,7 @@ namespace Game.Spells // Also remove applied auras foreach (TargetInfo target in m_UniqueTargetInfo) { - Unit unit = m_caster.GetGUID() == target.targetGUID ? m_caster : Global.ObjAccessor.GetUnit(m_caster, target.targetGUID); + Unit unit = m_caster.GetGUID() == target.TargetGUID ? m_caster.ToUnit() : Global.ObjAccessor.GetUnit(m_caster, target.TargetGUID); if (unit) unit.RemoveOwnedAura(m_spellInfo.Id, m_originalCasterGUID, 0, AuraRemoveMode.Cancel); } @@ -3089,44 +3158,50 @@ namespace Game.Spells public void Finish(bool ok = true) { + if (m_spellState == SpellState.Finished) + return; + + m_spellState = SpellState.Finished; + if (!m_caster) return; - if (m_spellState == SpellState.Finished) + Unit unitCaster = m_caster.ToUnit(); + if (unitCaster != null) return; - m_spellState = SpellState.Finished; if (m_spellInfo.IsChanneled()) - m_caster.UpdateInterruptMask(); + unitCaster.UpdateInterruptMask(); - if (m_caster.HasUnitState(UnitState.Casting) && !m_caster.IsNonMeleeSpellCast(false, false, true)) - m_caster.ClearUnitState(UnitState.Casting); + if (unitCaster.HasUnitState(UnitState.Casting) && !unitCaster.IsNonMeleeSpellCast(false, false, true)) + unitCaster.ClearUnitState(UnitState.Casting); // Unsummon summon as possessed creatures on spell cancel - if (m_spellInfo.IsChanneled() && m_caster.IsTypeId(TypeId.Player)) + if (m_spellInfo.IsChanneled() && unitCaster.IsTypeId(TypeId.Player)) { - Unit charm = m_caster.GetCharm(); + Unit charm = unitCaster.GetCharm(); if (charm != null) if (charm.IsTypeId(TypeId.Unit) && charm.ToCreature().HasUnitTypeMask(UnitTypeMask.Puppet) && charm.m_unitData.CreatedBySpell == m_spellInfo.Id) ((Puppet)charm).UnSummon(); } - - if (m_caster.IsTypeId(TypeId.Unit)) - m_caster.ToCreature().ReleaseFocus(this); + + Creature creatureCaster = unitCaster.ToCreature(); + if (creatureCaster != null) + creatureCaster.ReleaseFocus(this); if (!ok) return; - if (m_caster.IsTypeId(TypeId.Unit) && m_caster.ToCreature().IsSummon()) + if (unitCaster.IsTypeId(TypeId.Unit) && unitCaster.ToCreature().IsSummon()) { // Unsummon statue - uint spell = m_caster.m_unitData.CreatedBySpell; + uint spell = unitCaster.m_unitData.CreatedBySpell; SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo(spell, GetCastDifficulty()); if (spellInfo != null && spellInfo.IconFileDataId == 134230) { - Log.outDebug(LogFilter.Spells, "Statue {0} is unsummoned in spell {1} finish", m_caster.GetGUID().ToString(), m_spellInfo.Id); - m_caster.SetDeathState(DeathState.JustDied); + Log.outDebug(LogFilter.Spells, "Statue {0} is unsummoned in spell {1} finish", unitCaster.GetGUID().ToString(), m_spellInfo.Id); + unitCaster.SetDeathState(DeathState.JustDied); return; } } @@ -3135,23 +3210,23 @@ namespace Game.Spells { if (!m_spellInfo.HasAttribute(SpellAttr2.NotResetAutoActions)) { - m_caster.ResetAttackTimer(WeaponAttackType.BaseAttack); - if (m_caster.HaveOffhandWeapon()) - m_caster.ResetAttackTimer(WeaponAttackType.OffAttack); - m_caster.ResetAttackTimer(WeaponAttackType.RangedAttack); + unitCaster.ResetAttackTimer(WeaponAttackType.BaseAttack); + if (unitCaster.HaveOffhandWeapon()) + unitCaster.ResetAttackTimer(WeaponAttackType.OffAttack); + unitCaster.ResetAttackTimer(WeaponAttackType.RangedAttack); } } // potions disabled by client, send event "not in combat" if need - if (m_caster.IsTypeId(TypeId.Player)) + if (unitCaster.IsTypeId(TypeId.Player)) { if (m_triggeredByAuraSpell == null) - m_caster.ToPlayer().UpdatePotionCooldown(this); + unitCaster.ToPlayer().UpdatePotionCooldown(this); } // Stop Attack for some spells if (m_spellInfo.HasAttribute(SpellAttr0.StopAttackTarget)) - m_caster.AttackStop(); + unitCaster.AttackStop(); } static void FillSpellCastFailedArgs(T packet, ObjectGuid castId, SpellInfo spellInfo, SpellCastResult result, SpellCustomErrors customError, uint? param1, uint? param2, Player caster) where T : CastFailedBase @@ -3420,8 +3495,15 @@ namespace Game.Spells return; SpellCastFlags castFlags = SpellCastFlags.HasTrajectory; - uint schoolImmunityMask = m_caster.GetSchoolImmunityMask(); - uint mechanicImmunityMask = m_caster.GetMechanicImmunityMask(); + uint schoolImmunityMask = 0; + uint mechanicImmunityMask = 0; + Unit unitCaster = m_caster.ToUnit(); + if (unitCaster != null) + { + schoolImmunityMask = unitCaster.GetSchoolImmunityMask(); + mechanicImmunityMask = unitCaster.GetMechanicImmunityMask(); + } + if (schoolImmunityMask != 0 || mechanicImmunityMask != 0) castFlags |= SpellCastFlags.Immunity; @@ -3431,7 +3513,7 @@ namespace Game.Spells if (m_spellInfo.HasAttribute(SpellAttr0.ReqAmmo) || m_spellInfo.HasAttribute(SpellCustomAttributes.NeedsAmmoData)) castFlags |= SpellCastFlags.Projectile; - if ((m_caster.IsTypeId(TypeId.Player) || (m_caster.IsTypeId(TypeId.Unit) && m_caster.IsPet())) && m_powerCost.Any(cost => cost.Power != PowerType.Health)) + if ((m_caster.IsTypeId(TypeId.Player) || (m_caster.IsTypeId(TypeId.Unit) && m_caster.ToCreature().IsPet())) && m_powerCost.Any(cost => cost.Power != PowerType.Health)) castFlags |= SpellCastFlags.PowerLeftSelf; if (HasPowerTypeCost(PowerType.Runes)) @@ -3462,7 +3544,7 @@ namespace Game.Spells { SpellPowerData powerData; powerData.Type = cost.Power; - powerData.Cost = m_caster.GetPower(cost.Power); + powerData.Cost = m_caster.ToUnit().GetPower(cost.Power); castData.RemainingPower.Add(powerData); } } @@ -3532,10 +3614,10 @@ namespace Game.Spells if (m_spellInfo.HasAttribute(SpellAttr0.ReqAmmo) || m_spellInfo.HasAttribute(SpellCustomAttributes.NeedsAmmoData)) castFlags |= SpellCastFlags.Projectile; // arrows/bullets visual - if ((m_caster.IsTypeId(TypeId.Player) || (m_caster.IsTypeId(TypeId.Unit) && m_caster.IsPet())) && m_powerCost.Any(cost => cost.Power != PowerType.Health)) - castFlags |= SpellCastFlags.PowerLeftSelf; // should only be sent to self, but the current messaging doesn't make that possible + if ((m_caster.IsTypeId(TypeId.Player) || (m_caster.IsTypeId(TypeId.Unit) && m_caster.ToCreature().IsPet())) && m_powerCost.Any(cost => cost.Power != PowerType.Health)) + castFlags |= SpellCastFlags.PowerLeftSelf; - if (m_caster.IsTypeId(TypeId.Player) && m_caster.GetClass() == Class.Deathknight && + if (m_caster.IsTypeId(TypeId.Player) && m_caster.ToPlayer().GetClass() == Class.Deathknight && HasPowerTypeCost(PowerType.Runes) && !_triggeredCastFlags.HasAnyFlag(TriggerCastFlags.IgnorePowerAndReagentCost)) { castFlags |= SpellCastFlags.NoGCD; // same as in SMSG_SPELL_START @@ -3577,7 +3659,7 @@ namespace Game.Spells { SpellPowerData powerData; powerData.Type = cost.Power; - powerData.Cost = m_caster.GetPower(cost.Power); + powerData.Cost = m_caster.ToUnit().GetPower(cost.Power); castData.RemainingPower.Add(powerData); } } @@ -3628,22 +3710,22 @@ namespace Game.Spells // m_needAliveTargetMask req for stop channelig if one target die foreach (var targetInfo in m_UniqueTargetInfo) { - if (targetInfo.effectMask == 0) // No effect apply - all immuned add state + if (targetInfo.EffectMask == 0) // No effect apply - all immuned add state // possibly SPELL_MISS_IMMUNE2 for this?? - targetInfo.missCondition = SpellMissInfo.Immune2; + targetInfo.MissCondition = SpellMissInfo.Immune2; - if (targetInfo.missCondition == SpellMissInfo.None) // hits + if (targetInfo.MissCondition == SpellMissInfo.None) // hits { - data.HitTargets.Add(targetInfo.targetGUID); + data.HitTargets.Add(targetInfo.TargetGUID); data.HitStatus.Add(new SpellHitStatus(SpellMissInfo.None)); - m_channelTargetEffectMask |= targetInfo.effectMask; + m_channelTargetEffectMask |= targetInfo.EffectMask; } else // misses { - data.MissTargets.Add(targetInfo.targetGUID); + data.MissTargets.Add(targetInfo.TargetGUID); - data.MissStatus.Add(new SpellMissStatus(targetInfo.missCondition, targetInfo.reflectResult)); + data.MissStatus.Add(new SpellMissStatus(targetInfo.MissCondition, targetInfo.ReflectResult)); } } @@ -3660,15 +3742,16 @@ namespace Game.Spells InventoryType ammoInventoryType = 0; uint ammoDisplayID = 0; - if (m_caster.IsTypeId(TypeId.Player)) + Player playerCaster = m_caster.ToPlayer(); + if (playerCaster != null) { - Item pItem = m_caster.ToPlayer().GetWeaponForAttack(WeaponAttackType.RangedAttack); + Item pItem = playerCaster.GetWeaponForAttack(WeaponAttackType.RangedAttack); if (pItem) { ammoInventoryType = pItem.GetTemplate().GetInventoryType(); if (ammoInventoryType == InventoryType.Thrown) - ammoDisplayID = pItem.GetDisplayId(m_caster.ToPlayer()); - else if (m_caster.HasAura(46699)) // Requires No Ammo + ammoDisplayID = pItem.GetDisplayId(playerCaster); + else if (playerCaster.HasAura(46699)) // Requires No Ammo { ammoDisplayID = 5996; // normal arrow ammoInventoryType = InventoryType.Ammo; @@ -3677,35 +3760,39 @@ namespace Game.Spells } else { - for (byte i = 0; i < 3; ++i) + Unit unitCaster = m_caster.ToUnit(); + if (unitCaster != null) { - uint itemId = m_caster.GetVirtualItemId(i); - if (itemId != 0) + for (byte i = (int)WeaponAttackType.BaseAttack; i < (int)WeaponAttackType.Max; ++i) { - ItemRecord itemEntry = CliDB.ItemStorage.LookupByKey(itemId); - if (itemEntry != null) + uint itemId = unitCaster.GetVirtualItemId(i); + if (itemId != 0) { - if (itemEntry.ClassID == ItemClass.Weapon) + ItemRecord itemEntry = CliDB.ItemStorage.LookupByKey(itemId); + if (itemEntry != null) { - switch ((ItemSubClassWeapon)itemEntry.SubclassID) + if (itemEntry.ClassID == ItemClass.Weapon) { - case ItemSubClassWeapon.Thrown: - ammoDisplayID = Global.DB2Mgr.GetItemDisplayId(itemId, m_caster.GetVirtualItemAppearanceMod(i)); - ammoInventoryType = (InventoryType)itemEntry.inventoryType; - break; - case ItemSubClassWeapon.Bow: - case ItemSubClassWeapon.Crossbow: - ammoDisplayID = 5996; // is this need fixing? - ammoInventoryType = InventoryType.Ammo; - break; - case ItemSubClassWeapon.Gun: - ammoDisplayID = 5998; // is this need fixing? - ammoInventoryType = InventoryType.Ammo; + switch ((ItemSubClassWeapon)itemEntry.SubclassID) + { + case ItemSubClassWeapon.Thrown: + ammoDisplayID = Global.DB2Mgr.GetItemDisplayId(itemId, unitCaster.GetVirtualItemAppearanceMod(i)); + ammoInventoryType = (InventoryType)itemEntry.inventoryType; + break; + case ItemSubClassWeapon.Bow: + case ItemSubClassWeapon.Crossbow: + ammoDisplayID = 5996; // is this need fixing? + ammoInventoryType = InventoryType.Ammo; + break; + case ItemSubClassWeapon.Gun: + ammoDisplayID = 5998; // is this need fixing? + ammoInventoryType = InventoryType.Ammo; + break; + } + + if (ammoDisplayID != 0) break; } - - if (ammoDisplayID != 0) - break; } } } @@ -3744,6 +3831,8 @@ namespace Game.Spells spellExecuteLog.Effects.Add(spellLogEffect); } + spellExecuteLog.LogData.Initialize(this); + if (!spellExecuteLog.Effects.Empty()) m_caster.SendCombatLogMessage(spellExecuteLog); @@ -3871,29 +3960,39 @@ namespace Game.Spells public void SendChannelUpdate(uint time) { + // GameObjects don't channel + Unit unitCaster = m_caster.ToUnit(); + if (unitCaster == null) + return; + if (time == 0) { - m_caster.ClearChannelObjects(); - m_caster.SetChannelSpellId(0); - m_caster.SetChannelVisual(new SpellCastVisualField()); + unitCaster.ClearChannelObjects(); + unitCaster.SetChannelSpellId(0); + unitCaster.SetChannelVisual(new SpellCastVisualField()); } SpellChannelUpdate spellChannelUpdate = new(); - spellChannelUpdate.CasterGUID = m_caster.GetGUID(); + spellChannelUpdate.CasterGUID = unitCaster.GetGUID(); spellChannelUpdate.TimeRemaining = (int)time; - m_caster.SendMessageToSet(spellChannelUpdate, true); + unitCaster.SendMessageToSet(spellChannelUpdate, true); } void SendChannelStart(uint duration) { + // GameObjects don't channel + Unit unitCaster = m_caster.ToUnit(); + if (unitCaster == null) + return; + SpellChannelStart spellChannelStart = new(); - spellChannelStart.CasterGUID = m_caster.GetGUID(); + spellChannelStart.CasterGUID = unitCaster.GetGUID(); spellChannelStart.SpellID = (int)m_spellInfo.Id; spellChannelStart.Visual = m_SpellVisual; spellChannelStart.ChannelDuration = duration; - uint schoolImmunityMask = m_caster.GetSchoolImmunityMask(); - uint mechanicImmunityMask = m_caster.GetMechanicImmunityMask(); + uint schoolImmunityMask = unitCaster.GetSchoolImmunityMask(); + uint mechanicImmunityMask = unitCaster.GetMechanicImmunityMask(); if (schoolImmunityMask != 0 || mechanicImmunityMask != 0) { @@ -3901,7 +4000,7 @@ namespace Game.Spells spellChannelStart.InterruptImmunities.Value.SchoolImmunities = (int)schoolImmunityMask; spellChannelStart.InterruptImmunities.Value.Immunities = (int)mechanicImmunityMask; } - m_caster.SendMessageToSet(spellChannelStart, true); + unitCaster.SendMessageToSet(spellChannelStart, true); m_timer = (int)duration; @@ -3910,9 +4009,9 @@ namespace Game.Spells // if there is an explicit target, only add channel objects from effects that also hit ut if (!m_targets.GetUnitTargetGUID().IsEmpty()) { - var explicitTarget = m_UniqueTargetInfo.Find(target => target.targetGUID == m_targets.GetUnitTargetGUID()); + var explicitTarget = m_UniqueTargetInfo.Find(target => target.TargetGUID == m_targets.GetUnitTargetGUID()); if (explicitTarget != null) - explicitTargetEffectMask = explicitTarget.effectMask; + explicitTargetEffectMask = explicitTarget.EffectMask; } foreach (SpellEffectInfo effect in m_spellInfo.GetEffects()) @@ -3921,38 +4020,43 @@ namespace Game.Spells foreach (TargetInfo target in m_UniqueTargetInfo) { - if ((target.effectMask & channelAuraMask) != 0) - m_caster.AddChannelObject(target.targetGUID); + if ((target.EffectMask & channelAuraMask) != 0) + unitCaster.AddChannelObject(target.TargetGUID); if (m_UniqueTargetInfo.Count == 1 && m_UniqueGOTargetInfo.Empty()) { - if (target.targetGUID != m_caster.GetGUID()) + if (target.TargetGUID != unitCaster.GetGUID()) { - Creature creatureCaster = m_caster.ToCreature(); + Creature creatureCaster = unitCaster.ToCreature(); if (creatureCaster != null) if (!creatureCaster.IsFocusing(this)) - creatureCaster.FocusTarget(this, Global.ObjAccessor.GetWorldObject(creatureCaster, target.targetGUID)); + creatureCaster.FocusTarget(this, Global.ObjAccessor.GetWorldObject(creatureCaster, target.TargetGUID)); } } } foreach (GOTargetInfo target in m_UniqueGOTargetInfo) - if ((target.effectMask & channelAuraMask) != 0) - m_caster.AddChannelObject(target.TargetGUID); + if ((target.EffectMask & channelAuraMask) != 0) + unitCaster.AddChannelObject(target.TargetGUID); - m_caster.SetChannelSpellId(m_spellInfo.Id); - m_caster.SetChannelVisual(m_SpellVisual); + unitCaster.SetChannelSpellId(m_spellInfo.Id); + unitCaster.SetChannelVisual(m_SpellVisual); } void SendResurrectRequest(Player target) { - // get ressurector name for creature resurrections, otherwise packet will be not accepted + // get resurrector name for creature resurrections, otherwise packet will be not accepted // for player resurrections the name is looked up by guid - string sentName = (m_caster.IsTypeId(TypeId.Player) ? "" : m_caster.GetName(target.GetSession().GetSessionDbLocaleIndex())); + string sentName = ""; + if (!m_caster.IsPlayer()) + sentName = m_caster.GetName(target.GetSession().GetSessionDbLocaleIndex()); ResurrectRequest resurrectRequest = new(); resurrectRequest.ResurrectOffererGUID = m_caster.GetGUID(); resurrectRequest.ResurrectOffererVirtualRealmAddress = Global.WorldMgr.GetVirtualRealmAddress(); + resurrectRequest.Name = sentName; + resurrectRequest.Sickness = m_caster.IsUnit() && !m_caster.IsTypeId(TypeId.Player); // "you'll be afflicted with resurrection sickness" + resurrectRequest.UseTimer = !m_spellInfo.HasAttribute(SpellAttr3.IgnoreResurrectionTimer); Pet pet = target.GetPet(); if (pet) @@ -3964,11 +4068,6 @@ namespace Game.Spells resurrectRequest.SpellID = m_spellInfo.Id; - //packet.ReadBit("UseTimer"); // @todo: 6.x Has to be implemented - resurrectRequest.Sickness = !m_caster.IsTypeId(TypeId.Player); // "you'll be afflicted with resurrection sickness" - - resurrectRequest.Name = sentName; - target.SendPacket(resurrectRequest); } @@ -4041,20 +4140,25 @@ namespace Game.Spells void TakePower() { + // GameObjects don't use power + Unit unitCaster = m_caster.ToUnit(); + if (!unitCaster) + return; + if (m_CastItem != null || m_triggeredByAuraSpell != null) return; //Don't take power if the spell is cast while .cheat power is enabled. - if (m_caster.IsTypeId(TypeId.Player)) + if (unitCaster.IsTypeId(TypeId.Player)) { - if (m_caster.ToPlayer().GetCommandStatus(PlayerCommandStates.Power)) + if (unitCaster.ToPlayer().GetCommandStatus(PlayerCommandStates.Power)) return; } foreach (SpellPowerCost cost in m_powerCost) { bool hit = true; - if (m_caster.IsTypeId(TypeId.Player)) + if (unitCaster.IsTypeId(TypeId.Player)) { if (cost.Power == PowerType.Rage || cost.Power == PowerType.Energy || cost.Power == PowerType.Runes) { @@ -4066,7 +4170,7 @@ namespace Game.Spells { hit = false; //lower spell cost on fail (by talent aura) - Player modOwner = m_caster.ToPlayer().GetSpellModOwner(); + Player modOwner = unitCaster.GetSpellModOwner(); if (modOwner != null) modOwner.ApplySpellMod(m_spellInfo, SpellModOp.PowerCostOnMiss, ref cost.Amount); } @@ -4086,7 +4190,7 @@ namespace Game.Spells // health as power used if (cost.Power == PowerType.Health) { - m_caster.ModifyHealth(-cost.Amount); + unitCaster.ModifyHealth(-cost.Amount); continue; } @@ -4096,7 +4200,7 @@ namespace Game.Spells continue; } - m_caster.ModifyPower(cost.Power, -cost.Amount); + unitCaster.ModifyPower(cost.Power, -cost.Amount); } } @@ -4126,7 +4230,7 @@ namespace Game.Spells void TakeRunePower(bool didHit) { - if (!m_caster.IsTypeId(TypeId.Player) || m_caster.GetClass() != Class.Deathknight) + if (!m_caster.IsTypeId(TypeId.Player) || m_caster.ToPlayer().GetClass() != Class.Deathknight) return; Player player = m_caster.ToPlayer(); @@ -4196,6 +4300,11 @@ namespace Game.Spells void HandleThreatSpells() { + // wild GameObject spells don't cause threat + Unit unitCaster = (m_originalCaster ? m_originalCaster : m_caster.ToUnit()); + if (unitCaster == null) + return; + if (m_UniqueTargetInfo.Empty()) return; @@ -4207,7 +4316,7 @@ namespace Game.Spells if (threatEntry != null) { if (threatEntry.apPctMod != 0.0f) - threat += threatEntry.apPctMod * m_caster.GetTotalAttackPowerValue(WeaponAttackType.BaseAttack); + threat += threatEntry.apPctMod * unitCaster.GetTotalAttackPowerValue(WeaponAttackType.BaseAttack); threat += threatEntry.flatMod; } @@ -4224,23 +4333,23 @@ namespace Game.Spells foreach (var ihit in m_UniqueTargetInfo) { float threatToAdd = threat; - if (ihit.missCondition != SpellMissInfo.None) + if (ihit.MissCondition != SpellMissInfo.None) threatToAdd = 0.0f; - Unit target = Global.ObjAccessor.GetUnit(m_caster, ihit.targetGUID); + Unit target = Global.ObjAccessor.GetUnit(unitCaster, ihit.TargetGUID); if (target == null) continue; // positive spells distribute threat among all units that are in combat with target, like healing if (IsPositive()) - target.GetThreatManager().ForwardThreatForAssistingMe(m_caster, threatToAdd, m_spellInfo); + target.GetThreatManager().ForwardThreatForAssistingMe(unitCaster, threatToAdd, m_spellInfo); // for negative spells threat gets distributed among affected targets else { if (!target.CanHaveThreatList()) continue; - target.GetThreatManager().AddThreat(m_caster, threatToAdd, m_spellInfo, true); + target.GetThreatManager().AddThreat(unitCaster, threatToAdd, m_spellInfo, true); } } Log.outDebug(LogFilter.Spells, "Spell {0}, added an additional {1} threat for {2} {3} target(s)", m_spellInfo.Id, threat, IsPositive() ? "assisting" : "harming", m_UniqueTargetInfo.Count); @@ -4253,6 +4362,7 @@ namespace Game.Spells itemTarget = pItemTarget; gameObjTarget = pGOTarget; destTarget = m_destTargets[i].Position; + unitCaster = m_originalCaster ? m_originalCaster : m_caster.ToUnit(); effectInfo = m_spellInfo.GetEffect(i); if (effectInfo == null) @@ -4260,16 +4370,14 @@ namespace Game.Spells Log.outError(LogFilter.Spells, "Spell: {0} HandleEffects at EffectIndex: {1} missing effect", m_spellInfo.Id, i); return; } - SpellEffectName eff = effectInfo.Effect; - - Log.outDebug(LogFilter.Spells, "Spell: {0} Effect : {1}", m_spellInfo.Id, eff); + SpellEffectName effect = effectInfo.Effect; damage = CalculateDamage(i, unitTarget, out _variance); bool preventDefault = CallScriptEffectHandlers(i, mode); - if (!preventDefault && eff < SpellEffectName.TotalSpellEffects) - Global.SpellMgr.GetSpellEffectHandler(eff).Invoke(this, i); + if (!preventDefault) + Global.SpellMgr.GetSpellEffectHandler(effect).Invoke(this, i); } public static Spell ExtractSpellFromEvent(BasicEvent basicEvent) @@ -4292,28 +4400,29 @@ namespace Game.Spells SpellCastResult castResult; // check death state - if (!m_caster.IsAlive() && !m_spellInfo.IsPassive() && !(m_spellInfo.HasAttribute(SpellAttr0.CastableWhileDead) || (IsTriggered() && m_triggeredByAuraSpell == null))) + if (m_caster.ToUnit() && !m_caster.ToUnit().IsAlive() && !m_spellInfo.IsPassive() && !(m_spellInfo.HasAttribute(SpellAttr0.CastableWhileDead) || (IsTriggered() && m_triggeredByAuraSpell == null))) return SpellCastResult.CasterDead; // check cooldowns to prevent cheating if (!m_spellInfo.IsPassive()) { - if (m_caster.IsTypeId(TypeId.Player)) + Player playerCaster = m_caster.ToPlayer(); + if (playerCaster != null) { //can cast triggered (by aura only?) spells while have this flag if (!_triggeredCastFlags.HasAnyFlag(TriggerCastFlags.IgnoreCasterAurastate)) { // These two auras check SpellFamilyName defined by db2 class data instead of current spell SpellFamilyName - if (m_caster.HasAuraType(AuraType.DisableCastingExceptAbilities) + if (playerCaster.HasAuraType(AuraType.DisableCastingExceptAbilities) && !m_spellInfo.HasAttribute(SpellAttr0.ReqAmmo) && !m_spellInfo.HasEffect(SpellEffectName.Attack) && !m_spellInfo.HasAttribute(SpellAttr12.IgnoreCastingDisabled) - && !m_caster.HasAuraTypeWithFamilyFlags(AuraType.DisableCastingExceptAbilities, CliDB.ChrClassesStorage.LookupByKey(m_caster.GetClass()).SpellClassSet, m_spellInfo.SpellFamilyFlags)) + && !playerCaster.HasAuraTypeWithFamilyFlags(AuraType.DisableCastingExceptAbilities, CliDB.ChrClassesStorage.LookupByKey(playerCaster.GetClass()).SpellClassSet, m_spellInfo.SpellFamilyFlags)) return SpellCastResult.CantDoThatRightNow; - if (m_caster.HasAuraType(AuraType.DisableAttackingExceptAbilities)) + if (playerCaster.HasAuraType(AuraType.DisableAttackingExceptAbilities)) { - if (!m_caster.HasAuraTypeWithFamilyFlags(AuraType.DisableAttackingExceptAbilities, CliDB.ChrClassesStorage.LookupByKey(m_caster.GetClass()).SpellClassSet, m_spellInfo.SpellFamilyFlags)) + if (!playerCaster.HasAuraTypeWithFamilyFlags(AuraType.DisableAttackingExceptAbilities, CliDB.ChrClassesStorage.LookupByKey(playerCaster.GetClass()).SpellClassSet, m_spellInfo.SpellFamilyFlags)) { if (m_spellInfo.HasAttribute(SpellAttr0.ReqAmmo) || m_spellInfo.IsNextMeleeSwingSpell() @@ -4330,11 +4439,11 @@ namespace Game.Spells } // check if we are using a potion in combat for the 2nd+ time. Cooldown is added only after caster gets out of combat - if (!IsIgnoringCooldowns() && m_caster.ToPlayer().GetLastPotionId() != 0 && m_CastItem && (m_CastItem.IsPotion() || m_spellInfo.IsCooldownStartedOnEvent())) + if (!IsIgnoringCooldowns() && playerCaster.GetLastPotionId() != 0 && m_CastItem && (m_CastItem.IsPotion() || m_spellInfo.IsCooldownStartedOnEvent())) return SpellCastResult.NotReady; } - if (!m_caster.GetSpellHistory().IsReady(m_spellInfo, m_castItemEntry, IsIgnoringCooldowns())) + if (m_caster.IsUnit() && !m_caster.ToUnit().GetSpellHistory().IsReady(m_spellInfo, m_castItemEntry, IsIgnoringCooldowns())) { if (m_triggeredByAuraSpell != null) return SpellCastResult.DontReport; @@ -4343,7 +4452,7 @@ namespace Game.Spells } } - if (m_spellInfo.HasAttribute(SpellAttr7.IsCheatSpell) && !m_caster.HasUnitFlag2(UnitFlags2.AllowCheatSpells)) + if (m_spellInfo.HasAttribute(SpellAttr7.IsCheatSpell) && m_caster.IsUnit() && !m_caster.ToUnit().HasUnitFlag2(UnitFlags2.AllowCheatSpells)) { m_customError = SpellCustomErrors.GmOnly; return SpellCastResult.CustomError; @@ -4371,86 +4480,90 @@ namespace Game.Spells return SpellCastResult.OnlyIndoors; } - // only check at first call, Stealth auras are already removed at second call - // for now, ignore triggered spells - if (strict && !Convert.ToBoolean(_triggeredCastFlags & TriggerCastFlags.IgnoreShapeshift)) + Unit unitCaster = m_caster.ToUnit(); + if (unitCaster != null) { - bool checkForm = true; - // Ignore form req aura - var ignore = m_caster.GetAuraEffectsByType(AuraType.ModIgnoreShapeshift); - foreach (var aurEff in ignore) + // only check at first call, Stealth auras are already removed at second call + // for now, ignore triggered spells + if (strict && !_triggeredCastFlags.HasFlag(TriggerCastFlags.IgnoreShapeshift)) { - if (!aurEff.IsAffectingSpell(m_spellInfo)) - continue; - - checkForm = false; - break; - } - - if (checkForm) - { - // Cannot be used in this stance/form - SpellCastResult shapeError = m_spellInfo.CheckShapeshift(m_caster.GetShapeshiftForm()); - if (shapeError != SpellCastResult.SpellCastOk) - return shapeError; - - if (m_spellInfo.HasAttribute(SpellAttr0.OnlyStealthed) && !m_caster.HasStealthAura()) - return SpellCastResult.OnlyStealthed; - } - } - - bool reqCombat = true; - var stateAuras = m_caster.GetAuraEffectsByType(AuraType.AbilityIgnoreAurastate); - foreach (var aura in stateAuras) - { - if (aura.IsAffectingSpell(m_spellInfo)) - { - m_needComboPoints = false; - if (aura.GetMiscValue() == 1) + bool checkForm = true; + // Ignore form req aura + var ignore = unitCaster.GetAuraEffectsByType(AuraType.ModIgnoreShapeshift); + foreach (var aurEff in ignore) { - reqCombat = false; + if (!aurEff.IsAffectingSpell(m_spellInfo)) + continue; + + checkForm = false; break; } + + if (checkForm) + { + // Cannot be used in this stance/form + SpellCastResult shapeError = m_spellInfo.CheckShapeshift(unitCaster.GetShapeshiftForm()); + if (shapeError != SpellCastResult.SpellCastOk) + return shapeError; + + if (m_spellInfo.HasAttribute(SpellAttr0.OnlyStealthed) && !unitCaster.HasStealthAura()) + return SpellCastResult.OnlyStealthed; + } } - } - // caster state requirements - // not for triggered spells (needed by execute) - if (!Convert.ToBoolean(_triggeredCastFlags & TriggerCastFlags.IgnoreCasterAurastate)) - { - if (m_spellInfo.CasterAuraState != 0 && !m_caster.HasAuraState(m_spellInfo.CasterAuraState, m_spellInfo, m_caster)) - return SpellCastResult.CasterAurastate; - if (m_spellInfo.ExcludeCasterAuraState != 0 && m_caster.HasAuraState(m_spellInfo.ExcludeCasterAuraState, m_spellInfo, m_caster)) - return SpellCastResult.CasterAurastate; + bool reqCombat = true; + var stateAuras = unitCaster.GetAuraEffectsByType(AuraType.AbilityIgnoreAurastate); + foreach (var aura in stateAuras) + { + if (aura.IsAffectingSpell(m_spellInfo)) + { + m_needComboPoints = false; + if (aura.GetMiscValue() == 1) + { + reqCombat = false; + break; + } + } + } - // Note: spell 62473 requres casterAuraSpell = triggering spell - if (m_spellInfo.CasterAuraSpell != 0 && !m_caster.HasAura(m_spellInfo.CasterAuraSpell)) - return SpellCastResult.CasterAurastate; - if (m_spellInfo.ExcludeCasterAuraSpell != 0 && m_caster.HasAura(m_spellInfo.ExcludeCasterAuraSpell)) - return SpellCastResult.CasterAurastate; + // caster state requirements + // not for triggered spells (needed by execute) + if (!_triggeredCastFlags.HasFlag(TriggerCastFlags.IgnoreCasterAurastate)) + { + if (m_spellInfo.CasterAuraState != 0 && !unitCaster.HasAuraState(m_spellInfo.CasterAuraState, m_spellInfo, unitCaster)) + return SpellCastResult.CasterAurastate; + if (m_spellInfo.ExcludeCasterAuraState != 0 && unitCaster.HasAuraState(m_spellInfo.ExcludeCasterAuraState, m_spellInfo, unitCaster)) + return SpellCastResult.CasterAurastate; - if (reqCombat && m_caster.IsInCombat() && !m_spellInfo.CanBeUsedInCombat()) - return SpellCastResult.AffectingCombat; - } + // Note: spell 62473 requres casterAuraSpell = triggering spell + if (m_spellInfo.CasterAuraSpell != 0 && !unitCaster.HasAura(m_spellInfo.CasterAuraSpell)) + return SpellCastResult.CasterAurastate; + if (m_spellInfo.ExcludeCasterAuraSpell != 0 && unitCaster.HasAura(m_spellInfo.ExcludeCasterAuraSpell)) + return SpellCastResult.CasterAurastate; - // cancel autorepeat spells if cast start when moving - // (not wand currently autorepeat cast delayed to moving stop anyway in spell update code) - // Do not cancel spells which are affected by a SPELL_AURA_CAST_WHILE_WALKING effect - if (m_caster.IsTypeId(TypeId.Player) && m_caster.ToPlayer().IsMoving() && (!m_caster.IsCharmed() || !m_caster.GetCharmerGUID().IsCreature()) && !m_caster.HasAuraTypeWithAffectMask(AuraType.CastWhileWalking, m_spellInfo)) - { - // skip stuck spell to allow use it in falling case and apply spell limitations at movement - SpellEffectInfo effect = m_spellInfo.GetEffect(0); - if ((!m_caster.HasUnitMovementFlag(MovementFlag.FallingFar) || (effect != null && effect.Effect != SpellEffectName.Stuck)) && - (IsAutoRepeat() || m_spellInfo.HasAuraInterruptFlag(SpellAuraInterruptFlags.Standing))) - return SpellCastResult.Moving; - } + if (reqCombat && unitCaster.IsInCombat() && !m_spellInfo.CanBeUsedInCombat()) + return SpellCastResult.AffectingCombat; + } - // Check vehicle flags - if (!Convert.ToBoolean(_triggeredCastFlags & TriggerCastFlags.IgnoreCasterMountedOrOnVehicle)) - { - SpellCastResult vehicleCheck = m_spellInfo.CheckVehicle(m_caster); - if (vehicleCheck != SpellCastResult.SpellCastOk) - return vehicleCheck; + // cancel autorepeat spells if cast start when moving + // (not wand currently autorepeat cast delayed to moving stop anyway in spell update code) + // Do not cancel spells which are affected by a SPELL_AURA_CAST_WHILE_WALKING effect + if (unitCaster.IsPlayer() && unitCaster.ToPlayer().IsMoving() && (!unitCaster.IsCharmed() || !unitCaster.GetCharmerGUID().IsCreature()) && !unitCaster.HasAuraTypeWithAffectMask(AuraType.CastWhileWalking, m_spellInfo)) + { + // skip stuck spell to allow use it in falling case and apply spell limitations at movement + SpellEffectInfo effect = m_spellInfo.GetEffect(0); + if ((!unitCaster.HasUnitMovementFlag(MovementFlag.FallingFar) || (effect != null && effect.Effect != SpellEffectName.Stuck)) && + (IsAutoRepeat() || m_spellInfo.HasAuraInterruptFlag(SpellAuraInterruptFlags.Standing))) + return SpellCastResult.Moving; + } + + // Check vehicle flags + if (!Convert.ToBoolean(_triggeredCastFlags & TriggerCastFlags.IgnoreCasterMountedOrOnVehicle)) + { + SpellCastResult vehicleCheck = m_spellInfo.CheckVehicle(unitCaster); + if (vehicleCheck != SpellCastResult.SpellCastOk) + return vehicleCheck; + } } // check spell cast conditions from database @@ -4478,9 +4591,9 @@ namespace Game.Spells // also, such casts shouldn't be sent to client if (!(m_spellInfo.IsPassive() && (m_targets.GetUnitTarget() == null || m_targets.GetUnitTarget() == m_caster))) { - // Check explicit target for m_originalCaster - todo: get rid of such workarounds - Unit caster = m_caster; - if (m_originalCaster && m_caster.GetEntry() != SharedConst.WorldTrigger) // Do a simplified check for gameobject casts + // Check explicit target for m_originalCaster - todo: get rid of such workarounds + WorldObject caster = m_caster; + if (m_originalCaster != null) caster = m_originalCaster; castResult = m_spellInfo.CheckExplicitTarget(caster, m_targets.GetObjectTarget(), m_targets.GetItemTarget()); @@ -4491,21 +4604,25 @@ namespace Game.Spells Unit unitTarget = m_targets.GetUnitTarget(); if (unitTarget != null) { - castResult = m_spellInfo.CheckTarget(m_caster, unitTarget, m_caster.GetEntry() == SharedConst.WorldTrigger); // skip stealth checks for GO casts + castResult = m_spellInfo.CheckTarget(m_caster, unitTarget, m_caster.IsGameObject()); // skip stealth checks for GO casts if (castResult != SpellCastResult.SpellCastOk) return castResult; - // If it's not a melee spell, check if vision is obscured by SPELL_AURA_INTERFERE_TARGETTING - if (m_spellInfo.DmgClass != SpellDmgClass.Melee) - { - foreach (var auraEffect in m_caster.GetAuraEffectsByType(AuraType.InterfereTargetting)) - if (!m_caster.IsFriendlyTo(auraEffect.GetCaster()) && !unitTarget.HasAura(auraEffect.GetId(), auraEffect.GetCasterGUID())) - return SpellCastResult.VisionObscured; + // If it's not a melee spell, check if vision is obscured by SPELL_AURA_INTERFERE_TARGETTING + if (m_spellInfo.DmgClass != SpellDmgClass.Melee) + { + Unit unitCaster1 = m_caster.ToUnit(); + if (unitCaster1 != null) + { + foreach (var auraEffect in unitCaster1.GetAuraEffectsByType(AuraType.InterfereTargetting)) + if (!unitCaster1.IsFriendlyTo(auraEffect.GetCaster()) && !unitTarget.HasAura(auraEffect.GetId(), auraEffect.GetCasterGUID())) + return SpellCastResult.VisionObscured; - foreach (var auraEffect in unitTarget.GetAuraEffectsByType(AuraType.InterfereTargetting)) - if (!m_caster.IsFriendlyTo(auraEffect.GetCaster()) && (!unitTarget.HasAura(auraEffect.GetId(), auraEffect.GetCasterGUID()) || !m_caster.HasAura(auraEffect.GetId(), auraEffect.GetCasterGUID()))) - return SpellCastResult.VisionObscured; - } + foreach (var auraEffect in unitTarget.GetAuraEffectsByType(AuraType.InterfereTargetting)) + if (!unitCaster1.IsFriendlyTo(auraEffect.GetCaster()) && (!unitTarget.HasAura(auraEffect.GetId(), auraEffect.GetCasterGUID()) || !unitCaster1.HasAura(auraEffect.GetId(), auraEffect.GetCasterGUID()))) + return SpellCastResult.VisionObscured; + } + } if (unitTarget != m_caster) { @@ -4517,17 +4634,18 @@ namespace Game.Spells if (m_spellInfo.HasAttribute(SpellCustomAttributes.ReqTargetFacingCaster) && !unitTarget.HasInArc(MathFunctions.PI, m_caster)) return SpellCastResult.NotInfront; - if (m_caster.GetEntry() != SharedConst.WorldTrigger) // Ignore LOS for gameobjects casts (wrongly casted by a trigger) - { + // Ignore LOS for gameobjects casts + if (!m_caster.IsGameObject()) + { WorldObject losTarget = m_caster; if (IsTriggered() && m_triggeredByAuraSpell != null) { - DynamicObject dynObj = m_caster.GetDynObject(m_triggeredByAuraSpell.Id); + DynamicObject dynObj = m_caster.ToUnit().GetDynObject(m_triggeredByAuraSpell.Id); if (dynObj) losTarget = dynObj; } - if (!m_spellInfo.HasAttribute(SpellAttr2.CanTargetNotInLos) && !m_spellInfo.HasAttribute(SpellAttr5.AlwaysAoeLineOfSight) && !Global.DisableMgr.IsDisabledFor(DisableType.Spell, m_spellInfo.Id, null, DisableFlags.SpellLOS) + if (!m_spellInfo.HasAttribute(SpellAttr2.CanTargetNotInLos) && !m_spellInfo.HasAttribute(SpellAttr5.AlwaysAoeLineOfSight) && !Global.DisableMgr.IsDisabledFor(DisableType.Spell, m_spellInfo.Id, null, (byte)DisableFlags.SpellLOS) && !unitTarget.IsWithinLOSInMap(losTarget, LineOfSightChecks.All, ModelIgnoreFlags.M2)) return SpellCastResult.LineOfSight; } @@ -4540,24 +4658,27 @@ namespace Game.Spells float x, y, z; m_targets.GetDstPos().GetPosition(out x, out y, out z); - if (!m_spellInfo.HasAttribute(SpellAttr2.CanTargetNotInLos) && !m_spellInfo.HasAttribute(SpellAttr5.AlwaysAoeLineOfSight) && !Global.DisableMgr.IsDisabledFor(DisableType.Spell, m_spellInfo.Id, null, DisableFlags.SpellLOS) + if (!m_spellInfo.HasAttribute(SpellAttr2.CanTargetNotInLos) && !m_spellInfo.HasAttribute(SpellAttr5.AlwaysAoeLineOfSight) && !Global.DisableMgr.IsDisabledFor(DisableType.Spell, m_spellInfo.Id, null, (byte)DisableFlags.SpellLOS) && !m_caster.IsWithinLOS(x, y, z, LineOfSightChecks.All, ModelIgnoreFlags.M2)) return SpellCastResult.LineOfSight; } // check pet presence - foreach (SpellEffectInfo effect in m_spellInfo.GetEffects()) + if (unitCaster != null) { - if (effect != null && effect.TargetA.GetTarget() == Targets.UnitPet) + foreach (SpellEffectInfo effect in m_spellInfo.GetEffects()) { - if (m_caster.GetGuardianPet() == null) + if (effect != null && effect.TargetA.GetTarget() == Targets.UnitPet) { - if (m_triggeredByAuraSpell != null) // not report pet not existence for triggered spells - return SpellCastResult.DontReport; - else - return SpellCastResult.NoPet; + if (unitCaster.GetGuardianPet() == null) + { + if (m_triggeredByAuraSpell != null) // not report pet not existence for triggered spells + return SpellCastResult.DontReport; + else + return SpellCastResult.NoPet; + } + break; } - break; } } @@ -4577,7 +4698,7 @@ namespace Game.Spells } // zone check - if (m_caster.IsTypeId(TypeId.Unit) || !m_caster.ToPlayer().IsGameMaster()) + if (!m_caster.IsPlayer() || !m_caster.ToPlayer().IsGameMaster()) { uint zone, area; m_caster.GetZoneAndAreaId(out zone, out area); @@ -4588,19 +4709,21 @@ namespace Game.Spells } // not let players cast spells at mount (and let do it to creatures) - if (m_caster.IsMounted() && m_caster.IsTypeId(TypeId.Player) && !Convert.ToBoolean(_triggeredCastFlags & TriggerCastFlags.IgnoreCasterMountedOrOnVehicle) && - !m_spellInfo.IsPassive() && !m_spellInfo.HasAttribute(SpellAttr0.CastableWhileMounted)) + if (!_triggeredCastFlags.HasFlag(TriggerCastFlags.IgnoreCasterMountedOrOnVehicle)) { - if (m_caster.IsInFlight()) - return SpellCastResult.NotOnTaxi; - else - return SpellCastResult.NotMounted; + if (m_caster.IsPlayer() && m_caster.ToPlayer().IsMounted() && !m_spellInfo.IsPassive() && !m_spellInfo.HasAttribute(SpellAttr0.CastableWhileMounted)) + { + if (m_caster.ToPlayer().IsInFlight()) + return SpellCastResult.NotOnTaxi; + else + return SpellCastResult.NotMounted; + } } // check spell focus object if (m_spellInfo.RequiresSpellFocus != 0) { - if (!m_caster.HasAuraTypeWithMiscvalue(AuraType.ProvideSpellFocus, (int)m_spellInfo.RequiresSpellFocus)) + if (!m_caster.IsUnit() || !m_caster.ToUnit().HasAuraTypeWithMiscvalue(AuraType.ProvideSpellFocus, (int)m_spellInfo.RequiresSpellFocus)) { focusObject = SearchSpellFocus(); if (!focusObject) @@ -4800,14 +4923,17 @@ namespace Game.Spells if (foodItem.GetTemplate().GetBaseItemLevel() + 30 <= pet.GetLevel()) return SpellCastResult.FoodLowlevel; - if (m_caster.IsInCombat() || pet.IsInCombat()) + if (m_caster.ToPlayer().IsInCombat() || pet.IsInCombat()) return SpellCastResult.AffectingCombat; break; } case SpellEffectName.Charge: { - if (!_triggeredCastFlags.HasAnyFlag(TriggerCastFlags.IgnoreCasterAuras) && m_caster.HasUnitState(UnitState.Root)) + if (unitCaster == null) + return SpellCastResult.BadTargets; + + if (!_triggeredCastFlags.HasAnyFlag(TriggerCastFlags.IgnoreCasterAuras) && unitCaster.HasUnitState(UnitState.Root)) return SpellCastResult.Rooted; if (GetSpellInfo().NeedsExplicitUnitTarget()) @@ -4817,12 +4943,13 @@ namespace Game.Spells return SpellCastResult.DontReport; // 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(m_caster)) //Do full LoS/Path check. Don't exclude m2 + if (!target.IsWithinLOSInMap(unitCaster)) //Do full LoS/Path check. Don't exclude m2 return SpellCastResult.LineOfSight; float objSize = target.GetCombatReach(); - float range = m_spellInfo.GetMaxRange(true, m_caster, this) * 1.5f + objSize; // can't be overly strict + float range = m_spellInfo.GetMaxRange(true, unitCaster, this) * 1.5f + objSize; // can't be overly strict + m_preGeneratedPath = new(unitCaster); m_preGeneratedPath.SetPathLengthLimit(range); //first try with raycast, if it fails fall back to normal path float targetObjectSize = Math.Min(target.GetCombatReach(), 4.0f); @@ -4925,8 +5052,10 @@ namespace Game.Spells } case SpellEffectName.ResurrectPet: { - Creature pet = m_caster.GetGuardianPet(); + if (unitCaster == null) + return SpellCastResult.BadTargets; + Creature pet = unitCaster.GetGuardianPet(); if (pet != null && pet.IsAlive()) return SpellCastResult.AlreadyHaveSummon; @@ -4935,6 +5064,9 @@ namespace Game.Spells // This is generic summon effect case SpellEffectName.Summon: { + if (unitCaster == null) + break; + var SummonProperties = CliDB.SummonPropertiesStorage.LookupByKey(effect.MiscValueB); if (SummonProperties == null) break; @@ -4942,11 +5074,11 @@ namespace Game.Spells switch (SummonProperties.Control) { case SummonCategory.Pet: - if (!m_spellInfo.HasAttribute(SpellAttr1.DismissPet) && !m_caster.GetPetGUID().IsEmpty()) + if (!m_spellInfo.HasAttribute(SpellAttr1.DismissPet) && !unitCaster.GetPetGUID().IsEmpty()) return SpellCastResult.AlreadyHaveSummon; - break; + goto case SummonCategory.Puppet; case SummonCategory.Puppet: - if (!m_caster.GetCharmGUID().IsEmpty()) + if (!unitCaster.GetCharmGUID().IsEmpty()) return SpellCastResult.AlreadyHaveCharm; break; } @@ -4965,13 +5097,16 @@ namespace Game.Spells } case SpellEffectName.SummonPet: { - if (!m_caster.GetPetGUID().IsEmpty()) //let warlock do a replacement summon + if (unitCaster == null) + return SpellCastResult.BadTargets; + + if (!unitCaster.GetPetGUID().IsEmpty()) //let warlock do a replacement summon { - if (m_caster.IsTypeId(TypeId.Player)) + if (unitCaster.IsTypeId(TypeId.Player)) { if (strict) //starting cast, trigger pet stun (cast by pet so it doesn't attack player) { - Pet pet = m_caster.ToPlayer().GetPet(); + Pet pet = unitCaster.ToPlayer().GetPet(); if (pet != null) pet.CastSpell(pet, 32752, new CastSpellExtraArgs(pet.GetGUID())); } @@ -4980,7 +5115,7 @@ namespace Game.Spells return SpellCastResult.AlreadyHaveSummon; } - if (!m_caster.GetCharmGUID().IsEmpty()) + if (!unitCaster.GetCharmGUID().IsEmpty()) return SpellCastResult.AlreadyHaveCharm; break; } @@ -4988,6 +5123,7 @@ namespace Game.Spells { if (!m_caster.IsTypeId(TypeId.Player)) return SpellCastResult.BadTargets; + if (m_caster.ToPlayer().GetTarget().IsEmpty()) return SpellCastResult.BadTargets; @@ -5063,9 +5199,12 @@ namespace Game.Spells } case SpellEffectName.LeapBack: { - if (m_caster.HasUnitState(UnitState.Root)) + if (unitCaster == null) + return SpellCastResult.BadTargets; + + if (unitCaster.HasUnitState(UnitState.Root)) { - if (m_caster.IsTypeId(TypeId.Player)) + if (unitCaster.IsTypeId(TypeId.Player)) return SpellCastResult.Rooted; else return SpellCastResult.DontReport; @@ -5075,7 +5214,10 @@ namespace Game.Spells case SpellEffectName.Jump: case SpellEffectName.JumpDest: { - if (m_caster.HasUnitState(UnitState.Root)) + if (unitCaster == null) + return SpellCastResult.BadTargets; + + if (unitCaster.HasUnitState(UnitState.Root)) return SpellCastResult.Rooted; break; } @@ -5086,7 +5228,7 @@ namespace Game.Spells if (!playerCaster) return SpellCastResult.TargetNotPlayer; - if (spec == null || (spec.ClassID != (uint)m_caster.GetClass() && !spec.IsPetSpecialization())) + if (spec == null || (spec.ClassID != (uint)player.GetClass() && !spec.IsPetSpecialization())) return SpellCastResult.NoSpec; if (spec.IsPetSpecialization()) @@ -5105,13 +5247,15 @@ namespace Game.Spells } case SpellEffectName.RemoveTalent: { - if (!m_caster.IsTypeId(TypeId.Player)) + Player playerCaster = m_caster.ToPlayer(); + if (playerCaster == null) return SpellCastResult.BadTargets; TalentRecord talent = CliDB.TalentStorage.LookupByKey(m_misc.TalentId); if (talent == null) return SpellCastResult.DontReport; - if (m_caster.GetSpellHistory().HasCooldown(talent.SpellID)) + + if (playerCaster.GetSpellHistory().HasCooldown(talent.SpellID)) { param1 = talent.SpellID; return SpellCastResult.CantUntalent; @@ -5121,15 +5265,18 @@ namespace Game.Spells case SpellEffectName.GiveArtifactPower: case SpellEffectName.GiveArtifactPowerNoBonus: { - if (!m_caster.IsTypeId(TypeId.Player)) + Player playerCaster = m_caster.ToPlayer(); + if (playerCaster == null) return SpellCastResult.BadTargets; - Aura artifactAura = m_caster.GetAura(PlayerConst.ArtifactsAllWeaponsGeneralWeaponEquippedPassive); + Aura artifactAura = playerCaster.GetAura(PlayerConst.ArtifactsAllWeaponsGeneralWeaponEquippedPassive); if (artifactAura == null) return SpellCastResult.NoArtifactEquipped; - Item artifact = m_caster.ToPlayer().GetItemByGuid(artifactAura.GetCastItemGUID()); + + Item artifact = playerCaster.ToPlayer().GetItemByGuid(artifactAura.GetCastItemGUID()); if (artifact == null) return SpellCastResult.NoArtifactEquipped; + if (effect.Effect == SpellEffectName.GiveArtifactPower) { ArtifactRecord artifactEntry = CliDB.ArtifactStorage.LookupByKey(artifact.GetTemplate().GetArtifactID()); @@ -5167,16 +5314,19 @@ namespace Game.Spells case AuraType.ModCharm: case AuraType.AoeCharm: { - if (!m_caster.GetCharmerGUID().IsEmpty()) + Unit unitCaster1 = (m_originalCaster ? m_originalCaster : m_caster.ToUnit()); + if (unitCaster1 == null) + return SpellCastResult.BadTargets; + + if (!unitCaster1.GetCharmerGUID().IsEmpty()) return SpellCastResult.Charmed; - if (effect.ApplyAuraName == AuraType.ModCharm - || effect.ApplyAuraName == AuraType.ModPossess) + if (effect.ApplyAuraName == AuraType.ModCharm || effect.ApplyAuraName == AuraType.ModPossess) { - if (!m_spellInfo.HasAttribute(SpellAttr1.DismissPet) && !m_caster.GetPetGUID().IsEmpty()) + if (!m_spellInfo.HasAttribute(SpellAttr1.DismissPet) && !unitCaster1.GetPetGUID().IsEmpty()) return SpellCastResult.AlreadyHaveSummon; - if (!m_caster.GetCharmGUID().IsEmpty()) + if (!unitCaster1.GetCharmGUID().IsEmpty()) return SpellCastResult.AlreadyHaveCharm; } @@ -5204,18 +5354,22 @@ namespace Game.Spells } case AuraType.Mounted: { - if (m_caster.IsInWater() && m_spellInfo.HasAura(AuraType.ModIncreaseMountedFlightSpeed)) + if (unitCaster == null) + return SpellCastResult.BadTargets; + + if (unitCaster.IsInWater() && m_spellInfo.HasAura(AuraType.ModIncreaseMountedFlightSpeed)) return SpellCastResult.OnlyAbovewater; // Ignore map check if spell have AreaId. AreaId already checked and this prevent special mount spells - bool allowMount = !m_caster.GetMap().IsDungeon() || m_caster.GetMap().IsBattlegroundOrArena(); - InstanceTemplate it = Global.ObjectMgr.GetInstanceTemplate(m_caster.GetMapId()); + bool allowMount = !unitCaster.GetMap().IsDungeon() || unitCaster.GetMap().IsBattlegroundOrArena(); + InstanceTemplate it = Global.ObjectMgr.GetInstanceTemplate(unitCaster.GetMapId()); if (it != null) allowMount = it.AllowMount; - if (m_caster.IsTypeId(TypeId.Player) && !allowMount && m_spellInfo.RequiredAreasID == 0) + + if (unitCaster.IsTypeId(TypeId.Player) && !allowMount && m_spellInfo.RequiredAreasID == 0) return SpellCastResult.NoMountsAllowed; - if (m_caster.IsInDisallowedMountForm()) + if (unitCaster.IsInDisallowedMountForm()) { SendMountResult(MountResult.Shapeshifted); // mount result gets sent before the cast result return SpellCastResult.DontReport; @@ -5308,7 +5462,8 @@ namespace Game.Spells public SpellCastResult CheckPetCast(Unit target) { - if (m_caster.HasUnitState(UnitState.Casting) && !Convert.ToBoolean(_triggeredCastFlags & TriggerCastFlags.IgnoreCastInProgress)) //prevent spellcast interruption by another spellcast + Unit unitCaster = m_caster.ToUnit(); + if (unitCaster != null && unitCaster.HasUnitState(UnitState.Casting) && !_triggeredCastFlags.HasFlag(TriggerCastFlags.IgnoreCastInProgress)) //prevent spellcast interruption by another spellcast return SpellCastResult.SpellInProgress; // dead owner (pets still alive when owners ressed?) @@ -5335,7 +5490,7 @@ namespace Game.Spells // Check if spell is affected by GCD if (m_spellInfo.StartRecoveryCategory > 0) - if (m_caster.GetCharmInfo() != null && m_caster.GetSpellHistory().HasGlobalCooldown(m_spellInfo)) + if (unitCaster.GetCharmInfo() != null && unitCaster.GetSpellHistory().HasGlobalCooldown(m_spellInfo)) return SpellCastResult.NotReady; return CheckCast(true); @@ -5343,6 +5498,10 @@ namespace Game.Spells SpellCastResult CheckCasterAuras(ref uint param1) { + Unit unitCaster = (m_originalCaster ? m_originalCaster : m_caster.ToUnit()); + if (unitCaster == null) + return SpellCastResult.SpellCastOk; + // spells totally immuned to caster auras (wsg flag drop, give marks etc) if (m_spellInfo.HasAttribute(SpellAttr6.IgnoreCasterAuras)) return SpellCastResult.SpellCastOk; @@ -5362,15 +5521,15 @@ namespace Game.Spells // Check whether the cast should be prevented by any state you might have. SpellCastResult result = SpellCastResult.SpellCastOk; // Get unit state - UnitFlags unitflag = (UnitFlags)(uint)m_caster.m_unitData.Flags; + UnitFlags unitflag = (UnitFlags)(uint)unitCaster.m_unitData.Flags; // this check should only be done when player does cast directly // (ie not when it's called from a script) Breaks for example PlayerAI when charmed - /*if (!m_caster.GetCharmerGUID().IsEmpty()) + /*if (!unitCaster.GetCharmerGUID().IsEmpty()) { - Unit charmer = m_caster.GetCharmer(); + Unit charmer = unitCaster.GetCharmer(); if (charmer) - if (charmer.GetUnitBeingMoved() != m_caster && !CheckSpellCancelsCharm(ref param1)) + if (charmer.GetUnitBeingMoved() != unitCaster && !CheckSpellCancelsCharm(ref param1)) result = SpellCastResult.Charmed; }*/ @@ -5378,7 +5537,7 @@ namespace Game.Spells SpellCastResult mechanicCheck(AuraType auraType, ref uint _param1) { bool foundNotMechanic = false; - var auras = m_caster.GetAuraEffectsByType(auraType); + var auras = unitCaster.GetAuraEffectsByType(auraType); foreach (AuraEffect aurEff in auras) { uint mechanicMask = aurEff.GetSpellInfo().GetAllEffectsMechanicMask(); @@ -5451,7 +5610,7 @@ namespace Game.Spells else if (!CheckSpellCancelsConfuse(ref param1)) result = SpellCastResult.Confused; } - else if (m_caster.HasUnitFlag2(UnitFlags2.NoActions) && m_spellInfo.PreventionType.HasAnyFlag(SpellPreventionType.NoActions) && !CheckSpellCancelsNoActions(ref param1)) + else if (unitCaster.HasUnitFlag2(UnitFlags2.NoActions) && m_spellInfo.PreventionType.HasAnyFlag(SpellPreventionType.NoActions) && !CheckSpellCancelsNoActions(ref param1)) result = SpellCastResult.NoActions; // Attr must make flag drop spell totally immune from all effects @@ -5463,8 +5622,12 @@ namespace Game.Spells bool CheckSpellCancelsAuraEffect(AuraType auraType, ref uint param1) { + Unit unitCaster = (m_originalCaster ? m_originalCaster : m_caster.ToUnit()); + if (unitCaster == null) + return false; + // Checking auras is needed now, because you are prevented by some state but the spell grants immunity. - var auraEffects = m_caster.GetAuraEffectsByType(auraType); + var auraEffects = unitCaster.GetAuraEffectsByType(auraType); if (auraEffects.Empty()) return true; @@ -5607,7 +5770,7 @@ namespace Game.Spells SelectSpellTargets(); //check if among target units, our WANTED target is as well (.only self cast spells return false) foreach (var ihit in m_UniqueTargetInfo) - if (ihit.targetGUID == targetguid) + if (ihit.TargetGUID == targetguid) return true; } // either the cast failed or the intended target wouldn't be hit @@ -5641,7 +5804,7 @@ namespace Game.Spells if (m_caster.IsTypeId(TypeId.Player) && ((m_spellInfo.FacingCasterFlags.HasAnyFlag(1u) && !m_caster.HasInArc((float)Math.PI, target)) - && !m_caster.IsWithinBoundaryRadius(target))) + && !m_caster.ToPlayer().IsWithinBoundaryRadius(target))) return SpellCastResult.UnitNotInfront; } @@ -5656,30 +5819,34 @@ namespace Game.Spells return SpellCastResult.SpellCastOk; } - Tuple GetMinMaxRange(bool strict) + (float minRange, float maxRange) GetMinMaxRange(bool strict) { float rangeMod = 0.0f; float minRange = 0.0f; float maxRange = 0.0f; if (strict && m_spellInfo.IsNextMeleeSwingSpell()) - { - maxRange = 100.0f; - return Tuple.Create(minRange, maxRange); - } + return (0.0f, 100.0f); + Unit unitCaster = m_caster.ToUnit(); if (m_spellInfo.RangeEntry != null) { Unit target = m_targets.GetUnitTarget(); if (m_spellInfo.RangeEntry.Flags.HasAnyFlag(SpellRangeFlag.Melee)) { - rangeMod = m_caster.GetMeleeRange(target ? target : m_caster); // when the target is not a unit, take the caster's combat reach as the target's combat reach. + // when the target is not a unit, take the caster's combat reach as the target's combat reach. + if (unitCaster) + rangeMod = unitCaster.GetMeleeRange(target ? target : unitCaster); } else { float meleeRange = 0.0f; if (m_spellInfo.RangeEntry.Flags.HasAnyFlag(SpellRangeFlag.Ranged)) - meleeRange = m_caster.GetMeleeRange(target ? target : m_caster); // when the target is not a unit, take the caster's combat reach as the target's combat reach. + { + // when the target is not a unit, take the caster's combat reach as the target's combat reach. + if (unitCaster != null) + meleeRange = unitCaster.GetMeleeRange(target ? target : unitCaster); + } minRange = m_caster.GetSpellMinRangeForTarget(target, m_spellInfo) + meleeRange; maxRange = m_caster.GetSpellMaxRangeForTarget(target, m_spellInfo); @@ -5693,8 +5860,8 @@ namespace Game.Spells } } - if (target && m_caster.IsMoving() && target.IsMoving() && !m_caster.IsWalking() && !target.IsWalking() && - (m_spellInfo.RangeEntry.Flags.HasAnyFlag(SpellRangeFlag.Melee) || target.IsTypeId(TypeId.Player))) + if (target != null && unitCaster != null && unitCaster.IsMoving() && target.IsMoving() && !unitCaster.IsWalking() && !target.IsWalking() && + (m_spellInfo.RangeEntry.Flags.HasFlag(SpellRangeFlag.Melee) || target.IsPlayer())) rangeMod += 8.0f / 3.0f; } @@ -5711,11 +5878,15 @@ namespace Game.Spells maxRange += rangeMod; - return Tuple.Create(minRange, maxRange); + return (minRange, maxRange); } SpellCastResult CheckPower() { + Unit unitCaster = m_caster.ToUnit(); + if (unitCaster == null) + return SpellCastResult.SpellCastOk; + // item cast not used power if (m_CastItem != null) return SpellCastResult.SpellCastOk; @@ -5725,10 +5896,11 @@ namespace Game.Spells // health as power used - need check health amount if (cost.Power == PowerType.Health) { - if (m_caster.GetHealth() <= (uint)cost.Amount) + if (unitCaster.GetHealth() <= (ulong)cost.Amount) return SpellCastResult.CasterAurastate; continue; } + // Check valid power type if (cost.Power >= PowerType.Max) { @@ -5745,7 +5917,7 @@ namespace Game.Spells } // Check power amount - if (m_caster.GetPower(cost.Power) < cost.Amount) + if (unitCaster.GetPower(cost.Power) < cost.Amount) return SpellCastResult.NoPower; } @@ -5857,7 +6029,7 @@ namespace Game.Spells { Item targetItem = m_targets.GetItemTarget(); if (targetItem != null) - if (targetItem.GetOwnerGUID() != m_caster.GetGUID()) + if (targetItem.GetOwnerGUID() != player.GetGUID()) checkReagents = true; } @@ -5951,23 +6123,23 @@ namespace Game.Spells case SpellEffectName.CreateLoot: { // m_targets.GetUnitTarget() means explicit cast, otherwise we dont check for possible equip error - Unit target = m_targets.GetUnitTarget() ? m_targets.GetUnitTarget() : m_caster; - if (target != null && target.GetTypeId() == TypeId.Player && !IsTriggered() && effect.ItemType != 0) + Unit target = m_targets.GetUnitTarget() ? m_targets.GetUnitTarget() : player; + if (target.IsPlayer() && !IsTriggered() && effect.ItemType != 0) { List dest = new(); InventoryResult msg = target.ToPlayer().CanStoreNewItem(ItemConst.NullBag, ItemConst.NullSlot, dest, effect.ItemType, 1); if (msg != InventoryResult.Ok) { - ItemTemplate pProto = Global.ObjectMgr.GetItemTemplate(effect.ItemType); + ItemTemplate itemTemplate = Global.ObjectMgr.GetItemTemplate(effect.ItemType); // @todo Needs review - if (pProto != null && pProto.GetItemLimitCategory() == 0) + if (itemTemplate != null && itemTemplate.GetItemLimitCategory() == 0) { player.SendEquipError(msg, null, null, effect.ItemType); return SpellCastResult.DontReport; } else { - SpellEffectInfo efi; + // Conjure Food/Water/Refreshment spells if (!(m_spellInfo.SpellFamilyName == SpellFamilyNames.Mage && m_spellInfo.SpellFamilyFlags[0].HasAnyFlag(0x40000000u))) return SpellCastResult.TooManyOfItem; else if (!target.ToPlayer().HasItemCount(effect.ItemType)) @@ -5975,9 +6147,13 @@ namespace Game.Spells player.SendEquipError(msg, null, null, effect.ItemType); return SpellCastResult.DontReport; } - else if ((efi = m_spellInfo.GetEffect(1)) != null) - player.CastSpell(m_caster, (uint)efi.CalcValue(), new CastSpellExtraArgs(false)); // move this to anywhere - return SpellCastResult.DontReport; + else + { + SpellEffectInfo efi = m_spellInfo.GetEffect(1); + if (efi != null) + player.CastSpell(m_caster, (uint)efi.CalcValue(), new CastSpellExtraArgs(false)); // move this to anywhere + return SpellCastResult.DontReport; + } } } } @@ -5987,7 +6163,7 @@ namespace Game.Spells if (effect.ItemType != 0 && m_targets.GetItemTarget() != null && m_targets.GetItemTarget().IsVellum()) { // cannot enchant vellum for other player - if (m_targets.GetItemTarget().GetOwner() != m_caster) + if (m_targets.GetItemTarget().GetOwner() != player) return SpellCastResult.NotTradeable; // do not allow to enchant vellum from scroll made by vellum-prevent exploit if (m_CastItem != null && Convert.ToBoolean(m_CastItem.GetTemplate().GetFlags() & ItemFlags.NoReagentCost)) @@ -6049,7 +6225,7 @@ namespace Game.Spells } // Not allow enchant in trade slot for some enchant type - if (targetItem.GetOwner() != m_caster) + if (targetItem.GetOwner() != player) { if (enchantEntry == null) return SpellCastResult.Error; @@ -6064,7 +6240,7 @@ namespace Game.Spells if (item == null) return SpellCastResult.ItemNotFound; // Not allow enchant in trade slot for some enchant type - if (item.GetOwner() != m_caster) + if (item.GetOwner() != player) { int enchant_id = effect.MiscValue; var pEnchant = CliDB.SpellItemEnchantmentStorage.LookupByKey(enchant_id); @@ -6094,7 +6270,7 @@ namespace Game.Spells return SpellCastResult.CantBeDisenchanted; // prevent disenchanting in trade slot - if (item.GetOwnerGUID() != m_caster.GetGUID()) + if (item.GetOwnerGUID() != player.GetGUID()) return SpellCastResult.CantBeDisenchanted; ItemTemplate itemProto = item.GetTemplate(); @@ -6117,7 +6293,7 @@ namespace Game.Spells if (!Convert.ToBoolean(item.GetTemplate().GetFlags() & ItemFlags.IsProspectable)) return SpellCastResult.CantBeProspected; //prevent prospecting in trade slot - if (item.GetOwnerGUID() != m_caster.GetGUID()) + if (item.GetOwnerGUID() != player.GetGUID()) return SpellCastResult.CantBeProspected; //Check for enough skill in jewelcrafting uint item_prospectingskilllevel = item.GetTemplate().GetRequiredSkillRank(); @@ -6145,7 +6321,7 @@ namespace Game.Spells if (!(item.GetTemplate().GetFlags().HasAnyFlag(ItemFlags.IsMillable))) return SpellCastResult.CantBeMilled; //prevent milling in trade slot - if (item.GetOwnerGUID() != m_caster.GetGUID()) + if (item.GetOwnerGUID() != player.GetGUID()) return SpellCastResult.CantBeMilled; //Check for enough skill in inscription uint item_millingskilllevel = item.GetTemplate().GetRequiredSkillRank(); @@ -6167,15 +6343,30 @@ namespace Game.Spells case SpellEffectName.WeaponDamage: case SpellEffectName.WeaponDamageNoSchool: { - if (!m_caster.IsTypeId(TypeId.Player)) - return SpellCastResult.TargetNotPlayer; - if (m_attackType != WeaponAttackType.RangedAttack) break; - Item pItem = m_caster.ToPlayer().GetWeaponForAttack(m_attackType); - if (pItem == null || pItem.IsBroken()) + Item item = player.GetWeaponForAttack(m_attackType); + if (item == null || item.IsBroken()) return SpellCastResult.EquippedItem; + + switch ((ItemSubClassWeapon)item.GetTemplate().GetSubClass()) + { + case ItemSubClassWeapon.Thrown: + { + uint ammo = item.GetEntry(); + if (!player.HasItemCount(ammo)) + return SpellCastResult.NoAmmo; + break; + } + case ItemSubClassWeapon.Gun: + case ItemSubClassWeapon.Bow: + case ItemSubClassWeapon.Crossbow: + case ItemSubClassWeapon.Wand: + break; + default: + break; + } break; } case SpellEffectName.RechargeItem: @@ -6236,7 +6427,7 @@ namespace Game.Spells { var weaponCheck = new Func(attackType => { - Item item = m_caster.ToPlayer().GetWeaponForAttack(attackType); + Item item = player.ToPlayer().GetWeaponForAttack(attackType); // skip spell if no weapon in slot or broken if (!item || item.IsBroken()) @@ -6271,7 +6462,8 @@ namespace Game.Spells public void Delayed() // only called in DealDamage() { - if (m_caster == null) + Unit unitCaster = m_caster.ToUnit(); + if (unitCaster == null) return; if (IsDelayableNoMore()) // Spells may only be delayed twice @@ -6280,8 +6472,12 @@ namespace Game.Spells //check pushback reduce int delaytime = 500; // spellcasting delay is normally 500ms int delayReduce = 100; // must be initialized to 100 for percent modifiers - m_caster.ToPlayer().ApplySpellMod(m_spellInfo, SpellModOp.ResistPushback, ref delayReduce, this); - delayReduce += m_caster.GetTotalAuraModifier(AuraType.ReducePushback) - 100; + + Player player = unitCaster.GetSpellModOwner(); + if (player != null) + player.ApplySpellMod(m_spellInfo, SpellModOp.ResistPushback, ref delayReduce, this); + + delayReduce += unitCaster.GetTotalAuraModifier(AuraType.ReducePushback) - 100; if (delayReduce >= 100) return; @@ -6295,18 +6491,20 @@ namespace Game.Spells else m_timer += delaytime; - Log.outDebug(LogFilter.Spells, "Spell {0} partially interrupted for ({1}) ms at damage", m_spellInfo.Id, delaytime); - SpellDelayed spellDelayed = new(); - spellDelayed.Caster = m_caster.GetGUID(); + spellDelayed.Caster = unitCaster.GetGUID(); spellDelayed.ActualDelay = delaytime; - m_caster.SendMessageToSet(spellDelayed, true); + unitCaster.SendMessageToSet(spellDelayed, true); } public void DelayedChannel() { - if (m_caster == null || !m_caster.IsTypeId(TypeId.Player) || GetState() != SpellState.Casting) + Unit unitCaster = m_caster.ToUnit(); + if (unitCaster == null) + return; + + if (m_spellState != SpellState.Casting) return; if (IsDelayableNoMore()) // Spells may only be delayed twice @@ -6315,8 +6513,12 @@ namespace Game.Spells //check pushback reduce int delaytime = MathFunctions.CalculatePct(m_spellInfo.GetDuration(), 25); // channeling delay is normally 25% of its time per hit int delayReduce = 100; // must be initialized to 100 for percent modifiers - m_caster.ToPlayer().ApplySpellMod(m_spellInfo, SpellModOp.ResistPushback, ref delayReduce, this); - delayReduce += m_caster.GetTotalAuraModifier(AuraType.ReducePushback) - 100; + + Player player = unitCaster.GetSpellModOwner(); + if (player != null) + player.ApplySpellMod(m_spellInfo, SpellModOp.ResistPushback, ref delayReduce, this); + + delayReduce += unitCaster.GetTotalAuraModifier(AuraType.ReducePushback) - 100; if (delayReduce >= 100) return; @@ -6330,18 +6532,18 @@ namespace Game.Spells else m_timer -= delaytime; - Log.outDebug(LogFilter.Spells, "Spell {0} partially interrupted for {1} ms, new duration: {2} ms", m_spellInfo.Id, delaytime, m_timer); - foreach (var ihit in m_UniqueTargetInfo) - if (ihit.missCondition == SpellMissInfo.None) + { + if (ihit.MissCondition == SpellMissInfo.None) { - Unit unit = (m_caster.GetGUID() == ihit.targetGUID) ? m_caster : Global.ObjAccessor.GetUnit(m_caster, ihit.targetGUID); + Unit unit = (unitCaster.GetGUID() == ihit.TargetGUID) ? unitCaster : Global.ObjAccessor.GetUnit(unitCaster, ihit.TargetGUID); if (unit != null) unit.DelayOwnedAuras(m_spellInfo.Id, m_originalCasterGUID, delaytime); } + } // partially interrupt persistent area auras - DynamicObject dynObj = m_caster.GetDynObject(m_spellInfo.Id); + DynamicObject dynObj = unitCaster.GetDynObject(m_spellInfo.Id); if (dynObj != null) dynObj.Delay(delaytime); @@ -6356,7 +6558,7 @@ namespace Game.Spells bool UpdatePointers() { if (m_originalCasterGUID == m_caster.GetGUID()) - m_originalCaster = m_caster; + m_originalCaster = m_caster.ToUnit(); else { m_originalCaster = Global.ObjAccessor.GetUnit(m_caster, m_originalCasterGUID); @@ -6455,11 +6657,11 @@ namespace Game.Spells } // check for ignore LOS on the effect itself - if (m_spellInfo.HasAttribute(SpellAttr2.CanTargetNotInLos) || Global.DisableMgr.IsDisabledFor(DisableType.Spell, m_spellInfo.Id, null, DisableFlags.SpellLOS)) + if (m_spellInfo.HasAttribute(SpellAttr2.CanTargetNotInLos) || Global.DisableMgr.IsDisabledFor(DisableType.Spell, m_spellInfo.Id, null, (byte)DisableFlags.SpellLOS)) return true; // if spell is triggered, need to check for LOS disable on the aura triggering it and inherit that behaviour - if (IsTriggered() && m_triggeredByAuraSpell != null && (m_triggeredByAuraSpell.HasAttribute(SpellAttr2.CanTargetNotInLos) || Global.DisableMgr.IsDisabledFor(DisableType.Spell, m_triggeredByAuraSpell.Id, null, DisableFlags.SpellLOS))) + if (IsTriggered() && m_triggeredByAuraSpell != null && (m_triggeredByAuraSpell.HasAttribute(SpellAttr2.CanTargetNotInLos) || Global.DisableMgr.IsDisabledFor(DisableType.Spell, m_triggeredByAuraSpell.Id, null, (byte)DisableFlags.SpellLOS))) return true; // @todo shit below shouldn't be here, but it's temporary @@ -6614,29 +6816,29 @@ namespace Game.Spells Unit unit = null; // In case spell hit target, do all effect on that target if (targetInfo.MissCondition == SpellMissInfo.None) - unit = m_caster.GetGUID() == targetInfo.TargetGUID ? m_caster : Global.ObjAccessor.GetUnit(m_caster, targetInfo.TargetGUID); + unit = m_caster.GetGUID() == targetInfo.TargetGUID ? m_caster.ToUnit() : Global.ObjAccessor.GetUnit(m_caster, targetInfo.TargetGUID); // In case spell reflect from target, do all effect on caster (if hit) else if (targetInfo.MissCondition == SpellMissInfo.Reflect && targetInfo.ReflectResult == SpellMissInfo.None) - unit = m_caster; + unit = m_caster.ToUnit(); if (unit == null) return; // This will only cause combat - the target will engage once the projectile hits (in DoAllEffectOnTarget) - if (targetInfo.MissCondition != SpellMissInfo.Evade && !m_caster.IsFriendlyTo(unit) && (!m_spellInfo.IsPositive() || m_spellInfo.HasEffect(SpellEffectName.Dispel)) && (m_spellInfo.HasInitialAggro() || unit.IsEngaged())) - m_caster.SetInCombatWith(unit); + if (m_originalCaster != null && targetInfo.MissCondition != SpellMissInfo.Evade && !m_originalCaster.IsFriendlyTo(unit) && (!m_spellInfo.IsPositive() || m_spellInfo.HasEffect(SpellEffectName.Dispel)) && (m_spellInfo.HasInitialAggro() || unit.IsEngaged())) + m_originalCaster.SetInCombatWith(unit); m_damage = 0; m_healing = 0; HandleEffects(unit, null, null, effect.EffectIndex, SpellEffectHandleMode.LaunchTarget); - if (m_damage > 0) + if (m_originalCaster != null && m_damage > 0) { if (effect.IsTargetingArea() || effect.IsAreaAuraEffect() || effect.IsEffect(SpellEffectName.PersistentAreaAura)) { - m_damage = unit.CalculateAOEAvoidance(m_damage, (uint)m_spellInfo.SchoolMask, m_caster.GetGUID()); + m_damage = unit.CalculateAOEAvoidance(m_damage, (uint)m_spellInfo.SchoolMask, m_originalCaster.GetGUID()); - if (m_caster.IsPlayer()) + if (m_originalCaster.IsPlayer()) { // cap damage of player AOE long targetAmount = GetUnitTargetCountForEffect(effect.EffectIndex); @@ -6657,12 +6859,16 @@ namespace Game.Spells targetInfo.Damage += m_damage; targetInfo.Healing += m_healing; - + float critChance = m_spellValue.CriticalChance; - if (critChance == 0) - critChance = m_caster.SpellCritChanceDone(this, null, m_spellSchoolMask, m_attackType); + if (m_originalCaster != null) + { + if (critChance == 0) + critChance = m_originalCaster.SpellCritChanceDone(this, null, m_spellSchoolMask, m_attackType); + critChance = unit.SpellCritChanceTaken(m_originalCaster, this, null, m_spellSchoolMask, critChance, m_attackType); + } - targetInfo.IsCrit = RandomHelper.randChance(unit.SpellCritChanceTaken(m_caster, this, null, m_spellSchoolMask, critChance, m_attackType)); + targetInfo.IsCrit = RandomHelper.randChance(critChance); } SpellCastResult CanOpenLock(uint effIndex, uint lockId, ref SkillType skillId, ref int reqSkillValue, ref int skillValue) @@ -6670,6 +6876,10 @@ namespace Game.Spells if (lockId == 0) // possible case for GO and maybe for items. return SpellCastResult.SpellCastOk; + Unit unitCaster = m_caster.ToUnit(); + if (unitCaster == null) + return SpellCastResult.BadTargets; + // Get LockInfo var lockInfo = CliDB.LockStorage.LookupByKey(lockId); @@ -6709,10 +6919,10 @@ namespace Game.Spells // castitem check: rogue using skeleton keys. the skill values should not be added in this case. skillValue = 0; - if (!m_CastItem && m_caster.IsTypeId(TypeId.Player)) - skillValue = m_caster.ToPlayer().GetSkillValue(skillId); + if (!m_CastItem && unitCaster.IsTypeId(TypeId.Player)) + skillValue = unitCaster.ToPlayer().GetSkillValue(skillId); else if (lockInfo.Index[j] == (uint)LockType.Lockpicking) - skillValue = (int)m_caster.GetLevel() * 5; + skillValue = (int)unitCaster.GetLevel() * 5; // skill bonus provided by casting spell (mostly item spells) // add the effect base points modifier from the spell cast (cheat lock / skeleton key etc.) @@ -7032,10 +7242,14 @@ namespace Game.Spells void PrepareTriggersExecutedOnHit() { + Unit unitCaster = m_caster.ToUnit(); + if (unitCaster == null) + return; + // handle SPELL_AURA_ADD_TARGET_TRIGGER auras: // save auras which were present on spell caster on cast, to prevent triggered auras from affecting caster // and to correctly calculate proc chance when combopoints are present - var targetTriggers = m_caster.GetAuraEffectsByType(AuraType.AddTargetTrigger); + var targetTriggers = unitCaster.GetAuraEffectsByType(AuraType.AddTargetTrigger); foreach (var aurEff in targetTriggers) { if (!aurEff.IsAffectingSpell(m_spellInfo)) @@ -7048,7 +7262,7 @@ namespace Game.Spells // this possibly needs fixing int auraBaseAmount = aurEff.GetBaseAmount(); // proc chance is stored in effect amount - int chance = m_caster.CalculateSpellDamage(null, aurEff.GetSpellInfo(), aurEff.GetEffIndex(), auraBaseAmount); + int chance = unitCaster.CalculateSpellDamage(null, aurEff.GetSpellInfo(), aurEff.GetEffIndex(), auraBaseAmount); chance *= aurEff.GetBase().GetStackAmount(); // build trigger and add to the list @@ -7057,23 +7271,30 @@ namespace Game.Spells } } - bool HasGlobalCooldown() + bool CanHaveGlobalCooldown(WorldObject caster) { // Only players or controlled units have global cooldown - if (!m_caster.IsTypeId(TypeId.Player) && m_caster.GetCharmInfo() == null) + if (!caster.IsPlayer() && (!caster.IsCreature() || caster.ToCreature().GetCharmInfo() == null)) return false; - return m_caster.GetSpellHistory().HasGlobalCooldown(m_spellInfo); + return true; + } + + bool HasGlobalCooldown() + { + if (!CanHaveGlobalCooldown(m_caster)) + return false; + + return m_caster.ToUnit().GetSpellHistory().HasGlobalCooldown(m_spellInfo); } void TriggerGlobalCooldown() { - int gcd = (int)m_spellInfo.StartRecoveryTime; - if (gcd == 0 || m_spellInfo.StartRecoveryCategory == 0) + if (!CanHaveGlobalCooldown(m_caster)) return; - // Only players or controlled units have global cooldown - if (!m_caster.IsTypeId(TypeId.Player) && m_caster.GetCharmInfo() == null) + int gcd = (int)m_spellInfo.StartRecoveryTime; + if (gcd == 0 || m_spellInfo.StartRecoveryCategory == 0) return; if (m_caster.IsTypeId(TypeId.Player)) @@ -7096,58 +7317,60 @@ namespace Game.Spells // Apply haste rating if (gcd > 750 && (m_spellInfo.StartRecoveryCategory == 133 && !isMeleeOrRangedSpell)) { - gcd = (int)(gcd * m_caster.m_unitData.ModSpellHaste); + gcd = (int)(gcd * m_caster.ToUnit().m_unitData.ModSpellHaste); MathFunctions.RoundToInterval(ref gcd, 750, 1500); } - if (gcd > 750 && m_caster.HasAuraTypeWithAffectMask(AuraType.ModGlobalCooldownByHasteRegen, m_spellInfo)) + if (gcd > 750 && m_caster.ToUnit().HasAuraTypeWithAffectMask(AuraType.ModGlobalCooldownByHasteRegen, m_spellInfo)) { - gcd = (int)(gcd * m_caster.m_unitData.ModHasteRegen); + gcd = (int)(gcd * m_caster.ToUnit().m_unitData.ModHasteRegen); MathFunctions.RoundToInterval(ref gcd, 750, 1500); } } - m_caster.GetSpellHistory().AddGlobalCooldown(m_spellInfo, (uint)gcd); + m_caster.ToUnit().GetSpellHistory().AddGlobalCooldown(m_spellInfo, (uint)gcd); } void CancelGlobalCooldown() { + if (!CanHaveGlobalCooldown(m_caster)) + return; + if (m_spellInfo.StartRecoveryTime == 0) return; // Cancel global cooldown when interrupting current cast - if (m_caster.GetCurrentSpell(CurrentSpellTypes.Generic) != this) + if (m_caster.ToUnit().GetCurrentSpell(CurrentSpellTypes.Generic) != this) return; - // Only players or controlled units have global cooldown - if (!m_caster.IsTypeId(TypeId.Player) && m_caster.GetCharmInfo() == null) - return; - - m_caster.GetSpellHistory().CancelGlobalCooldown(m_spellInfo); + m_caster.ToUnit().GetSpellHistory().CancelGlobalCooldown(m_spellInfo); } List m_loadedScripts = new(); - int CalculateDamage(uint i, Unit target) + int CalculateDamage(uint effIndex, Unit target) { int? basePoint = null; - if ((m_spellValue.CustomBasePointsMask & (1 << (int)i)) != 0) - basePoint = m_spellValue.EffectBasePoints[i]; + if ((m_spellValue.CustomBasePointsMask & (1 << (int)effIndex)) != 0) + basePoint = m_spellValue.EffectBasePoints[effIndex]; - return m_caster.CalculateSpellDamage(target, m_spellInfo, i, basePoint, m_castItemEntry, m_castItemLevel); + return m_caster.CalculateSpellDamage(target, m_spellInfo, effIndex, basePoint, m_castItemEntry, m_castItemLevel); } - int CalculateDamage(uint i, Unit target, out float variance) + + int CalculateDamage(uint effIndex, Unit target, out float variance) { int? basePoint = null; - if ((m_spellValue.CustomBasePointsMask & (1 << (int)i)) != 0) - basePoint = m_spellValue.EffectBasePoints[i]; + if ((m_spellValue.CustomBasePointsMask & (1 << (int)effIndex)) != 0) + basePoint = m_spellValue.EffectBasePoints[effIndex]; - return m_caster.CalculateSpellDamage(target, m_spellInfo, i, out variance, basePoint, m_castItemEntry, m_castItemLevel); + return m_caster.CalculateSpellDamage(out variance, target, m_spellInfo, effIndex, basePoint, m_castItemEntry, m_castItemLevel); } + public SpellState GetState() { return m_spellState; } + public void SetState(SpellState state) { m_spellState = state; @@ -7157,6 +7380,7 @@ namespace Game.Spells { if (!m_targets.HasSrc()) m_targets.SetSrc(m_caster); } + void CheckDst() { if (!m_targets.HasDst()) m_targets.SetDst(m_caster); @@ -7166,14 +7390,17 @@ namespace Game.Spells { return m_casttime; } + bool IsAutoRepeat() { return m_autoRepeat; } + void SetAutoRepeat(bool rep) { m_autoRepeat = rep; } + void ReSetTimer() { m_timer = m_casttime > 0 ? m_casttime : 0; @@ -7184,7 +7411,7 @@ namespace Game.Spells public bool IsIgnoringCooldowns() { return _triggeredCastFlags.HasAnyFlag(TriggerCastFlags.IgnoreSpellAndCategoryCD); } public bool IsFocusDisabled() { return _triggeredCastFlags.HasFlag(TriggerCastFlags.IgnoreSetFacing) || (m_spellInfo.IsChanneled() && !m_spellInfo.HasAttribute(SpellAttr1.ChannelTrackTarget)); } public bool IsProcDisabled() { return _triggeredCastFlags.HasAnyFlag(TriggerCastFlags.DisallowProcEvents); } - public bool IsChannelActive() { return m_caster.GetChannelSpellId() != 0; } + public bool IsChannelActive() { return m_caster.IsUnit() && m_caster.ToUnit().GetChannelSpellId() != 0; } public bool IsDeletable() { @@ -7215,7 +7442,7 @@ namespace Game.Spells return m_delayMoment; } - public Unit GetCaster() + public WorldObject GetCaster() { return m_caster; } @@ -7237,7 +7464,7 @@ namespace Game.Spells if (m_delayAtDamageCount >= 2) return true; - m_delayAtDamageCount++; + ++m_delayAtDamageCount; return false; } @@ -7279,7 +7506,7 @@ namespace Game.Spells public List m_appliedMods; - Unit m_caster; + WorldObject m_caster; public SpellValue m_spellValue; ObjectGuid m_originalCasterGUID; Unit m_originalCaster; @@ -7320,6 +7547,7 @@ namespace Game.Spells SpellEffectHandleMode effectHandleMode; public SpellEffectInfo effectInfo; // used in effects handlers + Unit unitCaster; internal UnitAura spellAura; internal DynObjAura dynObjAura; @@ -7626,7 +7854,7 @@ namespace Game.Spells public override void DoDamageAndTriggers(Spell spell) { - Unit unit = spell.GetCaster().GetGUID() == TargetGUID ? spell.GetCaster() : Global.ObjAccessor.GetUnit(spell.GetCaster(), TargetGUID); + Unit unit = spell.GetCaster().GetGUID() == TargetGUID ? spell.GetCaster().ToUnit() : Global.ObjAccessor.GetUnit(spell.GetCaster(), TargetGUID); if (unit == null) return; @@ -7641,7 +7869,7 @@ namespace Game.Spells // Get original caster (if exist) and calculate damage/healing from him data // Skip if m_originalCaster not available - Unit caster = spell.GetOriginalCaster() ? spell.GetOriginalCaster() : spell.GetCaster(); + Unit caster = spell.GetOriginalCaster() ? spell.GetOriginalCaster() : spell.GetCaster().ToUnit(); if (caster == null) return; @@ -7748,7 +7976,7 @@ namespace Game.Spells caster.CalculateSpellDamageTaken(damageInfo, spell.m_damage, spell.m_spellInfo, spell.m_attackType, IsCrit); Unit.DealDamageMods(damageInfo.attacker, damageInfo.target, ref damageInfo.damage, ref damageInfo.absorb); - hitMask |= createProcHitMask(damageInfo, MissCondition); + hitMask |= Unit.CreateProcHitMask(damageInfo, MissCondition); procVictim |= ProcFlags.TakenDamage; spell.m_damage = (int)damageInfo.damage; @@ -7776,7 +8004,7 @@ namespace Game.Spells { // Fill base damage struct (unitTarget - is real spell target) SpellNonMeleeDamage damageInfo = new(caster, spell.unitTarget, spell.m_spellInfo, spell.m_SpellVisual, spell.m_spellSchoolMask); - hitMask |= createProcHitMask(damageInfo, MissCondition); + hitMask |= Unit.CreateProcHitMask(damageInfo, MissCondition); // Do triggers for unit if (canEffectTrigger) { @@ -7799,7 +8027,7 @@ namespace Game.Spells Unit.ProcSkillsAndAuras(caster, spell.unitTarget, procAttacker, procVictim, procSpellType, ProcFlagsSpellPhase.Hit, hitMask, spell, spellDamageInfo, healInfo); // item spells (spell hit of non-damage spell may also activate items, for example seal of corruption hidden hit) - if (caster.IsPlayer() && procSpellType.HasFlag(ProcFlagsSpellType.Damage | ProcFlagsSpellType.NoDmgHeal)) + if (caster.IsPlayer() && procSpellType.HasAnyFlag(ProcFlagsSpellType.Damage | ProcFlagsSpellType.NoDmgHeal)) { if (spell.m_spellInfo.DmgClass == SpellDmgClass.Melee || spell.m_spellInfo.DmgClass == SpellDmgClass.Ranged) if (!spell.m_spellInfo.HasAttribute(SpellAttr0.StopAttackTarget) && !spell.m_spellInfo.HasAttribute(SpellAttr4.SuppressWeaponProcs)) @@ -7827,7 +8055,7 @@ namespace Game.Spells // Check for SPELL_ATTR7_INTERRUPT_ONLY_NONPLAYER if (MissCondition == SpellMissInfo.None && spell.m_spellInfo.HasAttribute(SpellAttr7.InterruptOnlyNonplayer) && !unit.IsPlayer()) - caster.CastSpell(unit, SPELL_INTERRUPT_NONPLAYER, true); + caster.CastSpell(unit, 32747, true); if (_spellHitTarget) { @@ -7910,7 +8138,7 @@ namespace Game.Spells public class SpellValue { - public SpellValue(SpellInfo proto, Unit caster) + public SpellValue(SpellInfo proto, WorldObject caster) { foreach (SpellEffectInfo effect in proto.GetEffects()) if (effect != null) @@ -7956,7 +8184,15 @@ namespace Game.Spells public class WorldObjectSpellTargetCheck : ICheck { - public WorldObjectSpellTargetCheck(Unit caster, Unit referer, SpellInfo spellInfo, SpellTargetCheckTypes selectionType, List condList, SpellTargetObjectTypes objectType) + internal WorldObject _caster; + WorldObject _referer; + internal SpellInfo _spellInfo; + SpellTargetCheckTypes _targetSelectionType; + ConditionSourceInfo _condSrcInfo; + List _condList; + SpellTargetObjectTypes _objectType; + + public WorldObjectSpellTargetCheck(WorldObject caster, WorldObject referer, SpellInfo spellInfo, SpellTargetCheckTypes selectionType, List condList, SpellTargetObjectTypes objectType) { _caster = caster; _referer = referer; @@ -7967,8 +8203,6 @@ namespace Game.Spells if (condList != null) _condSrcInfo = new ConditionSourceInfo(null, caster); - else - _condSrcInfo = null; } public virtual bool Invoke(WorldObject obj) @@ -7988,6 +8222,7 @@ namespace Game.Spells return false; } + Unit refUnit = _referer.ToUnit(); if (unitTarget != null) { // do only faction checks here @@ -7996,7 +8231,7 @@ namespace Game.Spells case SpellTargetCheckTypes.Enemy: if (unitTarget.IsTotem()) return false; - if (!_caster.IsValidAttackTarget(unitTarget, _spellInfo, null, false)) + if (!_caster.IsValidAttackTarget(unitTarget, _spellInfo, false)) return false; break; case SpellTargetCheckTypes.Ally: @@ -8006,22 +8241,29 @@ namespace Game.Spells return false; break; case SpellTargetCheckTypes.Party: + if (refUnit == null) + return false; if (unitTarget.IsTotem()) return false; if (!_caster.IsValidAssistTarget(unitTarget, _spellInfo)) return false; - if (!_referer.IsInPartyWith(unitTarget)) + if (!refUnit.IsInPartyWith(unitTarget)) return false; break; case SpellTargetCheckTypes.RaidClass: + if (!refUnit) + return false; + if (refUnit.GetClass() != unitTarget.GetClass()) + return false; + goto case SpellTargetCheckTypes.Raid; case SpellTargetCheckTypes.Raid: - if (_referer.GetClass() != unitTarget.GetClass()) + if (refUnit == null) return false; if (unitTarget.IsTotem()) return false; if (!_caster.IsValidAssistTarget(unitTarget, _spellInfo)) return false; - if (!_referer.IsInRaidWith(unitTarget)) + if (!refUnit.IsInRaidWith(unitTarget)) return false; break; case SpellTargetCheckTypes.Summoned: @@ -8031,7 +8273,7 @@ namespace Game.Spells return false; break; case SpellTargetCheckTypes.Threat: - if (_referer.GetThreatManager().GetThreat(unitTarget, true) <= 0.0f) + if (!_referer.IsUnit() || _referer.ToUnit().GetThreatManager().GetThreat(unitTarget, true) <= 0.0f) return false; break; case SpellTargetCheckTypes.Tap: @@ -8075,14 +8317,6 @@ namespace Game.Spells _condSrcInfo.mConditionTargets[0] = obj; return Global.ConditionMgr.IsObjectMeetToConditions(_condSrcInfo, _condList); } - - public Unit _caster { get; set; } - Unit _referer; - public SpellInfo _spellInfo { get; set; } - SpellTargetCheckTypes _targetSelectionType; - ConditionSourceInfo _condSrcInfo; - List _condList; - SpellTargetObjectTypes _objectType; } public class WorldObjectSpellNearbyTargetCheck : WorldObjectSpellTargetCheck @@ -8090,7 +8324,7 @@ namespace Game.Spells float _range; Position _position; - public WorldObjectSpellNearbyTargetCheck(float range, Unit caster, SpellInfo spellInfo, SpellTargetCheckTypes selectionType, List condList, SpellTargetObjectTypes objectType) + public WorldObjectSpellNearbyTargetCheck(float range, WorldObject caster, SpellInfo spellInfo, SpellTargetCheckTypes selectionType, List condList, SpellTargetObjectTypes objectType) : base(caster, caster, spellInfo, selectionType, condList, objectType) { _range = range; @@ -8114,7 +8348,7 @@ namespace Game.Spells float _range; Position _position; - public WorldObjectSpellAreaTargetCheck(float range, Position position, Unit caster, Unit referer, SpellInfo spellInfo, SpellTargetCheckTypes selectionType, List condList, SpellTargetObjectTypes objectType) + public WorldObjectSpellAreaTargetCheck(float range, Position position, WorldObject caster, WorldObject referer, SpellInfo spellInfo, SpellTargetCheckTypes selectionType, List condList, SpellTargetObjectTypes objectType) : base(caster, referer, spellInfo, selectionType, condList, objectType) { _range = range; @@ -8143,8 +8377,11 @@ namespace Game.Spells } public class WorldObjectSpellConeTargetCheck : WorldObjectSpellAreaTargetCheck - { - public WorldObjectSpellConeTargetCheck(float coneAngle, float lineWidth, float range, Unit caster, SpellInfo spellInfo, SpellTargetCheckTypes selectionType, List condList, SpellTargetObjectTypes objectType) + { + float _coneAngle; + float _lineWidth; + + public WorldObjectSpellConeTargetCheck(float coneAngle, float lineWidth, float range, WorldObject caster, SpellInfo spellInfo, SpellTargetCheckTypes selectionType, List condList, SpellTargetObjectTypes objectType) : base(range, caster.GetPosition(), caster, caster, spellInfo, selectionType, condList, objectType) { _coneAngle = coneAngle; @@ -8165,7 +8402,7 @@ namespace Game.Spells } else { - if (!_caster.IsWithinBoundaryRadius(target.ToUnit())) + if (!_caster.IsUnit() || !_caster.ToUnit().IsWithinBoundaryRadius(target.ToUnit())) // ConeAngle > 0 . select targets in front // ConeAngle < 0 . select targets in back if (_caster.HasInArc(_coneAngle, target) != MathFunctions.fuzzyGe(_coneAngle, 0.0f)) @@ -8173,9 +8410,6 @@ namespace Game.Spells } return base.Invoke(target); } - - float _coneAngle; - float _lineWidth; } public class WorldObjectSpellTrajTargetCheck : WorldObjectSpellTargetCheck @@ -8183,7 +8417,7 @@ namespace Game.Spells float _range; Position _position; - public WorldObjectSpellTrajTargetCheck(float range, Position position, Unit caster, SpellInfo spellInfo, SpellTargetCheckTypes selectionType, List condList, SpellTargetObjectTypes objectType) + public WorldObjectSpellTrajTargetCheck(float range, Position position, WorldObject caster, SpellInfo spellInfo, SpellTargetCheckTypes selectionType, List condList, SpellTargetObjectTypes objectType) : base(caster, caster, spellInfo, selectionType, condList, objectType) { _range = range; @@ -8209,7 +8443,7 @@ namespace Game.Spells Position _dstPosition; float _lineWidth; - public WorldObjectSpellLineTargetCheck(Position srcPosition, Position dstPosition, float lineWidth, float range, Unit caster, SpellInfo spellInfo, SpellTargetCheckTypes selectionType, List condList, SpellTargetObjectTypes objectType) + public WorldObjectSpellLineTargetCheck(Position srcPosition, Position dstPosition, float lineWidth, float range, WorldObject caster, SpellInfo spellInfo, SpellTargetCheckTypes selectionType, List condList, SpellTargetObjectTypes objectType) : base(range, caster, caster, caster, spellInfo, selectionType, condList, objectType) { _srcPosition = srcPosition; diff --git a/Source/Game/Spells/SpellCastTargets.cs b/Source/Game/Spells/SpellCastTargets.cs index d3023edb8..19798590a 100644 --- a/Source/Game/Spells/SpellCastTargets.cs +++ b/Source/Game/Spells/SpellCastTargets.cs @@ -345,9 +345,9 @@ namespace Game.Spells m_targetMask &= ~SpellCastTargetFlags.DestLocation; } - public void Update(Unit caster) + public void Update(WorldObject caster) { - m_objectTarget = !m_objectTargetGUID.IsEmpty() ? ((m_objectTargetGUID == caster.GetGUID()) ? caster : Global.ObjAccessor.GetWorldObject(caster, m_objectTargetGUID)) : null; + m_objectTarget = (m_objectTargetGUID == caster.GetGUID()) ? caster : Global.ObjAccessor.GetWorldObject(caster, m_objectTargetGUID); m_itemTarget = null; if (caster is Player) @@ -403,7 +403,6 @@ namespace Game.Spells public bool HasDst() { return Convert.ToBoolean(m_targetMask & SpellCastTargetFlags.DestLocation); } public bool HasTraj() { return m_speed != 0; } - public float GetPitch() { return m_pitch; } public void SetPitch(float pitch) { m_pitch = pitch; } float GetSpeed() { return m_speed; } diff --git a/Source/Game/Spells/SpellEffects.cs b/Source/Game/Spells/SpellEffects.cs index 98b782865..5d0b61bfa 100644 --- a/Source/Game/Spells/SpellEffects.cs +++ b/Source/Game/Spells/SpellEffects.cs @@ -110,7 +110,7 @@ namespace Game.Spells data.SpellID = m_spellInfo.Id; m_caster.SendMessageToSet(data, true); - Unit.DealDamage(m_caster, unitTarget, (uint)unitTarget.GetHealth(), null, DamageEffectType.NoDamage, SpellSchoolMask.Normal, null, false); + Unit.DealDamage(unitCaster, unitTarget, (uint)unitTarget.GetHealth(), null, DamageEffectType.NoDamage, SpellSchoolMask.Normal, null, false); } [SpellEffectHandler(SpellEffectName.EnvironmentalDamage)] @@ -127,16 +127,17 @@ namespace Game.Spells unitTarget.ToPlayer().EnvironmentalDamage(EnviromentalDamage.Fire, (uint)damage); else { - DamageInfo damageInfo = new(m_caster, unitTarget, (uint)damage, m_spellInfo, m_spellInfo.GetSchoolMask(), DamageEffectType.SpellDirect, WeaponAttackType.BaseAttack); + DamageInfo damageInfo = new(unitCaster, unitTarget, (uint)damage, m_spellInfo, m_spellInfo.GetSchoolMask(), DamageEffectType.SpellDirect, WeaponAttackType.BaseAttack); Unit.CalcAbsorbResist(damageInfo); - SpellNonMeleeDamage log = new(m_caster, unitTarget, m_spellInfo, m_SpellVisual, m_spellInfo.GetSchoolMask(), m_castId); + SpellNonMeleeDamage log = new(unitCaster, unitTarget, m_spellInfo, m_SpellVisual, m_spellInfo.GetSchoolMask(), m_castId); log.damage = damageInfo.GetDamage(); log.originalDamage = (uint)damage; log.absorb = damageInfo.GetAbsorb(); log.resist = damageInfo.GetResist(); - m_caster.SendSpellNonMeleeDamageLog(log); + if (unitCaster != null) + unitCaster.SendSpellNonMeleeDamageLog(log); } } @@ -163,74 +164,84 @@ namespace Game.Spells switch (m_spellInfo.SpellFamilyName) { case SpellFamilyNames.Generic: + { + ///@todo: move those to scripts + switch (m_spellInfo.Id) // better way to check unknown { - switch (m_spellInfo.Id) // better way to check unknown + // Consumption + case 28865: + damage = 2750; + if (m_caster.GetMap().IsHeroic()) + damage = 4250; + break; + // percent from health with min + case 25599: // Thundercrash { - // Consumption - case 28865: - damage = m_caster.GetMap().GetDifficultyID() == Difficulty.None ? 2750 : 4250; - break; - // percent from health with min - case 25599: // Thundercrash - { - damage = (int)unitTarget.GetHealth() / 2; - if (damage < 200) - damage = 200; - break; - } - // arcane charge. must only affect demons (also undead?) - case 45072: - { - if (unitTarget.GetCreatureType() != CreatureType.Demon - && unitTarget.GetCreatureType() != CreatureType.Undead) - return; - break; - } - // Gargoyle Strike - case 51963: - { - // about +4 base spell dmg per level - damage = (int)(m_caster.GetLevel() - 60) * 4 + 60; - break; - } - } - break; - } - case SpellFamilyNames.Warrior: - { - // Victory Rush - if (m_spellInfo.Id == 34428) - MathFunctions.ApplyPct(ref damage, m_caster.GetTotalAttackPowerValue(WeaponAttackType.BaseAttack)); - // Shockwave - else if (m_spellInfo.Id == 46968) - { - int pct = m_caster.CalculateSpellDamage(unitTarget, m_spellInfo, 2); - if (pct > 0) - damage += (int)MathFunctions.CalculatePct(m_caster.GetTotalAttackPowerValue(WeaponAttackType.BaseAttack), pct); + damage = (int)unitTarget.GetHealth() / 2; + if (damage < 200) + damage = 200; break; } - break; - } - case SpellFamilyNames.Deathknight: - { - // Blood Boil - bonus for diseased targets - if (m_spellInfo.SpellFamilyFlags[0].HasAnyFlag(0x00040000u)) + // arcane charge. must only affect demons (also undead?) + case 45072: { - if (unitTarget.GetAuraEffect(AuraType.PeriodicDamage, SpellFamilyNames.Deathknight, new FlagArray128(0, 0, 0x00000002), m_caster.GetGUID()) != null) - { - damage += m_damage / 2; - damage += (int)(m_caster.GetTotalAttackPowerValue(WeaponAttackType.BaseAttack) * 0.035f); - } + if ((unitTarget.GetCreatureTypeMask() & (uint)CreatureType.MaskDemonOrUnDead) == 0) + return; + break; } + // Gargoyle Strike + case 51963: + { + damage = 60; + // about +4 base spell dmg per level + if (unitCaster && unitCaster.GetLevel() >= 60) + damage += (int)((unitCaster.GetLevel() - 60) * 4); + break; + } + } + break; + } + case SpellFamilyNames.Warrior: + { + if (unitCaster == null) + break; + + // Victory Rush + if (m_spellInfo.Id == 34428) + MathFunctions.ApplyPct(ref damage, unitCaster.GetTotalAttackPowerValue(WeaponAttackType.BaseAttack)); + // Shockwave + else if (m_spellInfo.Id == 46968) + { + int pct = unitCaster.CalculateSpellDamage(unitTarget, m_spellInfo, 2); + if (pct > 0) + damage += (int)MathFunctions.CalculatePct(unitCaster.GetTotalAttackPowerValue(WeaponAttackType.BaseAttack), pct); break; } + break; + } + case SpellFamilyNames.Deathknight: + { + if (unitCaster == null) + break; + + // Blood Boil - bonus for diseased targets + if (m_spellInfo.SpellFamilyFlags[0].HasAnyFlag(0x00040000u)) + { + if (unitTarget.GetAuraEffect(AuraType.PeriodicDamage, SpellFamilyNames.Deathknight, new FlagArray128(0, 0, 0x00000002), unitCaster.GetGUID()) != null) + { + damage += m_damage / 2; + damage += (int)(unitCaster.GetTotalAttackPowerValue(WeaponAttackType.BaseAttack) * 0.035f); + } + } + break; + } } - if (m_originalCaster != null && apply_direct_bonus) + if (unitCaster != null && apply_direct_bonus) { - uint bonus = m_originalCaster.SpellDamageBonusDone(unitTarget, m_spellInfo, (uint)damage, DamageEffectType.SpellDirect, effectInfo); + uint bonus = unitCaster.SpellDamageBonusDone(unitTarget, m_spellInfo, (uint)damage, DamageEffectType.SpellDirect, effectInfo); damage = (int)(bonus + (bonus * _variance)); - damage = (int)unitTarget.SpellDamageBonusTaken(m_originalCaster, m_spellInfo, (uint)damage, DamageEffectType.SpellDirect); + damage = (int)unitTarget.SpellDamageBonusTaken(unitCaster, m_spellInfo, (uint)damage, DamageEffectType.SpellDirect); } m_damage += damage; @@ -253,31 +264,31 @@ namespace Game.Spells switch (m_spellInfo.Id) { case 31789: // Righteous Defense (step 1) + { + // Clear targets for eff 1 + foreach (var hit in m_UniqueTargetInfo) + hit.EffectMask &= ~Convert.ToUInt32(1 << 1); + + // not empty (checked), copy + var attackers = unitTarget.GetAttackers(); + + // remove invalid attackers + foreach (var att in attackers) + if (!att.IsValidAttackTarget(m_caster)) + attackers.Remove(att); + + // selected from list 3 + int maxTargets = Math.Min(3, attackers.Count); + for (uint i = 0; i < maxTargets; ++i) { - // Clear targets for eff 1 - foreach (var hit in m_UniqueTargetInfo) - hit.effectMask &= ~Convert.ToUInt32(1 << 1); - - // not empty (checked), copy - var attackers = unitTarget.GetAttackers(); - - // remove invalid attackers - foreach (var att in attackers) - if (!att.IsValidAttackTarget(m_caster)) - attackers.Remove(att); - - // selected from list 3 - int maxTargets = Math.Min(3, attackers.Count); - for (uint i = 0; i < maxTargets; ++i) - { - Unit attacker = attackers.SelectRandom(); - AddUnitTarget(attacker, 1 << 1); - attackers.Remove(attacker); - } - - // now let next effect cast spell at each target. - return; + Unit attacker = attackers.SelectRandom(); + AddUnitTarget(attacker, 1 << 1); + attackers.Remove(attacker); } + + // now let next effect cast spell at each target. + return; + } } break; } @@ -316,39 +327,39 @@ namespace Game.Spells { // Demonic Empowerment -- succubus case 54437: - { - unitTarget.RemoveMovementImpairingAuras(true); - unitTarget.RemoveAurasByType(AuraType.ModStalked); - unitTarget.RemoveAurasByType(AuraType.ModStun); + { + unitTarget.RemoveMovementImpairingAuras(true); + unitTarget.RemoveAurasByType(AuraType.ModStalked); + unitTarget.RemoveAurasByType(AuraType.ModStun); - // Cast Lesser Invisibility - unitTarget.CastSpell(unitTarget, 7870, true); - return; - } + // Cast Lesser Invisibility + unitTarget.CastSpell(unitTarget, 7870, true); + return; + } // Brittle Armor - (need add max stack of 24575 Brittle Armor) case 29284: - { - // Brittle Armor - SpellInfo spell = Global.SpellMgr.GetSpellInfo(24575, GetCastDifficulty()); - if (spell == null) - return; - - for (uint j = 0; j < spell.StackAmount; ++j) - m_caster.CastSpell(unitTarget, spell.Id, true); + { + // Brittle Armor + SpellInfo spell = Global.SpellMgr.GetSpellInfo(24575, GetCastDifficulty()); + if (spell == null) return; - } + + for (uint j = 0; j < spell.StackAmount; ++j) + m_caster.CastSpell(unitTarget, spell.Id, true); + return; + } // Mercurial Shield - (need add max stack of 26464 Mercurial Shield) case 29286: - { - // Mercurial Shield - SpellInfo spell = Global.SpellMgr.GetSpellInfo(26464, GetCastDifficulty()); - if (spell == null) - return; - - for (uint j = 0; j < spell.StackAmount; ++j) - m_caster.CastSpell(unitTarget, spell.Id, true); + { + // Mercurial Shield + SpellInfo spell = Global.SpellMgr.GetSpellInfo(26464, GetCastDifficulty()); + if (spell == null) return; - } + + for (uint j = 0; j < spell.StackAmount; ++j) + m_caster.CastSpell(unitTarget, spell.Id, true); + return; + } } } @@ -375,7 +386,21 @@ namespace Game.Spells if (spellInfo.GetExplicitTargetMask().HasAnyFlag(SpellCastTargetFlags.DestLocation)) targets.SetDst(m_targets); - targets.SetUnitTarget(m_caster); + Unit target = m_targets.GetUnitTarget(); + if (target != null) + targets.SetUnitTarget(target); + else + { + Unit unit = m_caster.ToUnit(); + if (unit != null) + targets.SetUnitTarget(unit); + else + { + GameObject go = m_caster.ToGameObject(); + if (go != null) + targets.SetGOTarget(go); + } + } } CastSpellExtraArgs args = new(m_originalCasterGUID); @@ -421,7 +446,15 @@ namespace Game.Spells if (spellInfo.GetExplicitTargetMask().HasAnyFlag(SpellCastTargetFlags.DestLocation)) targets.SetDst(m_targets); - targets.SetUnitTarget(m_caster); + Unit unit = m_caster.ToUnit(); + if (unit != null) + targets.SetUnitTarget(unit); + else + { + GameObject go = m_caster.ToGameObject(); + if (go != null) + targets.SetGOTarget(go); + } } CastSpellExtraArgs args = new(m_originalCasterGUID); @@ -465,12 +498,12 @@ namespace Game.Spells break; case 52463: // Hide In Mine Car case 52349: // Overtake - { - CastSpellExtraArgs args1 = new(m_originalCasterGUID); - args1.AddSpellMod(SpellValueMod.BasePoint0, damage); - unitTarget.CastSpell(unitTarget, spellInfo.Id, args1); - return; - } + { + CastSpellExtraArgs args1 = new(m_originalCasterGUID); + args1.AddSpellMod(SpellValueMod.BasePoint0, damage); + unitTarget.CastSpell(unitTarget, spellInfo.Id, args1); + return; + } } } @@ -509,45 +542,6 @@ namespace Game.Spells m_caster.CastSpell((Unit)null, spellInfo.Id, new CastSpellExtraArgs(false)); } - [SpellEffectHandler(SpellEffectName.Jump)] - void EffectJump(uint effIndex) - { - if (effectHandleMode != SpellEffectHandleMode.LaunchTarget) - return; - - if (m_caster.IsInFlight()) - return; - - if (unitTarget == null) - return; - - float speedXY, speedZ; - CalculateJumpSpeeds(effectInfo, m_caster.GetExactDist2d(unitTarget), out speedXY, out speedZ); - JumpArrivalCastArgs arrivalCast = new(); - arrivalCast.SpellId = effectInfo.TriggerSpell; - arrivalCast.Target = unitTarget.GetGUID(); - m_caster.GetMotionMaster().MoveJump(unitTarget, speedXY, speedZ, EventId.Jump, false, arrivalCast); - } - - [SpellEffectHandler(SpellEffectName.JumpDest)] - void EffectJumpDest(uint effIndex) - { - if (effectHandleMode != SpellEffectHandleMode.Launch) - return; - - if (m_caster.IsInFlight()) - return; - - if (!m_targets.HasDst()) - return; - - float speedXY, speedZ; - CalculateJumpSpeeds(effectInfo, m_caster.GetExactDist2d(destTarget), out speedXY, out speedZ); - JumpArrivalCastArgs arrivalCast = new(); - arrivalCast.SpellId = effectInfo.TriggerSpell; - m_caster.GetMotionMaster().MoveJump(destTarget, speedXY, speedZ, EventId.Jump, !m_targets.GetObjectTargetGUID().IsEmpty(), arrivalCast); - } - void CalculateJumpSpeeds(SpellEffectInfo effInfo, float dist, out float speedXY, out float speedZ) { if (effInfo.MiscValue != 0) @@ -560,6 +554,51 @@ namespace Game.Spells speedXY = dist * 10.0f / speedZ; } + [SpellEffectHandler(SpellEffectName.Jump)] + void EffectJump(uint effIndex) + { + if (effectHandleMode != SpellEffectHandleMode.LaunchTarget) + return; + + if (unitCaster == null) + return; + + if (unitCaster.IsInFlight()) + return; + + if (unitTarget == null) + return; + + float speedXY, speedZ; + CalculateJumpSpeeds(effectInfo, unitCaster.GetExactDist2d(unitTarget), out speedXY, out speedZ); + JumpArrivalCastArgs arrivalCast = new(); + arrivalCast.SpellId = effectInfo.TriggerSpell; + arrivalCast.Target = unitTarget.GetGUID(); + unitCaster.GetMotionMaster().MoveJump(unitTarget, speedXY, speedZ, EventId.Jump, false, arrivalCast); + } + + [SpellEffectHandler(SpellEffectName.JumpDest)] + void EffectJumpDest(uint effIndex) + { + if (effectHandleMode != SpellEffectHandleMode.Launch) + return; + + if (unitCaster == null) + return; + + if (unitCaster.IsInFlight()) + return; + + if (!m_targets.HasDst()) + return; + + float speedXY, speedZ; + CalculateJumpSpeeds(effectInfo, unitCaster.GetExactDist2d(destTarget), out speedXY, out speedZ); + JumpArrivalCastArgs arrivalCast = new(); + arrivalCast.SpellId = effectInfo.TriggerSpell; + unitCaster.GetMotionMaster().MoveJump(destTarget, speedXY, speedZ, EventId.Jump, !m_targets.GetObjectTargetGUID().IsEmpty(), arrivalCast); + } + [SpellEffectHandler(SpellEffectName.TeleportUnits)] void EffectTeleportUnits(uint effIndex) { @@ -592,7 +631,7 @@ namespace Game.Spells if (customLoadingScreenId != 0) player.SendPacket(new CustomLoadScreen(m_spellInfo.Id, customLoadingScreenId)); } - + if (targetDest.GetMapId() == unitTarget.GetMapId()) unitTarget.NearTeleportTo(targetDest, unitTarget == m_caster); else if (player != null) @@ -608,95 +647,95 @@ namespace Game.Spells { // Dimensional Ripper - Everlook case 23442: + { + int r = RandomHelper.IRand(0, 119); + if (r >= 70) // 7/12 success { - int r = RandomHelper.IRand(0, 119); - if (r >= 70) // 7/12 success - { - if (r < 100) // 4/12 evil twin - m_caster.CastSpell(m_caster, 23445, true); - else // 1/12 fire - m_caster.CastSpell(m_caster, 23449, true); - } - return; + if (r < 100) // 4/12 evil twin + m_caster.CastSpell(m_caster, 23445, true); + else // 1/12 fire + m_caster.CastSpell(m_caster, 23449, true); } + return; + } // Ultrasafe Transporter: Toshley's Station case 36941: + { + if (RandomHelper.randChance(50)) // 50% success { - if (RandomHelper.randChance(50)) // 50% success + int rand_eff = RandomHelper.IRand(1, 7); + switch (rand_eff) { - int rand_eff = RandomHelper.IRand(1, 7); - switch (rand_eff) + case 1: + // soul split - evil + m_caster.CastSpell(m_caster, 36900, true); + break; + case 2: + // soul split - good + m_caster.CastSpell(m_caster, 36901, true); + break; + case 3: + // Increase the size + m_caster.CastSpell(m_caster, 36895, true); + break; + case 4: + // Decrease the size + m_caster.CastSpell(m_caster, 36893, true); + break; + case 5: + // Transform { - case 1: - // soul split - evil - m_caster.CastSpell(m_caster, 36900, true); - break; - case 2: - // soul split - good - m_caster.CastSpell(m_caster, 36901, true); - break; - case 3: - // Increase the size - m_caster.CastSpell(m_caster, 36895, true); - break; - case 4: - // Decrease the size - m_caster.CastSpell(m_caster, 36893, true); - break; - case 5: - // Transform - { - if (m_caster.ToPlayer().GetTeam() == Team.Alliance) - m_caster.CastSpell(m_caster, 36897, true); - else - m_caster.CastSpell(m_caster, 36899, true); - break; - } - case 6: - // chicken - m_caster.CastSpell(m_caster, 36940, true); - break; - case 7: - // evil twin - m_caster.CastSpell(m_caster, 23445, true); - break; + if (m_caster.ToPlayer().GetTeam() == Team.Alliance) + m_caster.CastSpell(m_caster, 36897, true); + else + m_caster.CastSpell(m_caster, 36899, true); + break; } + case 6: + // chicken + m_caster.CastSpell(m_caster, 36940, true); + break; + case 7: + // evil twin + m_caster.CastSpell(m_caster, 23445, true); + break; } - return; } + return; + } // Dimensional Ripper - Area 52 case 36890: + { + if (RandomHelper.randChance(50)) // 50% success { - if (RandomHelper.randChance(50)) // 50% success + int rand_eff = RandomHelper.IRand(1, 4); + switch (rand_eff) { - int rand_eff = RandomHelper.IRand(1, 4); - switch (rand_eff) + case 1: + // soul split - evil + m_caster.CastSpell(m_caster, 36900, true); + break; + case 2: + // soul split - good + m_caster.CastSpell(m_caster, 36901, true); + break; + case 3: + // Increase the size + m_caster.CastSpell(m_caster, 36895, true); + break; + case 4: + // Transform { - case 1: - // soul split - evil - m_caster.CastSpell(m_caster, 36900, true); - break; - case 2: - // soul split - good - m_caster.CastSpell(m_caster, 36901, true); - break; - case 3: - // Increase the size - m_caster.CastSpell(m_caster, 36895, true); - break; - case 4: - // Transform - { - if (m_caster.ToPlayer().GetTeam() == Team.Alliance) - m_caster.CastSpell(m_caster, 36897, true); - else - m_caster.CastSpell(m_caster, 36899, true); - break; - } + if (m_caster.ToPlayer().GetTeam() == Team.Alliance) + m_caster.CastSpell(m_caster, 36897, true); + else + m_caster.CastSpell(m_caster, 36899, true); + break; } } - return; } + return; + } } } @@ -764,22 +803,23 @@ namespace Game.Spells return; // add spell damage bonus - uint bonus = m_caster.SpellDamageBonusDone(unitTarget, m_spellInfo, (uint)damage, DamageEffectType.SpellDirect, effectInfo); - damage = (int)(bonus + (bonus * _variance)); - damage = (int)unitTarget.SpellDamageBonusTaken(m_caster, m_spellInfo, (uint)damage, DamageEffectType.SpellDirect); + if (unitCaster != null) + { + uint bonus = unitCaster.SpellDamageBonusDone(unitTarget, m_spellInfo, (uint)damage, DamageEffectType.SpellDirect, effectInfo); + damage = (int)(bonus + (bonus * _variance)); + damage = (int)unitTarget.SpellDamageBonusTaken(unitCaster, m_spellInfo, (uint)damage, DamageEffectType.SpellDirect); + } int newDamage = -(unitTarget.ModifyPower(powerType, -damage)); - float gainMultiplier = 0.0f; - // Don't restore from self drain - if (m_caster != unitTarget) + float gainMultiplier = 0.0f; + if (unitCaster != null && unitCaster != unitTarget) { - gainMultiplier = effectInfo.CalcValueMultiplier(m_originalCaster, this); - + gainMultiplier = effectInfo.CalcValueMultiplier(unitCaster, this); int gain = (int)(newDamage * gainMultiplier); - m_caster.EnergizeBySpell(m_caster, m_spellInfo, gain, powerType); + unitCaster.EnergizeBySpell(unitCaster, m_spellInfo, gain, powerType); } ExecuteLogEffectTakeTargetPower(effIndex, unitTarget, powerType, (uint)newDamage, gainMultiplier); } @@ -845,7 +885,7 @@ namespace Game.Spells int newDamage = -(unitTarget.ModifyPower(powerType, -damage)); // NO - Not a typo - EffectPowerBurn uses effect value multiplier - not effect damage multiplier - float dmgMultiplier = effectInfo.CalcValueMultiplier(m_originalCaster, this); + float dmgMultiplier = effectInfo.CalcValueMultiplier(unitCaster, this); // add log data before multiplication (need power amount, not damage) ExecuteLogEffectTakeTargetPower(effIndex, unitTarget, powerType, (uint)newDamage, 0.0f); @@ -861,56 +901,54 @@ namespace Game.Spells if (effectHandleMode != SpellEffectHandleMode.LaunchTarget) return; - if (unitTarget != null && unitTarget.IsAlive() && damage >= 0) + if (unitTarget == null || !unitTarget.IsAlive() || damage < 0) + return; + + // Skip if m_originalCaster not available + if (unitCaster == null) + return; + + int addhealth = damage; + + // Vessel of the Naaru (Vial of the Sunwell trinket) + ///@todo: move this to scripts + if (m_spellInfo.Id == 45064) { - // Try to get original caster - Unit caster = !m_originalCasterGUID.IsEmpty() ? m_originalCaster : m_caster; - - // Skip if m_originalCaster not available - if (caster == null) - return; - - int addhealth = damage; - - // Vessel of the Naaru (Vial of the Sunwell trinket) - if (m_spellInfo.Id == 45064) + // Amount of heal - depends from stacked Holy Energy + int damageAmount = 0; + AuraEffect aurEff = unitCaster.GetAuraEffect(45062, 0); + if (aurEff != null) { - // Amount of heal - depends from stacked Holy Energy - int damageAmount = 0; - AuraEffect aurEff = m_caster.GetAuraEffect(45062, 0); - if (aurEff != null) - { - damageAmount += aurEff.GetAmount(); - m_caster.RemoveAurasDueToSpell(45062); - } - - addhealth += damageAmount; - } - // Runic Healing Injector (heal increased by 25% for engineers - 3.2.0 patch change) - else if (m_spellInfo.Id == 67489) - { - Player player = m_caster.ToPlayer(); - if (player != null) - if (player.HasSkill(SkillType.Engineering)) - MathFunctions.AddPct(ref addhealth, 25); - } - // Death Pact - return pct of max health to caster - else if (m_spellInfo.SpellFamilyName == SpellFamilyNames.Deathknight && m_spellInfo.SpellFamilyFlags[0].HasAnyFlag(0x00080000u)) - addhealth = (int)caster.SpellHealingBonusDone(unitTarget, m_spellInfo, (uint)caster.CountPctFromMaxHealth(damage), DamageEffectType.Heal, effectInfo); - else - { - uint bonus = caster.SpellHealingBonusDone(unitTarget, m_spellInfo, (uint)addhealth, DamageEffectType.Heal, effectInfo); - addhealth = (int)(bonus + (uint)(bonus * _variance)); + damageAmount += aurEff.GetAmount(); + unitCaster.RemoveAurasDueToSpell(45062); } - addhealth = (int)unitTarget.SpellHealingBonusTaken(caster, m_spellInfo, (uint)addhealth, DamageEffectType.Heal); - - // Remove Grievious bite if fully healed - if (unitTarget.HasAura(48920) && ((uint)(unitTarget.GetHealth() + (ulong)addhealth) >= unitTarget.GetMaxHealth())) - unitTarget.RemoveAura(48920); - - m_healing -= addhealth; + addhealth += damageAmount; } + // Runic Healing Injector (heal increased by 25% for engineers - 3.2.0 patch change) + else if (m_spellInfo.Id == 67489) + { + Player player = unitCaster.ToPlayer(); + if (player != null) + if (player.HasSkill(SkillType.Engineering)) + MathFunctions.AddPct(ref addhealth, 25); + } + // Death Pact - return pct of max health to caster + else if (m_spellInfo.SpellFamilyName == SpellFamilyNames.Deathknight && m_spellInfo.SpellFamilyFlags[0].HasAnyFlag(0x00080000u)) + addhealth = (int)unitCaster.SpellHealingBonusDone(unitTarget, m_spellInfo, (uint)unitCaster.CountPctFromMaxHealth(damage), DamageEffectType.Heal, effectInfo); + else + { + uint bonus = unitCaster.SpellHealingBonusDone(unitTarget, m_spellInfo, (uint)addhealth, DamageEffectType.Heal, effectInfo); + addhealth = (int)(bonus + (uint)(bonus * _variance)); + } + + addhealth = (int)unitTarget.SpellHealingBonusTaken(unitCaster, m_spellInfo, (uint)addhealth, DamageEffectType.Heal); + + // Remove Grievious bite if fully healed + if (unitTarget.HasAura(48920) && ((uint)(unitTarget.GetHealth() + (ulong)addhealth) >= unitTarget.GetMaxHealth())) + unitTarget.RemoveAura(48920); + + m_healing -= addhealth; } [SpellEffectHandler(SpellEffectName.HealPct)] @@ -922,12 +960,14 @@ namespace Game.Spells if (unitTarget == null || !unitTarget.IsAlive() || damage < 0) return; - // Skip if m_originalCaster not available - if (m_originalCaster == null) - return; + uint heal = (uint)unitTarget.CountPctFromMaxHealth(damage); + if (unitCaster) + { + heal = unitCaster.SpellHealingBonusDone(unitTarget, m_spellInfo, heal, DamageEffectType.Heal, effectInfo); + heal = unitTarget.SpellHealingBonusTaken(unitCaster, m_spellInfo, heal, DamageEffectType.Heal); + } - uint heal = m_originalCaster.SpellHealingBonusDone(unitTarget, m_spellInfo, (uint)unitTarget.CountPctFromMaxHealth(damage), DamageEffectType.Heal, effectInfo); - m_healing += (int)unitTarget.SpellHealingBonusTaken(m_originalCaster, m_spellInfo, heal, DamageEffectType.Heal); + m_healing += (int)heal; } [SpellEffectHandler(SpellEffectName.HealMechanical)] @@ -939,14 +979,15 @@ namespace Game.Spells if (unitTarget == null || !unitTarget.IsAlive() || damage < 0) return; - // Skip if m_originalCaster not available - if (m_originalCaster == null) - return; + uint heal = (uint)damage; + if (unitCaster) + heal = unitCaster.SpellHealingBonusDone(unitTarget, m_spellInfo, heal, DamageEffectType.Heal, effectInfo); - uint heal = m_originalCaster.SpellHealingBonusDone(unitTarget, m_spellInfo, (uint)damage, DamageEffectType.Heal, effectInfo); heal += (uint)(heal * _variance); + if (unitCaster) + heal = unitTarget.SpellHealingBonusTaken(unitCaster, m_spellInfo, heal, DamageEffectType.Heal); - m_healing += (int)unitTarget.SpellHealingBonusTaken(m_originalCaster, m_spellInfo, heal, DamageEffectType.Heal); + m_healing += (int)heal; } [SpellEffectHandler(SpellEffectName.HealthLeech)] @@ -958,25 +999,30 @@ namespace Game.Spells if (unitTarget == null || !unitTarget.IsAlive() || damage < 0) return; - uint bonus = m_caster.SpellDamageBonusDone(unitTarget, m_spellInfo, (uint)damage, DamageEffectType.SpellDirect, effectInfo); + uint bonus = 0; + if (unitCaster != null) + unitCaster.SpellDamageBonusDone(unitTarget, m_spellInfo, (uint)damage, DamageEffectType.SpellDirect, effectInfo); + damage = (int)(bonus + (bonus * _variance)); - damage = (int)unitTarget.SpellDamageBonusTaken(m_caster, m_spellInfo, (uint)damage, DamageEffectType.SpellDirect); + + if (unitCaster != null) + damage = (int)unitTarget.SpellDamageBonusTaken(unitCaster, m_spellInfo, (uint)damage, DamageEffectType.SpellDirect); Log.outDebug(LogFilter.Spells, "HealthLeech :{0}", damage); - float healMultiplier = effectInfo.CalcValueMultiplier(m_originalCaster, this); + float healMultiplier = effectInfo.CalcValueMultiplier(unitCaster, this); m_damage += damage; // get max possible damage, don't count overkill for heal uint healthGain = (uint)(-unitTarget.GetHealthGain(-damage) * healMultiplier); - if (m_caster.IsAlive()) + if (unitCaster != null && unitCaster.IsAlive()) { - healthGain = m_caster.SpellHealingBonusDone(m_caster, m_spellInfo, healthGain, DamageEffectType.Heal, effectInfo); - healthGain = m_caster.SpellHealingBonusTaken(m_caster, m_spellInfo, healthGain, DamageEffectType.Heal); + healthGain = unitCaster.SpellHealingBonusDone(unitCaster, m_spellInfo, healthGain, DamageEffectType.Heal, effectInfo); + healthGain = unitCaster.SpellHealingBonusTaken(unitCaster, m_spellInfo, healthGain, DamageEffectType.Heal); - HealInfo healInfo = new(m_caster, m_caster, healthGain, m_spellInfo, m_spellSchoolMask); - m_caster.HealBySpell(healInfo); + HealInfo healInfo = new(unitCaster, unitCaster, healthGain, m_spellInfo, m_spellSchoolMask); + unitCaster.HealBySpell(healInfo); } } @@ -1149,6 +1195,9 @@ namespace Game.Spells if (effectHandleMode != SpellEffectHandleMode.Hit) return; + if (unitCaster == null) + return; + // only handle at last effect for (uint i = effIndex + 1; i < SpellConst.MaxEffects; ++i) { @@ -1159,22 +1208,21 @@ namespace Game.Spells Cypher.Assert(dynObjAura == null); - Unit caster = m_caster.GetEntry() == SharedConst.WorldTrigger ? m_originalCaster : m_caster; - float radius = effectInfo.CalcRadius(caster); + float radius = effectInfo.CalcRadius(unitCaster); // Caster not in world, might be spell triggered from aura removal - if (!caster.IsInWorld) + if (!unitCaster.IsInWorld) return; DynamicObject dynObj = new(false); - if (!dynObj.CreateDynamicObject(caster.GetMap().GenerateLowGuid(HighGuid.DynamicObject), caster, m_spellInfo, destTarget, radius, DynamicObjectType.AreaSpell, m_SpellVisual)) + if (!dynObj.CreateDynamicObject(unitCaster.GetMap().GenerateLowGuid(HighGuid.DynamicObject), unitCaster, m_spellInfo, destTarget, radius, DynamicObjectType.AreaSpell, m_SpellVisual)) { dynObj.Dispose(); return; } AuraCreateInfo createInfo = new(m_castId, m_spellInfo, GetCastDifficulty(), SpellConst.MaxEffectMask, dynObj); - createInfo.SetCaster(caster); + createInfo.SetCaster(unitCaster); createInfo.SetBaseAmount(m_spellValue.EffectBasePoints); createInfo.SetCastItem(m_castItemGUID, m_castItemEntry, m_castItemLevel); @@ -1197,7 +1245,7 @@ namespace Game.Spells if (effectHandleMode != SpellEffectHandleMode.HitTarget) return; - if (unitTarget == null) + if (unitCaster == null || unitTarget == null) return; if (!unitTarget.IsAlive()) @@ -1215,25 +1263,25 @@ namespace Game.Spells { case 24571: // Blood Fury // Instantly increases your rage by ${(300-10*$max(0,$PL-60))/10}. - damage -= 10 * (int)Math.Max(0, Math.Min(30, m_caster.GetLevel() - 60)); + damage -= 10 * (int)Math.Max(0, Math.Min(30, unitCaster.GetLevel() - 60)); break; case 24532: // Burst of Energy // Instantly increases your energy by ${60-4*$max(0,$min(15,$PL-60))}. - damage -= 4 * (int)Math.Max(0, Math.Min(15, m_caster.GetLevel() - 60)); + damage -= 4 * (int)Math.Max(0, Math.Min(15, unitCaster.GetLevel() - 60)); break; case 67490: // Runic Mana Injector (mana gain increased by 25% for engineers - 3.2.0 patch change) - { - Player player = m_caster.ToPlayer(); - if (player != null) - if (player.HasSkill(SkillType.Engineering)) - MathFunctions.AddPct(ref damage, 25); - break; - } + { + Player player = unitCaster.ToPlayer(); + if (player != null) + if (player.HasSkill(SkillType.Engineering)) + MathFunctions.AddPct(ref damage, 25); + break; + } default: break; } - m_caster.EnergizeBySpell(unitTarget, m_spellInfo, damage, power); + unitCaster.EnergizeBySpell(unitTarget, m_spellInfo, damage, power); } [SpellEffectHandler(SpellEffectName.EnergizePct)] @@ -1242,8 +1290,9 @@ namespace Game.Spells if (effectHandleMode != SpellEffectHandleMode.HitTarget) return; - if (unitTarget == null) + if (unitCaster == null || unitTarget == null) return; + if (!unitTarget.IsAlive()) return; @@ -1256,7 +1305,7 @@ namespace Game.Spells return; int gain = (int)MathFunctions.CalculatePct(maxPower, damage); - m_caster.EnergizeBySpell(unitTarget, m_spellInfo, gain, power); + unitCaster.EnergizeBySpell(unitTarget, m_spellInfo, gain, power); } void SendLoot(ObjectGuid guid, LootType loottype) @@ -1277,7 +1326,7 @@ namespace Game.Spells // special case, already has GossipHello inside so return and avoid calling twice if (gameObjTarget.GetGoType() == GameObjectTypes.Goober) { - gameObjTarget.Use(m_caster); + gameObjTarget.Use(player); return; } @@ -1301,7 +1350,7 @@ namespace Game.Spells // triggering linked GO uint trapEntry = gameObjTarget.GetGoInfo().SpellFocus.linkedTrap; if (trapEntry != 0) - gameObjTarget.TriggeringLinkedGameObject(trapEntry, m_caster); + gameObjTarget.TriggeringLinkedGameObject(trapEntry, player); return; case GameObjectTypes.Chest: // @todo possible must be moved to loot release (in different from linked triggering) @@ -1314,7 +1363,7 @@ namespace Game.Spells // triggering linked GO uint _trapEntry = gameObjTarget.GetGoInfo().Chest.linkedTrap; if (_trapEntry != 0) - gameObjTarget.TriggeringLinkedGameObject(_trapEntry, m_caster); + gameObjTarget.TriggeringLinkedGameObject(_trapEntry, player); break; // Don't return, let loots been taken default: @@ -1593,21 +1642,22 @@ namespace Game.Spells return; } - if (m_originalCaster == null) - return; + WorldObject caster = m_caster; + if (m_originalCaster) + caster = m_originalCaster; - ObjectGuid privateObjectOwner = m_originalCaster.GetGUID(); + ObjectGuid privateObjectOwner = caster.GetGUID(); if (!properties.Flags.HasAnyFlag(SummonPropFlags.PersonalSpawn | SummonPropFlags.PersonalGroupSpawn)) privateObjectOwner = ObjectGuid.Empty; - if (m_originalCaster.IsPrivateObject()) - privateObjectOwner = m_originalCaster.GetPrivateObjectOwner(); + if (caster.IsPrivateObject()) + privateObjectOwner = caster.GetPrivateObjectOwner(); if (properties.Flags.HasAnyFlag(SummonPropFlags.PersonalGroupSpawn)) - if (m_originalCaster.IsPlayer() && m_originalCaster.ToPlayer().GetGroup()) - privateObjectOwner = m_originalCaster.ToPlayer().GetGroup().GetGUID(); + if (caster.IsPlayer() && m_originalCaster.ToPlayer().GetGroup()) + privateObjectOwner = caster.ToPlayer().GetGroup().GetGUID(); - int duration = m_spellInfo.CalcDuration(m_originalCaster); + int duration = m_spellInfo.CalcDuration(caster); TempSummon summon = null; @@ -1660,82 +1710,102 @@ namespace Game.Spells case SummonTitle.Minion: SummonGuardian(effIndex, entry, properties, numSummons, privateObjectOwner); break; - // Summons a vehicle, but doesn't force anyone to enter it (see SUMMON_CATEGORY_VEHICLE) + // Summons a vehicle, but doesn't force anyone to enter it (see SUMMON_CATEGORY_VEHICLE) case SummonTitle.Vehicle: case SummonTitle.Mount: - summon = m_caster.GetMap().SummonCreature(entry, destTarget, properties, (uint)duration, m_originalCaster, m_spellInfo.Id); + { + if (unitCaster == null) + return; + + summon = unitCaster.GetMap().SummonCreature(entry, destTarget, properties, (uint)duration, unitCaster, m_spellInfo.Id); break; + } case SummonTitle.LightWell: case SummonTitle.Totem: - { - summon = m_caster.GetMap().SummonCreature(entry, destTarget, properties, (uint)duration, m_originalCaster, m_spellInfo.Id, 0, privateObjectOwner); - if (summon == null || !summon.IsTotem()) - return; - - if (damage != 0) // if not spell info, DB values used - { - summon.SetMaxHealth((uint)damage); - summon.SetHealth((uint)damage); - } - break; - } - case SummonTitle.Companion: - { - summon = m_caster.GetMap().SummonCreature(entry, destTarget, properties, (uint)duration, m_originalCaster, m_spellInfo.Id, 0, privateObjectOwner); - if (summon == null || !summon.HasUnitTypeMask(UnitTypeMask.Minion)) - return; - - summon.SelectLevel(); // some summoned creaters have different from 1 DB data for level/hp - summon.SetNpcFlags((NPCFlags)((int)summon.GetCreatureTemplate().Npcflag & 0xFFFFFFFF)); - summon.SetNpcFlags2((NPCFlags2)((int)summon.GetCreatureTemplate().Npcflag >> 32)); - - summon.SetImmuneToAll(true); - - summon.GetAI().EnterEvadeMode(); - break; - } - default: - { - float radius = effectInfo.CalcRadius(); - - TempSummonType summonType = (duration == 0) ? TempSummonType.DeadDespawn : TempSummonType.TimedDespawn; - - for (uint count = 0; count < numSummons; ++count) - { - Position pos; - if (count == 0) - pos = destTarget; - else - // randomize position for multiple summons - pos = m_caster.GetRandomPoint(destTarget, radius); - - summon = m_originalCaster.SummonCreature(entry, pos, summonType, (uint)duration, 0, privateObjectOwner); - if (summon == null) - continue; - - if (properties.Control == SummonCategory.Ally) - { - summon.SetOwnerGUID(m_originalCaster.GetGUID()); - summon.SetFaction(m_originalCaster.GetFaction()); - summon.SetCreatedBySpell(m_spellInfo.Id); - } - - ExecuteLogEffectSummonObject(effIndex, summon); - } + { + if (unitCaster == null) return; + + summon = unitCaster.GetMap().SummonCreature(entry, destTarget, properties, (uint)duration, unitCaster, m_spellInfo.Id, 0, privateObjectOwner); + if (summon == null || !summon.IsTotem()) + return; + + if (damage != 0) // if not spell info, DB values used + { + summon.SetMaxHealth((uint)damage); + summon.SetHealth((uint)damage); } + break; + } + case SummonTitle.Companion: + { + if (unitCaster == null) + return; + + summon = unitCaster.GetMap().SummonCreature(entry, destTarget, properties, (uint)duration, unitCaster, m_spellInfo.Id, 0, privateObjectOwner); + if (summon == null || !summon.HasUnitTypeMask(UnitTypeMask.Minion)) + return; + + summon.SelectLevel(); // some summoned creaters have different from 1 DB data for level/hp + summon.SetNpcFlags((NPCFlags)((int)summon.GetCreatureTemplate().Npcflag & 0xFFFFFFFF)); + summon.SetNpcFlags2((NPCFlags2)((int)summon.GetCreatureTemplate().Npcflag >> 32)); + + summon.SetImmuneToAll(true); + + summon.GetAI().EnterEvadeMode(); + break; + } + default: + { + float radius = effectInfo.CalcRadius(); + + TempSummonType summonType = (duration == 0) ? TempSummonType.DeadDespawn : TempSummonType.TimedDespawn; + + for (uint count = 0; count < numSummons; ++count) + { + Position pos; + if (count == 0) + pos = destTarget; + else + // randomize position for multiple summons + pos = caster.GetRandomPoint(destTarget, radius); + + summon = caster.SummonCreature(entry, pos, summonType, (uint)duration, 0, privateObjectOwner); + if (summon == null) + continue; + + if (properties.Control == SummonCategory.Ally) + { + summon.SetOwnerGUID(caster.GetGUID()); + summon.SetFaction(caster.GetFaction()); + summon.SetCreatedBySpell(m_spellInfo.Id); + } + + ExecuteLogEffectSummonObject(effIndex, summon); + } + return; + } }//switch break; case SummonCategory.Pet: SummonGuardian(effIndex, entry, properties, numSummons, privateObjectOwner); break; case SummonCategory.Puppet: - summon = m_caster.GetMap().SummonCreature(entry, destTarget, properties, (uint)duration, m_originalCaster, m_spellInfo.Id, 0, privateObjectOwner); + { + if (unitCaster == null) + return; + + summon = unitCaster.GetMap().SummonCreature(entry, destTarget, properties, (uint)duration, unitCaster, m_spellInfo.Id, 0, privateObjectOwner); break; + } case SummonCategory.Vehicle: + { + if (unitCaster == null) + return; + // Summoning spells (usually triggered by npc_spellclick) that spawn a vehicle and that cause the clicker // to cast a ride vehicle spell on the summoned unit. - summon = m_originalCaster.GetMap().SummonCreature(entry, destTarget, properties, (uint)duration, m_caster, m_spellInfo.Id); + summon = unitCaster.GetMap().SummonCreature(entry, destTarget, properties, (uint)duration, unitCaster, m_spellInfo.Id); if (summon == null || !summon.IsVehicle()) return; @@ -1755,19 +1825,20 @@ namespace Game.Spells if (basePoints > 0 && basePoints < SharedConst.MaxVehicleSeats) args.AddSpellMod(SpellValueMod.BasePoint0, basePoints); - m_originalCaster.CastSpell(summon, spellId, args); + unitCaster.CastSpell(summon, spellId, args); uint faction = properties.Faction; if (faction == 0) - faction = m_originalCaster.GetFaction(); + faction = unitCaster.GetFaction(); summon.SetFaction(faction); break; + } } if (summon != null) { - summon.SetCreatorGUID(m_originalCaster.GetGUID()); + summon.SetCreatorGUID(caster.GetGUID()); ExecuteLogEffectSummonObject(effIndex, summon); } } @@ -1944,17 +2015,18 @@ namespace Game.Spells if (effectHandleMode != SpellEffectHandleMode.Hit) return; - if (!m_caster.IsTypeId(TypeId.Player)) + Player player = m_caster.ToPlayer(); + if (player == null) return; float radius = effectInfo.CalcRadius(); int duration = m_spellInfo.CalcDuration(m_caster); // Caster not in world, might be spell triggered from aura removal - if (!m_caster.IsInWorld) + if (!player.IsInWorld) return; DynamicObject dynObj = new(true); - if (!dynObj.CreateDynamicObject(m_caster.GetMap().GenerateLowGuid(HighGuid.DynamicObject), m_caster, m_spellInfo, destTarget, radius, DynamicObjectType.FarsightFocus, m_SpellVisual)) + if (!dynObj.CreateDynamicObject(player.GetMap().GenerateLowGuid(HighGuid.DynamicObject), player, m_spellInfo, destTarget, radius, DynamicObjectType.FarsightFocus, m_SpellVisual)) return; dynObj.SetDuration(duration); @@ -2254,7 +2326,7 @@ namespace Game.Spells if (effectHandleMode != SpellEffectHandleMode.HitTarget) return; - if (!m_caster.GetPetGUID().IsEmpty()) + if (unitCaster == null || !unitCaster.GetPetGUID().IsEmpty()) return; if (unitTarget == null) @@ -2268,13 +2340,13 @@ namespace Game.Spells if (creatureTarget.IsPet()) return; - if (m_caster.GetClass() != Class.Hunter) + if (unitCaster.GetClass() != Class.Hunter) return; // cast finish successfully Finish(); - Pet pet = m_caster.CreateTamedPetFrom(creatureTarget, m_spellInfo.Id); + Pet pet = unitCaster.CreateTamedPetFrom(creatureTarget, m_spellInfo.Id); if (pet == null) // in very specific state like near world end/etc. return; @@ -2293,12 +2365,12 @@ namespace Game.Spells pet.SetLevel(level); // caster have pet now - m_caster.SetMinion(pet, true); + unitCaster.SetMinion(pet, true); if (m_caster.IsTypeId(TypeId.Player)) { pet.SavePetToDB(PetSaveMode.AsCurrent); - m_caster.ToPlayer().PetSpellInitialize(); + unitCaster.ToPlayer().PetSpellInitialize(); } } @@ -2309,11 +2381,11 @@ namespace Game.Spells return; Player owner = null; - if (m_originalCaster != null) + if (unitCaster != null) { - owner = m_originalCaster.ToPlayer(); - if (owner == null && m_originalCaster.IsTotem()) - owner = m_originalCaster.GetCharmerOrOwnerPlayerOrPlayerItself(); + owner = unitCaster.ToPlayer(); + if (owner == null && unitCaster.IsTotem()) + owner = unitCaster.GetCharmerOrOwnerPlayerOrPlayerItself(); } uint petentry = (uint)effectInfo.MiscValue; @@ -2364,7 +2436,7 @@ namespace Game.Spells if (m_caster.IsTypeId(TypeId.Unit)) { - if (m_caster.IsTotem()) + if (m_caster.ToCreature().IsTotem()) pet.SetReactState(ReactStates.Aggressive); else pet.SetReactState(ReactStates.Defensive); @@ -2413,6 +2485,9 @@ namespace Game.Spells if (effectHandleMode != SpellEffectHandleMode.HitTarget) return; + if (unitCaster == null) + return; + // this effect use before aura Taunt apply for prevent taunt already attacking target // for spell as marked "non effective at already attacking target" if (unitTarget == null || !unitTarget.CanHaveThreatList()) @@ -2422,7 +2497,7 @@ namespace Game.Spells } ThreatManager mgr = unitTarget.GetThreatManager(); - if (mgr.GetCurrentVictim() == m_caster) + if (mgr.GetCurrentVictim() == unitCaster) { SendCastResult(SpellCastResult.DontReport); return; @@ -2430,7 +2505,7 @@ namespace Game.Spells if (!mgr.IsThreatListEmpty()) // Set threat equal to highest threat currently on target - mgr.MatchUnitThreatToHighestThreat(m_caster); + mgr.MatchUnitThreatToHighestThreat(unitCaster); } [SpellEffectHandler(SpellEffectName.WeaponDamageNoSchool)] @@ -2442,6 +2517,9 @@ namespace Game.Spells if (effectHandleMode != SpellEffectHandleMode.LaunchTarget) return; + if (unitCaster == null) + return; + if (unitTarget == null || !unitTarget.IsAlive()) return; @@ -2472,89 +2550,89 @@ namespace Game.Spells switch (m_spellInfo.SpellFamilyName) { case SpellFamilyNames.Warrior: + { + // Devastate (player ones) + if (m_spellInfo.SpellFamilyFlags[1].HasAnyFlag(0x40u)) { - // Devastate (player ones) - if (m_spellInfo.SpellFamilyFlags[1].HasAnyFlag(0x40u)) - { - // Player can apply only 58567 Sunder Armor effect. - bool needCast = !unitTarget.HasAura(58567, m_caster.GetGUID()); - if (needCast) - m_caster.CastSpell(unitTarget, 58567, true); + // Player can apply only 58567 Sunder Armor effect. + bool needCast = !unitTarget.HasAura(58567, m_caster.GetGUID()); + if (needCast) + m_caster.CastSpell(unitTarget, 58567, true); - Aura aur = unitTarget.GetAura(58567, m_caster.GetGUID()); - if (aur != null) - { - int num = (needCast ? 0 : 1); - if (num != 0) - aur.ModStackAmount(num); - fixed_bonus += (aur.GetStackAmount() - 1) * CalculateDamage(2, unitTarget); - } + Aura aur = unitTarget.GetAura(58567, m_caster.GetGUID()); + if (aur != null) + { + int num = (needCast ? 0 : 1); + if (num != 0) + aur.ModStackAmount(num); + fixed_bonus += (aur.GetStackAmount() - 1) * CalculateDamage(2, unitTarget); } - break; } + break; + } case SpellFamilyNames.Rogue: + { + // Hemorrhage + if (m_spellInfo.SpellFamilyFlags[0].HasAnyFlag(0x2000000u)) { - // Hemorrhage - if (m_spellInfo.SpellFamilyFlags[0].HasAnyFlag(0x2000000u)) + if (m_caster.IsTypeId(TypeId.Player)) + m_caster.ToPlayer().AddComboPoints(1, this); + // 50% more damage with daggers + if (unitCaster.IsPlayer()) { - if (m_caster.IsTypeId(TypeId.Player)) - m_caster.ToPlayer().AddComboPoints(1, this); - // 50% more damage with daggers - if (m_caster.IsTypeId(TypeId.Player)) - { - Item item = m_caster.ToPlayer().GetWeaponForAttack(m_attackType, true); - if (item != null) - if (item.GetTemplate().GetSubClass() == (uint)ItemSubClassWeapon.Dagger) - totalDamagePercentMod *= 1.5f; - } + Item item = unitCaster.ToPlayer().GetWeaponForAttack(m_attackType, true); + if (item != null) + if (item.GetTemplate().GetSubClass() == (uint)ItemSubClassWeapon.Dagger) + totalDamagePercentMod *= 1.5f; } - break; } + break; + } case SpellFamilyNames.Shaman: - { - // Skyshatter Harness item set bonus - // Stormstrike - AuraEffect aurEff = m_caster.IsScriptOverriden(m_spellInfo, 5634); - if (aurEff != null) - m_caster.CastSpell(m_caster, 38430, new CastSpellExtraArgs(aurEff)); - break; - } + { + // Skyshatter Harness item set bonus + // Stormstrike + AuraEffect aurEff = unitCaster.IsScriptOverriden(m_spellInfo, 5634); + if (aurEff != null) + unitCaster.CastSpell((WorldObject)null, 38430, new CastSpellExtraArgs(aurEff)); + break; + } case SpellFamilyNames.Druid: + { + // Mangle (Cat): CP + if (m_spellInfo.SpellFamilyFlags[1].HasAnyFlag(0x400u)) { - // Mangle (Cat): CP - if (m_spellInfo.SpellFamilyFlags[1].HasAnyFlag(0x400u)) - { - if (m_caster.IsTypeId(TypeId.Player)) - m_caster.ToPlayer().AddComboPoints(1, this); - } - break; + if (m_caster.IsTypeId(TypeId.Player)) + m_caster.ToPlayer().AddComboPoints(1, this); } + break; + } case SpellFamilyNames.Hunter: - { - // Kill Shot - bonus damage from Ranged Attack Power - if (m_spellInfo.SpellFamilyFlags[1].HasAnyFlag(0x800000u)) - spell_bonus += (int)(0.45f * m_caster.GetTotalAttackPowerValue(WeaponAttackType.RangedAttack)); - break; - } + { + // Kill Shot - bonus damage from Ranged Attack Power + if (m_spellInfo.SpellFamilyFlags[1].HasAnyFlag(0x800000u)) + spell_bonus += (int)(0.45f * unitCaster.GetTotalAttackPowerValue(WeaponAttackType.RangedAttack)); + break; + } case SpellFamilyNames.Deathknight: + { + // Blood Strike + if (m_spellInfo.SpellFamilyFlags[0].HasAnyFlag(0x400000u)) { - // Blood Strike - if (m_spellInfo.SpellFamilyFlags[0].HasAnyFlag(0x400000u)) + SpellEffectInfo effect = m_spellInfo.GetEffect(2); + if (effect != null) { - SpellEffectInfo effect = m_spellInfo.GetEffect(2); - if (effect != null) - { - float bonusPct = effect.CalcValue(m_caster) * unitTarget.GetDiseasesByCaster(m_caster.GetGUID()) / 2.0f; - // Death Knight T8 Melee 4P Bonus - AuraEffect aurEff = m_caster.GetAuraEffect(64736, 0); - if (aurEff != null) - MathFunctions.AddPct(ref bonusPct, aurEff.GetAmount()); - MathFunctions.AddPct(ref totalDamagePercentMod, bonusPct); - } - break; + float bonusPct = effect.CalcValue(m_caster) * unitTarget.GetDiseasesByCaster(m_caster.GetGUID()) / 2.0f; + // Death Knight T8 Melee 4P Bonus + AuraEffect aurEff = unitCaster.GetAuraEffect(64736, 0); + if (aurEff != null) + MathFunctions.AddPct(ref bonusPct, aurEff.GetAmount()); + MathFunctions.AddPct(ref totalDamagePercentMod, bonusPct); } break; } + break; + } } bool normalized = false; @@ -2602,14 +2680,14 @@ namespace Game.Spells break; } - float weapon_total_pct = m_caster.GetPctModifierValue(unitMod, UnitModifierPctType.Total); + float weapon_total_pct = unitCaster.GetPctModifierValue(unitMod, UnitModifierPctType.Total); if (fixed_bonus != 0) fixed_bonus = (int)(fixed_bonus * weapon_total_pct); if (spell_bonus != 0) spell_bonus = (int)(spell_bonus * weapon_total_pct); } - uint weaponDamage = m_caster.CalculateDamage(m_attackType, normalized, addPctMods); + uint weaponDamage = unitCaster.CalculateDamage(m_attackType, normalized, addPctMods); // Sequence is important foreach (SpellEffectInfo effect in m_spellInfo.GetEffects()) @@ -2641,8 +2719,8 @@ namespace Game.Spells weaponDamage = Math.Max(weaponDamage, 0); // Add melee damage bonuses (also check for negative) - weaponDamage = m_caster.MeleeDamageBonusDone(unitTarget, weaponDamage, m_attackType, DamageEffectType.SpellDirect, m_spellInfo); - m_damage += (int)unitTarget.MeleeDamageBonusTaken(m_caster, weaponDamage, m_attackType, DamageEffectType.SpellDirect, m_spellInfo); + weaponDamage = unitCaster.MeleeDamageBonusDone(unitTarget, weaponDamage, m_attackType, DamageEffectType.SpellDirect, m_spellInfo); + m_damage += (int)unitTarget.MeleeDamageBonusTaken(unitCaster, weaponDamage, m_attackType, DamageEffectType.SpellDirect, m_spellInfo); } [SpellEffectHandler(SpellEffectName.Threat)] @@ -2651,13 +2729,16 @@ namespace Game.Spells if (effectHandleMode != SpellEffectHandleMode.HitTarget) return; - if (unitTarget == null || !m_caster.IsAlive()) + if (unitCaster == null || !unitCaster.IsAlive()) + return; + + if (unitTarget == null) return; if (!unitTarget.CanHaveThreatList()) return; - unitTarget.GetThreatManager().AddThreat(m_caster, damage, m_spellInfo, true); + unitTarget.GetThreatManager().AddThreat(unitCaster, damage, m_spellInfo, true); } [SpellEffectHandler(SpellEffectName.HealMaxHealth)] @@ -2666,6 +2747,9 @@ namespace Game.Spells if (effectHandleMode != SpellEffectHandleMode.HitTarget) return; + if (unitCaster == null) + return; + if (unitTarget == null || !unitTarget.IsAlive()) return; @@ -2673,7 +2757,7 @@ namespace Game.Spells // damage == 0 - heal for caster max health if (damage == 0) - addhealth = (int)m_caster.GetMaxHealth(); + addhealth = (int)unitCaster.GetMaxHealth(); else addhealth = (int)(unitTarget.GetMaxHealth() - unitTarget.GetHealth()); @@ -2703,14 +2787,14 @@ namespace Game.Spells || (spell.GetState() == SpellState.Preparing && spell.GetCastTime() > 0.0f)) && curSpellInfo.CanBeInterrupted(m_caster, unitTarget)) { - if (m_originalCaster != null) + if (unitCaster != null) { int duration = m_spellInfo.GetDuration(); unitTarget.GetSpellHistory().LockSpellSchool(curSpellInfo.GetSchoolMask(), (uint)unitTarget.ModSpellDuration(m_spellInfo, unitTarget, duration, false, (uint)(1 << (int)effIndex))); if (m_spellInfo.DmgClass == SpellDmgClass.Magic) - Unit.ProcSkillsAndAuras(m_originalCaster, unitTarget, ProcFlags.DoneSpellMagicDmgClassNeg, ProcFlags.TakenSpellMagicDmgClassNeg, ProcFlagsSpellType.MaskAll, ProcFlagsSpellPhase.Hit, ProcFlagsHit.Interrupt, null, null, null); + Unit.ProcSkillsAndAuras(unitCaster, unitTarget, ProcFlags.DoneSpellMagicDmgClassNeg, ProcFlags.TakenSpellMagicDmgClassNeg, ProcFlagsSpellType.MaskAll, ProcFlagsSpellPhase.Hit, ProcFlagsHit.Interrupt, null, null, null); else if (m_spellInfo.DmgClass == SpellDmgClass.Melee) - Unit.ProcSkillsAndAuras(m_originalCaster, unitTarget, ProcFlags.DoneSpellMeleeDmgClass, ProcFlags.TakenSpellMeleeDmgClass, ProcFlagsSpellType.MaskAll, ProcFlagsSpellPhase.Hit, ProcFlagsHit.Interrupt, null, null, null); + Unit.ProcSkillsAndAuras(unitCaster, unitTarget, ProcFlags.DoneSpellMeleeDmgClass, ProcFlags.TakenSpellMeleeDmgClass, ProcFlagsSpellType.MaskAll, ProcFlagsSpellPhase.Hit, ProcFlagsHit.Interrupt, null, null, null); } ExecuteLogEffectInterruptCast(effIndex, unitTarget, curSpellInfo.Id); unitTarget.InterruptSpell(i, false); @@ -2785,442 +2869,446 @@ namespace Game.Spells // @todo we must implement hunter pet summon at login there (spell 6962) + /// @todo: move this to scripts switch (m_spellInfo.SpellFamilyName) { case SpellFamilyNames.Generic: + { + switch (m_spellInfo.Id) { - switch (m_spellInfo.Id) + case 45204: // Clone Me! + m_caster.CastSpell(unitTarget, (uint)damage, new CastSpellExtraArgs(true)); + break; + case 55693: // Remove Collapsing Cave Aura + if (unitTarget == null) + return; + unitTarget.RemoveAurasDueToSpell((uint)effectInfo.CalcValue()); + break; + // Bending Shinbone + case 8856: { - case 45204: // Clone Me! - m_caster.CastSpell(unitTarget, (uint)damage, new CastSpellExtraArgs(true)); - break; - case 55693: // Remove Collapsing Cave Aura - if (unitTarget == null) - return; - unitTarget.RemoveAurasDueToSpell((uint)effectInfo.CalcValue()); - break; - // Bending Shinbone - case 8856: - { - if (itemTarget == null && !m_caster.IsTypeId(TypeId.Player)) - return; - - uint spell_id = RandomHelper.Rand32(20) != 0 ? 8854u : 8855u; - - m_caster.CastSpell(m_caster, spell_id, new CastSpellExtraArgs(true)); - return; - } - // Brittle Armor - need remove one 24575 Brittle Armor aura - case 24590: - unitTarget.RemoveAuraFromStack(24575); + if (itemTarget == null && !m_caster.IsTypeId(TypeId.Player)) return; - // Mercurial Shield - need remove one 26464 Mercurial Shield aura - case 26465: - unitTarget.RemoveAuraFromStack(26464); + + uint spell_id = RandomHelper.Rand32(20) != 0 ? 8854u : 8855u; + + m_caster.CastSpell(m_caster, spell_id, new CastSpellExtraArgs(true)); + return; + } + // Brittle Armor - need remove one 24575 Brittle Armor aura + case 24590: + unitTarget.RemoveAuraFromStack(24575); + return; + // Mercurial Shield - need remove one 26464 Mercurial Shield aura + case 26465: + unitTarget.RemoveAuraFromStack(26464); + return; + // Shadow Flame (All script effects, not just end ones to prevent player from dodging the last triggered spell) + case 22539: + case 22972: + case 22975: + case 22976: + case 22977: + case 22978: + case 22979: + case 22980: + case 22981: + case 22982: + case 22983: + case 22984: + case 22985: + { + if (unitTarget == null || !unitTarget.IsAlive()) return; - // Shadow Flame (All script effects, not just end ones to prevent player from dodging the last triggered spell) - case 22539: - case 22972: - case 22975: - case 22976: - case 22977: - case 22978: - case 22979: - case 22980: - case 22981: - case 22982: - case 22983: - case 22984: - case 22985: - { - if (unitTarget == null || !unitTarget.IsAlive()) - return; - // Onyxia Scale Cloak - if (unitTarget.HasAura(22683)) - return; - - // Shadow Flame - m_caster.CastSpell(unitTarget, 22682, new CastSpellExtraArgs(true)); - return; - } - // Mirren's Drinking Hat - case 29830: - { - uint item = 0; - switch (RandomHelper.IRand(1, 6)) - { - case 1: - case 2: - case 3: - item = 23584; - break; // Loch Modan Lager - case 4: - case 5: - item = 23585; - break; // Stouthammer Lite - case 6: - item = 23586; - break; // Aerie Peak Pale Ale - } - if (item != 0) - DoCreateItem(effIndex, item); - break; - } - case 20589: // Escape artist - case 30918: // Improved Sprint - { - // Removes snares and roots. - unitTarget.RemoveMovementImpairingAuras(true); - break; - } - // Plant Warmaul Ogre Banner - case 32307: - Player caster = m_caster.ToPlayer(); - if (caster != null) - { - caster.RewardPlayerAndGroupAtEvent(18388, unitTarget); - Creature target = unitTarget.ToCreature(); - if (target != null) - target.DespawnOrUnsummon(); - } - break; - // Mug Transformation - case 41931: - { - if (!m_caster.IsTypeId(TypeId.Player)) - return; - - byte bag = 19; - byte slot = 0; - Item item; - - while (bag != 0) // 256 = 0 due to var type - { - item = m_caster.ToPlayer().GetItemByPos(bag, slot); - if (item != null && item.GetEntry() == 38587) - break; - - ++slot; - if (slot == 39) - { - slot = 0; - ++bag; - } - } - if (bag != 0) - { - if (m_caster.ToPlayer().GetItemByPos(bag, slot).GetCount() == 1) m_caster.ToPlayer().RemoveItem(bag, slot, true); - else m_caster.ToPlayer().GetItemByPos(bag, slot).SetCount(m_caster.ToPlayer().GetItemByPos(bag, slot).GetCount() - 1); - // Spell 42518 (Braufest - Gratisprobe des Braufest herstellen) - m_caster.CastSpell(m_caster, 42518, new CastSpellExtraArgs(true)); - return; - } - break; - } - // Brutallus - Burn - case 45141: - case 45151: - { - //Workaround for Range ... should be global for every ScriptEffect - float radius = effectInfo.CalcRadius(); - if (unitTarget != null && unitTarget.IsTypeId(TypeId.Player) && unitTarget.GetDistance(m_caster) >= radius && !unitTarget.HasAura(46394) && unitTarget != m_caster) - unitTarget.CastSpell(unitTarget, 46394, new CastSpellExtraArgs(true)); - - break; - } - // Goblin Weather Machine - case 46203: - { - if (unitTarget == null) - return; - - uint spellId = 0; - switch (RandomHelper.IRand(1, 4)) - { - case 0: spellId = 46740; break; - case 1: spellId = 46739; break; - case 2: spellId = 46738; break; - case 3: spellId = 46736; break; - } - unitTarget.CastSpell(unitTarget, spellId, new CastSpellExtraArgs(true)); - break; - } - // 5, 000 Gold - case 46642: - { - if (unitTarget == null || !unitTarget.IsTypeId(TypeId.Player)) - return; - - unitTarget.ToPlayer().ModifyMoney(5000 * MoneyConstants.Gold); - - break; - } - // Death Knight Initiate Visual - case 51519: - { - if (unitTarget == null || !unitTarget.IsTypeId(TypeId.Unit)) - return; - - uint iTmpSpellId; - switch (unitTarget.GetDisplayId()) - { - case 25369: iTmpSpellId = 51552; break; // bloodelf female - case 25373: iTmpSpellId = 51551; break; // bloodelf male - case 25363: iTmpSpellId = 51542; break; // draenei female - case 25357: iTmpSpellId = 51541; break; // draenei male - case 25361: iTmpSpellId = 51537; break; // dwarf female - case 25356: iTmpSpellId = 51538; break; // dwarf male - case 25372: iTmpSpellId = 51550; break; // forsaken female - case 25367: iTmpSpellId = 51549; break; // forsaken male - case 25362: iTmpSpellId = 51540; break; // gnome female - case 25359: iTmpSpellId = 51539; break; // gnome male - case 25355: iTmpSpellId = 51534; break; // human female - case 25354: iTmpSpellId = 51520; break; // human male - case 25360: iTmpSpellId = 51536; break; // nightelf female - case 25358: iTmpSpellId = 51535; break; // nightelf male - case 25368: iTmpSpellId = 51544; break; // orc female - case 25364: iTmpSpellId = 51543; break; // orc male - case 25371: iTmpSpellId = 51548; break; // tauren female - case 25366: iTmpSpellId = 51547; break; // tauren male - case 25370: iTmpSpellId = 51545; break; // troll female - case 25365: iTmpSpellId = 51546; break; // troll male - default: return; - } - - unitTarget.CastSpell(unitTarget, iTmpSpellId, new CastSpellExtraArgs(true)); - Creature npc = unitTarget.ToCreature(); - npc.LoadEquipment(); - return; - } - // Emblazon Runeblade - case 51770: - { - if (m_originalCaster == null) - return; - - m_originalCaster.CastSpell(m_originalCaster, (uint)damage, new CastSpellExtraArgs(false)); - break; - } - // Deathbolt from Thalgran Blightbringer - // reflected by Freya's Ward - // Retribution by Sevenfold Retribution - case 51854: - { - if (unitTarget == null) - return; - if (unitTarget.HasAura(51845)) - unitTarget.CastSpell(m_caster, 51856, new CastSpellExtraArgs(true)); - else - m_caster.CastSpell(unitTarget, 51855, new CastSpellExtraArgs(true)); - break; - } - // Summon Ghouls On Scarlet Crusade - case 51904: - { - if (!m_targets.HasDst()) - return; - - float x, y, z; - float radius = effectInfo.CalcRadius(); - for (byte i = 0; i < 15; ++i) - { - m_caster.GetRandomPoint(destTarget, radius, out x, out y, out z); - m_caster.CastSpell(new Position(x, y, z), 54522, new CastSpellExtraArgs(true)); - } - break; - } - case 52173: // Coyote Spirit Despawn - case 60243: // Blood Parrot Despawn - if (unitTarget.IsTypeId(TypeId.Unit) && unitTarget.ToCreature().IsSummon()) - unitTarget.ToTempSummon().UnSummon(); + // Onyxia Scale Cloak + if (unitTarget.HasAura(22683)) return; - case 52479: // Gift of the Harvester - if (unitTarget != null && m_originalCaster != null) - m_originalCaster.CastSpell(unitTarget, Convert.ToBoolean(RandomHelper.IRand(0, 1)) ? (uint)damage : 52505, new CastSpellExtraArgs(true)); + + // Shadow Flame + m_caster.CastSpell(unitTarget, 22682, new CastSpellExtraArgs(true)); + return; + } + // Mirren's Drinking Hat + case 29830: + { + uint item = 0; + switch (RandomHelper.IRand(1, 6)) + { + case 1: + case 2: + case 3: + item = 23584; + break; // Loch Modan Lager + case 4: + case 5: + item = 23585; + break; // Stouthammer Lite + case 6: + item = 23586; + break; // Aerie Peak Pale Ale + } + if (item != 0) + DoCreateItem(effIndex, item); + break; + } + case 20589: // Escape artist + case 30918: // Improved Sprint + { + // Removes snares and roots. + unitTarget.RemoveMovementImpairingAuras(true); + break; + } + // Plant Warmaul Ogre Banner + case 32307: + Player caster = m_caster.ToPlayer(); + if (caster != null) + { + caster.RewardPlayerAndGroupAtEvent(18388, unitTarget); + Creature target = unitTarget.ToCreature(); + if (target != null) + target.DespawnOrUnsummon(); + } + break; + // Mug Transformation + case 41931: + { + if (!m_caster.IsTypeId(TypeId.Player)) return; - case 53110: // Devour Humanoid - if (unitTarget != null) - unitTarget.CastSpell(m_caster, (uint)damage, new CastSpellExtraArgs(true)); - return; - case 57347: // Retrieving (Wintergrasp RP-GG pickup spell) - { - if (unitTarget == null || !unitTarget.IsTypeId(TypeId.Unit) || !m_caster.IsTypeId(TypeId.Player)) - return; - unitTarget.ToCreature().DespawnOrUnsummon(); + byte bag = 19; + byte slot = 0; + Item item; - return; - } - case 57349: // Drop RP-GG (Wintergrasp RP-GG at death drop spell) - { - if (!m_caster.IsTypeId(TypeId.Player)) - return; - - // Delete item from inventory at death - m_caster.ToPlayer().DestroyItemCount((uint)damage, 5, true); - - return; - } - case 58418: // Portal to Orgrimmar - case 58420: // Portal to Stormwind - { - if (unitTarget == null || !unitTarget.IsTypeId(TypeId.Player) || effIndex != 0) - return; - - uint spellID = (uint)m_spellInfo.GetEffect(0).CalcValue(); - uint questID = (uint)m_spellInfo.GetEffect(1).CalcValue(); - - if (unitTarget.ToPlayer().GetQuestStatus(questID) == QuestStatus.Complete) - unitTarget.CastSpell(unitTarget, spellID, new CastSpellExtraArgs(true)); - - return; - } - case 58941: // Rock Shards - if (unitTarget != null && m_originalCaster != null) - { - for (uint i = 0; i < 3; ++i) - { - m_originalCaster.CastSpell(unitTarget, 58689, new CastSpellExtraArgs(true)); - m_originalCaster.CastSpell(unitTarget, 58692, new CastSpellExtraArgs(true)); - } - if (m_originalCaster.GetMap().GetDifficultyID() == Difficulty.None) - { - m_originalCaster.CastSpell(unitTarget, 58695, new CastSpellExtraArgs(true)); - m_originalCaster.CastSpell(unitTarget, 58696, new CastSpellExtraArgs(true)); - } - else - { - m_originalCaster.CastSpell(unitTarget, 60883, new CastSpellExtraArgs(true)); - m_originalCaster.CastSpell(unitTarget, 60884, new CastSpellExtraArgs(true)); - } - } - return; - case 59317: // Teleporting - { - - if (unitTarget == null || !unitTarget.IsTypeId(TypeId.Player)) - return; - - // return from top - if (unitTarget.ToPlayer().GetAreaId() == 4637) - unitTarget.CastSpell(unitTarget, 59316, new CastSpellExtraArgs(true)); - // teleport atop - else - unitTarget.CastSpell(unitTarget, 59314, new CastSpellExtraArgs(true)); - - return; - } - case 62482: // Grab Crate - { - if (unitTarget != null) - { - Unit seat = m_caster.GetVehicleBase(); - if (seat != null) - { - Unit parent = seat.GetVehicleBase(); - if (parent != null) - { - // @todo a hack, range = 11, should after some time cast, otherwise too far - m_caster.CastSpell(parent, 62496, new CastSpellExtraArgs(true)); - unitTarget.CastSpell(parent, (uint)m_spellInfo.GetEffect(0).CalcValue()); - } - } - } - return; - } - case 60123: // Lightwell - { - if (!m_caster.IsTypeId(TypeId.Unit) || !m_caster.ToCreature().IsSummon()) - return; - - uint spell_heal; - - switch (m_caster.GetEntry()) - { - case 31897: spell_heal = 7001; break; - case 31896: spell_heal = 27873; break; - case 31895: spell_heal = 27874; break; - case 31894: spell_heal = 28276; break; - case 31893: spell_heal = 48084; break; - case 31883: spell_heal = 48085; break; - default: - Log.outError(LogFilter.Spells, "Unknown Lightwell spell caster {0}", m_caster.GetEntry()); - return; - } - - // proc a spellcast - Aura chargesAura = m_caster.GetAura(59907); - if (chargesAura != null) - { - m_caster.CastSpell(unitTarget, spell_heal, new CastSpellExtraArgs(m_caster.ToTempSummon().GetSummonerGUID())); - if (chargesAura.ModCharges(-1)) - m_caster.ToTempSummon().UnSummon(); - } - - return; - } - // Stoneclaw Totem - case 55328: // Rank 1 - case 55329: // Rank 2 - case 55330: // Rank 3 - case 55332: // Rank 4 - case 55333: // Rank 5 - case 55335: // Rank 6 - case 55278: // Rank 7 - case 58589: // Rank 8 - case 58590: // Rank 9 - case 58591: // Rank 10 - { - // Cast Absorb on totems - for (byte slot = (int)SummonSlot.Totem; slot < SharedConst.MaxTotemSlot; ++slot) - { - if (unitTarget.m_SummonSlot[slot].IsEmpty()) - continue; - - Creature totem = unitTarget.GetMap().GetCreature(unitTarget.m_SummonSlot[slot]); - if (totem != null && totem.IsTotem()) - { - CastSpellExtraArgs args = new(TriggerCastFlags.FullMask); - args.AddSpellMod(SpellValueMod.BasePoint0, damage); - m_caster.CastSpell(totem, 55277, args); - } - } + while (bag != 0) // 256 = 0 due to var type + { + item = m_caster.ToPlayer().GetItemByPos(bag, slot); + if (item != null && item.GetEntry() == 38587) break; - } - case 45668: // Ultra-Advanced Proto-Typical Shortening Blaster + + ++slot; + if (slot == 39) { - if (unitTarget == null || !unitTarget.IsTypeId(TypeId.Unit)) - return; + slot = 0; + ++bag; + } + } + if (bag != 0) + { + if (m_caster.ToPlayer().GetItemByPos(bag, slot).GetCount() == 1) m_caster.ToPlayer().RemoveItem(bag, slot, true); + else m_caster.ToPlayer().GetItemByPos(bag, slot).SetCount(m_caster.ToPlayer().GetItemByPos(bag, slot).GetCount() - 1); + // Spell 42518 (Braufest - Gratisprobe des Braufest herstellen) + m_caster.CastSpell(m_caster, 42518, new CastSpellExtraArgs(true)); + return; + } + break; + } + // Brutallus - Burn + case 45141: + case 45151: + { + //Workaround for Range ... should be global for every ScriptEffect + float radius = effectInfo.CalcRadius(); + if (unitTarget != null && unitTarget.IsTypeId(TypeId.Player) && unitTarget.GetDistance(m_caster) >= radius && !unitTarget.HasAura(46394) && unitTarget != m_caster) + unitTarget.CastSpell(unitTarget, 46394, new CastSpellExtraArgs(true)); - if (RandomHelper.randChance(50)) // chance unknown, using 50 - return; + break; + } + // Goblin Weather Machine + case 46203: + { + if (unitTarget == null) + return; - uint[] spellPlayer = new uint[5] + uint spellId = 0; + switch (RandomHelper.IRand(1, 4)) + { + case 0: spellId = 46740; break; + case 1: spellId = 46739; break; + case 2: spellId = 46738; break; + case 3: spellId = 46736; break; + } + unitTarget.CastSpell(unitTarget, spellId, new CastSpellExtraArgs(true)); + break; + } + // 5, 000 Gold + case 46642: + { + if (unitTarget == null || !unitTarget.IsTypeId(TypeId.Player)) + return; + + unitTarget.ToPlayer().ModifyMoney(5000 * MoneyConstants.Gold); + + break; + } + // Death Knight Initiate Visual + case 51519: + { + if (unitTarget == null || !unitTarget.IsTypeId(TypeId.Unit)) + return; + + uint iTmpSpellId; + switch (unitTarget.GetDisplayId()) + { + case 25369: iTmpSpellId = 51552; break; // bloodelf female + case 25373: iTmpSpellId = 51551; break; // bloodelf male + case 25363: iTmpSpellId = 51542; break; // draenei female + case 25357: iTmpSpellId = 51541; break; // draenei male + case 25361: iTmpSpellId = 51537; break; // dwarf female + case 25356: iTmpSpellId = 51538; break; // dwarf male + case 25372: iTmpSpellId = 51550; break; // forsaken female + case 25367: iTmpSpellId = 51549; break; // forsaken male + case 25362: iTmpSpellId = 51540; break; // gnome female + case 25359: iTmpSpellId = 51539; break; // gnome male + case 25355: iTmpSpellId = 51534; break; // human female + case 25354: iTmpSpellId = 51520; break; // human male + case 25360: iTmpSpellId = 51536; break; // nightelf female + case 25358: iTmpSpellId = 51535; break; // nightelf male + case 25368: iTmpSpellId = 51544; break; // orc female + case 25364: iTmpSpellId = 51543; break; // orc male + case 25371: iTmpSpellId = 51548; break; // tauren female + case 25366: iTmpSpellId = 51547; break; // tauren male + case 25370: iTmpSpellId = 51545; break; // troll female + case 25365: iTmpSpellId = 51546; break; // troll male + default: return; + } + + unitTarget.CastSpell(unitTarget, iTmpSpellId, new CastSpellExtraArgs(true)); + Creature npc = unitTarget.ToCreature(); + npc.LoadEquipment(); + return; + } + // Emblazon Runeblade + case 51770: + { + if (m_originalCaster == null) + return; + + m_originalCaster.CastSpell(m_originalCaster, (uint)damage, new CastSpellExtraArgs(false)); + break; + } + // Deathbolt from Thalgran Blightbringer + // reflected by Freya's Ward + // Retribution by Sevenfold Retribution + case 51854: + { + if (unitTarget == null) + return; + if (unitTarget.HasAura(51845)) + unitTarget.CastSpell(m_caster, 51856, new CastSpellExtraArgs(true)); + else + m_caster.CastSpell(unitTarget, 51855, new CastSpellExtraArgs(true)); + break; + } + // Summon Ghouls On Scarlet Crusade + case 51904: + { + if (!m_targets.HasDst()) + return; + + float x, y, z; + float radius = effectInfo.CalcRadius(); + for (byte i = 0; i < 15; ++i) + { + m_caster.GetRandomPoint(destTarget, radius, out x, out y, out z); + m_caster.CastSpell(new Position(x, y, z), 54522, new CastSpellExtraArgs(true)); + } + break; + } + case 52173: // Coyote Spirit Despawn + case 60243: // Blood Parrot Despawn + if (unitTarget.IsTypeId(TypeId.Unit) && unitTarget.ToCreature().IsSummon()) + unitTarget.ToTempSummon().UnSummon(); + return; + case 52479: // Gift of the Harvester + if (unitTarget != null && unitCaster != null) + unitCaster.CastSpell(unitTarget, Convert.ToBoolean(RandomHelper.IRand(0, 1)) ? (uint)damage : 52505, new CastSpellExtraArgs(true)); + return; + case 53110: // Devour Humanoid + if (unitTarget != null) + unitTarget.CastSpell(m_caster, (uint)damage, new CastSpellExtraArgs(true)); + return; + case 57347: // Retrieving (Wintergrasp RP-GG pickup spell) + { + if (unitTarget == null || !unitTarget.IsTypeId(TypeId.Unit) || !m_caster.IsTypeId(TypeId.Player)) + return; + + unitTarget.ToCreature().DespawnOrUnsummon(); + + return; + } + case 57349: // Drop RP-GG (Wintergrasp RP-GG at death drop spell) + { + if (!m_caster.IsTypeId(TypeId.Player)) + return; + + // Delete item from inventory at death + m_caster.ToPlayer().DestroyItemCount((uint)damage, 5, true); + + return; + } + case 58418: // Portal to Orgrimmar + case 58420: // Portal to Stormwind + { + if (unitTarget == null || !unitTarget.IsTypeId(TypeId.Player) || effIndex != 0) + return; + + uint spellID = (uint)m_spellInfo.GetEffect(0).CalcValue(); + uint questID = (uint)m_spellInfo.GetEffect(1).CalcValue(); + + if (unitTarget.ToPlayer().GetQuestStatus(questID) == QuestStatus.Complete) + unitTarget.CastSpell(unitTarget, spellID, new CastSpellExtraArgs(true)); + + return; + } + case 58941: // Rock Shards + if (unitTarget != null && m_originalCaster != null) + { + for (uint i = 0; i < 3; ++i) + { + m_originalCaster.CastSpell(unitTarget, 58689, new CastSpellExtraArgs(true)); + m_originalCaster.CastSpell(unitTarget, 58692, new CastSpellExtraArgs(true)); + } + if (m_originalCaster.GetMap().GetDifficultyID() == Difficulty.None) + { + m_originalCaster.CastSpell(unitTarget, 58695, new CastSpellExtraArgs(true)); + m_originalCaster.CastSpell(unitTarget, 58696, new CastSpellExtraArgs(true)); + } + else + { + m_originalCaster.CastSpell(unitTarget, 60883, new CastSpellExtraArgs(true)); + m_originalCaster.CastSpell(unitTarget, 60884, new CastSpellExtraArgs(true)); + } + } + return; + case 59317: // Teleporting + { + + if (unitTarget == null || !unitTarget.IsTypeId(TypeId.Player)) + return; + + // return from top + if (unitTarget.ToPlayer().GetAreaId() == 4637) + unitTarget.CastSpell(unitTarget, 59316, new CastSpellExtraArgs(true)); + // teleport atop + else + unitTarget.CastSpell(unitTarget, 59314, new CastSpellExtraArgs(true)); + + return; + } + case 62482: // Grab Crate + { + if (unitCaster == null) + return; + + if (unitTarget != null) + { + Unit seat = unitCaster.GetVehicleBase(); + if (seat != null) + { + Unit parent = seat.GetVehicleBase(); + if (parent != null) { + // @todo a hack, range = 11, should after some time cast, otherwise too far + unitCaster.CastSpell(parent, 62496, new CastSpellExtraArgs(true)); + unitTarget.CastSpell(parent, (uint)m_spellInfo.GetEffect(0).CalcValue()); + } + } + } + return; + } + case 60123: // Lightwell + { + if (!m_caster.IsTypeId(TypeId.Unit) || !m_caster.ToCreature().IsSummon()) + return; + + uint spell_heal; + + switch (m_caster.GetEntry()) + { + case 31897: spell_heal = 7001; break; + case 31896: spell_heal = 27873; break; + case 31895: spell_heal = 27874; break; + case 31894: spell_heal = 28276; break; + case 31893: spell_heal = 48084; break; + case 31883: spell_heal = 48085; break; + default: + Log.outError(LogFilter.Spells, "Unknown Lightwell spell caster {0}", m_caster.GetEntry()); + return; + } + + // proc a spellcast + Aura chargesAura = m_caster.ToCreature().GetAura(59907); + if (chargesAura != null) + { + m_caster.CastSpell(unitTarget, spell_heal, new CastSpellExtraArgs(m_caster.ToCreature().ToTempSummon().GetSummonerGUID())); + if (chargesAura.ModCharges(-1)) + m_caster.ToCreature().ToTempSummon().UnSummon(); + } + + return; + } + // Stoneclaw Totem + case 55328: // Rank 1 + case 55329: // Rank 2 + case 55330: // Rank 3 + case 55332: // Rank 4 + case 55333: // Rank 5 + case 55335: // Rank 6 + case 55278: // Rank 7 + case 58589: // Rank 8 + case 58590: // Rank 9 + case 58591: // Rank 10 + { + // Cast Absorb on totems + for (byte slot = (int)SummonSlot.Totem; slot < SharedConst.MaxTotemSlot; ++slot) + { + if (unitTarget.m_SummonSlot[slot].IsEmpty()) + continue; + + Creature totem = unitTarget.GetMap().GetCreature(unitTarget.m_SummonSlot[slot]); + if (totem != null && totem.IsTotem()) + { + CastSpellExtraArgs args = new(TriggerCastFlags.FullMask); + args.AddSpellMod(SpellValueMod.BasePoint0, damage); + m_caster.CastSpell(totem, 55277, args); + } + } + break; + } + case 45668: // Ultra-Advanced Proto-Typical Shortening Blaster + { + if (unitTarget == null || !unitTarget.IsTypeId(TypeId.Unit)) + return; + + if (RandomHelper.randChance(50)) // chance unknown, using 50 + return; + + uint[] spellPlayer = new uint[5] + { 45674, // Bigger! 45675, // Shrunk 45678, // Yellow 45682, // Ghost 45684 // Polymorph - }; + }; - uint[] spellTarget = new uint[5] - { + uint[] spellTarget = new uint[5] + { 45673, // Bigger! 45672, // Shrunk 45677, // Yellow 45681, // Ghost 45683 // Polymorph - }; + }; - m_caster.CastSpell(m_caster, spellPlayer[RandomHelper.IRand(0, 4)], true); - unitTarget.CastSpell(unitTarget, spellTarget[RandomHelper.IRand(0, 4)], true); - break; - } + m_caster.CastSpell(m_caster, spellPlayer[RandomHelper.IRand(0, 4)], true); + unitTarget.CastSpell(unitTarget, spellTarget[RandomHelper.IRand(0, 4)], true); + break; } - break; } + break; + } } // normal DB scripted effect @@ -3285,13 +3373,13 @@ namespace Game.Spells } //CREATE DUEL FLAG OBJECT - Map map = m_caster.GetMap(); + Map map = caster.GetMap(); Position pos = new() { - posX = m_caster.GetPositionX() + (unitTarget.GetPositionX() - m_caster.GetPositionX()) / 2, - posY = m_caster.GetPositionY() + (unitTarget.GetPositionY() - m_caster.GetPositionY()) / 2, - posZ = m_caster.GetPositionZ(), - Orientation = m_caster.GetOrientation() + posX = caster.GetPositionX() + (unitTarget.GetPositionX() - caster.GetPositionX()) / 2, + posY = caster.GetPositionY() + (unitTarget.GetPositionY() - caster.GetPositionY()) / 2, + posZ = caster.GetPositionZ(), + Orientation = caster.GetOrientation() }; Quaternion rotation = Quaternion.fromEulerAnglesZYX(pos.GetOrientation(), 0.0f, 0.0f); @@ -3299,17 +3387,17 @@ namespace Game.Spells if (!go) return; - PhasingHandler.InheritPhaseShift(go, m_caster); + PhasingHandler.InheritPhaseShift(go, caster); - go.SetFaction(m_caster.GetFaction()); - go.SetLevel(m_caster.GetLevel() + 1); - int duration = m_spellInfo.CalcDuration(m_caster); + go.SetFaction(caster.GetFaction()); + go.SetLevel(caster.GetLevel() + 1); + int duration = m_spellInfo.CalcDuration(caster); go.SetRespawnTime(duration > 0 ? duration / Time.InMilliseconds : 0); go.SetSpellId(m_spellInfo.Id); ExecuteLogEffectSummonObject(effIndex, go); - m_caster.AddGameObject(go); + caster.AddGameObject(go); map.AddToMap(go); //END @@ -3398,10 +3486,13 @@ namespace Game.Spells if (effectHandleMode != SpellEffectHandleMode.HitTarget) return; + if (unitCaster == null) + return; + if (unitTarget == null || !unitTarget.IsTypeId(TypeId.Player)) return; - unitTarget.ToPlayer().SendSummonRequestFrom(m_caster); + unitTarget.ToPlayer().SendSummonRequestFrom(unitCaster); } [SpellEffectHandler(SpellEffectName.ActivateObject)] @@ -3618,19 +3709,22 @@ namespace Game.Spells if (effectHandleMode != SpellEffectHandleMode.Hit) return; + if (unitCaster == null) + return; + byte slot = (byte)(effectInfo.Effect - SpellEffectName.SummonObjectSlot1); - ObjectGuid guid = m_caster.m_ObjectSlot[slot]; + ObjectGuid guid = unitCaster.m_ObjectSlot[slot]; if (!guid.IsEmpty()) { - GameObject obj = m_caster.GetMap().GetGameObject(guid); + GameObject obj = unitCaster.GetMap().GetGameObject(guid); if (obj != null) { // Recast case - null spell id to make auras not be removed on object remove from world if (m_spellInfo.Id == obj.GetSpellId()) obj.SetSpellId(0); - m_caster.RemoveGameObject(obj, true); + unitCaster.RemoveGameObject(obj, true); } - m_caster.m_ObjectSlot[slot].Clear(); + unitCaster.m_ObjectSlot[slot].Clear(); } float x, y, z; @@ -3639,7 +3733,7 @@ namespace Game.Spells destTarget.GetPosition(out x, out y, out z); // Summon in random point all other units if location present else - m_caster.GetClosePoint(out x, out y, out z, SharedConst.DefaultPlayerBoundingRadius); + unitCaster.GetClosePoint(out x, out y, out z, SharedConst.DefaultPlayerBoundingRadius); Map map = m_caster.GetMap(); Position pos = new(x, y, z, m_caster.GetOrientation()); @@ -3650,16 +3744,18 @@ namespace Game.Spells PhasingHandler.InheritPhaseShift(go, m_caster); + go.SetFaction(unitCaster.GetFaction()); + go.SetLevel(unitCaster.GetLevel()); int duration = m_spellInfo.CalcDuration(m_caster); go.SetRespawnTime(duration > 0 ? duration / Time.InMilliseconds : 0); go.SetSpellId(m_spellInfo.Id); - m_caster.AddGameObject(go); + unitCaster.AddGameObject(go); ExecuteLogEffectSummonObject(effIndex, go); map.AddToMap(go); - m_caster.m_ObjectSlot[slot] = go.GetGUID(); + unitCaster.m_ObjectSlot[slot] = go.GetGUID(); } [SpellEffectHandler(SpellEffectName.Resurrect)] @@ -3796,6 +3892,9 @@ namespace Game.Spells if (effectHandleMode != SpellEffectHandleMode.Hit) return; + if (unitCaster == null) + return; + float dist = m_caster.GetVisibilityRange(); // clear focus @@ -3803,19 +3902,19 @@ namespace Game.Spells breakTarget.Data.UnitGUID = m_caster.GetGUID(); breakTarget.Data.Write(); - var notifierBreak = new MessageDistDelivererToHostile>(m_caster, breakTarget, dist); + var notifierBreak = new MessageDistDelivererToHostile>(unitCaster, breakTarget, dist); Cell.VisitWorldObjects(m_caster, notifierBreak, dist); // and selection PacketSenderOwning clearTarget = new(); clearTarget.Data.Guid = m_caster.GetGUID(); clearTarget.Data.Write(); - var notifierClear = new MessageDistDelivererToHostile>(m_caster, clearTarget, dist); + var notifierClear = new MessageDistDelivererToHostile>(unitCaster, clearTarget, dist); Cell.VisitWorldObjects(m_caster, notifierClear, dist); // we should also force pets to remove us from current target List attackerSet = new(); - foreach (var unit in m_caster.GetAttackers()) + foreach (var unit in unitCaster.GetAttackers()) if (unit.GetTypeId() == TypeId.Unit && !unit.CanHaveThreatList()) attackerSet.Add(unit); @@ -3829,11 +3928,8 @@ namespace Game.Spells if (effectHandleMode != SpellEffectHandleMode.Hit) return; - if (m_caster == null || m_caster.IsAlive()) - return; - if (!m_caster.IsTypeId(TypeId.Player)) - return; - if (!m_caster.IsInWorld) + Player player = m_caster.ToPlayer(); + if (player == null || !player.IsInWorld || player.IsAlive()) return; uint health; @@ -3848,12 +3944,11 @@ namespace Game.Spells // percent case else { - health = (uint)m_caster.CountPctFromMaxHealth(damage); - if (m_caster.GetMaxPower(PowerType.Mana) > 0) - mana = MathFunctions.CalculatePct(m_caster.GetMaxPower(PowerType.Mana), damage); + health = (uint)player.CountPctFromMaxHealth(damage); + if (player.GetMaxPower(PowerType.Mana) > 0) + mana = MathFunctions.CalculatePct(player.GetMaxPower(PowerType.Mana), damage); } - Player player = m_caster.ToPlayer(); player.ResurrectPlayer(0.0f); player.SetHealth(health); @@ -3873,7 +3968,9 @@ namespace Game.Spells if (!unitTarget.IsTypeId(TypeId.Unit)) return; - if (!m_caster.IsTypeId(TypeId.Player)) + + Player player = m_caster.ToPlayer(); + if (player == null) return; Creature creature = unitTarget.ToCreature(); @@ -3883,7 +3980,7 @@ namespace Game.Spells creature.RemoveUnitFlag(UnitFlags.Skinnable); creature.AddDynamicFlag(UnitDynFlags.Lootable); - m_caster.ToPlayer().SendLoot(creature.GetGUID(), LootType.Skinning); + player.SendLoot(creature.GetGUID(), LootType.Skinning); if (skill == SkillType.Skinning) { @@ -3928,11 +4025,14 @@ namespace Game.Spells if (unitTarget == null) return; + if (unitCaster == null) + return; + if (effectHandleMode == SpellEffectHandleMode.LaunchTarget) { // charge changes fall time - if (m_caster.GetTypeId() == TypeId.Player) - m_caster.ToPlayer().SetFallInformation(0, m_caster.GetPositionZ()); + if (unitCaster.IsPlayer()) + unitCaster.ToPlayer().SetFallInformation(0, m_caster.GetPositionZ()); float speed = MathFunctions.fuzzyGt(m_spellInfo.Speed, 0.0f) ? m_spellInfo.Speed : MotionMaster.SPEED_CHARGE; SpellEffectExtraData spellEffectExtraData = null; @@ -3949,7 +4049,7 @@ namespace Game.Spells if (MathFunctions.fuzzyGt(m_spellInfo.Speed, 0.0f) && m_spellInfo.HasAttribute(SpellAttr9.SpecialDelayCalculation)) speed = pos.GetExactDist(m_caster) / speed; - m_caster.GetMotionMaster().MoveCharge(pos.posX, pos.posY, pos.posZ, speed, EventId.Charge, false, unitTarget, spellEffectExtraData); + unitCaster.GetMotionMaster().MoveCharge(pos.posX, pos.posY, pos.posZ, speed, EventId.Charge, false, unitTarget, spellEffectExtraData); } else { @@ -3959,7 +4059,7 @@ namespace Game.Spells speed = new Position(pos.X, pos.Y, pos.Z).GetExactDist(m_caster) / speed; } - m_caster.GetMotionMaster().MoveCharge(m_preGeneratedPath, speed, unitTarget, spellEffectExtraData); + unitCaster.GetMotionMaster().MoveCharge(m_preGeneratedPath, speed, unitTarget, spellEffectExtraData); } } @@ -3967,7 +4067,7 @@ namespace Game.Spells { // not all charge effects used in negative spells if (!m_spellInfo.IsPositive() && m_caster.IsTypeId(TypeId.Player)) - m_caster.Attack(unitTarget, true); + unitCaster.Attack(unitTarget, true); if (effectInfo.TriggerSpell != 0) m_caster.CastSpell(unitTarget, effectInfo.TriggerSpell, new CastSpellExtraArgs(m_originalCasterGUID)); @@ -3980,17 +4080,20 @@ namespace Game.Spells if (destTarget == null) return; + if (unitCaster == null) + return; + if (effectHandleMode == SpellEffectHandleMode.Launch) { Position pos = destTarget.GetPosition(); - if (!m_caster.IsWithinLOS(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ())) + if (!unitCaster.IsWithinLOS(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ())) { - float angle = m_caster.GetRelativeAngle(pos.posX, pos.posY); - float dist = m_caster.GetDistance(pos); - pos = m_caster.GetFirstCollisionPosition(dist, angle); + float angle = unitCaster.GetRelativeAngle(pos.posX, pos.posY); + float dist = unitCaster.GetDistance(pos); + pos = unitCaster.GetFirstCollisionPosition(dist, angle); } - m_caster.GetMotionMaster().MoveCharge(pos.posX, pos.posY, pos.posZ); + unitCaster.GetMotionMaster().MoveCharge(pos.posX, pos.posY, pos.posZ); } else if (effectHandleMode == SpellEffectHandleMode.Hit) { @@ -4009,7 +4112,7 @@ namespace Game.Spells if (!unitTarget) return; - if (m_caster.GetTypeId() == TypeId.Player || m_caster.GetOwnerGUID().IsPlayer() || m_caster.IsHunterPet()) + if (m_caster.GetAffectingPlayer()) { Creature creatureTarget = unitTarget.ToCreature(); if (creatureTarget != null) @@ -4040,9 +4143,7 @@ namespace Game.Spells return; } else //if (GetEffect(i].Effect == SPELL_EFFECT_KNOCK_BACK) - { m_caster.GetPosition(out x, out y); - } unitTarget.KnockbackFrom(x, y, speedxy, speedz); } @@ -4254,20 +4355,23 @@ namespace Game.Spells if (effectHandleMode != SpellEffectHandleMode.Hit) return; + if (unitCaster == null) + return; + int mana = 0; for (byte slot = (int)SummonSlot.Totem; slot < SharedConst.MaxTotemSlot; ++slot) { - if (m_caster.m_SummonSlot[slot].IsEmpty()) + if (unitCaster.m_SummonSlot[slot].IsEmpty()) continue; - Creature totem = m_caster.GetMap().GetCreature(m_caster.m_SummonSlot[slot]); + Creature totem = unitCaster.GetMap().GetCreature(unitCaster.m_SummonSlot[slot]); if (totem != null && totem.IsTotem()) { uint spell_id = totem.m_unitData.CreatedBySpell; SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo(spell_id, GetCastDifficulty()); if (spellInfo != null) { - var costs = spellInfo.CalcPowerCost(m_caster, spellInfo.GetSchoolMask()); + var costs = spellInfo.CalcPowerCost(unitCaster, spellInfo.GetSchoolMask()); var m = costs.Find(cost => cost.Power == PowerType.Mana); if (m != null) mana += m.Amount; @@ -4280,7 +4384,7 @@ namespace Game.Spells { CastSpellExtraArgs args = new(TriggerCastFlags.FullMask); args.AddSpellMod(SpellValueMod.BasePoint0, mana); - m_caster.CastSpell(m_caster, 39104, args); + unitCaster.CastSpell(m_caster, 39104, args); } } @@ -4352,10 +4456,10 @@ namespace Game.Spells if (effectHandleMode != SpellEffectHandleMode.HitTarget) return; - if (unitTarget == null) + if (unitCaster == null || unitTarget == null) return; - unitTarget.GetThreatManager().ModifyThreatByPercent(m_caster, damage); + unitTarget.GetThreatManager().ModifyThreatByPercent(unitCaster, damage); } [SpellEffectHandler(SpellEffectName.TransDoor)] @@ -4364,9 +4468,12 @@ namespace Game.Spells if (effectHandleMode != SpellEffectHandleMode.Hit) return; + if (unitCaster == null) + return; + uint name_id = (uint)effectInfo.MiscValue; - var overrideSummonedGameObjects = m_caster.GetAuraEffectsByType(AuraType.OverrideSummonedObject); + var overrideSummonedGameObjects = unitCaster.GetAuraEffectsByType(AuraType.OverrideSummonedObject); foreach (AuraEffect aurEff in overrideSummonedGameObjects) { if (aurEff.GetMiscValue() == name_id) @@ -4390,8 +4497,8 @@ namespace Game.Spells //FIXME: this can be better check for most objects but still hack else if (effectInfo.HasRadius() && m_spellInfo.Speed == 0) { - float dis = effectInfo.CalcRadius(m_originalCaster); - m_caster.GetClosePoint(out fx, out fy, out fz, SharedConst.DefaultPlayerBoundingRadius, dis); + float dis = effectInfo.CalcRadius(unitCaster); + unitCaster.GetClosePoint(out fx, out fy, out fz, SharedConst.DefaultPlayerBoundingRadius, dis); } else { @@ -4400,16 +4507,17 @@ namespace Game.Spells float max_dis = m_spellInfo.GetMaxRange(true); float dis = (float)RandomHelper.NextDouble() * (max_dis - min_dis) + min_dis; - m_caster.GetClosePoint(out fx, out fy, out fz, SharedConst.DefaultPlayerBoundingRadius, dis); + unitCaster.GetClosePoint(out fx, out fy, out fz, SharedConst.DefaultPlayerBoundingRadius, dis); } - Map cMap = m_caster.GetMap(); + Map cMap = unitCaster.GetMap(); // if gameobject is summoning object, it should be spawned right on caster's position if (goinfo.type == GameObjectTypes.Ritual) - m_caster.GetPosition(out fx, out fy, out fz); + unitCaster.GetPosition(out fx, out fy, out fz); + + Position pos = new(fx, fy, fz, unitCaster.GetOrientation()); + Quaternion rotation = Quaternion.fromEulerAnglesZYX(unitCaster.GetOrientation(), 0.0f, 0.0f); - Position pos = new(fx, fy, fz, m_caster.GetOrientation()); - Quaternion rotation = Quaternion.fromEulerAnglesZYX(m_caster.GetOrientation(), 0.0f, 0.0f); GameObject go = GameObject.CreateGameObject(name_id, cMap, pos, rotation, 255, GameObjectState.Ready); if (!go) return; @@ -4421,38 +4529,38 @@ namespace Game.Spells switch (goinfo.type) { case GameObjectTypes.FishingNode: + { + go.SetFaction(unitCaster.GetFaction()); + ObjectGuid bobberGuid = go.GetGUID(); + // client requires fishing bobber guid in channel object slot 0 to be usable + unitCaster.SetChannelObject(0, bobberGuid); + unitCaster.AddGameObject(go); // will removed at spell cancel + + // end time of range when possible catch fish (FISHING_BOBBER_READY_TIME..GetDuration(m_spellInfo)) + // start time == fish-FISHING_BOBBER_READY_TIME (0..GetDuration(m_spellInfo)-FISHING_BOBBER_READY_TIME) + int lastSec = 0; + switch (RandomHelper.IRand(0, 3)) { - go.SetFaction(m_caster.GetFaction()); - ObjectGuid bobberGuid = go.GetGUID(); - // client requires fishing bobber guid in channel object slot 0 to be usable - m_caster.SetChannelObject(0, bobberGuid); - m_caster.AddGameObject(go); // will removed at spell cancel - - // end time of range when possible catch fish (FISHING_BOBBER_READY_TIME..GetDuration(m_spellInfo)) - // start time == fish-FISHING_BOBBER_READY_TIME (0..GetDuration(m_spellInfo)-FISHING_BOBBER_READY_TIME) - int lastSec = 0; - switch (RandomHelper.IRand(0, 3)) - { - case 0: lastSec = 3; break; - case 1: lastSec = 7; break; - case 2: lastSec = 13; break; - case 3: lastSec = 17; break; - } - - duration = duration - lastSec * Time.InMilliseconds + 5 * Time.InMilliseconds; - break; + case 0: lastSec = 3; break; + case 1: lastSec = 7; break; + case 2: lastSec = 13; break; + case 3: lastSec = 17; break; } + + duration = duration - lastSec * Time.InMilliseconds + 5 * Time.InMilliseconds; + break; + } case GameObjectTypes.Ritual: + { + if (unitCaster.IsPlayer()) { - if (m_caster.IsTypeId(TypeId.Player)) - { - go.AddUniqueUse(m_caster.ToPlayer()); - m_caster.AddGameObject(go); // will be removed at spell cancel - } - break; + go.AddUniqueUse(unitCaster.ToPlayer()); + unitCaster.AddGameObject(go); // will be removed at spell cancel } + break; + } case GameObjectTypes.DuelArbiter: // 52991 - m_caster.AddGameObject(go); + unitCaster.AddGameObject(go); break; case GameObjectTypes.FishingHole: case GameObjectTypes.Chest: @@ -4461,7 +4569,7 @@ namespace Game.Spells } go.SetRespawnTime(duration > 0 ? duration / Time.InMilliseconds : 0); - go.SetOwnerGUID(m_caster.GetGUID()); + go.SetOwnerGUID(unitCaster.GetGUID()); go.SetSpellId(m_spellInfo.Id); ExecuteLogEffectSummonObject(effIndex, go); @@ -4475,7 +4583,7 @@ namespace Game.Spells PhasingHandler.InheritPhaseShift(linkedTrap, m_caster); linkedTrap.SetRespawnTime(duration > 0 ? duration / Time.InMilliseconds : 0); linkedTrap.SetSpellId(m_spellInfo.Id); - linkedTrap.SetOwnerGUID(m_caster.GetGUID()); + linkedTrap.SetOwnerGUID(unitCaster.GetGUID()); ExecuteLogEffectSummonObject(effIndex, linkedTrap); } @@ -4805,8 +4913,11 @@ namespace Game.Spells if (effectHandleMode != SpellEffectHandleMode.HitTarget) return; + if (unitCaster == null) + return; + if (unitTarget != null) - m_caster.GetThreatManager().RegisterRedirectThreat(m_spellInfo.Id, unitTarget.GetGUID(), (uint)damage); + unitCaster.GetThreatManager().RegisterRedirectThreat(m_spellInfo.Id, unitTarget.GetGUID(), (uint)damage); } [SpellEffectHandler(SpellEffectName.GameObjectDamage)] @@ -4818,15 +4929,11 @@ namespace Game.Spells if (gameObjTarget == null) return; - Unit caster = m_originalCaster; - if (caster == null) - return; - - FactionTemplateRecord casterFaction = caster.GetFactionTemplateEntry(); + FactionTemplateRecord casterFaction = m_caster.GetFactionTemplateEntry(); FactionTemplateRecord targetFaction = CliDB.FactionTemplateStorage.LookupByKey(gameObjTarget.GetFaction()); // Do not allow to damage GO's of friendly factions (ie: Wintergrasp Walls/Ulduar Storm Beacons) if (targetFaction == null || (casterFaction != null && !casterFaction.IsFriendlyTo(targetFaction))) - gameObjTarget.ModifyHealth(-damage, caster, GetSpellInfo().Id); + gameObjTarget.ModifyHealth(-damage, m_caster, GetSpellInfo().Id); } [SpellEffectHandler(SpellEffectName.GameobjectRepair)] @@ -4847,33 +4954,31 @@ namespace Game.Spells if (effectHandleMode != SpellEffectHandleMode.HitTarget) return; - if (gameObjTarget == null || m_originalCaster == null) + if (gameObjTarget == null) return; - Player player = m_originalCaster.GetCharmerOrOwnerPlayerOrPlayerItself(); - gameObjTarget.SetDestructibleState((GameObjectDestructibleState)effectInfo.MiscValue, player, true); + gameObjTarget.SetDestructibleState((GameObjectDestructibleState)effectInfo.MiscValue, m_caster, true); } void SummonGuardian(uint i, uint entry, SummonPropertiesRecord properties, uint numGuardians, ObjectGuid privateObjectOwner) { - Unit caster = m_originalCaster; - if (caster == null) + if (unitCaster == null) return; - if (caster.IsTotem()) - caster = caster.ToTotem().GetOwner(); + if (unitCaster.IsTotem()) + unitCaster = unitCaster.ToTotem().GetOwner(); // in another case summon new - uint level = caster.GetLevel(); + uint level = unitCaster.GetLevel(); // level of pet summoned using engineering item based at engineering skill level - if (m_CastItem != null && caster.IsTypeId(TypeId.Player)) + if (m_CastItem != null && unitCaster.IsPlayer()) { ItemTemplate proto = m_CastItem.GetTemplate(); if (proto != null) if (proto.GetRequiredSkill() == (uint)SkillType.Engineering) { - ushort skill202 = caster.ToPlayer().GetSkillValue(SkillType.Engineering); + ushort skill202 = unitCaster.ToPlayer().GetSkillValue(SkillType.Engineering); if (skill202 != 0) level = (uint)(skill202 / 5); } @@ -4883,7 +4988,7 @@ namespace Game.Spells int duration = m_spellInfo.CalcDuration(m_originalCaster); //TempSummonType summonType = (duration == 0) ? TempSummonType.DeadDespawn : TempSummonType.TimedDespawn; - Map map = caster.GetMap(); + Map map = unitCaster.GetMap(); for (uint count = 0; count < numGuardians; ++count) { @@ -4892,19 +4997,19 @@ namespace Game.Spells pos = destTarget; else // randomize position for multiple summons - pos = m_caster.GetRandomPoint(destTarget, radius); + pos = unitCaster.GetRandomPoint(destTarget, radius); - TempSummon summon = map.SummonCreature(entry, pos, properties, (uint)duration, caster, m_spellInfo.Id, 0, privateObjectOwner); + TempSummon summon = map.SummonCreature(entry, pos, properties, (uint)duration, unitCaster, m_spellInfo.Id, 0, privateObjectOwner); if (summon == null) return; if (summon.HasUnitTypeMask(UnitTypeMask.Guardian)) ((Guardian)summon).InitStatsForLevel(level); if (properties != null && properties.Control == SummonCategory.Ally) - summon.SetFaction(caster.GetFaction()); + summon.SetFaction(unitCaster.GetFaction()); if (summon.HasUnitTypeMask(UnitTypeMask.Minion) && m_targets.HasDst()) - ((Minion)summon).SetFollowAngle(m_caster.GetAngle(summon.GetPosition())); + ((Minion)summon).SetFollowAngle(unitCaster.GetAngle(summon.GetPosition())); if (summon.GetEntry() == 27893) { @@ -5056,16 +5161,16 @@ namespace Game.Spells if (effectHandleMode != SpellEffectHandleMode.Hit) return; - if (!m_caster.IsTypeId(TypeId.Player)) + Player player = m_caster.ToPlayer(); + if (player == null) return; - Player p_caster = m_caster.ToPlayer(); int button_id = effectInfo.MiscValue + 132; int n_buttons = effectInfo.MiscValueB; for (; n_buttons != 0; --n_buttons, ++button_id) { - ActionButton ab = p_caster.GetActionButton((byte)button_id); + ActionButton ab = player.GetActionButton((byte)button_id); if (ab == null || ab.GetButtonType() != ActionButtonType.Spell) continue; @@ -5079,7 +5184,7 @@ namespace Game.Spells if (spellInfo == null) continue; - if (!p_caster.HasSpell(spell_id) || p_caster.GetSpellHistory().HasCooldown(spell_id)) + if (!player.HasSpell(spell_id) || player.GetSpellHistory().HasCooldown(spell_id)) continue; if (!spellInfo.HasAttribute(SpellAttr9.SummonPlayerTotem)) @@ -5280,11 +5385,11 @@ namespace Game.Spells if (effectHandleMode != SpellEffectHandleMode.Hit) return; - if (!m_targets.HasDst()) + if (unitCaster == null || !m_targets.HasDst()) return; int duration = GetSpellInfo().CalcDuration(GetCaster()); - AreaTrigger.CreateAreaTrigger((uint)effectInfo.MiscValue, GetCaster(), null, GetSpellInfo(), destTarget.GetPosition(), duration, m_SpellVisual, m_castId); + AreaTrigger.CreateAreaTrigger((uint)effectInfo.MiscValue, unitCaster, null, GetSpellInfo(), destTarget.GetPosition(), duration, m_SpellVisual, m_castId); } [SpellEffectHandler(SpellEffectName.RemoveTalent)] @@ -5352,10 +5457,10 @@ namespace Game.Spells if (effectHandleMode != SpellEffectHandleMode.Hit) return; - if (!m_targets.HasDst()) + if (unitCaster == null || !m_targets.HasDst()) return; - Conversation.CreateConversation((uint)effectInfo.MiscValue, GetCaster(), destTarget.GetPosition(), new List() { GetCaster().GetGUID() }, GetSpellInfo()); + Conversation.CreateConversation((uint)effectInfo.MiscValue, unitCaster, destTarget.GetPosition(), new List() { GetCaster().GetGUID() }, GetSpellInfo()); } [SpellEffectHandler(SpellEffectName.AddGarrisonFollower)] @@ -5568,13 +5673,14 @@ namespace Game.Spells if (effectHandleMode != SpellEffectHandleMode.LaunchTarget) return; - if (!m_caster.IsTypeId(TypeId.Player)) + Player playerCaster = m_caster.ToPlayer(); + if (playerCaster == null) return; - Aura artifactAura = m_caster.GetAura(PlayerConst.ArtifactsAllWeaponsGeneralWeaponEquippedPassive); + Aura artifactAura = playerCaster.GetAura(PlayerConst.ArtifactsAllWeaponsGeneralWeaponEquippedPassive); if (artifactAura != null) { - Item artifact = m_caster.ToPlayer().GetItemByGuid(artifactAura.GetCastItemGUID()); + Item artifact = playerCaster.GetItemByGuid(artifactAura.GetCastItemGUID()); if (artifact) artifact.GiveArtifactXp((ulong)damage, m_CastItem, (ArtifactCategory)effectInfo.MiscValue); } @@ -5699,10 +5805,10 @@ namespace Game.Spells if (effectHandleMode != SpellEffectHandleMode.Hit) return; - if (!unitTarget || !unitTarget.IsTypeId(TypeId.Player)) + if (unitCaster == null || unitTarget == null || !unitTarget.IsPlayer()) return; - Conversation.CreateConversation((uint)effectInfo.MiscValue, GetCaster(), unitTarget.GetPosition(), new List() { unitTarget.GetGUID() }, GetSpellInfo()); + Conversation.CreateConversation((uint)effectInfo.MiscValue, unitCaster, unitTarget.GetPosition(), new List() { unitTarget.GetGUID() }, GetSpellInfo()); } [SpellEffectHandler(SpellEffectName.SendChatMessage)] @@ -5711,12 +5817,15 @@ namespace Game.Spells if (effectHandleMode != SpellEffectHandleMode.Hit) return; + if (unitCaster == null) + return; + uint broadcastTextId = (uint)effectInfo.MiscValue; if (!CliDB.BroadcastTextStorage.ContainsKey(broadcastTextId)) return; ChatMsg chatType = (ChatMsg)effectInfo.MiscValueB; - m_caster.Talk(broadcastTextId, chatType, Global.CreatureTextMgr.GetRangeForChatType(chatType), unitTarget); + unitCaster.Talk(broadcastTextId, chatType, Global.CreatureTextMgr.GetRangeForChatType(chatType), unitTarget); } } diff --git a/Source/Game/Spells/SpellInfo.cs b/Source/Game/Spells/SpellInfo.cs index a0d1a9c25..c31b90325 100644 --- a/Source/Game/Spells/SpellInfo.cs +++ b/Source/Game/Spells/SpellInfo.cs @@ -1010,7 +1010,7 @@ namespace Game.Spells return SpellCastResult.SpellCastOk; } - public SpellCastResult CheckTarget(Unit caster, WorldObject target, bool Implicit = true) + public SpellCastResult CheckTarget(WorldObject caster, WorldObject target, bool Implicit = true) { if (HasAttribute(SpellAttr1.CantTargetSelf) && caster == target) return SpellCastResult.BadTargets; @@ -1112,7 +1112,7 @@ namespace Game.Spells } // check GM mode and GM invisibility - only for player casts (npc casts are controlled by AI) and negative spells - if (unitTarget != caster && (caster.IsControlledByPlayer() || !IsPositive()) && unitTarget.IsTypeId(TypeId.Player)) + if (unitTarget != caster && (caster.GetAffectingPlayer() != null || !IsPositive()) && unitTarget.IsTypeId(TypeId.Player)) { if (!unitTarget.ToPlayer().IsVisible()) return SpellCastResult.BmOrInvisgod; @@ -1132,13 +1132,17 @@ namespace Game.Spells him, because it would be it's passenger, there's no such case where this gets to fail legitimacy, this problem cannot be solved from within the check in other way since target type cannot be called for the spell currently Spell examples: [ID - 52864 Devour Water, ID - 52862 Devour Wind, ID - 49370 Wyrmrest Defender: Destabilize Azure Dragonshrine Effect] */ - if (!caster.IsVehicle() && caster.GetCharmerOrOwner() != target) + Unit unitCaster = caster.ToUnit(); + if (unitCaster != null) { - if (TargetAuraState != 0 && !unitTarget.HasAuraState(TargetAuraState, this, caster)) - return SpellCastResult.TargetAurastate; + if (!unitCaster.IsVehicle() && unitCaster.GetCharmerOrOwner() != target) + { + if (TargetAuraState != 0 && !unitTarget.HasAuraState(TargetAuraState, this, unitCaster)) + return SpellCastResult.TargetAurastate; - if (ExcludeTargetAuraState != 0 && unitTarget.HasAuraState(ExcludeTargetAuraState, this, caster)) - return SpellCastResult.TargetAurastate; + if (ExcludeTargetAuraState != 0 && unitTarget.HasAuraState(ExcludeTargetAuraState, this, unitCaster)) + return SpellCastResult.TargetAurastate; + } } if (TargetAuraSpell != 0 && !unitTarget.HasAura(TargetAuraSpell)) @@ -1170,7 +1174,7 @@ namespace Game.Spells return SpellCastResult.SpellCastOk; } - public SpellCastResult CheckExplicitTarget(Unit caster, WorldObject target, Item itemTarget = null) + public SpellCastResult CheckExplicitTarget(WorldObject caster, WorldObject target, Item itemTarget = null) { SpellCastTargetFlags neededTargets = GetExplicitTargetMask(); if (target == null) @@ -1183,22 +1187,27 @@ namespace Game.Spells Unit unitTarget = target.ToUnit(); if (unitTarget != null) { - if (Convert.ToBoolean(neededTargets & (SpellCastTargetFlags.UnitEnemy | SpellCastTargetFlags.UnitAlly | SpellCastTargetFlags.UnitRaid | SpellCastTargetFlags.UnitParty | SpellCastTargetFlags.UnitMinipet | SpellCastTargetFlags.UnitPassenger))) + if (neededTargets.HasAnyFlag(SpellCastTargetFlags.UnitEnemy | SpellCastTargetFlags.UnitAlly | SpellCastTargetFlags.UnitRaid | SpellCastTargetFlags.UnitParty | SpellCastTargetFlags.UnitMinipet | SpellCastTargetFlags.UnitPassenger)) { - if (Convert.ToBoolean(neededTargets & SpellCastTargetFlags.UnitEnemy)) + Unit unitCaster = caster.ToUnit(); + if (neededTargets.HasFlag(SpellCastTargetFlags.UnitEnemy)) if (caster.IsValidAttackTarget(unitTarget, this)) return SpellCastResult.SpellCastOk; - if (neededTargets.HasAnyFlag(SpellCastTargetFlags.UnitAlly) - || (neededTargets.HasAnyFlag(SpellCastTargetFlags.UnitParty) && caster.IsInPartyWith(unitTarget)) - || (neededTargets.HasAnyFlag(SpellCastTargetFlags.UnitRaid) && caster.IsInRaidWith(unitTarget))) + + if (neededTargets.HasFlag(SpellCastTargetFlags.UnitAlly) + || (neededTargets.HasFlag(SpellCastTargetFlags.UnitParty) && unitCaster != null && unitCaster.IsInPartyWith(unitTarget)) + || (neededTargets.HasFlag(SpellCastTargetFlags.UnitRaid) && unitCaster != null && unitCaster.IsInRaidWith(unitTarget))) if (caster.IsValidAssistTarget(unitTarget, this)) return SpellCastResult.SpellCastOk; - if (Convert.ToBoolean(neededTargets & SpellCastTargetFlags.UnitMinipet)) - if (unitTarget.GetGUID() == caster.GetCritterGUID()) + + if (neededTargets.HasFlag(SpellCastTargetFlags.UnitMinipet) && unitCaster != null) + if (unitTarget.GetGUID() == unitCaster.GetCritterGUID()) return SpellCastResult.SpellCastOk; - if (Convert.ToBoolean(neededTargets & SpellCastTargetFlags.UnitPassenger)) - if (unitTarget.IsOnVehicle(caster)) + + if (neededTargets.HasFlag(SpellCastTargetFlags.UnitPassenger) && unitCaster != null) + if (unitTarget.IsOnVehicle(unitCaster)) return SpellCastResult.SpellCastOk; + return SpellCastResult.BadTargets; } } @@ -2625,7 +2634,7 @@ namespace Game.Spells return RangeEntry.RangeMin[positive ? 1 : 0]; } - public float GetMaxRange(bool positive = false, Unit caster = null, Spell spell = null) + public float GetMaxRange(bool positive = false, WorldObject caster = null, Spell spell = null) { if (RangeEntry == null) return 0.0f; @@ -2640,7 +2649,7 @@ namespace Game.Spells return range; } - public int CalcDuration(Unit caster = null) + public int CalcDuration(WorldObject caster = null) { int duration = GetDuration(); @@ -2733,8 +2742,13 @@ namespace Game.Spells return RecoveryTime > CategoryRecoveryTime ? RecoveryTime : CategoryRecoveryTime; } - public SpellPowerCost CalcPowerCost(PowerType powerType, bool optionalCost, Unit caster, SpellSchoolMask schoolMask, Spell spell = null) + public SpellPowerCost CalcPowerCost(PowerType powerType, bool optionalCost, WorldObject caster, SpellSchoolMask schoolMask, Spell spell = null) { + // gameobject casts don't use power + Unit unitCaster = caster.ToUnit(); + if (unitCaster == null) + return null; + var spellPowerRecord = PowerCosts.FirstOrDefault(spellPowerEntry => spellPowerEntry?.PowerType == powerType); if (spellPowerRecord == null) return null; @@ -2742,9 +2756,14 @@ namespace Game.Spells return CalcPowerCost(spellPowerRecord, optionalCost, caster, schoolMask, spell); } - public SpellPowerCost CalcPowerCost(SpellPowerRecord power, bool optionalCost, Unit caster, SpellSchoolMask schoolMask, Spell spell = null) + public SpellPowerCost CalcPowerCost(SpellPowerRecord power, bool optionalCost, WorldObject caster, SpellSchoolMask schoolMask, Spell spell = null) { - if (power.RequiredAuraSpellID != 0 && !caster.HasAura(power.RequiredAuraSpellID)) + // gameobject casts don't use power + Unit unitCaster = caster.ToUnit(); + if (!unitCaster) + return null; + + if (power.RequiredAuraSpellID != 0 && !unitCaster.HasAura(power.RequiredAuraSpellID)) return null; SpellPowerCost cost = new(); @@ -2756,14 +2775,14 @@ namespace Game.Spells if (power.PowerType == PowerType.Health) { cost.Power = PowerType.Health; - cost.Amount = (int)caster.GetHealth(); + cost.Amount = (int)unitCaster.GetHealth(); return cost; } // Else drain all power if (power.PowerType < PowerType.Max) { cost.Power = power.PowerType; - cost.Amount = caster.GetPower(cost.Power); + cost.Amount = unitCaster.GetPower(cost.Power); return cost; } @@ -2784,12 +2803,12 @@ namespace Game.Spells // health as power used case PowerType.Health: if (MathFunctions.fuzzyEq(power.PowerCostPct, 0.0f)) - powerCost += (int)MathFunctions.CalculatePct(caster.GetMaxHealth(), power.PowerCostMaxPct); + powerCost += (int)MathFunctions.CalculatePct(unitCaster.GetMaxHealth(), power.PowerCostMaxPct); else - powerCost += (int)MathFunctions.CalculatePct(caster.GetMaxHealth(), power.PowerCostPct); + powerCost += (int)MathFunctions.CalculatePct(unitCaster.GetMaxHealth(), power.PowerCostPct); break; case PowerType.Mana: - powerCost += (int)MathFunctions.CalculatePct(caster.GetCreateMana(), power.PowerCostPct); + powerCost += (int)MathFunctions.CalculatePct(unitCaster.GetCreateMana(), power.PowerCostPct); break; case PowerType.AlternatePower: Log.outError(LogFilter.Spells, $"SpellInfo.CalcPowerCost: Unknown power type '{power.PowerType}' in spell {Id}"); @@ -2812,7 +2831,7 @@ namespace Game.Spells else { powerCost = (int)power.OptionalCost; - powerCost += caster.GetTotalAuraModifier(AuraType.ModAdditionalPowerCost, aurEff => + powerCost += unitCaster.GetTotalAuraModifier(AuraType.ModAdditionalPowerCost, aurEff => { return aurEff.GetMiscValue() == (int)power.PowerType && aurEff.IsAffectingSpell(this); }); @@ -2824,7 +2843,7 @@ namespace Game.Spells if (HasAttribute(SpellAttr4.SpellVsExtendCost)) { uint speed = 0; - SpellShapeshiftFormRecord ss = CliDB.SpellShapeshiftFormStorage.LookupByKey(caster.GetShapeshiftForm()); + SpellShapeshiftFormRecord ss = CliDB.SpellShapeshiftFormStorage.LookupByKey(unitCaster.GetShapeshiftForm()); if (ss != null) speed = ss.CombatRoundTime; else @@ -2833,7 +2852,7 @@ namespace Game.Spells if (!HasAttribute(SpellAttr3.MainHand) && HasAttribute(SpellAttr3.ReqOffhand)) slot = WeaponAttackType.OffAttack; - speed = caster.GetBaseAttackTime(slot); + speed = unitCaster.GetBaseAttackTime(slot); } powerCost += (int)speed / 100; @@ -2844,7 +2863,7 @@ namespace Game.Spells if (!optionalCost) { // Flat mod from caster auras by spell school and power type - foreach (AuraEffect aura in caster.GetAuraEffectsByType(AuraType.ModPowerCostSchool)) + foreach (AuraEffect aura in unitCaster.GetAuraEffectsByType(AuraType.ModPowerCostSchool)) { if ((aura.GetMiscValue() & (int)schoolMask) == 0) continue; @@ -2857,7 +2876,7 @@ namespace Game.Spells } // PCT mod from user auras by spell school and power type - foreach (var schoolCostPct in caster.GetAuraEffectsByType(AuraType.ModPowerCostSchoolPct)) + foreach (var schoolCostPct in unitCaster.GetAuraEffectsByType(AuraType.ModPowerCostSchoolPct)) { if ((schoolCostPct.GetMiscValue() & (int)schoolMask) == 0) continue; @@ -2870,7 +2889,7 @@ namespace Game.Spells } // Apply cost mod by spell - Player modOwner = caster.GetSpellModOwner(); + Player modOwner = unitCaster.GetSpellModOwner(); if (modOwner != null) { SpellModOp mod = SpellModOp.Max; @@ -2904,19 +2923,19 @@ namespace Game.Spells } } - if (!caster.IsControlledByPlayer() && MathFunctions.fuzzyEq(power.PowerCostPct, 0.0f) && SpellLevel != 0 && power.PowerType == PowerType.Mana) + if (!unitCaster.IsControlledByPlayer() && MathFunctions.fuzzyEq(power.PowerCostPct, 0.0f) && SpellLevel != 0 && power.PowerType == PowerType.Mana) { if (HasAttribute(SpellAttr0.LevelDamageCalculation)) { GtNpcManaCostScalerRecord spellScaler = CliDB.NpcManaCostScalerGameTable.GetRow(SpellLevel); - GtNpcManaCostScalerRecord casterScaler = CliDB.NpcManaCostScalerGameTable.GetRow(caster.GetLevel()); + GtNpcManaCostScalerRecord casterScaler = CliDB.NpcManaCostScalerGameTable.GetRow(unitCaster.GetLevel()); if (spellScaler != null && casterScaler != null) powerCost *= (int)(casterScaler.Scaler / spellScaler.Scaler); } } if (power.PowerType == PowerType.Mana) - powerCost = (int)((float)powerCost * (1.0f + caster.m_unitData.ManaCostMultiplier)); + powerCost = (int)((float)powerCost * (1.0f + unitCaster.m_unitData.ManaCostMultiplier)); // power cost cannot become negative if initially positive if (initiallyNegative != (powerCost < 0)) @@ -2927,42 +2946,41 @@ namespace Game.Spells return cost; } - public List CalcPowerCost(Unit caster, SpellSchoolMask schoolMask, Spell spell = null) + public List CalcPowerCost(WorldObject caster, SpellSchoolMask schoolMask, Spell spell = null) { List costs = new(); - - SpellPowerCost getOrCreatePowerCost(PowerType powerType) + if (caster.IsUnit()) { - var itr = costs.Find(cost => + SpellPowerCost getOrCreatePowerCost(PowerType powerType) { - return cost.Power == powerType; - }); - if (itr != null) - return itr; + var itr = costs.Find(cost => cost.Power == powerType); + if (itr != null) + return itr; - SpellPowerCost cost = new(); - cost.Power = powerType; - cost.Amount = 0; - costs.Add(cost); - return costs.Last(); - } + SpellPowerCost cost = new(); + cost.Power = powerType; + cost.Amount = 0; + costs.Add(cost); + return costs.Last(); + } - foreach (SpellPowerRecord power in PowerCosts) - { - if (power == null) - continue; - - SpellPowerCost cost = CalcPowerCost(power, false, caster, schoolMask, spell); - if (cost != null) - getOrCreatePowerCost(cost.Power).Amount += cost.Amount; - - SpellPowerCost optionalCost = CalcPowerCost(power, true, caster, schoolMask, spell); - if (optionalCost != null) + foreach (SpellPowerRecord power in PowerCosts) { - SpellPowerCost cost1 = getOrCreatePowerCost(optionalCost.Power); - int remainingPower = caster.GetPower(optionalCost.Power) - cost1.Amount; - if (remainingPower > 0) - cost1.Amount += Math.Min(optionalCost.Amount, remainingPower); + if (power == null) + continue; + + SpellPowerCost cost = CalcPowerCost(power, false, caster, schoolMask, spell); + if (cost != null) + getOrCreatePowerCost(cost.Power).Amount += cost.Amount; + + SpellPowerCost optionalCost = CalcPowerCost(power, true, caster, schoolMask, spell); + if (optionalCost != null) + { + SpellPowerCost cost1 = getOrCreatePowerCost(optionalCost.Power); + int remainingPower = caster.ToUnit().GetPower(optionalCost.Power) - cost1.Amount; + if (remainingPower > 0) + cost1.Amount += Math.Min(optionalCost.Amount, remainingPower); + } } } @@ -3195,11 +3213,10 @@ namespace Game.Spells return false; } - public uint GetSpellXSpellVisualId(Unit caster = null) + public uint GetSpellXSpellVisualId(WorldObject caster = null) { foreach (SpellXSpellVisualRecord visual in _visuals) { - PlayerConditionRecord playerCondition = CliDB.PlayerConditionStorage.LookupByKey(visual.CasterPlayerConditionID); if (playerCondition == null || (caster && caster.GetTypeId() == TypeId.Player && ConditionManager.IsPlayerMeetingCondition(caster.ToPlayer(), playerCondition))) return visual.Id; @@ -3208,7 +3225,7 @@ namespace Game.Spells return 0; } - public uint GetSpellVisual(Unit caster = null) + public uint GetSpellVisual(WorldObject caster = null) { var visual = CliDB.SpellXSpellVisualStorage.LookupByKey(GetSpellXSpellVisualId(caster)); if (visual != null) @@ -3817,13 +3834,13 @@ namespace Game.Spells public bool HasAttribute(SpellAttr14 attribute) { return Convert.ToBoolean(AttributesEx14 & attribute); } public bool HasAttribute(SpellCustomAttributes attribute) { return Convert.ToBoolean(AttributesCu & attribute); } - public bool CanBeInterrupted(Unit interruptCaster, Unit interruptTarget) + public bool CanBeInterrupted(WorldObject interruptCaster, Unit interruptTarget) { return HasAttribute(SpellAttr7.CanAlwaysBeInterrupted) || HasChannelInterruptFlag(SpellAuraInterruptFlags.Damage | SpellAuraInterruptFlags.EnteringCombat) || (interruptTarget.IsPlayer() && InterruptFlags.HasFlag(SpellInterruptFlags.DamageCancelsPlayerOnly)) || InterruptFlags.HasFlag(SpellInterruptFlags.DamageCancels) - || interruptCaster.HasAuraTypeWithMiscvalue(AuraType.AllowInterruptSpell, (int)Id) + || interruptCaster.IsUnit() && interruptCaster.ToUnit().HasAuraTypeWithMiscvalue(AuraType.AllowInterruptSpell, (int)Id) || ((interruptTarget.GetMechanicImmunityMask() & (1 << (int)Mechanics.Interrupt)) == 0 && !interruptTarget.HasAuraTypeWithAffectMask(AuraType.PreventInterrupt, this) && PreventionType.HasAnyFlag(SpellPreventionType.Silence)); @@ -4028,12 +4045,12 @@ namespace Game.Spells return IsAreaAuraEffect() || Effect == SpellEffectName.ApplyAura || Effect == SpellEffectName.ApplyAuraOnPet; } - public int CalcValue(Unit caster = null, int? bp = null, Unit target = null, uint castItemId = 0, int itemLevel = -1) + public int CalcValue(WorldObject caster = null, int? bp = null, Unit target = null, uint castItemId = 0, int itemLevel = -1) { return CalcValue(out _, caster, bp, target, castItemId, itemLevel); } - public int CalcValue(out float variance, Unit caster = null, int? bp = null, Unit target = null, uint castItemId = 0, int itemLevel = -1) + public int CalcValue(out float variance, WorldObject caster = null, int? bp = null, Unit target = null, uint castItemId = 0, int itemLevel = -1) { variance = 0.0f; float basePointsPerLevel = RealPointsPerLevel; @@ -4042,6 +4059,10 @@ namespace Game.Spells float value = bp.HasValue ? bp.Value : basePoints; float comboDamage = PointsPerResource; + Unit casterUnit = null; + if (caster != null) + casterUnit = caster.ToUnit(); + if (Scaling.Variance != 0) { float delta = Math.Abs(Scaling.Variance * 0.5f); @@ -4058,9 +4079,9 @@ namespace Game.Spells } else if (GetScalingExpectedStat() == ExpectedStatType.None) { - if (caster != null && basePointsPerLevel != 0.0f) + if (casterUnit != null && basePointsPerLevel != 0.0f) { - int level = (int)caster.GetLevel(); + int level = (int)casterUnit.GetLevel(); if (level > (int)_spellInfo.MaxLevel && _spellInfo.MaxLevel > 0) level = (int)_spellInfo.MaxLevel; @@ -4072,31 +4093,32 @@ namespace Game.Spells } } // random damage - if (caster) + if (casterUnit != null) { // bonus amount from combo points - if (caster.m_playerMovingMe && comboDamage != 0) + if (comboDamage != 0) { - uint comboPoints = caster.m_playerMovingMe.GetComboPoints(); + uint comboPoints = casterUnit.GetComboPoints(); if (comboPoints != 0) value += comboDamage * comboPoints; } - value = caster.ApplyEffectModifiers(_spellInfo, EffectIndex, value); + if (caster != null) + value = caster.ApplyEffectModifiers(_spellInfo, EffectIndex, value); } return (int)Math.Round(value); } - public int CalcBaseValue(Unit caster, Unit target, uint itemId, int itemLevel) + public int CalcBaseValue(WorldObject caster, Unit target, uint itemId, int itemLevel) { if (Scaling.Coefficient != 0.0f) { uint level = _spellInfo.SpellLevel; if (target && _spellInfo.IsPositiveEffect(EffectIndex) && (Effect == SpellEffectName.ApplyAura)) level = target.GetLevel(); - else if (caster) - level = caster.GetLevel(); + else if (caster != null && caster.IsUnit()) + level = caster.ToUnit().GetLevel(); if (_spellInfo.BaseLevel != 0 && !_spellInfo.HasAttribute(SpellAttr11.ScalesWithItemLevel) && _spellInfo.HasAttribute(SpellAttr10.UseSpellBaseLevelForScaling)) level = _spellInfo.BaseLevel; @@ -4188,7 +4210,7 @@ namespace Game.Spells if (contentTuning != null) expansion = contentTuning.ExpansionID; - uint level = caster ? caster.GetLevel() : 1; + uint level = caster != null && caster.IsUnit() ? caster.ToUnit().GetLevel() : 1; tempValue = Global.DB2Mgr.EvaluateExpectedStat(stat, level, expansion, 0, Class.None) * BasePoints / 100.0f; } @@ -4196,7 +4218,7 @@ namespace Game.Spells } } - public float CalcValueMultiplier(Unit caster, Spell spell = null) + public float CalcValueMultiplier(WorldObject caster, Spell spell = null) { float multiplier = Amplitude; Player modOwner = (caster != null ? caster.GetSpellModOwner() : null); @@ -4205,7 +4227,7 @@ namespace Game.Spells return multiplier; } - public float CalcDamageMultiplier(Unit caster, Spell spell = null) + public float CalcDamageMultiplier(WorldObject caster, Spell spell = null) { float multiplierPercent = ChainAmplitude * 100.0f; Player modOwner = (caster != null ? caster.GetSpellModOwner() : null); @@ -4224,7 +4246,7 @@ namespace Game.Spells return MaxRadiusEntry != null; } - public float CalcRadius(Unit caster = null, Spell spell = null) + public float CalcRadius(WorldObject caster = null, Spell spell = null) { SpellRadiusRecord entry = RadiusEntry; if (!HasRadius() && HasMaxRadius()) @@ -4241,7 +4263,10 @@ namespace Game.Spells if (caster != null) { - radius += entry.RadiusPerLevel * caster.GetLevel(); + Unit casterUnit = caster.ToUnit(); + if (casterUnit != null) + radius += entry.RadiusPerLevel * casterUnit.GetLevel(); + radius = Math.Min(radius, entry.RadiusMax); Player modOwner = caster.GetSpellModOwner(); if (modOwner != null) diff --git a/Source/Game/Spells/SpellManager.cs b/Source/Game/Spells/SpellManager.cs index a3d17dc89..656dbc7d3 100644 --- a/Source/Game/Spells/SpellManager.cs +++ b/Source/Game/Spells/SpellManager.cs @@ -494,7 +494,7 @@ namespace Game.Entities } // always trigger for these types - if (eventInfo.GetTypeMask().HasFlag(ProcFlags.Killed | ProcFlags.Kill | ProcFlags.Death)) + if (eventInfo.GetTypeMask().HasAnyFlag(ProcFlags.Killed | ProcFlags.Kill | ProcFlags.Death)) return true; // Do not consider autoattacks as triggered spells diff --git a/Source/Scripts/Spells/Druid.cs b/Source/Scripts/Spells/Druid.cs index 4403b28d0..19ce5f39c 100644 --- a/Source/Scripts/Spells/Druid.cs +++ b/Source/Scripts/Spells/Druid.cs @@ -906,7 +906,7 @@ namespace Scripts.Spells.Druid if (caster != null) { // 0.01 * $AP * cp - byte cp = caster.ToPlayer().GetComboPoints(); + byte cp = (byte)caster.ToPlayer().GetComboPoints(); // Idol of Feral Shadows. Can't be handled as SpellMod due its dependency from CPs AuraEffect idol = caster.GetAuraEffect(SpellIds.IdolOfFeralShadows, 0); diff --git a/Source/Scripts/Spells/Items.cs b/Source/Scripts/Spells/Items.cs index 226681461..f88b4da80 100644 --- a/Source/Scripts/Spells/Items.cs +++ b/Source/Scripts/Spells/Items.cs @@ -1380,7 +1380,7 @@ namespace Scripts.Spells.Items void HandleProc(AuraEffect aurEff, ProcEventInfo eventInfo) { - if (eventInfo.GetTypeMask().HasFlag(ProcFlags.DoneRangedAutoAttack | ProcFlags.DoneSpellRangedDmgClass)) + if (eventInfo.GetTypeMask().HasAnyFlag(ProcFlags.DoneRangedAutoAttack | ProcFlags.DoneSpellRangedDmgClass)) { // in that case, do not cast heal spell PreventDefaultAction(); diff --git a/Source/Scripts/World/NpcSpecial.cs b/Source/Scripts/World/NpcSpecial.cs index da74a8051..60dcdb038 100644 --- a/Source/Scripts/World/NpcSpecial.cs +++ b/Source/Scripts/World/NpcSpecial.cs @@ -1463,7 +1463,7 @@ namespace Scripts.World.NpcSpecial public override void Reset() { } public override void JustEngagedWith(Unit who) { } - public override void OnPossess(bool apply) + public void OnPossess(bool apply) { if (apply) {