diff --git a/Source/Framework/Constants/CliDBConst.cs b/Source/Framework/Constants/CliDBConst.cs index a9668f28c..d85cc418c 100644 --- a/Source/Framework/Constants/CliDBConst.cs +++ b/Source/Framework/Constants/CliDBConst.cs @@ -2128,6 +2128,123 @@ namespace Framework.Constants Orphan = 6 } + [Flags] + public enum UnitConditionFlags + { + LogicOr = 0x1 + } + + public enum UnitConditionOp + { + EqualTo = 1, + NotEqualTo = 2, + LessThan = 3, + LessThanOrEqualTo = 4, + GreaterThan = 5, + GreaterThanOrEqualTo = 6 + } + + public enum UnitConditionVariable + { + None = 0, // - NONE - + Race = 1, // Race {$Is/Is Not} "{ChrRaces}" + Class = 2, // Class {$Is/Is Not} "{ChrClasses}" + Level = 3, // Level {$Relative Op} "{#Level}" + IsSelf = 4, // Is self? {$Yes/No}{=1} + IsMyPet = 5, // Is my pet? {$Yes/No}{=1} + IsMaster = 6, // Is master? {$Yes/No}{=1} + IsTarget = 7, // Is target? {$Yes/No}{=1} + CanAssist = 8, // Can assist? {$Yes/No}{=1} + CanAttack = 9, // Can attack? {$Yes/No}{=1} + HasPet = 10, // Has pet? {$Yes/No}{=1} + HasWeapon = 11, // Has weapon? {$Yes/No}{=1} + HealthPct = 12, // Health {$Relative Op} {#Health %}% + ManaPct = 13, // Mana {$Relative Op} {#Mana %}% + RagePct = 14, // Rage {$Relative Op} {#Rage %}% + EnergyPct = 15, // Energy {$Relative Op} {#Energy %}% + ComboPoints = 16, // Combo Points {$Relative Op} {#Points} + HasHelpfulAuraSpell = 17, // Has helpful aura spell? {$Yes/No} "{Spell}" + HasHelpfulAuraDispelType = 18, // Has helpful aura dispel type? {$Yes/No} "{SpellDispelType}" + HasHelpfulAuraMechanic = 19, // Has helpful aura mechanic? {$Yes/No} "{SpellMechanic}" + HasHarmfulAuraSpell = 20, // Has harmful aura spell? {$Yes/No} "{Spell}" + HasHarmfulAuraDispelType = 21, // Has harmful aura dispel type? {$Yes/No} "{SpellDispelType}" + HasHarmfulAuraMechanic = 22, // Has harmful aura mechanic? {$Yes/No} "{SpellMechanic}" + HasHarmfulAuraSchool = 23, // Has harmful aura school? {$Yes/No} "{Resistances}" + DamagePhysicalPct = 24, // NYI Damage (Physical) {$Relative Op} {#Physical Damage %}% + DamageHolyPct = 25, // NYI Damage (Holy) {$Relative Op} {#Holy Damage %}% + DamageFirePct = 26, // NYI Damage (Fire) {$Relative Op} {#Fire Damage %}% + DamageNaturePct = 27, // NYI Damage (Nature) {$Relative Op} {#Nature Damage %}% + DamageFrostPct = 28, // NYI Damage (Frost) {$Relative Op} {#Frost Damage %}% + DamageShadowPct = 29, // NYI Damage (Shadow) {$Relative Op} {#Shadow Damage %}% + DamageArcanePct = 30, // NYI Damage (Arcane) {$Relative Op} {#Arcane Damage %}% + InCombat = 31, // NYI In combat? {$Yes/No}{=1} + IsMoving = 32, // NYI Is moving? {$Yes/No}{=1} + IsCasting = 33, // NYI Is casting? {$Yes/No}{=1} + IsCastingSpell = 34, // NYI Is casting spell? {$Yes/No}{=1} + IsChanneling = 35, // NYI Is channeling? {$Yes/No}{=1} + IsChannelingSpell = 36, // NYI Is channeling spell? {$Yes/No}{=1} + NumberOfMeleeAttackers = 37, // NYI Number of melee attackers {$Relative Op} {#Attackers} + IsAttackingMe = 38, // NYI Is attacking me? {$Yes/No}{=1} + Range = 39, // NYI Range {$Relative Op} {#Yards} + InMeleeRange = 40, // NYI In melee range? {$Yes/No}{=1} + PursuitTime = 41, // NYI Pursuit time {$Relative Op} {#Seconds} + HasHarmfulAuraCanceledByDamage = 42, // NYI Has harmful aura canceled by damage? {$Yes/No}{=1} + HasHarmfulAuraWithPeriodicDamage = 43, // NYI Has harmful aura with periodic damage? {$Yes/No}{=1} + NumberOfEnemies = 44, // NYI Number of enemies {$Relative Op} {#Enemies} + NumberOfFriends = 45, // NYI Number of friends {$Relative Op} {#Friends} + ThreatPhysicalPct = 46, // NYI Threat (Physical) {$Relative Op} {#Physical Threat %}% + ThreatHolyPct = 47, // NYI Threat (Holy) {$Relative Op} {#Holy Threat %}% + ThreatFirePct = 48, // NYI Threat (Fire) {$Relative Op} {#Fire Threat %}% + ThreatNaturePct = 49, // NYI Threat (Nature) {$Relative Op} {#Nature Threat %}% + ThreatFrostPct = 50, // NYI Threat (Frost) {$Relative Op} {#Frost Threat %}% + ThreatShadowPct = 51, // NYI Threat (Shadow) {$Relative Op} {#Shadow Threat %}% + ThreatArcanePct = 52, // NYI Threat (Arcane) {$Relative Op} {#Arcane Threat %}% + IsInterruptible = 53, // NYI Is interruptible? {$Yes/No}{=1} + NumberOfAttackers = 54, // NYI Number of attackers {$Relative Op} {#Attackers} + NumberOfRangedAttackers = 55, // NYI Number of ranged attackers {$Relative Op} {#Ranged Attackers} + CreatureType = 56, // NYI Creature type {$Is/Is Not} "{CreatureType}" + IsMeleeAttacking = 57, // NYI Is melee-attacking? {$Yes/No}{=1} + IsRangedAttacking = 58, // NYI Is ranged-attacking? {$Yes/No}{=1} + Health = 59, // NYI Health {$Relative Op} {#HP} HP + SpellKnown = 60, // NYI Spell known? {$Yes/No} "{Spell}" + HasHarmfulAuraEffect = 61, // NYI Has harmful aura effect? {$Yes/No} "{#Spell Aura}" + IsImmuneToAreaOfEffect = 62, // NYI Is immune to area-of-effect? {$Yes/No}{=1} + IsPlayer = 63, // NYI Is player? {$Yes/No}{=1} + DamageMagicPct = 64, // NYI Damage (Magic) {$Relative Op} {#Magic Damage %}% + DamageTotalPct = 65, // NYI Damage (Total) {$Relative Op} {#Damage %}% + ThreatMagicPct = 66, // NYI Threat (Magic) {$Relative Op} {#Magic Threat %}% + ThreatTotalPct = 67, // NYI Threat (Total) {$Relative Op} {#Threat %}% + HasCritter = 68, // NYI Has critter? {$Yes/No}{=1} + HasTotemInSlot1 = 69, // NYI Has totem in slot 1? {$Yes/No}{=1} + HasTotemInSlot2 = 70, // NYI Has totem in slot 2? {$Yes/No}{=1} + HasTotemInSlot3 = 71, // NYI Has totem in slot 3? {$Yes/No}{=1} + HasTotemInSlot4 = 72, // NYI Has totem in slot 4? {$Yes/No}{=1} + HasTotemInSlot5 = 73, // NYI Has totem in slot 5? {$Yes/No}{=1} + Creature = 74, // NYI Creature {$Is/Is Not} "{Creature}" + StringID = 75, // NYI String ID {$Is/Is Not} "{StringID}" + HasAura = 76, // NYI Has aura? {$Yes/No} {Spell} + IsEnemy = 77, // NYI Is enemy? {$Yes/No}{=1} + IsSpecMelee = 78, // NYI Is spec - melee? {$Yes/No}{=1} + IsSpecTank = 79, // NYI Is spec - tank? {$Yes/No}{=1} + IsSpecRanged = 80, // NYI Is spec - ranged? {$Yes/No}{=1} + IsSpecHealer = 81, // NYI Is spec - healer? {$Yes/No}{=1} + IsPlayerControlledNPC = 82, // NYI Is player controlled NPC? {$Yes/No}{=1} + IsDying = 83, // NYI Is dying? {$Yes/No}{=1} + PathFailCount = 84, // NYI Path fail count {$Relative Op} {#Path Fail Count} + IsMounted = 85, // NYI Is mounted? {$Yes/No}{=1} + Label = 86, // NYI Label {$Is/Is Not} "{Label}" + IsMySummon = 87, // NYI + IsSummoner = 88, // NYI + IsMyTarget = 89, // NYI + Sex = 90, // NYI Sex {$Is/Is Not} "{UnitSex}" + LevelWithinContentTuning = 91, // NYI Level is within {$Is/Is Not} {ContentTuning} + + IsFlying = 93, // NYI Is flying? {$Yes/No}{=1} + IsHovering = 94, // NYI Is hovering? {$Yes/No}{=1} + HasHelpfulAuraEffect = 95, // NYI Has helpful aura effect? {$Yes/No} "{#Spell Aura}" + HasHelpfulAuraSchool = 96, // Has helpful aura school? {$Yes/No} "{Resistances}" + } + public enum WorldStateExpressionValueType { Constant = 1, diff --git a/Source/Game/Conditions/ConditionManager.cs b/Source/Game/Conditions/ConditionManager.cs index ac7eb1475..9b8fc7af2 100644 --- a/Source/Game/Conditions/ConditionManager.cs +++ b/Source/Game/Conditions/ConditionManager.cs @@ -2366,6 +2366,258 @@ namespace Game return finalResult; } + static int GetUnitConditionVariable(Unit unit, Unit otherUnit, UnitConditionVariable variable, int value) + { + switch (variable) + { + case UnitConditionVariable.Race: + return (int)unit.GetRace(); + case UnitConditionVariable.Class: + return (int)unit.GetClass(); + case UnitConditionVariable.Level: + return (int)unit.GetLevel(); + case UnitConditionVariable.IsSelf: + return unit == otherUnit ? 1 : 0; + case UnitConditionVariable.IsMyPet: + return (otherUnit != null && unit.GetCharmerOrOwnerGUID() == otherUnit.GetGUID()) ? 1 : 0; + case UnitConditionVariable.IsMaster: + return (otherUnit && otherUnit.GetCharmerOrOwnerGUID() == unit.GetGUID()) ? 1 : 0; + case UnitConditionVariable.IsTarget: + return (otherUnit && otherUnit.GetTarget() == unit.GetGUID()) ? 1 : 0; + case UnitConditionVariable.CanAssist: + return (otherUnit && unit.IsValidAssistTarget(otherUnit)) ? 1 : 0; + case UnitConditionVariable.CanAttack: + return (otherUnit && unit.IsValidAttackTarget(otherUnit)) ? 1 : 0; + case UnitConditionVariable.HasPet: + return (!unit.GetCharmedGUID().IsEmpty() || !unit.GetMinionGUID().IsEmpty()) ? 1 : 0; + case UnitConditionVariable.HasWeapon: + Player player = unit.ToPlayer(); + if (player != null) + return (player.GetWeaponForAttack(WeaponAttackType.BaseAttack) || player.GetWeaponForAttack(WeaponAttackType.OffAttack)) ? 1 : 0; + return (unit.GetVirtualItemId(0) != 0 || unit.GetVirtualItemId(1) != 0) ? 1 : 0; + case UnitConditionVariable.HealthPct: + return (int)unit.GetHealthPct(); + case UnitConditionVariable.ManaPct: + return (int)unit.GetPowerPct(PowerType.Mana); + case UnitConditionVariable.RagePct: + return (int)unit.GetPowerPct(PowerType.Rage); + case UnitConditionVariable.EnergyPct: + return (int)unit.GetPowerPct(PowerType.Energy); + case UnitConditionVariable.ComboPoints: + return unit.GetPower(PowerType.ComboPoints); + case UnitConditionVariable.HasHelpfulAuraSpell: + return unit.GetAuraApplication((uint)value, aurApp => !aurApp.GetFlags().HasFlag(AuraFlags.Negative)) != null ? value : 0; + case UnitConditionVariable.HasHelpfulAuraDispelType: + return unit.GetAuraApplication(aurApp => !aurApp.GetFlags().HasFlag(AuraFlags.Negative) && (int)aurApp.GetBase().GetSpellInfo().Dispel == value) != null ? value : 0; + case UnitConditionVariable.HasHelpfulAuraMechanic: + return unit.GetAuraApplication(aurApp => !aurApp.GetFlags().HasFlag(AuraFlags.Negative) && (aurApp.GetBase().GetSpellInfo().GetSpellMechanicMaskByEffectMask(aurApp.GetEffectMask()) & (1 << value)) != 0) != null ? value : 0; + case UnitConditionVariable.HasHarmfulAuraSpell: + return unit.GetAuraApplication((uint)value, aurApp => aurApp.GetFlags().HasFlag(AuraFlags.Negative)) != null ? value : 0; + case UnitConditionVariable.HasHarmfulAuraDispelType: + return unit.GetAuraApplication(aurApp => aurApp.GetFlags().HasFlag(AuraFlags.Negative) && (int)aurApp.GetBase().GetSpellInfo().Dispel == value) != null ? value : 0; + case UnitConditionVariable.HasHarmfulAuraMechanic: + return unit.GetAuraApplication(aurApp => aurApp.GetFlags().HasFlag(AuraFlags.Negative) && (aurApp.GetBase().GetSpellInfo().GetSpellMechanicMaskByEffectMask(aurApp.GetEffectMask()) & (1 << value)) != 0) != null ? value : 0; + case UnitConditionVariable.HasHarmfulAuraSchool: + return unit.GetAuraApplication(aurApp => aurApp.GetFlags().HasFlag(AuraFlags.Negative) && ((int)aurApp.GetBase().GetSpellInfo().GetSchoolMask() & (1 << value)) != 0) != null ? value : 0; + case UnitConditionVariable.DamagePhysicalPct: + break; + case UnitConditionVariable.DamageHolyPct: + break; + case UnitConditionVariable.DamageFirePct: + break; + case UnitConditionVariable.DamageNaturePct: + break; + case UnitConditionVariable.DamageFrostPct: + break; + case UnitConditionVariable.DamageShadowPct: + break; + case UnitConditionVariable.DamageArcanePct: + break; + case UnitConditionVariable.InCombat: + break; + case UnitConditionVariable.IsMoving: + break; + case UnitConditionVariable.IsCasting: + break; + case UnitConditionVariable.IsCastingSpell: + break; + case UnitConditionVariable.IsChanneling: + break; + case UnitConditionVariable.IsChannelingSpell: + break; + case UnitConditionVariable.NumberOfMeleeAttackers: + break; + case UnitConditionVariable.IsAttackingMe: + break; + case UnitConditionVariable.Range: + break; + case UnitConditionVariable.InMeleeRange: + break; + case UnitConditionVariable.PursuitTime: + break; + case UnitConditionVariable.HasHarmfulAuraCanceledByDamage: + break; + case UnitConditionVariable.HasHarmfulAuraWithPeriodicDamage: + break; + case UnitConditionVariable.NumberOfEnemies: + break; + case UnitConditionVariable.NumberOfFriends: + break; + case UnitConditionVariable.ThreatPhysicalPct: + break; + case UnitConditionVariable.ThreatHolyPct: + break; + case UnitConditionVariable.ThreatFirePct: + break; + case UnitConditionVariable.ThreatNaturePct: + break; + case UnitConditionVariable.ThreatFrostPct: + break; + case UnitConditionVariable.ThreatShadowPct: + break; + case UnitConditionVariable.ThreatArcanePct: + break; + case UnitConditionVariable.IsInterruptible: + break; + case UnitConditionVariable.NumberOfAttackers: + break; + case UnitConditionVariable.NumberOfRangedAttackers: + break; + case UnitConditionVariable.CreatureType: + break; + case UnitConditionVariable.IsMeleeAttacking: + break; + case UnitConditionVariable.IsRangedAttacking: + break; + case UnitConditionVariable.Health: + break; + case UnitConditionVariable.SpellKnown: + break; + case UnitConditionVariable.HasHarmfulAuraEffect: + break; + case UnitConditionVariable.IsImmuneToAreaOfEffect: + break; + case UnitConditionVariable.IsPlayer: + break; + case UnitConditionVariable.DamageMagicPct: + break; + case UnitConditionVariable.DamageTotalPct: + break; + case UnitConditionVariable.ThreatMagicPct: + break; + case UnitConditionVariable.ThreatTotalPct: + break; + case UnitConditionVariable.HasCritter: + return unit.GetCritterGUID().IsEmpty() ? 0 : 1; + case UnitConditionVariable.HasTotemInSlot1: + break; + case UnitConditionVariable.HasTotemInSlot2: + break; + case UnitConditionVariable.HasTotemInSlot3: + break; + case UnitConditionVariable.HasTotemInSlot4: + break; + case UnitConditionVariable.HasTotemInSlot5: + break; + case UnitConditionVariable.Creature: + break; + case UnitConditionVariable.StringID: + break; + case UnitConditionVariable.HasAura: + break; + case UnitConditionVariable.IsEnemy: + break; + case UnitConditionVariable.IsSpecMelee: + break; + case UnitConditionVariable.IsSpecTank: + break; + case UnitConditionVariable.IsSpecRanged: + break; + case UnitConditionVariable.IsSpecHealer: + break; + case UnitConditionVariable.IsPlayerControlledNPC: + break; + case UnitConditionVariable.IsDying: + break; + case UnitConditionVariable.PathFailCount: + break; + case UnitConditionVariable.IsMounted: + break; + case UnitConditionVariable.Label: + break; + case UnitConditionVariable.IsMySummon: + break; + case UnitConditionVariable.IsSummoner: + break; + case UnitConditionVariable.IsMyTarget: + break; + case UnitConditionVariable.Sex: + break; + case UnitConditionVariable.LevelWithinContentTuning: + break; + case UnitConditionVariable.IsFlying: + break; + case UnitConditionVariable.IsHovering: + break; + case UnitConditionVariable.HasHelpfulAuraEffect: + break; + case UnitConditionVariable.HasHelpfulAuraSchool: + return unit.GetAuraApplication(aurApp => + { + return !aurApp.GetFlags().HasFlag(AuraFlags.Negative) && ((int)aurApp.GetBase().GetSpellInfo().GetSchoolMask() & (1 << value)) != 0; + }) != null ? value : 0; + default: + break; + } + + return 0; + } + + public static bool IsUnitMeetingCondition(Unit unit, Unit otherUnit, UnitConditionRecord condition) + { + for (int i = 0; i < condition.Variable.Length; ++i) + { + if (condition.Variable[i] == 0) + break; + + int unitValue = GetUnitConditionVariable(unit, otherUnit, (UnitConditionVariable)condition.Variable[i], condition.Value[i]); + bool meets = false; + switch ((UnitConditionOp)condition.Op[i]) + { + case UnitConditionOp.EqualTo: + meets = unitValue == condition.Value[i]; + break; + case UnitConditionOp.NotEqualTo: + meets = unitValue != condition.Value[i]; + break; + case UnitConditionOp.LessThan: + meets = unitValue < condition.Value[i]; + break; + case UnitConditionOp.LessThanOrEqualTo: + meets = unitValue <= condition.Value[i]; + break; + case UnitConditionOp.GreaterThan: + meets = unitValue > condition.Value[i]; + break; + case UnitConditionOp.GreaterThanOrEqualTo: + meets = unitValue >= condition.Value[i]; + break; + default: + break; + } + + if (condition.GetFlags().HasFlag(UnitConditionFlags.LogicOr)) + { + if (meets) + return true; + } + else if (!meets) + return false; + } + + return !condition.GetFlags().HasFlag(UnitConditionFlags.LogicOr); + } + static int EvalSingleValue(ByteBuffer buffer, Player player) { WorldStateExpressionValueType valueType = (WorldStateExpressionValueType)buffer.ReadUInt8(); diff --git a/Source/Game/DataStorage/CliDB.cs b/Source/Game/DataStorage/CliDB.cs index 918f6f005..531e5e6e9 100644 --- a/Source/Game/DataStorage/CliDB.cs +++ b/Source/Game/DataStorage/CliDB.cs @@ -725,7 +725,7 @@ namespace Game.DataStorage public static DB6Storage UiMapLinkStorage; public static DB6Storage UiMapXMapArtStorage; public static DB6Storage UISplashScreenStorage; - public static DB6Storage sUnitConditionStorage; + public static DB6Storage UnitConditionStorage; public static DB6Storage UnitPowerBarStorage; public static DB6Storage VehicleStorage; public static DB6Storage VehicleSeatStorage; diff --git a/Source/Game/DataStorage/Structs/U_Records.cs b/Source/Game/DataStorage/Structs/U_Records.cs index 26427a1a6..1e7d985a0 100644 --- a/Source/Game/DataStorage/Structs/U_Records.cs +++ b/Source/Game/DataStorage/Structs/U_Records.cs @@ -101,7 +101,9 @@ namespace Game.DataStorage public byte[] Variable = new byte[8]; public sbyte[] Op = new sbyte[8]; public int[] Value = new int[8]; - }; + + public UnitConditionFlags GetFlags() { return (UnitConditionFlags)Flags; } + } public sealed class UnitPowerBarRecord { diff --git a/Source/Game/Spells/SpellInfo.cs b/Source/Game/Spells/SpellInfo.cs index baf756cbb..75d4eb77d 100644 --- a/Source/Game/Spells/SpellInfo.cs +++ b/Source/Game/Spells/SpellInfo.cs @@ -3222,21 +3222,29 @@ namespace Game.Spells return false; } - public uint GetSpellXSpellVisualId(WorldObject caster = null) + public uint GetSpellXSpellVisualId(WorldObject caster = null, WorldObject viewer = 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; + var playerCondition = CliDB.PlayerConditionStorage.LookupByKey(visual.CasterPlayerConditionID); + if (playerCondition != null) + if (!caster || !caster.IsPlayer() || !ConditionManager.IsPlayerMeetingCondition(caster.ToPlayer(), playerCondition)) + continue; + + var unitCondition = CliDB.UnitConditionStorage.LookupByKey(visual.CasterUnitConditionID); + if (unitCondition != null) + if (!caster || !caster.IsUnit() || !ConditionManager.IsUnitMeetingCondition(caster.ToUnit(), viewer.ToUnit(), unitCondition)) + continue; + + return visual.Id; } return 0; } - public uint GetSpellVisual(WorldObject caster = null) + public uint GetSpellVisual(WorldObject caster = null, WorldObject viewer = null) { - var visual = CliDB.SpellXSpellVisualStorage.LookupByKey(GetSpellXSpellVisualId(caster)); + var visual = CliDB.SpellXSpellVisualStorage.LookupByKey(GetSpellXSpellVisualId(caster, viewer)); if (visual != null) { //if (visual.LowViolenceSpellVisualID && forPlayer.GetViolenceLevel() operator 2)