From 9ec956becfd9b45372b02e92b5162d72f5948504 Mon Sep 17 00:00:00 2001 From: hondacrx Date: Tue, 5 May 2020 18:38:43 -0400 Subject: [PATCH] Core/Auras: reworked multiplicative AuraEffects calculation Port From (https://github.com/TrinityCore/TrinityCore/commit/ebc06b1d0401be69066986e18d0e926170c58023) --- Source/Framework/Constants/UnitConst.cs | 18 +- Source/Game/Entities/Creature/Creature.cs | 31 +- Source/Game/Entities/Player/Player.Combat.cs | 7 +- Source/Game/Entities/Player/Player.Fields.cs | 3 +- Source/Game/Entities/Player/Player.Items.cs | 107 +++---- Source/Game/Entities/Player/Player.Spells.cs | 119 +++++--- Source/Game/Entities/Player/Player.cs | 140 +++++++-- Source/Game/Entities/StatSystem.cs | 266 +++++++++++----- Source/Game/Entities/TemporarySummon.cs | 60 ++-- Source/Game/Entities/Unit/Unit.Combat.cs | 90 ++++-- Source/Game/Entities/Unit/Unit.Fields.cs | 3 +- Source/Game/Entities/Unit/Unit.Spells.cs | 15 +- Source/Game/Entities/Unit/Unit.cs | 39 +-- Source/Game/Spells/Auras/AuraEffect.cs | 306 ++++++++++++------- Source/Game/Spells/SpellEffects.cs | 20 +- 15 files changed, 796 insertions(+), 428 deletions(-) diff --git a/Source/Framework/Constants/UnitConst.cs b/Source/Framework/Constants/UnitConst.cs index 0b68cb084..eef49aa2f 100644 --- a/Source/Framework/Constants/UnitConst.cs +++ b/Source/Framework/Constants/UnitConst.cs @@ -87,7 +87,7 @@ namespace Framework.Constants public enum BaseModType { FlatMod, - PCTmod, + PctMod, End } @@ -178,14 +178,18 @@ namespace Framework.Constants PowerStart = Mana, PowerEnd = Pain + 1 } - public enum UnitModifierType + public enum UnitModifierFlatType { - BaseValue = 0, + Base = 0, BasePCTExcludeCreate = 1, // percent modifier affecting all stat values from auras and gear but not player base for level - BasePCT = 2, - TotalValue = 3, - TotalPCT = 4, - End = 5 + Total = 2, + End = 3 + } + public enum UnitModifierPctType + { + Base = 0, + Total = 1, + End = 2 } public enum VictimState { diff --git a/Source/Game/Entities/Creature/Creature.cs b/Source/Game/Entities/Creature/Creature.cs index c6f0695ee..ac0b43bcf 100644 --- a/Source/Game/Entities/Creature/Creature.cs +++ b/Source/Game/Entities/Creature/Creature.cs @@ -312,12 +312,12 @@ namespace Game.Entities UpdateLevelDependantStats(); // We still re-initialize level dependant stats on entry update SetMeleeDamageSchool((SpellSchools)cInfo.DmgSchool); - SetModifierValue(UnitMods.ResistanceHoly, UnitModifierType.BaseValue, cInfo.Resistance[(int)SpellSchools.Holy]); - SetModifierValue(UnitMods.ResistanceFire, UnitModifierType.BaseValue, cInfo.Resistance[(int)SpellSchools.Fire]); - SetModifierValue(UnitMods.ResistanceNature, UnitModifierType.BaseValue, cInfo.Resistance[(int)SpellSchools.Nature]); - SetModifierValue(UnitMods.ResistanceFrost, UnitModifierType.BaseValue, cInfo.Resistance[(int)SpellSchools.Frost]); - SetModifierValue(UnitMods.ResistanceShadow, UnitModifierType.BaseValue, cInfo.Resistance[(int)SpellSchools.Shadow]); - SetModifierValue(UnitMods.ResistanceArcane, UnitModifierType.BaseValue, cInfo.Resistance[(int)SpellSchools.Arcane]); + SetStatFlatModifier(UnitMods.ResistanceHoly, UnitModifierFlatType.Base, cInfo.Resistance[(int)SpellSchools.Holy]); + SetStatFlatModifier(UnitMods.ResistanceFire, UnitModifierFlatType.Base, cInfo.Resistance[(int)SpellSchools.Fire]); + SetStatFlatModifier(UnitMods.ResistanceNature, UnitModifierFlatType.Base, cInfo.Resistance[(int)SpellSchools.Nature]); + SetStatFlatModifier(UnitMods.ResistanceFrost, UnitModifierFlatType.Base, cInfo.Resistance[(int)SpellSchools.Frost]); + SetStatFlatModifier(UnitMods.ResistanceShadow, UnitModifierFlatType.Base, cInfo.Resistance[(int)SpellSchools.Shadow]); + SetStatFlatModifier(UnitMods.ResistanceArcane, UnitModifierFlatType.Base, cInfo.Resistance[(int)SpellSchools.Arcane]); SetCanModifyStats(true); UpdateAllStats(); @@ -563,7 +563,7 @@ namespace Game.Entities if (curValue >= maxValue) return; - float addvalue = 0.0f; + float addvalue; switch (power) { @@ -614,7 +614,7 @@ namespace Game.Entities if (curValue >= maxValue) return; - long addvalue = 0; + long addvalue; // Not only pet, but any controlled creature (and not polymorphed) if (!GetCharmerOrOwnerGUID().IsEmpty() && !IsPolymorphed()) @@ -1166,7 +1166,7 @@ namespace Game.Entities break; } - SetModifierValue(UnitMods.Health, UnitModifierType.BaseValue, health); + SetStatFlatModifier(UnitMods.Health, UnitModifierFlatType.Base, health); //Damage float basedamage = stats.GenerateBaseDamage(cInfo); @@ -1182,11 +1182,11 @@ namespace Game.Entities SetBaseWeaponDamage(WeaponAttackType.RangedAttack, WeaponDamageRange.MinDamage, weaponBaseMinDamage); SetBaseWeaponDamage(WeaponAttackType.RangedAttack, WeaponDamageRange.MaxDamage, weaponBaseMaxDamage); - SetModifierValue(UnitMods.AttackPower, UnitModifierType.BaseValue, stats.AttackPower); - SetModifierValue(UnitMods.AttackPowerRanged, UnitModifierType.BaseValue, stats.RangedAttackPower); + SetStatFlatModifier(UnitMods.AttackPower, UnitModifierFlatType.Base, stats.AttackPower); + SetStatFlatModifier(UnitMods.AttackPowerRanged, UnitModifierFlatType.Base, stats.RangedAttackPower); float armor = stats.GenerateArmor(cInfo); // @todo Why is this treated as uint32 when it's a float? - SetModifierValue(UnitMods.Armor, UnitModifierType.BaseValue, armor); + SetStatFlatModifier(UnitMods.Armor, UnitModifierFlatType.Base, armor); } float _GetHealthMod(CreatureEliteType Rank) @@ -2026,7 +2026,6 @@ namespace Game.Entities return false; } - Unit myVictim = GetAttackerForHelper(); Unit targetVictim = target.GetAttackerForHelper(); // if I'm already fighting target, or I'm hostile towards the target, the target is acceptable @@ -2263,13 +2262,11 @@ namespace Game.Entities public void GetRespawnPosition(out float x, out float y, out float z) { - float notUsed; - GetRespawnPosition(out x, out y, out z, out notUsed, out notUsed); + GetRespawnPosition(out x, out y, out z, out _, out _); } public void GetRespawnPosition(out float x, out float y, out float z, out float ori) { - float notUsed; - GetRespawnPosition(out x, out y, out z, out ori, out notUsed); + GetRespawnPosition(out x, out y, out z, out ori, out _); } public void GetRespawnPosition(out float x, out float y, out float z, out float ori, out float dist) { diff --git a/Source/Game/Entities/Player/Player.Combat.cs b/Source/Game/Entities/Player/Player.Combat.cs index e65eea330..d533d3038 100644 --- a/Source/Game/Entities/Player/Player.Combat.cs +++ b/Source/Game/Entities/Player/Player.Combat.cs @@ -157,7 +157,7 @@ namespace Game.Entities return; // @todo research if talents/effects that increase total agility by x% should increase non-diminishing part - float base_agility = GetCreateStat(Stats.Agility) * m_auraModifiersGroup[(int)UnitMods.StatAgility][(int)UnitModifierType.BasePCT]; + float base_agility = GetCreateStat(Stats.Agility) * GetPctModifierValue(UnitMods(UNIT_MOD_STAT_START + STAT_AGILITY), BASE_PCT); float bonus_agility = GetStat(Stats.Agility) - base_agility; // calculate diminishing (green in char screen) and non-diminishing (white) contribution @@ -166,11 +166,6 @@ namespace Game.Entities */ } - float GetTotalPercentageModValue(BaseModGroup modGroup) - { - return m_auraBaseMod[(int)modGroup][0] + m_auraBaseMod[(int)modGroup][1]; - } - public float GetExpertiseDodgeOrParryReduction(WeaponAttackType attType) { float baseExpertise = 7.5f; diff --git a/Source/Game/Entities/Player/Player.Fields.cs b/Source/Game/Entities/Player/Player.Fields.cs index 0959a4ac8..c0b9b84f4 100644 --- a/Source/Game/Entities/Player/Player.Fields.cs +++ b/Source/Game/Entities/Player/Player.Fields.cs @@ -141,7 +141,8 @@ namespace Game.Entities //Combat int[] baseRatingValue = new int[(int)CombatRating.Max]; - public float[][] m_auraBaseMod = new float[(int)BaseModGroup.End][]; + float[] m_auraBaseFlatMod = new float[(int)BaseModGroup.End]; + float[] m_auraBasePctMod = new float[(int)BaseModGroup.End]; public DuelInfo duel; bool m_canParry; bool m_canBlock; diff --git a/Source/Game/Entities/Player/Player.Items.cs b/Source/Game/Entities/Player/Player.Items.cs index d84452524..9489a39c2 100644 --- a/Source/Game/Entities/Player/Player.Items.cs +++ b/Source/Game/Entities/Player/Player.Items.cs @@ -1098,8 +1098,7 @@ namespace Game.Entities } public InventoryResult CanStoreNewItem(byte bag, byte slot, List dest, uint item, uint count) { - uint notused; - return CanStoreItem(bag, slot, dest, item, count, null, false, out notused); + return CanStoreItem(bag, slot, dest, item, count, null, false, out _); } Item _StoreItem(ushort pos, Item pItem, uint count, bool clone, bool update) @@ -1233,8 +1232,7 @@ namespace Game.Entities // attempt equip by one while (titem_amount > 0) { - ushort eDest = 0; - msg = CanEquipNewItem(ItemConst.NullSlot, out eDest, titem_id, false); + msg = CanEquipNewItem(ItemConst.NullSlot, out ushort eDest, titem_id, false); if (msg != InventoryResult.Ok) break; @@ -1890,7 +1888,7 @@ namespace Game.Entities //Add/Remove/Misc Item public bool AddItem(uint itemId, uint count) { - uint noSpaceForCount = 0; + uint noSpaceForCount; List dest = new List(); InventoryResult msg = CanStoreNewItem(ItemConst.NullBag, ItemConst.NullSlot, dest, itemId, count, out noSpaceForCount); if (msg != InventoryResult.Ok) @@ -3525,7 +3523,7 @@ namespace Game.Entities } uint stacks = count / crItem.maxcount; - ItemExtendedCostRecord iece = null; + ItemExtendedCostRecord iece; if (crItem.ExtendedCost != 0) { iece = CliDB.ItemExtendedCostStorage.LookupByKey(crItem.ExtendedCost); @@ -3991,8 +3989,15 @@ namespace Game.Entities _ApplyItemBonuses(item, slot, apply); ApplyItemEquipSpell(item, apply); + if (updateItemAuras) + { ApplyItemDependentAuras(item, apply); + WeaponAttackType attackType = Player.GetAttackBySlot(slot, item.GetTemplate().GetInventoryType()); + if (attackType != WeaponAttackType.Max) + UpdateWeaponDependentAuras(attackType); + } + ApplyArtifactPowers(item, apply); ApplyAzeritePowers(item, apply); ApplyEnchantment(item, apply); @@ -4025,22 +4030,22 @@ namespace Game.Entities switch ((ItemModType)statType) { case ItemModType.Mana: - HandleStatModifier(UnitMods.Mana, UnitModifierType.BaseValue, (float)val, apply); + HandleStatFlatModifier(UnitMods.Mana, UnitModifierFlatType.Base, (float)val, apply); break; case ItemModType.Health: // modify HP - HandleStatModifier(UnitMods.Health, UnitModifierType.BaseValue, (float)val, apply); + HandleStatFlatModifier(UnitMods.Health, UnitModifierFlatType.Base, (float)val, apply); break; case ItemModType.Agility: // modify agility - HandleStatModifier(UnitMods.StatAgility, UnitModifierType.BaseValue, (float)val, apply); - ApplyStatBuffMod(Stats.Agility, MathFunctions.CalculatePct(val, GetModifierValue(UnitMods.StatAgility, UnitModifierType.BasePCTExcludeCreate)), apply); + HandleStatFlatModifier(UnitMods.StatAgility, UnitModifierFlatType.Base, (float)val, apply); + UpdateStatBuffMod(Stats.Agility); break; case ItemModType.Strength: //modify strength - HandleStatModifier(UnitMods.StatStrength, UnitModifierType.BaseValue, (float)val, apply); - ApplyStatBuffMod(Stats.Strength, MathFunctions.CalculatePct(val, GetModifierValue(UnitMods.StatStrength, UnitModifierType.BasePCTExcludeCreate)), apply); + HandleStatFlatModifier(UnitMods.StatStrength, UnitModifierFlatType.Base, (float)val, apply); + UpdateStatBuffMod(Stats.Strength); break; case ItemModType.Intellect: //modify intellect - HandleStatModifier(UnitMods.StatIntellect, UnitModifierType.BaseValue, (float)val, apply); - ApplyStatBuffMod(Stats.Intellect, MathFunctions.CalculatePct(val, GetModifierValue(UnitMods.StatIntellect, UnitModifierType.BasePCTExcludeCreate)), apply); + HandleStatFlatModifier(UnitMods.StatIntellect, UnitModifierFlatType.Base, (float)val, apply); + UpdateStatBuffMod(Stats.Intellect); break; //case ItemModType.Spirit: //modify spirit //HandleStatModifier(UnitMods.StatSpirit, UnitModifierType.BaseValue, (float)val, apply); @@ -4051,8 +4056,8 @@ namespace Game.Entities if (staminaMult != null) val = (int)(val * CliDB.GetIlvlStatMultiplier(staminaMult, proto.GetInventoryType())); - HandleStatModifier(UnitMods.StatStamina, UnitModifierType.BaseValue, (float)val, apply); - ApplyStatBuffMod(Stats.Stamina, MathFunctions.CalculatePct(val, GetModifierValue(UnitMods.StatStamina, UnitModifierType.BasePCTExcludeCreate)), apply); + HandleStatFlatModifier(UnitMods.StatStamina, UnitModifierFlatType.Base, (float)val, apply); + UpdateStatBuffMod(Stats.Stamina); break; case ItemModType.DefenseSkillRating: ApplyRatingMod(CombatRating.DefenseSkill, (int)(val * combatRatingMultiplier), apply); @@ -4118,11 +4123,11 @@ namespace Game.Entities ApplyRatingMod(CombatRating.Expertise, (int)(val * combatRatingMultiplier), apply); break; case ItemModType.AttackPower: - HandleStatModifier(UnitMods.AttackPower, UnitModifierType.TotalValue, (float)val, apply); - HandleStatModifier(UnitMods.AttackPowerRanged, UnitModifierType.TotalValue, (float)val, apply); + HandleStatFlatModifier(UnitMods.AttackPower, UnitModifierFlatType.Total, (float)val, apply); + HandleStatFlatModifier(UnitMods.AttackPowerRanged, UnitModifierFlatType.Total, (float)val, apply); break; case ItemModType.RangedAttackPower: - HandleStatModifier(UnitMods.AttackPowerRanged, UnitModifierType.TotalValue, (float)val, apply); + HandleStatFlatModifier(UnitMods.AttackPowerRanged, UnitModifierFlatType.Total, (float)val, apply); break; case ItemModType.Versatility: ApplyRatingMod(CombatRating.VersatilityDamageDone, (int)(val * combatRatingMultiplier), apply); @@ -4148,25 +4153,25 @@ namespace Game.Entities ApplyRatingMod(CombatRating.Mastery, (int)(val * combatRatingMultiplier), apply); break; case ItemModType.ExtraArmor: - HandleStatModifier(UnitMods.Armor, UnitModifierType.TotalValue, (float)val, apply); + HandleStatFlatModifier(UnitMods.Armor, UnitModifierFlatType.Total, (float)val, apply); break; case ItemModType.FireResistance: - HandleStatModifier(UnitMods.ResistanceFire, UnitModifierType.BaseValue, (float)val, apply); + HandleStatFlatModifier(UnitMods.ResistanceFire, UnitModifierFlatType.Base, (float)val, apply); break; case ItemModType.FrostResistance: - HandleStatModifier(UnitMods.ResistanceFrost, UnitModifierType.BaseValue, (float)val, apply); + HandleStatFlatModifier(UnitMods.ResistanceFrost, UnitModifierFlatType.Base, (float)val, apply); break; case ItemModType.HolyResistance: - HandleStatModifier(UnitMods.ResistanceHoly, UnitModifierType.BaseValue, (float)val, apply); + HandleStatFlatModifier(UnitMods.ResistanceHoly, UnitModifierFlatType.Base, (float)val, apply); break; case ItemModType.ShadowResistance: - HandleStatModifier(UnitMods.ResistanceShadow, UnitModifierType.BaseValue, (float)val, apply); + HandleStatFlatModifier(UnitMods.ResistanceShadow, UnitModifierFlatType.Base, (float)val, apply); break; case ItemModType.NatureResistance: - HandleStatModifier(UnitMods.ResistanceNature, UnitModifierType.BaseValue, (float)val, apply); + HandleStatFlatModifier(UnitMods.ResistanceNature, UnitModifierFlatType.Base, (float)val, apply); break; case ItemModType.ArcaneResistance: - HandleStatModifier(UnitMods.ResistanceArcane, UnitModifierType.BaseValue, (float)val, apply); + HandleStatFlatModifier(UnitMods.ResistanceArcane, UnitModifierFlatType.Base, (float)val, apply); break; case ItemModType.PvpPower: ApplyRatingMod(CombatRating.PvpPower, val, apply); @@ -4202,37 +4207,37 @@ namespace Game.Entities ApplyRatingMod(CombatRating.Unused12, val, apply); break; case ItemModType.AgiStrInt: - HandleStatModifier(UnitMods.StatAgility, UnitModifierType.BaseValue, val, apply); - HandleStatModifier(UnitMods.StatStrength, UnitModifierType.BaseValue, val, apply); - HandleStatModifier(UnitMods.StatIntellect, UnitModifierType.BaseValue, val, apply); - ApplyStatBuffMod(Stats.Agility, MathFunctions.CalculatePct(val, GetModifierValue(UnitMods.StatAgility, UnitModifierType.BasePCTExcludeCreate)), apply); - ApplyStatBuffMod(Stats.Strength, MathFunctions.CalculatePct(val, GetModifierValue(UnitMods.StatStrength, UnitModifierType.BasePCTExcludeCreate)), apply); - ApplyStatBuffMod(Stats.Intellect, MathFunctions.CalculatePct(val, GetModifierValue(UnitMods.StatIntellect, UnitModifierType.BasePCTExcludeCreate)), apply); + HandleStatFlatModifier(UnitMods.StatAgility, UnitModifierFlatType.Base, val, apply); + HandleStatFlatModifier(UnitMods.StatStrength, UnitModifierFlatType.Base, val, apply); + HandleStatFlatModifier(UnitMods.StatIntellect, UnitModifierFlatType.Base, val, apply); + UpdateStatBuffMod(Stats.Agility); + UpdateStatBuffMod(Stats.Strength); + UpdateStatBuffMod(Stats.Intellect); break; case ItemModType.AgiStr: - HandleStatModifier(UnitMods.StatAgility, UnitModifierType.BaseValue, val, apply); - HandleStatModifier(UnitMods.StatStrength, UnitModifierType.BaseValue, val, apply); - ApplyStatBuffMod(Stats.Agility, MathFunctions.CalculatePct(val, GetModifierValue(UnitMods.StatAgility, UnitModifierType.BasePCTExcludeCreate)), apply); - ApplyStatBuffMod(Stats.Strength, MathFunctions.CalculatePct(val, GetModifierValue(UnitMods.StatStrength, UnitModifierType.BasePCTExcludeCreate)), apply); + HandleStatFlatModifier(UnitMods.StatAgility, UnitModifierFlatType.Base, val, apply); + HandleStatFlatModifier(UnitMods.StatStrength, UnitModifierFlatType.Base, val, apply); + UpdateStatBuffMod(Stats.Agility); + UpdateStatBuffMod(Stats.Strength); break; case ItemModType.AgiInt: - HandleStatModifier(UnitMods.StatAgility, UnitModifierType.BaseValue, val, apply); - HandleStatModifier(UnitMods.StatIntellect, UnitModifierType.BaseValue, val, apply); - ApplyStatBuffMod(Stats.Agility, MathFunctions.CalculatePct(val, GetModifierValue(UnitMods.StatAgility, UnitModifierType.BasePCTExcludeCreate)), apply); - ApplyStatBuffMod(Stats.Intellect, MathFunctions.CalculatePct(val, GetModifierValue(UnitMods.StatIntellect, UnitModifierType.BasePCTExcludeCreate)), apply); + HandleStatFlatModifier(UnitMods.StatAgility, UnitModifierFlatType.Base, val, apply); + HandleStatFlatModifier(UnitMods.StatIntellect, UnitModifierFlatType.Base, val, apply); + UpdateStatBuffMod(Stats.Agility); + UpdateStatBuffMod(Stats.Intellect); break; case ItemModType.StrInt: - HandleStatModifier(UnitMods.StatStrength, UnitModifierType.BaseValue, val, apply); - HandleStatModifier(UnitMods.StatIntellect, UnitModifierType.BaseValue, val, apply); - ApplyStatBuffMod(Stats.Strength, MathFunctions.CalculatePct(val, GetModifierValue(UnitMods.StatStrength, UnitModifierType.BasePCTExcludeCreate)), apply); - ApplyStatBuffMod(Stats.Intellect, MathFunctions.CalculatePct(val, GetModifierValue(UnitMods.StatIntellect, UnitModifierType.BasePCTExcludeCreate)), apply); + HandleStatFlatModifier(UnitMods.StatStrength, UnitModifierFlatType.Base, val, apply); + HandleStatFlatModifier(UnitMods.StatIntellect, UnitModifierFlatType.Base, val, apply); + UpdateStatBuffMod(Stats.Strength); + UpdateStatBuffMod(Stats.Intellect); break; } } uint armor = item.GetArmor(this); if (armor != 0) - HandleStatModifier(UnitMods.Armor, UnitModifierType.BaseValue, (float)armor, apply); + HandleStatFlatModifier(UnitMods.Armor, UnitModifierFlatType.Base, (float)armor, apply); WeaponAttackType attType = WeaponAttackType.BaseAttack; if (slot == EquipmentSlot.MainHand && (proto.GetInventoryType() == InventoryType.Ranged || proto.GetInventoryType() == InventoryType.RangedRight)) @@ -4425,6 +4430,10 @@ namespace Game.Entities ApplyItemDependentAuras(m_items[i], true); _ApplyItemBonuses(m_items[i], i, true); + + WeaponAttackType attackType = Player.GetAttackBySlot(i, m_items[i].GetTemplate().GetInventoryType()); + if (attackType != WeaponAttackType.Max) + UpdateWeaponDependentAuras(attackType); } } @@ -6229,11 +6238,7 @@ namespace Game.Entities public void SetLootGUID(ObjectGuid guid) { SetUpdateFieldValue(m_values.ModifyValue(m_playerData).ModifyValue(m_playerData.LootTargetGUID), guid); } public void StoreLootItem(byte lootSlot, Loot loot, AELootResult aeResult = null) { - NotNormalLootItem qitem = null; - NotNormalLootItem ffaitem = null; - NotNormalLootItem conditem = null; - - LootItem item = loot.LootItemInSlot(lootSlot, this, out qitem, out ffaitem, out conditem); + LootItem item = loot.LootItemInSlot(lootSlot, this, out NotNormalLootItem qitem, out NotNormalLootItem ffaitem, out NotNormalLootItem conditem); if (item == null) { SendEquipError(InventoryResult.LootGone); @@ -6385,7 +6390,7 @@ namespace Game.Entities if (!currentLootGuid.IsEmpty() && !aeLooting) Session.DoLootRelease(currentLootGuid); - Loot loot = null; + Loot loot; PermissionTypes permission = PermissionTypes.All; Log.outDebug(LogFilter.Loot, "Player.SendLoot"); diff --git a/Source/Game/Entities/Player/Player.Spells.cs b/Source/Game/Entities/Player/Player.Spells.cs index 05c199797..0d1fec965 100644 --- a/Source/Game/Entities/Player/Player.Spells.cs +++ b/Source/Game/Entities/Player/Player.Spells.cs @@ -153,9 +153,6 @@ namespace Game.Entities if (skillStatusData == null || skillStatusData.State == SkillState.Deleted || skillInfo.SkillRank[skillStatusData.Pos] == 0) return 0; - var field = (ushort)(skillStatusData.Pos / 2); - var offset = (byte)(skillStatusData.Pos & 1); - return skillInfo.SkillStep[skillStatusData.Pos]; } @@ -613,15 +610,11 @@ namespace Game.Entities // processed in Player.CastItemCombatSpell break; case ItemEnchantmentType.Damage: - if (item.GetSlot() == EquipmentSlot.MainHand) { - if (item.GetTemplate().GetInventoryType() != InventoryType.Ranged && item.GetTemplate().GetInventoryType() != InventoryType.RangedRight) - HandleStatModifier(UnitMods.DamageMainHand, UnitModifierType.TotalValue, enchant_amount, apply); - else - HandleStatModifier(UnitMods.DamageRanged, UnitModifierType.TotalValue, enchant_amount, apply); + WeaponAttackType attackType = Player.GetAttackBySlot(item.GetSlot(), item.GetTemplate().GetInventoryType()); + if (attackType != WeaponAttackType.Max) + UpdateDamageDoneMods(attackType); } - else if (item.GetSlot() == EquipmentSlot.OffHand) - HandleStatModifier(UnitMods.DamageOffHand, UnitModifierType.TotalValue, enchant_amount, apply); break; case ItemEnchantmentType.EquipSpell: if (enchant_spell_id != 0) @@ -654,7 +647,7 @@ namespace Game.Entities } enchant_amount = Math.Max(enchant_amount, 1u); - HandleStatModifier((UnitMods)((uint)UnitMods.ResistanceStart + enchant_spell_id), UnitModifierType.TotalValue, enchant_amount, apply); + HandleStatFlatModifier((UnitMods)((uint)UnitMods.ResistanceStart + enchant_spell_id), UnitModifierFlatType.Total, enchant_amount, apply); break; case ItemEnchantmentType.Stat: { @@ -685,26 +678,26 @@ namespace Game.Entities { case ItemModType.Mana: Log.outDebug(LogFilter.Player, "+ {0} MANA", enchant_amount); - HandleStatModifier(UnitMods.Mana, UnitModifierType.BaseValue, enchant_amount, apply); + HandleStatFlatModifier(UnitMods.Mana, UnitModifierFlatType.Base, enchant_amount, apply); break; case ItemModType.Health: Log.outDebug(LogFilter.Player, "+ {0} HEALTH", enchant_amount); - HandleStatModifier(UnitMods.Health, UnitModifierType.BaseValue, enchant_amount, apply); + HandleStatFlatModifier(UnitMods.Health, UnitModifierFlatType.Base, enchant_amount, apply); break; case ItemModType.Agility: Log.outDebug(LogFilter.Player, "+ {0} AGILITY", enchant_amount); - HandleStatModifier(UnitMods.StatAgility, UnitModifierType.TotalValue, enchant_amount, apply); - ApplyStatBuffMod(Stats.Agility, enchant_amount, apply); + HandleStatFlatModifier(UnitMods.StatAgility, UnitModifierFlatType.Total, enchant_amount, apply); + UpdateStatBuffMod(Stats.Agility); break; case ItemModType.Strength: Log.outDebug(LogFilter.Player, "+ {0} STRENGTH", enchant_amount); - HandleStatModifier(UnitMods.StatStrength, UnitModifierType.TotalValue, enchant_amount, apply); - ApplyStatBuffMod(Stats.Strength, enchant_amount, apply); + HandleStatFlatModifier(UnitMods.StatStrength, UnitModifierFlatType.Total, enchant_amount, apply); + UpdateStatBuffMod(Stats.Strength); break; case ItemModType.Intellect: Log.outDebug(LogFilter.Player, "+ {0} INTELLECT", enchant_amount); - HandleStatModifier(UnitMods.StatIntellect, UnitModifierType.TotalValue, enchant_amount, apply); - ApplyStatBuffMod(Stats.Intellect, enchant_amount, apply); + HandleStatFlatModifier(UnitMods.StatIntellect, UnitModifierFlatType.Total, enchant_amount, apply); + UpdateStatBuffMod(Stats.Intellect); break; //case ItemModType.Spirit: //Log.outDebug(LogFilter.Player, "+ {0} SPIRIT", enchant_amount); @@ -713,8 +706,8 @@ namespace Game.Entities //break; case ItemModType.Stamina: Log.outDebug(LogFilter.Player, "+ {0} STAMINA", enchant_amount); - HandleStatModifier(UnitMods.StatStamina, UnitModifierType.TotalValue, enchant_amount, apply); - ApplyStatBuffMod(Stats.Stamina, enchant_amount, apply); + HandleStatFlatModifier(UnitMods.StatStamina, UnitModifierFlatType.Total, enchant_amount, apply); + UpdateStatBuffMod(Stats.Stamina); break; case ItemModType.DefenseSkillRating: ApplyRatingMod(CombatRating.DefenseSkill, (int)enchant_amount, apply); @@ -786,12 +779,12 @@ namespace Game.Entities Log.outDebug(LogFilter.Player, "+ {0} EXPERTISE", enchant_amount); break; case ItemModType.AttackPower: - HandleStatModifier(UnitMods.AttackPower, UnitModifierType.TotalValue, enchant_amount, apply); - HandleStatModifier(UnitMods.AttackPowerRanged, UnitModifierType.TotalValue, enchant_amount, apply); + HandleStatFlatModifier(UnitMods.AttackPower, UnitModifierFlatType.Total, enchant_amount, apply); + HandleStatFlatModifier(UnitMods.AttackPowerRanged, UnitModifierFlatType.Total, enchant_amount, apply); Log.outDebug(LogFilter.Player, "+ {0} ATTACK_POWER", enchant_amount); break; case ItemModType.RangedAttackPower: - HandleStatModifier(UnitMods.AttackPowerRanged, UnitModifierType.TotalValue, enchant_amount, apply); + HandleStatFlatModifier(UnitMods.AttackPowerRanged, UnitModifierFlatType.Total, enchant_amount, apply); Log.outDebug(LogFilter.Player, "+ {0} RANGED_ATTACK_POWER", enchant_amount); break; case ItemModType.ManaRegeneration: @@ -815,7 +808,7 @@ namespace Game.Entities Log.outDebug(LogFilter.Player, "+ {0} SPELL_PENETRATION", enchant_amount); break; case ItemModType.BlockValue: - HandleBaseModValue(BaseModGroup.ShieldBlockValue, BaseModType.FlatMod, enchant_amount, apply); + HandleBaseModFlatValue(BaseModGroup.ShieldBlockValue, enchant_amount, apply); Log.outDebug(LogFilter.Player, "+ {0} BLOCK_VALUE", enchant_amount); break; case ItemModType.MasteryRating: @@ -835,20 +828,9 @@ namespace Game.Entities } case ItemEnchantmentType.Totem: // Shaman Rockbiter Weapon { - if (GetClass() == Class.Shaman) - { - float addValue = 0.0f; - if (item.GetSlot() == EquipmentSlot.MainHand) - { - addValue = enchant_amount * item.GetTemplate().GetDelay() / 1000.0f; - HandleStatModifier(UnitMods.DamageMainHand, UnitModifierType.TotalValue, addValue, apply); - } - else if (item.GetSlot() == EquipmentSlot.OffHand) - { - addValue = enchant_amount * item.GetTemplate().GetDelay() / 1000.0f; - HandleStatModifier(UnitMods.DamageOffHand, UnitModifierType.TotalValue, addValue, apply); - } - } + WeaponAttackType attackType = Player.GetAttackBySlot(item.GetSlot(), item.GetTemplate().GetInventoryType()); + if (attackType != WeaponAttackType.Max) + UpdateDamageDoneMods(attackType); break; } case ItemEnchantmentType.UseSpell: @@ -1187,14 +1169,14 @@ namespace Game.Entities if (skillSlot == 0) { - Log.outError(LogFilter.Misc, $"Tried to add skill {id} but player {GetName()} ({GetGUID().ToString()}) cannot have additional skills"); + Log.outError(LogFilter.Misc, $"Tried to add skill {id} but player {GetName()} ({GetGUID()}) cannot have additional skills"); return; } SkillLineRecord skillEntry = CliDB.SkillLineStorage.LookupByKey(id); if (skillEntry == null) { - Log.outError(LogFilter.Misc, $"Player.SetSkill: Skill (SkillID: {id}) not found in SkillLineStore for player '{GetName()}' ({GetGUID().ToString()})"); + Log.outError(LogFilter.Misc, $"Player.SetSkill: Skill (SkillID: {id}) not found in SkillLineStore for player '{GetName()}' ({GetGUID()})"); return; } @@ -1829,6 +1811,48 @@ namespace Game.Entities } } + // this one rechecks weapon auras and stores them in BaseModGroup container + // needed for things like axe specialization applying only to axe weapons in case of dual-wield + void UpdateWeaponDependentCritAuras(WeaponAttackType attackType) + { + BaseModGroup modGroup; + switch (attackType) + { + case WeaponAttackType.BaseAttack: + modGroup = BaseModGroup.CritPercentage; + break; + case WeaponAttackType.OffAttack: + modGroup = BaseModGroup.OffhandCritPercentage; + break; + case WeaponAttackType.RangedAttack: + modGroup = BaseModGroup.RangedCritPercentage; + break; + default: + return; + } + + float amount = 0.0f; + amount += GetTotalAuraModifier(AuraType.ModWeaponCritPercent, auraEffect => CheckAttackFitToAuraRequirement(attackType, auraEffect)); + + // these auras don't have item requirement (only Combat Expertise in 3.3.5a) + amount += GetTotalAuraModifier(AuraType.ModCritPct); + + SetBaseModFlatValue(modGroup, amount); + } + + public void UpdateAllWeaponDependentCritAuras() + { + for (var attackType = WeaponAttackType.BaseAttack; attackType < WeaponAttackType.Max; ++attackType) + UpdateWeaponDependentCritAuras(attackType); + } + + public void UpdateWeaponDependentAuras(WeaponAttackType attackType) + { + UpdateWeaponDependentCritAuras(attackType); + UpdateDamageDoneMods(attackType); + UpdateDamagePctDoneMods(attackType); + } + public void ApplyItemDependentAuras(Item item, bool apply) { if (apply) @@ -1851,6 +1875,19 @@ namespace Game.Entities RemoveItemDependentAurasAndCasts(item); } + public override bool CheckAttackFitToAuraRequirement(WeaponAttackType attackType, AuraEffect aurEff) + { + SpellInfo spellInfo = aurEff.GetSpellInfo(); + if (spellInfo.EquippedItemClass == ItemClass.None) + return true; + + Item item = GetWeaponForAttack(attackType, true); + if (item == null || !item.IsFitToSpellRequirements(spellInfo)) + return false; + + return true; + } + public void AddTemporarySpell(uint spellId) { var spell = m_spells.LookupByKey(spellId); diff --git a/Source/Game/Entities/Player/Player.cs b/Source/Game/Entities/Player/Player.cs index 6d3abe9b4..01d0b8928 100644 --- a/Source/Game/Entities/Player/Player.cs +++ b/Source/Game/Entities/Player/Player.cs @@ -83,7 +83,10 @@ namespace Game.Entities _specializationInfo = new SpecializationInfo(); for (byte i = 0; i < (byte)BaseModGroup.End; ++i) - m_auraBaseMod[i] = new float[] { 0.0f, 1.0f }; + { + m_auraBaseFlatMod[i] = 0.0f; + m_auraBasePctMod[i] = 1.0f; + } for (var i = 0; i < (int)SpellModOp.Max; ++i) { @@ -3791,7 +3794,7 @@ namespace Game.Entities if (powerType == null) return; - float addvalue = 0.0f; + float addvalue; if (!IsInCombat()) { @@ -4695,7 +4698,7 @@ namespace Game.Entities SpawnCorpseBones(); } - WorldSafeLocsEntry ClosestGrave = null; + WorldSafeLocsEntry ClosestGrave; // Special handle for Battlegroundmaps Battleground bg = GetBattleground(); @@ -5333,8 +5336,7 @@ namespace Game.Entities PlayerLevelInfo info = Global.ObjectMgr.GetPlayerLevelInfo(GetRace(), GetClass(), level); - uint basemana = 0; - Global.ObjectMgr.GetPlayerClassLevelInfo(GetClass(), level, out basemana); + Global.ObjectMgr.GetPlayerClassLevelInfo(GetClass(), level, out uint basemana); LevelUpInfo packet = new LevelUpInfo(); packet.Level = level; @@ -6567,7 +6569,7 @@ namespace Game.Entities if (level >= WorldConfig.GetIntValue(WorldCfg.MaxPlayerLevel)) return; - uint bonus_xp = 0; + uint bonus_xp; bool recruitAFriend = GetsRecruitAFriendBonus(true); // RaF does NOT stack with rested experience @@ -6603,36 +6605,104 @@ namespace Game.Entities SetXP(newXP); } - public void HandleBaseModValue(BaseModGroup modGroup, BaseModType modType, float amount, bool apply) + public void HandleBaseModFlatValue(BaseModGroup modGroup, float amount, bool apply) { - if (modGroup >= BaseModGroup.End || modType >= BaseModType.End) + if (modGroup >= BaseModGroup.End) { - Log.outError(LogFilter.Spells, "ERROR in HandleBaseModValue(): non existed BaseModGroup of wrong BaseModType!"); + Log.outError(LogFilter.Spells, $"Player.HandleBaseModFlatValue: Invalid BaseModGroup ({modGroup}) for player '{GetName()}' ({GetGUID()})"); + return; + } + m_auraBaseFlatMod[(int)modGroup] += apply ? amount : -amount; + UpdateBaseModGroup(modGroup); + } + + public void ApplyBaseModPctValue(BaseModGroup modGroup, float pct) + { + if (modGroup >= BaseModGroup.End) + { + Log.outError(LogFilter.Spells, $"Player.ApplyBaseModPctValue: Invalid BaseModGroup/BaseModType ({modGroup}/{BaseModType.FlatMod}) for player '{GetName()}' ({GetGUID()})"); return; } - switch (modType) + MathFunctions.AddPct(ref m_auraBasePctMod[(int)modGroup], pct); + UpdateBaseModGroup(modGroup); + } + + public void SetBaseModFlatValue(BaseModGroup modGroup, float val) + { + if (m_auraBaseFlatMod[(int)modGroup] == val) + return; + + m_auraBaseFlatMod[(int)modGroup] = val; + UpdateBaseModGroup(modGroup); + } + + public void SetBaseModPctValue(BaseModGroup modGroup, float val) + { + if (m_auraBasePctMod[(int)modGroup] == val) + return; + + m_auraBasePctMod[(int)modGroup] = val; + UpdateBaseModGroup(modGroup); + } + + public override void UpdateDamageDoneMods(WeaponAttackType attackType) + { + base.UpdateDamageDoneMods(attackType); + + UnitMods unitMod = attackType switch { - case BaseModType.FlatMod: - m_auraBaseMod[(int)modGroup][(int)modType] += apply ? amount : -amount; - break; - case BaseModType.PCTmod: - MathFunctions.ApplyPercentModFloatVar(ref m_auraBaseMod[(int)modGroup][(int)modType], amount, apply); - break; + WeaponAttackType.BaseAttack => UnitMods.DamageMainHand, + WeaponAttackType.OffAttack => UnitMods.DamageOffHand, + WeaponAttackType.RangedAttack => UnitMods.DamageRanged, + _ => throw new NotImplementedException(), + }; + + float amount = 0.0f; + Item item = GetWeaponForAttack(attackType, true); + if (item == null) + return; + + for (var slot = EnchantmentSlot.Perm; slot < EnchantmentSlot.Max; ++slot) + { + SpellItemEnchantmentRecord enchantmentEntry = CliDB.SpellItemEnchantmentStorage.LookupByKey(item.GetEnchantmentId(slot)); + if (enchantmentEntry == null) + continue; + + for (byte i = 0; i < ItemConst.MaxItemEnchantmentEffects; ++i) + { + switch (enchantmentEntry.Effect[i]) + { + case ItemEnchantmentType.Damage: + amount += enchantmentEntry.EffectScalingPoints[i]; + break; + case ItemEnchantmentType.Totem: + if (GetClass() == Class.Shaman) + amount += enchantmentEntry.EffectScalingPoints[i] * item.GetTemplate().GetDelay() / 1000.0f; + break; + default: + break; + } + } } + HandleStatFlatModifier(unitMod, UnitModifierFlatType.Total, amount, true); + } + + void UpdateBaseModGroup(BaseModGroup modGroup) + { if (!CanModifyStats()) return; switch (modGroup) { - case BaseModGroup.CritPercentage: - UpdateCritPercentage(WeaponAttackType.BaseAttack); + case BaseModGroup.CritPercentage: + UpdateCritPercentage(WeaponAttackType.BaseAttack); break; - case BaseModGroup.RangedCritPercentage: - UpdateCritPercentage(WeaponAttackType.RangedAttack); + case BaseModGroup.RangedCritPercentage: + UpdateCritPercentage(WeaponAttackType.RangedAttack); break; - case BaseModGroup.OffhandCritPercentage: + case BaseModGroup.OffhandCritPercentage: UpdateCritPercentage(WeaponAttackType.OffAttack); break; default: @@ -6640,6 +6710,28 @@ namespace Game.Entities } } + float GetBaseModValue(BaseModGroup modGroup, BaseModType modType) + { + if (modGroup >= BaseModGroup.End || modType >= BaseModType.End) + { + Log.outError(LogFilter.Spells, $"Player.GetBaseModValue: Invalid BaseModGroup/BaseModType ({modGroup}/{modType}) for player '{GetName()}' ({GetGUID()})"); + return 0.0f; + } + + return (modType == BaseModType.FlatMod ? m_auraBaseFlatMod[(int)modGroup] : m_auraBasePctMod[(int)modGroup]); + } + + float GetTotalBaseModValue(BaseModGroup modGroup) + { + if (modGroup >= BaseModGroup.End) + { + Log.outError(LogFilter.Spells, $"Player.GetTotalBaseModValue: Invalid BaseModGroup ({modGroup}) for player '{GetName()}' ({GetGUID()})"); + return 0.0f; + } + + return m_auraBaseFlatMod[(int)modGroup] * m_auraBasePctMod[(int)modGroup]; + } + public void AddComboPoints(sbyte count, Spell spell = null) { if (count == 0) @@ -6898,7 +6990,7 @@ namespace Game.Entities if (WorldConfig.GetBoolValue(WorldCfg.InstantTaxi)) { - var lastPathNode = CliDB.TaxiNodesStorage.LookupByKey(nodes[nodes.Count - 1]); + var lastPathNode = CliDB.TaxiNodesStorage.LookupByKey(nodes[^1]); m_taxi.ClearTaxiDestinations(); ModifyMoney(-totalcost); UpdateCriteria(CriteriaTypes.GoldSpentForTravelling, totalcost); @@ -6956,7 +7048,7 @@ namespace Game.Entities var nodeList = CliDB.TaxiPathNodesByPath[path]; - float distPrev = MapConst.MapSize * MapConst.MapSize; + float distPrev; float distNext = GetExactDistSq(nodeList[0].Loc.X, nodeList[0].Loc.Y, nodeList[0].Loc.Z); for (int i = 1; i < nodeList.Length; ++i) @@ -7277,7 +7369,7 @@ namespace Game.Entities return null; } - Item item = null; + Item item; if (useable) item = GetUseableItemByPos(InventorySlots.Bag0, slot); else diff --git a/Source/Game/Entities/StatSystem.cs b/Source/Game/Entities/StatSystem.cs index 6550b5788..74ef47157 100644 --- a/Source/Game/Entities/StatSystem.cs +++ b/Source/Game/Entities/StatSystem.cs @@ -27,35 +27,99 @@ namespace Game.Entities { public partial class Unit { - public bool HandleStatModifier(UnitMods unitMod, UnitModifierType modifierType, int amount, bool apply) + public void HandleStatFlatModifier(UnitMods unitMod, UnitModifierFlatType modifierType, float amount, bool apply) { - return HandleStatModifier(unitMod, modifierType, (float)amount, apply); - } - public bool HandleStatModifier(UnitMods unitMod, UnitModifierType modifierType, float amount, bool apply) - { - if (unitMod >= UnitMods.End || modifierType >= UnitModifierType.End) + if (unitMod >= UnitMods.End || modifierType >= UnitModifierFlatType.End) { - Log.outError(LogFilter.Unit, "ERROR in HandleStatModifier(): non-existing UnitMods or wrong UnitModifierType!"); - return false; + Log.outError(LogFilter.Unit, "ERROR in HandleStatFlatModifier(): non-existing UnitMods or wrong UnitModifierFlatType!"); + return; } + if (amount == 0) + return; + switch (modifierType) { - case UnitModifierType.BaseValue: - case UnitModifierType.BasePCTExcludeCreate: - case UnitModifierType.TotalValue: - m_auraModifiersGroup[(int)unitMod][(int)modifierType] += apply ? amount : -amount; - break; - case UnitModifierType.BasePCT: - case UnitModifierType.TotalPCT: - MathFunctions.ApplyPercentModFloatVar(ref m_auraModifiersGroup[(int)unitMod][(int)modifierType], amount, apply); + case UnitModifierFlatType.Base: + case UnitModifierFlatType.BasePCTExcludeCreate: + case UnitModifierFlatType.Total: + m_auraFlatModifiersGroup[(int)unitMod][(int)modifierType] += apply ? amount : -amount; break; default: break; } + UpdateUnitMod(unitMod); + } + + public void ApplyStatPctModifier(UnitMods unitMod, UnitModifierPctType modifierType, float pct) + { + if (unitMod >= UnitMods.End || modifierType >= UnitModifierPctType.End) + { + Log.outError(LogFilter.Unit, "ERROR in ApplyStatPctModifier(): non-existing UnitMods or wrong UnitModifierPctType!"); + return; + } + + if (pct == 0) + return; + + switch (modifierType) + { + case UnitModifierPctType.Base: + case UnitModifierPctType.Total: + MathFunctions.AddPct(ref m_auraPctModifiersGroup[(int)unitMod][(int)modifierType], pct); + break; + default: + break; + } + + UpdateUnitMod(unitMod); + } + + public void SetStatFlatModifier(UnitMods unitMod, UnitModifierFlatType modifierType, float val) + { + if (m_auraFlatModifiersGroup[(int)unitMod][(int)modifierType] == val) + return; + + m_auraFlatModifiersGroup[(int)unitMod][(int)modifierType] = val; + UpdateUnitMod(unitMod); + } + + public void SetStatPctModifier(UnitMods unitMod, UnitModifierPctType modifierType, float val) + { + if (m_auraPctModifiersGroup[(int)unitMod][(int)modifierType] == val) + return; + + m_auraPctModifiersGroup[(int)unitMod][(int)modifierType] = val; + UpdateUnitMod(unitMod); + } + + public float GetFlatModifierValue(UnitMods unitMod, UnitModifierFlatType modifierType) + { + if (unitMod >= UnitMods.End || modifierType >= UnitModifierFlatType.End) + { + Log.outError(LogFilter.Unit, "attempt to access non-existing modifier value from UnitMods!"); + return 0.0f; + } + + return m_auraFlatModifiersGroup[(int)unitMod][(int)modifierType]; + } + + public float GetPctModifierValue(UnitMods unitMod, UnitModifierPctType modifierType) + { + if (unitMod >= UnitMods.End || modifierType >= UnitModifierPctType.End) + { + Log.outError(LogFilter.Unit, "attempt to access non-existing modifier value from UnitMods!"); + return 0.0f; + } + + return m_auraPctModifiersGroup[(int)unitMod][(int)modifierType]; + } + + void UpdateUnitMod(UnitMods unitMod) + { if (!CanModifyStats()) - return false; + return; switch (unitMod) { @@ -118,8 +182,6 @@ namespace Game.Entities default: break; } - - return true; } int GetMinPower(PowerType power) { return power == PowerType.LunarPower ? -100 : 0; } @@ -181,16 +243,71 @@ namespace Game.Entities return stat; } - public void ApplyStatBuffMod(Stats stat, float val, bool apply) + public void UpdateStatBuffMod(Stats stat) { - MathFunctions.ApplyPercentModFloatVar(ref (val > 0 ? ref m_floatStatPosBuff[(int)stat] : ref m_floatStatNegBuff[(int)stat]), val, apply); + float modPos = 0.0f; + float modNeg = 0.0f; + float factor = 0.0f; + + UnitMods unitMod = UnitMods.StatStart + (int)stat; + + // includes value from items and enchantments + float modValue = GetFlatModifierValue(unitMod, UnitModifierFlatType.Base); + if (modValue > 0.0f) + modPos += modValue; + else + modNeg += modValue; + + if (IsGuardian()) + { + modValue = ((Guardian)this).GetBonusStatFromOwner(stat); + if (modValue > 0.0f) + modPos += modValue; + else + modNeg += modValue; + } + + // SPELL_AURA_MOD_STAT_BONUS_PCT only affects BASE_VALUE + modPos = MathFunctions.CalculatePct(modPos, Math.Max(GetFlatModifierValue(unitMod, UnitModifierFlatType.BasePCTExcludeCreate), -100.0f)); + modNeg = MathFunctions.CalculatePct(modPos, Math.Max(GetFlatModifierValue(unitMod, UnitModifierFlatType.BasePCTExcludeCreate), -100.0f)); + + modPos += GetTotalAuraModifier(AuraType.ModStat, aurEff => + { + if ((aurEff.GetMiscValue() < 0 || aurEff.GetMiscValue() == (int)stat) && aurEff.GetAmount() > 0) + return true; + return false; + }); + + modNeg += GetTotalAuraModifier(AuraType.ModStat, aurEff => + { + if ((aurEff.GetMiscValue() < 0 || aurEff.GetMiscValue() == (int)stat) && aurEff.GetAmount() < 0) + return true; + return false; + }); + + factor = GetTotalAuraMultiplier(AuraType.ModPercentStat, aurEff => + { + if (aurEff.GetMiscValue() == -1 || aurEff.GetMiscValue() == (int)stat) + return true; + return false; + }); + + factor *= GetTotalAuraMultiplier(AuraType.ModTotalStatPercentage, aurEff => + { + if (aurEff.GetMiscValue() == -1 || aurEff.GetMiscValue() == (int)stat) + return true; + return false; + }); + + modPos *= factor; + modNeg *= factor; + + m_floatStatPosBuff[(int)stat] = modPos; + m_floatStatNegBuff[(int)stat] = modNeg; + UpdateStatBuffModForClient(stat); } - public void ApplyStatPercentBuffMod(Stats stat, float val, bool apply) - { - MathFunctions.ApplyPercentModFloatVar(ref m_floatStatPosBuff[(int)stat], val, apply); - MathFunctions.ApplyPercentModFloatVar(ref m_floatStatNegBuff[(int)stat], val, apply); - } + void UpdateStatBuffModForClient(Stats stat) { SetUpdateFieldValue(ref m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.StatPosBuff, (int)stat), (int)m_floatStatPosBuff[(int)stat]); @@ -204,7 +321,7 @@ namespace Game.Entities if (school > SpellSchools.Normal) { UnitMods unitMod = UnitMods.ResistanceStart + (int)school; - SetResistance(school, (int)m_auraModifiersGroup[(int)unitMod][(int)UnitModifierType.BaseValue]); + SetResistance(school, (int)GetFlatModifierValue(unitMod, UnitModifierFlatType.Base)); SetBonusResistanceMod(school, (int)(GetTotalAuraModValue(unitMod) - GetResistance(school))); } else @@ -216,10 +333,7 @@ namespace Game.Entities public virtual void UpdateAttackPowerAndDamage(bool ranged = false) { } public virtual void UpdateDamagePhysical(WeaponAttackType attType) { - float minDamage = 0.0f; - float maxDamage = 0.0f; - - CalculateMinMaxDamage(attType, false, true, out minDamage, out maxDamage); + CalculateMinMaxDamage(attType, false, true, out float minDamage, out float maxDamage); switch (attType) { @@ -326,14 +440,11 @@ namespace Game.Entities { UnitMods unitMod = UnitMods.StatStart + (int)stat; - if (m_auraModifiersGroup[(int)unitMod][(int)UnitModifierType.TotalPCT] <= 0.0f) - return 0.0f; - - float value = MathFunctions.CalculatePct(m_auraModifiersGroup[(int)unitMod][(int)UnitModifierType.BaseValue], Math.Max(m_auraModifiersGroup[(int)unitMod][(int)UnitModifierType.BasePCTExcludeCreate], -100.0f)); + float value = MathFunctions.CalculatePct(GetFlatModifierValue(unitMod, UnitModifierFlatType.Base), Math.Max(GetFlatModifierValue(unitMod, UnitModifierFlatType.BasePCTExcludeCreate), -100.0f)); value += GetCreateStat(stat); - value *= m_auraModifiersGroup[(int)unitMod][(int)UnitModifierType.BasePCT]; - value += m_auraModifiersGroup[(int)unitMod][(int)UnitModifierType.TotalValue]; - value *= m_auraModifiersGroup[(int)unitMod][(int)UnitModifierType.TotalPCT]; + value *= GetPctModifierValue(unitMod, UnitModifierPctType.Base); + value += GetFlatModifierValue(unitMod, UnitModifierFlatType.Total); + value *= GetPctModifierValue(unitMod, UnitModifierPctType.Total); return value; } @@ -940,6 +1051,7 @@ namespace Game.Entities public void ApplyModDamageDonePos(SpellSchools school, int mod, bool apply) { ApplyModUpdateFieldValue(ref m_values.ModifyValue(m_activePlayerData).ModifyValue(m_activePlayerData.ModDamageDonePos, (int)school), mod, apply); } public void ApplyModDamageDoneNeg(SpellSchools school, int mod, bool apply) { ApplyModUpdateFieldValue(ref m_values.ModifyValue(m_activePlayerData).ModifyValue(m_activePlayerData.ModDamageDoneNeg, (int)school), mod, apply); } public void ApplyModDamageDonePercent(SpellSchools school, float pct, bool apply) { ApplyPercentModUpdateFieldValue(ref m_values.ModifyValue(m_activePlayerData).ModifyValue(m_activePlayerData.ModDamageDonePercent, (int)school), pct, apply); } + public void SetModDamageDonePercent(SpellSchools school, float pct) { SetUpdateFieldValue(ref m_values.ModifyValue(m_activePlayerData).ModifyValue(m_activePlayerData.ModDamageDonePercent, (int)school), pct); } public void ApplyRatingMod(CombatRating combatRating, int value, bool apply) { @@ -968,10 +1080,10 @@ namespace Game.Entities float attackPowerMod = Math.Max(GetAPMultiplier(attType, normalized), 0.25f); - float baseValue = GetModifierValue(unitMod, UnitModifierType.BaseValue) + GetTotalAttackPowerValue(attType) / 3.5f * attackPowerMod; - float basePct = GetModifierValue(unitMod, UnitModifierType.BasePCT); - float totalValue = GetModifierValue(unitMod, UnitModifierType.TotalValue); - float totalPct = addTotalPct ? GetModifierValue(unitMod, UnitModifierType.TotalPCT) : 1.0f; + float baseValue = GetFlatModifierValue(unitMod, UnitModifierFlatType.Base) + GetTotalAttackPowerValue(attType) / 3.5f * attackPowerMod; + float basePct = GetPctModifierValue(unitMod, UnitModifierPctType.Base); + float totalValue = GetFlatModifierValue(unitMod, UnitModifierFlatType.Total); + float totalPct = addTotalPct ? GetPctModifierValue(unitMod, UnitModifierPctType.Total) : 1.0f; float weaponMinDamage = GetWeaponDamageRange(attType, WeaponDamageRange.MinDamage); float weaponMaxDamage = GetWeaponDamageRange(attType, WeaponDamageRange.MaxDamage); @@ -1007,9 +1119,9 @@ namespace Game.Entities { float value = 5.0f; - SetBaseModValue(BaseModGroup.CritPercentage, BaseModType.PCTmod, value); - SetBaseModValue(BaseModGroup.OffhandCritPercentage, BaseModType.PCTmod, value); - SetBaseModValue(BaseModGroup.RangedCritPercentage, BaseModType.PCTmod, value); + SetBaseModPctValue(BaseModGroup.CritPercentage, value); + SetBaseModPctValue(BaseModGroup.OffhandCritPercentage, value); + SetBaseModPctValue(BaseModGroup.RangedCritPercentage, value); UpdateCritPercentage(WeaponAttackType.BaseAttack); UpdateCritPercentage(WeaponAttackType.OffAttack); @@ -1101,11 +1213,11 @@ namespace Game.Entities val2 = MathFunctions.CalculatePct(minSpellPower, m_activePlayerData.OverrideAPBySpellPowerPercent); } - SetModifierValue(unitMod, UnitModifierType.BaseValue, val2); + SetStatFlatModifier(unitMod, UnitModifierFlatType.Base, val2); - float base_attPower = GetModifierValue(unitMod, UnitModifierType.BaseValue) * GetModifierValue(unitMod, UnitModifierType.BasePCT); - float attPowerMod = GetModifierValue(unitMod, UnitModifierType.TotalValue); - float attPowerMultiplier = GetModifierValue(unitMod, UnitModifierType.TotalPCT) - 1.0f; + float base_attPower = GetFlatModifierValue(unitMod, UnitModifierFlatType.Base) * GetPctModifierValue(unitMod, UnitModifierPctType.Base); + float attPowerMod = GetFlatModifierValue(unitMod, UnitModifierFlatType.Total); + float attPowerMultiplier = GetPctModifierValue(unitMod, UnitModifierPctType.Total) - 1.0f; if (ranged) { @@ -1154,10 +1266,10 @@ namespace Game.Entities { UnitMods unitMod = UnitMods.Armor; - float value = GetModifierValue(unitMod, UnitModifierType.BaseValue); // base armor (from items) + float value = GetFlatModifierValue(unitMod, UnitModifierFlatType.Base); // base armor (from items) float baseValue = value; - value *= GetModifierValue(unitMod, UnitModifierType.BasePCT); // armor percent from items - value += GetModifierValue(unitMod, UnitModifierType.TotalValue); + value *= GetPctModifierValue(unitMod, UnitModifierPctType.Base); // armor percent from items + value += GetFlatModifierValue(unitMod, UnitModifierFlatType.Total); //add dynamic flat mods var mResbyIntellect = GetAuraEffectsByType(AuraType.ModResistanceOfStatPercent); @@ -1167,7 +1279,7 @@ namespace Game.Entities value += MathFunctions.CalculatePct(GetStat((Stats)i.GetMiscValueB()), i.GetAmount()); } - value *= GetModifierValue(unitMod, UnitModifierType.TotalPCT); + value *= GetPctModifierValue(unitMod, UnitModifierPctType.Total); SetArmor((int)baseValue, (int)(value - baseValue)); @@ -1477,7 +1589,7 @@ namespace Game.Entities public void UpdateCritPercentage(WeaponAttackType attType) { - float applyCritLimit(float value) + static float applyCritLimit(float value) { if (WorldConfig.GetBoolValue(WorldCfg.StatsLimitsEnable)) value = value > WorldConfig.GetFloatValue(WorldCfg.StatsLimitsCrit) ? WorldConfig.GetFloatValue(WorldCfg.StatsLimitsCrit) : value; @@ -1488,16 +1600,16 @@ namespace Game.Entities { case WeaponAttackType.OffAttack: SetUpdateFieldStatValue(m_values.ModifyValue(m_activePlayerData).ModifyValue(m_activePlayerData.OffhandCritPercentage), - applyCritLimit(GetTotalPercentageModValue(BaseModGroup.OffhandCritPercentage) + GetRatingBonusValue(CombatRating.CritMelee))); + applyCritLimit(GetBaseModValue(BaseModGroup.OffhandCritPercentage, BaseModType.FlatMod) + GetBaseModValue(BaseModGroup.OffhandCritPercentage, BaseModType.PctMod) + GetRatingBonusValue(CombatRating.CritMelee))); break; case WeaponAttackType.RangedAttack: SetUpdateFieldStatValue(m_values.ModifyValue(m_activePlayerData).ModifyValue(m_activePlayerData.RangedCritPercentage), - applyCritLimit(GetTotalPercentageModValue(BaseModGroup.RangedCritPercentage) + GetRatingBonusValue(CombatRating.CritRanged))); + applyCritLimit(GetBaseModValue(BaseModGroup.RangedCritPercentage, BaseModType.FlatMod) + GetBaseModValue(BaseModGroup.RangedCritPercentage, BaseModType.PctMod) + GetRatingBonusValue(CombatRating.CritRanged))); break; case WeaponAttackType.BaseAttack: default: SetUpdateFieldStatValue(m_values.ModifyValue(m_activePlayerData).ModifyValue(m_activePlayerData.CritPercentage), - applyCritLimit(GetTotalPercentageModValue(BaseModGroup.CritPercentage) + GetRatingBonusValue(CombatRating.CritMelee))); + applyCritLimit(GetBaseModValue(BaseModGroup.CritPercentage, BaseModType.FlatMod) + GetBaseModValue(BaseModGroup.CritPercentage, BaseModType.PctMod) + GetRatingBonusValue(CombatRating.CritMelee))); break; } } @@ -1638,10 +1750,10 @@ namespace Game.Entities { UnitMods unitMod = UnitMods.Health; - float value = GetModifierValue(unitMod, UnitModifierType.BaseValue) + GetCreateHealth(); - value *= GetModifierValue(unitMod, UnitModifierType.BasePCT); - value += GetModifierValue(unitMod, UnitModifierType.TotalValue) + GetHealthBonusFromStamina(); - value *= GetModifierValue(unitMod, UnitModifierType.TotalPCT); + float value = GetFlatModifierValue(unitMod, UnitModifierFlatType.Base) + GetCreateHealth(); + value *= GetPctModifierValue(unitMod, UnitModifierPctType.Base); + value += GetFlatModifierValue(unitMod, UnitModifierFlatType.Total) + GetHealthBonusFromStamina(); + value *= GetPctModifierValue(unitMod, UnitModifierPctType.Total); SetMaxHealth((uint)value); } @@ -1669,10 +1781,10 @@ namespace Game.Entities UnitMods unitMod = UnitMods.PowerStart + (int)power; - float value = GetModifierValue(unitMod, UnitModifierType.BaseValue) + GetCreatePowers(power); - value *= GetModifierValue(unitMod, UnitModifierType.BasePCT); - value += GetModifierValue(unitMod, UnitModifierType.TotalValue); - value *= GetModifierValue(unitMod, UnitModifierType.TotalPCT); + float value = GetFlatModifierValue(unitMod, UnitModifierFlatType.Base) + GetCreatePowers(power); + value *= GetPctModifierValue(unitMod, UnitModifierPctType.Base); + value += GetFlatModifierValue(unitMod, UnitModifierFlatType.Total); + value *= GetPctModifierValue(unitMod, UnitModifierPctType.Total); SetMaxPower(power, (int)Math.Round(value)); } @@ -1748,8 +1860,6 @@ namespace Game.Entities 0.9720f, // Druid 0.9830f // Demon Hunter }; - - void SetBaseModValue(BaseModGroup modGroup, BaseModType modType, float value) { m_auraBaseMod[(int)modGroup][(int)modType] = value; } } public partial class Creature @@ -1775,7 +1885,7 @@ namespace Game.Entities public override void UpdateArmor() { - float baseValue = GetModifierValue(UnitMods.Armor, UnitModifierType.BaseValue); + float baseValue = GetFlatModifierValue(UnitMods.Armor, UnitModifierFlatType.Base); float value = GetTotalAuraModValue(UnitMods.Armor); SetArmor((int)baseValue, (int)(value - baseValue)); } @@ -1805,10 +1915,10 @@ namespace Game.Entities UnitMods unitMod = UnitMods.PowerStart + (int)power; - float value = GetModifierValue(unitMod, UnitModifierType.BaseValue) + GetCreatePowers(power); - value *= GetModifierValue(unitMod, UnitModifierType.BasePCT); - value += GetModifierValue(unitMod, UnitModifierType.TotalValue); - value *= GetModifierValue(unitMod, UnitModifierType.TotalPCT); + float value = GetFlatModifierValue(unitMod, UnitModifierFlatType.Base) + GetCreatePowers(power); + value *= GetPctModifierValue(unitMod, UnitModifierPctType.Base); + value += GetFlatModifierValue(unitMod, UnitModifierFlatType.Total); + value *= GetPctModifierValue(unitMod, UnitModifierPctType.Total); SetMaxPower(power, (int)Math.Round(value)); } @@ -1817,8 +1927,8 @@ namespace Game.Entities { UnitMods unitMod = ranged ? UnitMods.AttackPowerRanged : UnitMods.AttackPower; - float baseAttackPower = GetModifierValue(unitMod, UnitModifierType.BaseValue) * GetModifierValue(unitMod, UnitModifierType.BasePCT); - float attackPowerMultiplier = GetModifierValue(unitMod, UnitModifierType.TotalPCT) - 1.0f; + float baseAttackPower = GetFlatModifierValue(unitMod, UnitModifierFlatType.Base) * GetPctModifierValue(unitMod, UnitModifierPctType.Base); + float attackPowerMultiplier = GetPctModifierValue(unitMod, UnitModifierPctType.Total) - 1.0f; if (ranged) { @@ -1881,10 +1991,10 @@ namespace Game.Entities float attackPower = GetTotalAttackPowerValue(attType); float attackSpeedMulti = Math.Max(GetAPMultiplier(attType, normalized), 0.25f); - float baseValue = GetModifierValue(unitMod, UnitModifierType.BaseValue) + (attackPower / 3.5f) * variance; - float basePct = GetModifierValue(unitMod, UnitModifierType.BasePCT) * attackSpeedMulti; - float totalValue = GetModifierValue(unitMod, UnitModifierType.TotalValue); - float totalPct = addTotalPct ? GetModifierValue(unitMod, UnitModifierType.TotalPCT) : 1.0f; + float baseValue = GetFlatModifierValue(unitMod, UnitModifierFlatType.Base) + (attackPower / 3.5f) * variance; + float basePct = GetPctModifierValue(unitMod, UnitModifierPctType.Base) * attackSpeedMulti; + float totalValue = GetFlatModifierValue(unitMod, UnitModifierFlatType.Total); + float totalPct = addTotalPct ? GetPctModifierValue(unitMod, UnitModifierPctType.Total) : 1.0f; float dmgMultiplier = GetCreatureTemplate().ModDamage; // = ModDamage * _GetDamageMod(rank); minDamage = ((weaponMinDamage + baseValue) * dmgMultiplier * basePct + totalValue) * totalPct; diff --git a/Source/Game/Entities/TemporarySummon.cs b/Source/Game/Entities/TemporarySummon.cs index efb0c3e81..a01b50e79 100644 --- a/Source/Game/Entities/TemporarySummon.cs +++ b/Source/Game/Entities/TemporarySummon.cs @@ -441,7 +441,7 @@ namespace Game.Entities SetMeleeDamageSchool((SpellSchools)cinfo.DmgSchool); - SetModifierValue(UnitMods.Armor, UnitModifierType.BaseValue, (float)petlevel * 50); + SetStatFlatModifier(UnitMods.Armor, UnitModifierFlatType.Base, (float)petlevel * 50); SetBaseAttackTime(WeaponAttackType.BaseAttack, SharedConst.BaseAttackTime); SetBaseAttackTime(WeaponAttackType.OffAttack, SharedConst.BaseAttackTime); @@ -467,7 +467,7 @@ namespace Game.Entities if (!IsHunterPet()) { for (int i = (int)SpellSchools.Holy; i < (int)SpellSchools.Max; ++i) - SetModifierValue(UnitMods.ResistanceStart + i, UnitModifierType.BaseValue, cinfo.Resistance[i]); + SetStatFlatModifier(UnitMods.ResistanceStart + i, UnitModifierFlatType.Base, cinfo.Resistance[i]); } // Health, Mana or Power, Armor @@ -478,7 +478,7 @@ namespace Game.Entities SetCreateMana(pInfo.mana); if (pInfo.armor > 0) - SetModifierValue(UnitMods.Armor, UnitModifierType.BaseValue, pInfo.armor); + SetStatFlatModifier(UnitMods.Armor, UnitModifierFlatType.Base, pInfo.armor); for (byte stat = 0; stat < (int)Stats.Max; ++stat) SetCreateStat((Stats)stat, pInfo.stats[stat]); @@ -584,7 +584,6 @@ namespace Game.Entities int bonus_dmg = (int)(GetOwner().SpellBaseDamageBonusDone(SpellSchoolMask.Shadow) * 0.3f); SetBaseWeaponDamage(WeaponAttackType.BaseAttack, WeaponDamageRange.MinDamage, (petlevel * 4 - petlevel) + bonus_dmg); SetBaseWeaponDamage(WeaponAttackType.BaseAttack, WeaponDamageRange.MaxDamage, (petlevel * 4 + petlevel) + bonus_dmg); - break; } case 19833: //Snake Trap - Venomous Snake @@ -610,8 +609,8 @@ namespace Game.Entities SetBaseWeaponDamage(WeaponAttackType.BaseAttack, WeaponDamageRange.MinDamage, (petlevel * 4 - petlevel)); SetBaseWeaponDamage(WeaponAttackType.BaseAttack, WeaponDamageRange.MaxDamage, (petlevel * 4 + petlevel)); - SetModifierValue(UnitMods.Armor, UnitModifierType.BaseValue, GetOwner().GetArmor() * 0.35f); // Bonus Armor (35% of player armor) - SetModifierValue(UnitMods.StatStamina, UnitModifierType.BaseValue, GetOwner().GetStat(Stats.Stamina) * 0.3f); // Bonus Stamina (30% of player stamina) + SetStatFlatModifier(UnitMods.Armor, UnitModifierFlatType.Base, GetOwner().GetArmor() * 0.35f); // Bonus Armor (35% of player armor) + SetStatFlatModifier(UnitMods.StatStamina, UnitModifierFlatType.Base, GetOwner().GetStat(Stats.Stamina) * 0.3f); // Bonus Stamina (30% of player stamina) if (!HasAura(58877))//prevent apply twice for the 2 wolves AddAura(58877, this);//Spirit Hunt, passive, Spirit Wolves' attacks heal them and their master for 150% of damage done. break; @@ -673,7 +672,7 @@ namespace Game.Entities public override bool UpdateStats(Stats stat) { float value = GetTotalStatValue(stat); - ApplyStatBuffMod(stat, m_statFromOwner[(int)stat], false); + UpdateStatBuffMod(stat); float ownersBonus = 0.0f; Unit owner = GetOwner(); @@ -712,7 +711,7 @@ namespace Game.Entities SetStat(stat, (int)value); m_statFromOwner[(int)stat] = ownersBonus; - ApplyStatBuffMod(stat, m_statFromOwner[(int)stat], true); + UpdateStatBuffMod(stat); switch (stat) { @@ -754,7 +753,7 @@ namespace Game.Entities { if (school > SpellSchools.Normal) { - float baseValue = GetModifierValue( UnitMods.ResistanceStart + (int)school, UnitModifierType.BaseValue); + float baseValue = GetFlatModifierValue(UnitMods.ResistanceStart + (int)school, UnitModifierFlatType.Base); float bonusValue = GetTotalAuraModValue(UnitMods.ResistanceStart + (int)school) - baseValue; // hunter and warlock pets gain 40% of owner's resistance @@ -773,8 +772,6 @@ namespace Game.Entities public override void UpdateArmor() { - float baseValue = 0.0f; - float value = 0.0f; float bonus_armor = 0.0f; UnitMods unitMod = UnitMods.Armor; @@ -784,11 +781,11 @@ namespace Game.Entities else if (IsPet()) bonus_armor = GetOwner().GetArmor(); - value = GetModifierValue(unitMod, UnitModifierType.BaseValue); - baseValue = value; - value *= GetModifierValue(unitMod, UnitModifierType.BasePCT); - value += GetModifierValue(unitMod, UnitModifierType.TotalValue) + bonus_armor; - value *= GetModifierValue(unitMod, UnitModifierType.TotalPCT); + float value = GetFlatModifierValue(unitMod, UnitModifierFlatType.Base); + float baseValue = value; + value *= GetPctModifierValue(unitMod, UnitModifierPctType.Base); + value += GetFlatModifierValue(unitMod, UnitModifierFlatType.Total) + bonus_armor; + value *= GetPctModifierValue(unitMod, UnitModifierPctType.Total); SetArmor((int)baseValue, (int)(value - baseValue)); } @@ -824,10 +821,10 @@ namespace Game.Entities break; } - float value = GetModifierValue(unitMod, UnitModifierType.BaseValue) + GetCreateHealth(); - value *= GetModifierValue(unitMod, UnitModifierType.BasePCT); - value += GetModifierValue(unitMod, UnitModifierType.TotalValue) + stamina * multiplicator; - value *= GetModifierValue(unitMod, UnitModifierType.TotalPCT); + float value = GetFlatModifierValue(unitMod, UnitModifierFlatType.Base) + GetCreateHealth(); + value *= GetPctModifierValue(unitMod, UnitModifierPctType.Base); + value += GetFlatModifierValue(unitMod, UnitModifierFlatType.Total) + stamina * multiplicator; + value *= GetPctModifierValue(unitMod, UnitModifierPctType.Total); SetMaxHealth((uint)value); } @@ -839,10 +836,10 @@ namespace Game.Entities UnitMods unitMod = UnitMods.PowerStart + (int)power; - float value = GetModifierValue(unitMod, UnitModifierType.BaseValue) + GetCreatePowers(power); - value *= GetModifierValue(unitMod, UnitModifierType.BasePCT); - value += GetModifierValue(unitMod, UnitModifierType.TotalValue); - value *= GetModifierValue(unitMod, UnitModifierType.TotalPCT); + float value = GetFlatModifierValue(unitMod, UnitModifierFlatType.Base) + GetCreatePowers(power); + value *= GetPctModifierValue(unitMod, UnitModifierPctType.Base); + value += GetFlatModifierValue(unitMod, UnitModifierFlatType.Total); + value *= GetPctModifierValue(unitMod, UnitModifierPctType.Total); SetMaxPower(power, (int)value); } @@ -902,11 +899,11 @@ namespace Game.Entities } } - SetModifierValue(UnitMods.AttackPower, UnitModifierType.BaseValue, val + bonusAP); + SetStatFlatModifier(UnitMods.AttackPower, UnitModifierFlatType.Base, val + bonusAP); //in BASE_VALUE of UNIT_MOD_ATTACK_POWER for creatures we store data of meleeattackpower field in DB - float base_attPower = GetModifierValue(unitMod, UnitModifierType.BaseValue) * GetModifierValue(unitMod, UnitModifierType.BasePCT); - float attPowerMultiplier = GetModifierValue(unitMod, UnitModifierType.TotalPCT) - 1.0f; + float base_attPower = GetFlatModifierValue(unitMod, UnitModifierFlatType.Base) * GetPctModifierValue(unitMod, UnitModifierPctType.Base); + float attPowerMultiplier = GetPctModifierValue(unitMod, UnitModifierPctType.Total) - 1.0f; SetAttackPower((int)base_attPower); SetAttackPowerMultiplier(attPowerMultiplier); @@ -944,10 +941,10 @@ namespace Game.Entities float att_speed = GetBaseAttackTime(WeaponAttackType.BaseAttack) / 1000.0f; - float base_value = GetModifierValue(unitMod, UnitModifierType.BaseValue) + GetTotalAttackPowerValue(attType) / 3.5f * att_speed + bonusDamage; - float base_pct = GetModifierValue(unitMod, UnitModifierType.BasePCT); - float total_value = GetModifierValue(unitMod, UnitModifierType.TotalValue); - float total_pct = GetModifierValue(unitMod, UnitModifierType.TotalPCT); + float base_value = GetFlatModifierValue(unitMod, UnitModifierFlatType.Base) + GetTotalAttackPowerValue(attType) / 3.5f * att_speed + bonusDamage; + float base_pct = GetPctModifierValue(unitMod, UnitModifierPctType.Base); + float total_value = GetFlatModifierValue(unitMod, UnitModifierFlatType.Total); + float total_pct = GetPctModifierValue(unitMod, UnitModifierPctType.Total); float weapon_mindamage = GetWeaponDamageRange(WeaponAttackType.BaseAttack, WeaponDamageRange.MinDamage); float weapon_maxdamage = GetWeaponDamageRange(WeaponAttackType.BaseAttack, WeaponDamageRange.MaxDamage); @@ -968,6 +965,7 @@ namespace Game.Entities } public int GetBonusDamage() { return m_bonusSpellDamage; } + public float GetBonusStatFromOwner(Stats stat) { return m_statFromOwner[(int)stat]; } int m_bonusSpellDamage; float[] m_statFromOwner = new float[(int)Stats.Max]; diff --git a/Source/Game/Entities/Unit/Unit.Combat.cs b/Source/Game/Entities/Unit/Unit.Combat.cs index bcf841053..c2167ae56 100644 --- a/Source/Game/Entities/Unit/Unit.Combat.cs +++ b/Source/Game/Entities/Unit/Unit.Combat.cs @@ -1351,7 +1351,7 @@ namespace Game.Entities return; if (PvP) - { + { combatTimer = 5000; Player me = ToPlayer(); if (me) @@ -2146,17 +2146,16 @@ namespace Game.Entities return MeleeHitOutcome.Normal; } public uint CalculateDamage(WeaponAttackType attType, bool normalized, bool addTotalPct) - { - float minDamage, maxDamage = 0.0f; + { + float minDamage; + float maxDamage; if (normalized || !addTotalPct) { CalculateMinMaxDamage(attType, normalized, addTotalPct, out minDamage, out maxDamage); if (IsInFeralForm() && attType == WeaponAttackType.BaseAttack) { - float minOffhandDamage = 0.0f; - float maxOffhandDamage = 0.0f; - CalculateMinMaxDamage(WeaponAttackType.OffAttack, normalized, addTotalPct, out minOffhandDamage, out maxOffhandDamage); + CalculateMinMaxDamage(WeaponAttackType.OffAttack, normalized, addTotalPct, out float minOffhandDamage, out float maxOffhandDamage); minDamage += minOffhandDamage; maxDamage += maxOffhandDamage; } @@ -2266,19 +2265,6 @@ namespace Game.Entities return ap * (1.0f + m_unitData.AttackPowerMultiplier); } } - public float GetModifierValue(UnitMods unitMod, UnitModifierType modifierType) - { - if (unitMod >= UnitMods.End || modifierType >= UnitModifierType.End) - { - Log.outError(LogFilter.Unit, "attempt to access non-existing modifier value from UnitMods!"); - return 0.0f; - } - - if (modifierType == UnitModifierType.TotalPCT && m_auraModifiersGroup[(int)unitMod][(int)modifierType] <= 0.0f) - return 0.0f; - - return m_auraModifiersGroup[(int)unitMod][(int)modifierType]; - } public bool IsWithinMeleeRange(Unit obj) { if (!obj || !IsInMap(obj) || !IsInPhase(obj)) @@ -2449,7 +2435,7 @@ namespace Game.Entities uint bossLevel = 83; float bossResistanceConstant = 510.0f; uint level = victim.GetLevelForTarget(this); - float resistanceConstant = 0.0f; + float resistanceConstant; if (level == bossLevel) resistanceConstant = bossResistanceConstant; @@ -2492,7 +2478,7 @@ namespace Game.Entities vSchoolAbsorbCopy.Sort(new AbsorbAuraOrderPred()); // absorb without mana cost - for (var i = 0; i < vSchoolAbsorbCopy.Count; ++i ) + for (var i = 0; i < vSchoolAbsorbCopy.Count; ++i) { var absorbAurEff = vSchoolAbsorbCopy[i]; if (damageInfo.GetDamage() == 0) @@ -2758,7 +2744,7 @@ namespace Game.Entities // no more than 100% MathFunctions.RoundToInterval(ref arpPct, 0.0f, 100.0f); - float maxArmorPen = 0.0f; + float maxArmorPen; if (victim.GetLevelForTarget(attacker) < 60) maxArmorPen = 400 + 85 * victim.GetLevelForTarget(attacker); else @@ -3014,10 +3000,15 @@ namespace Game.Entities return (CastingTime / 3500.0f) * DotFactor; } + void ApplyPercentModFloatVar(ref float var, float val, bool apply) + { + var *= (apply ? (100.0f + val) / 100.0f : 100.0f / (100.0f + val)); + } + public void ApplyAttackTimePercentMod(WeaponAttackType att, float val, bool apply) { float remainingTimePct = m_attackTimer[(int)att] / (m_baseAttackSpeed[(int)att] * m_modAttackSpeedPct[(int)att]); - if (val > 0) + if (val > 0.0f) { MathFunctions.ApplyPercentModFloatVar(ref m_modAttackSpeedPct[(int)att], val, !apply); @@ -3265,5 +3256,58 @@ namespace Game.Entities } return true; } + + public virtual bool CheckAttackFitToAuraRequirement(WeaponAttackType attackType, AuraEffect aurEff) { return true; } + + public virtual void UpdateDamageDoneMods(WeaponAttackType attackType) + { + UnitMods unitMod = attackType switch + { + WeaponAttackType.BaseAttack => UnitMods.DamageMainHand, + WeaponAttackType.OffAttack => UnitMods.DamageOffHand, + WeaponAttackType.RangedAttack => UnitMods.DamageRanged, + _ => throw new NotImplementedException(), + }; + + float amount = GetTotalAuraModifier(AuraType.ModDamageDone, auratype => CheckAttackFitToAuraRequirement(attackType, auratype)); + SetStatFlatModifier(unitMod, UnitModifierFlatType.Total, amount); + } + + public void UpdateAllDamageDoneMods() + { + for (var attackType = WeaponAttackType.BaseAttack; attackType < WeaponAttackType.Max; ++attackType) + UpdateDamageDoneMods(attackType); + } + + public void UpdateDamagePctDoneMods(WeaponAttackType attackType) + { + (UnitMods unitMod, float factor) = attackType switch + { + WeaponAttackType.BaseAttack => (UnitMods.DamageMainHand, 1.0f), + WeaponAttackType.OffAttack => (UnitMods.DamageOffHand, 0.5f), + WeaponAttackType.RangedAttack => (UnitMods.DamageRanged, 1.0f), + _ => throw new NotImplementedException(), + }; + + factor *= GetTotalAuraMultiplier(AuraType.ModDamagePercentDone, aurEff => + { + if (!aurEff.GetMiscValue().HasAnyFlag((int)SpellSchoolMask.Normal)) + return false; + + return CheckAttackFitToAuraRequirement(attackType, aurEff); + }); + + if (attackType == WeaponAttackType.OffAttack) + factor *= GetTotalAuraMultiplier(AuraType.ModOffhandDamagePct, auraEffect => CheckAttackFitToAuraRequirement(attackType, auraEffect)); + + SetStatPctModifier(unitMod, UnitModifierPctType.Total, factor); + } + + public void UpdateAllDamagePctDoneMods() + { + for (var attackType = WeaponAttackType.BaseAttack; attackType < WeaponAttackType.Max; ++attackType) + UpdateDamagePctDoneMods(attackType); + } + } } diff --git a/Source/Game/Entities/Unit/Unit.Fields.cs b/Source/Game/Entities/Unit/Unit.Fields.cs index 111da4ce5..4558579d4 100644 --- a/Source/Game/Entities/Unit/Unit.Fields.cs +++ b/Source/Game/Entities/Unit/Unit.Fields.cs @@ -103,7 +103,8 @@ namespace Game.Entities MultiMap m_appliedAuras = new MultiMap(); MultiMap m_ownedAuras = new MultiMap(); List m_scAuras = new List(); - protected float[][] m_auraModifiersGroup = new float[(int)UnitMods.End][]; + protected float[][] m_auraFlatModifiersGroup = new float[(int)UnitMods.End][]; + protected float[][] m_auraPctModifiersGroup = new float[(int)UnitMods.End][]; uint m_removedAurasCount; //General diff --git a/Source/Game/Entities/Unit/Unit.Spells.cs b/Source/Game/Entities/Unit/Unit.Spells.cs index 04c9ad0a5..26154ccbe 100644 --- a/Source/Game/Entities/Unit/Unit.Spells.cs +++ b/Source/Game/Entities/Unit/Unit.Spells.cs @@ -951,7 +951,7 @@ namespace Game.Entities int levelBasedHitDiff = leveldif; // Base hit chance from attacker and victim levels - int modHitChance = 100; + int modHitChance; if (levelBasedHitDiff >= 0) { if (!victim.IsTypeId(TypeId.Player)) @@ -2294,7 +2294,7 @@ namespace Game.Entities public void ApplyCastTimePercentMod(float val, bool apply) { - if (val > 0) + if (val > 0.0f) { ApplyPercentModUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.ModCastingSpeed), val, !apply); ApplyPercentModUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.ModSpellHaste), val, !apply); @@ -4547,13 +4547,10 @@ namespace Game.Entities return 0.0f; } - if (m_auraModifiersGroup[(int)unitMod][(int)UnitModifierType.TotalPCT] <= 0.0f) - return 0.0f; - - float value = MathFunctions.CalculatePct(m_auraModifiersGroup[(int)unitMod][(int)UnitModifierType.BaseValue], Math.Max(m_auraModifiersGroup[(int)unitMod][(int)UnitModifierType.BasePCTExcludeCreate], -100.0f)); - value *= m_auraModifiersGroup[(int)unitMod][(int)UnitModifierType.BasePCT]; - value += m_auraModifiersGroup[(int)unitMod][(int)UnitModifierType.TotalValue]; - value *= m_auraModifiersGroup[(int)unitMod][(int)UnitModifierType.TotalPCT]; + float value = MathFunctions.CalculatePct(GetFlatModifierValue(unitMod, UnitModifierFlatType.Base), Math.Max(GetFlatModifierValue(unitMod, UnitModifierFlatType.BasePCTExcludeCreate), -100.0f)); + value *= GetPctModifierValue(unitMod, UnitModifierPctType.Base); + value += GetFlatModifierValue(unitMod, UnitModifierFlatType.Total); + value *= GetPctModifierValue(unitMod, UnitModifierPctType.Total); return value; } diff --git a/Source/Game/Entities/Unit/Unit.cs b/Source/Game/Entities/Unit/Unit.cs index c84a2aaea..493358b88 100644 --- a/Source/Game/Entities/Unit/Unit.cs +++ b/Source/Game/Entities/Unit/Unit.cs @@ -57,9 +57,18 @@ namespace Game.Entities m_spellImmune[i] = new MultiMap(); for (byte i = 0; i < (int)UnitMods.End; ++i) - m_auraModifiersGroup[i] = new float[] { 0.0f, 100.0f, 1.0f, 0.0f, 1.0f }; + { + m_auraFlatModifiersGroup[i] = new float[(int)UnitModifierFlatType.End]; + m_auraFlatModifiersGroup[i][(int)UnitModifierFlatType.Base] = 0.0f; + m_auraFlatModifiersGroup[i][(int)UnitModifierFlatType.BasePCTExcludeCreate] = 100.0f; + m_auraFlatModifiersGroup[i][(int)UnitModifierFlatType.Total] = 0.0f; - m_auraModifiersGroup[(int)UnitMods.DamageOffHand][(int)UnitModifierType.TotalPCT] = 0.5f; + m_auraPctModifiersGroup[i] = new float[(int)UnitModifierPctType.End]; + m_auraPctModifiersGroup[i][(int)UnitModifierPctType.Base] = 1.0f; + m_auraPctModifiersGroup[i][(int)UnitModifierPctType.Total] = 1.0f; + } + + m_auraPctModifiersGroup[(int)UnitMods.DamageOffHand][(int)UnitModifierPctType.Total] = 0.5f; foreach (AuraType auraType in Enum.GetValues(typeof(AuraType))) m_modAuras[auraType] = new List(); @@ -1239,7 +1248,7 @@ namespace Game.Entities else if (GetRace() == Race.Worgen) { if (HasAura(210333)) // Glyph of the Feral Chameleon - hairColor = (byte)RandomHelper.URand(0, 9); + skinColor = (byte)RandomHelper.URand(0, 9); // Male if (GetGender() == Gender.Male) @@ -1284,7 +1293,7 @@ namespace Game.Entities else if (GetRace() == Race.Tauren) { if (HasAura(210333)) // Glyph of the Feral Chameleon - hairColor = (byte)RandomHelper.URand(0, 20); + skinColor = (byte)RandomHelper.URand(0, 20); if (GetGender() == Gender.Male) { @@ -1393,7 +1402,7 @@ namespace Game.Entities else if (GetRace() == Race.Worgen) { if (HasAura(210333)) // Glyph of the Feral Chameleon - hairColor = (byte)RandomHelper.URand(0, 8); + skinColor = (byte)RandomHelper.URand(0, 8); // Male if (GetGender() == Gender.Male) @@ -1438,7 +1447,7 @@ namespace Game.Entities else if (GetRace() == Race.Tauren) { if (HasAura(210333)) // Glyph of the Feral Chameleon - hairColor = (byte)RandomHelper.URand(0, 20); + skinColor = (byte)RandomHelper.URand(0, 20); if (GetGender() == Gender.Male) { @@ -1679,10 +1688,6 @@ namespace Game.Entities SetUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.ShapeshiftForm), (byte)form); } - public void SetModifierValue(UnitMods unitMod, UnitModifierType modifierType, float value) - { - m_auraModifiersGroup[(int)unitMod][(int)modifierType] = value; - } public int CalcSpellDuration(SpellInfo spellProto) { sbyte comboPoints = (sbyte)(m_playerMovingMe != null ? m_playerMovingMe.GetComboPoints() : 0); @@ -1932,20 +1937,16 @@ namespace Game.Entities FactionTemplateRecord entry = CliDB.FactionTemplateStorage.LookupByKey(GetFaction()); if (entry == null) { - ObjectGuid guid = ObjectGuid.Empty; // prevent repeating spam same faction problem - - if (GetGUID() != guid) + Player player = ToPlayer(); + if (player != null) + Log.outError(LogFilter.Unit, "Player {0} has invalid faction (faction template id) #{1}", player.GetName(), GetFaction()); + else { - Player player = ToPlayer(); Creature creature = ToCreature(); - if (player != null) - Log.outError(LogFilter.Unit, "Player {0} has invalid faction (faction template id) #{1}", player.GetName(), GetFaction()); - else if (creature != null) + 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()); - - guid = GetGUID(); } } return entry; diff --git a/Source/Game/Spells/Auras/AuraEffect.cs b/Source/Game/Spells/Auras/AuraEffect.cs index 79da7e29d..2da0b8688 100644 --- a/Source/Game/Spells/Auras/AuraEffect.cs +++ b/Source/Game/Spells/Auras/AuraEffect.cs @@ -235,8 +235,7 @@ namespace Game.Spells if (handleMask == 0) return; - List effectApplications = new List(); - GetApplicationList(out effectApplications); + GetApplicationList(out List effectApplications); foreach (var aurApp in effectApplications) { @@ -408,8 +407,7 @@ namespace Game.Spells m_periodicTimer += m_period - (int)diff; UpdatePeriodic(caster); - List effectApplications = new List(); - GetApplicationList(out effectApplications); + GetApplicationList(out List effectApplications); // tick on targets of effects foreach (var appt in effectApplications) if (appt.HasEffect(GetEffIndex())) @@ -1658,7 +1656,7 @@ namespace Game.Spells Unit target = aurApp.GetTarget(); float scale = target.GetObjectScale(); - MathFunctions.ApplyPercentModFloatVar(ref scale, GetAmount(), apply); + scale += MathFunctions.CalculatePct(1.0f, apply ? GetAmount() : -GetAmount()); target.SetObjectScale(scale); } @@ -1783,7 +1781,7 @@ namespace Game.Spells AuraType type = GetAuraType(); //Prevent handling aura twice - if ((apply) ? target.GetAuraEffectsByType(type).Count > 1 : target.HasAuraType(type)) + if (apply ? target.GetAuraEffectsByType(type).Count > 1 : target.HasAuraType(type)) return; Action flagAddFn = null; @@ -1831,11 +1829,15 @@ namespace Game.Spells Item item = player.GetItemByPos(InventorySlots.Bag0, (byte)slot); if (item != null) { - WeaponAttackType attacktype = Player.GetAttackBySlot((byte)slot, item.GetTemplate().GetInventoryType()); + WeaponAttackType attackType = Player.GetAttackBySlot((byte)slot, item.GetTemplate().GetInventoryType()); player.ApplyItemDependentAuras(item, !apply); - if (attacktype < WeaponAttackType.Max) + if (attackType < WeaponAttackType.Max) + { player._ApplyWeaponDamage(slot, item, !apply); + if (!apply) // apply case already handled on item dependent aura removal (if any) + player.UpdateWeaponDependentAuras(attackType); + } } } @@ -2359,7 +2361,15 @@ namespace Game.Spells Unit target = aurApp.GetTarget(); for (byte i = 0; i < (int)SpellSchools.Max; ++i) if (Convert.ToBoolean(GetMiscValue() & (1 << i))) - MathFunctions.ApplyPercentModFloatVar(ref target.m_threatModifier[i], GetAmount(), apply); + { + if (apply) + MathFunctions.AddPct(ref target.m_threatModifier[i], GetAmount()); + else + { + float amount = target.GetTotalAuraMultiplierByMiscMask(AuraType.ModThreat, 1u << i); + target.m_threatModifier[i] = amount; + } + } } [AuraEffectHandler(AuraType.ModTotalThreat)] @@ -2882,7 +2892,7 @@ namespace Game.Spells for (byte x = (byte)SpellSchools.Normal; x < (byte)SpellSchools.Max; x++) if (Convert.ToBoolean(GetMiscValue() & (1 << x))) - target.HandleStatModifier(UnitMods.ResistanceStart + x, UnitModifierType.TotalValue, GetAmount(), apply); + target.HandleStatFlatModifier(UnitMods.ResistanceStart + x, UnitModifierFlatType.Total, GetAmount(), apply); } [AuraEffectHandler(AuraType.ModBaseResistancePct)] @@ -2898,14 +2908,30 @@ namespace Game.Spells { //pets only have base armor if (target.IsPet() && Convert.ToBoolean(GetMiscValue() & (int)SpellSchoolMask.Normal)) - target.HandleStatModifier(UnitMods.Armor, UnitModifierType.BasePCT, GetAmount(), apply); + { + if (apply) + target.ApplyStatPctModifier(UnitMods.Armor, UnitModifierPctType.Base, GetAmount()); + else + { + float amount = target.GetTotalAuraMultiplierByMiscMask(AuraType.ModBaseResistancePct, (uint)SpellSchoolMask.Normal); + target.SetStatPctModifier(UnitMods.Armor, UnitModifierPctType.Base, amount); + } + } } else { for (byte x = (byte)SpellSchools.Normal; x < (byte)SpellSchools.Max; x++) { if (Convert.ToBoolean(GetMiscValue() & (1 << x))) - target.HandleStatModifier(UnitMods.ResistanceStart + x, UnitModifierType.BasePCT, GetAmount(), apply); + { + if (apply) + target.ApplyStatPctModifier(UnitMods.ResistanceStart + x, UnitModifierPctType.Base, GetAmount()); + else + { + float amount = target.GetTotalAuraMultiplierByMiscMask(AuraType.ModBaseResistancePct, 1u << x); + target.SetStatPctModifier(UnitMods.ResistanceStart + x, UnitModifierPctType.Base, amount); + } + } } } } @@ -2917,18 +2943,16 @@ namespace Game.Spells return; Unit target = aurApp.GetTarget(); - int spellGroupVal = target.GetHighestExclusiveSameEffectSpellGroupValue(this, AuraType.ModResistancePct); - if (Math.Abs(spellGroupVal) >= Math.Abs(GetAmount())) - return; for (byte i = (byte)SpellSchools.Normal; i < (byte)SpellSchools.Max; i++) { if (Convert.ToBoolean(GetMiscValue() & (1 << i))) { - if (spellGroupVal != 0) - target.HandleStatModifier(UnitMods.ResistanceStart + i, UnitModifierType.TotalPCT, (float)spellGroupVal, !apply); + float amount = target.GetTotalAuraMultiplierByMiscMask(AuraType.ModResistancePct, 1u << i); + if (target.GetPctModifierValue(UnitMods.ResistanceStart + i, UnitModifierPctType.Total) == amount) + continue; - target.HandleStatModifier(UnitMods.ResistanceStart + i, UnitModifierType.TotalPCT, GetAmount(), apply); + target.SetStatPctModifier(UnitMods.ResistanceStart + i, UnitModifierPctType.Total, amount); } } } @@ -2946,13 +2970,13 @@ namespace Game.Spells { //only pets have base stats if (target.IsPet() && Convert.ToBoolean(GetMiscValue() & (int)SpellSchoolMask.Normal)) - target.HandleStatModifier(UnitMods.Armor, UnitModifierType.TotalValue, GetAmount(), apply); + target.HandleStatFlatModifier(UnitMods.Armor, UnitModifierFlatType.Total, GetAmount(), apply); } else { for (byte i = (byte)SpellSchools.Normal; i < (byte)SpellSchools.Max; i++) if (Convert.ToBoolean(GetMiscValue() & (1 << i))) - target.HandleStatModifier(UnitMods.ResistanceStart + i, UnitModifierType.TotalValue, GetAmount(), apply); + target.HandleStatFlatModifier(UnitMods.ResistanceStart + i, UnitModifierFlatType.Total, GetAmount(), apply); } } @@ -3004,14 +3028,14 @@ namespace Game.Spells { if (spellGroupVal != 0) { - target.HandleStatModifier((UnitMods.StatStart + (int)i), UnitModifierType.TotalValue, (float)spellGroupVal, !apply); + target.HandleStatFlatModifier((UnitMods.StatStart + (int)i), UnitModifierFlatType.Total, (float)spellGroupVal, !apply); if (target.IsTypeId(TypeId.Player) || target.IsPet()) - target.ApplyStatBuffMod(i, spellGroupVal, !apply); + target.UpdateStatBuffMod(i); } - target.HandleStatModifier(UnitMods.StatStart + (int)i, UnitModifierType.TotalValue, GetAmount(), apply); + target.HandleStatFlatModifier(UnitMods.StatStart + (int)i, UnitModifierFlatType.Total, GetAmount(), apply); if (target.IsTypeId(TypeId.Player) || target.IsPet()) - target.ApplyStatBuffMod(i, GetAmount(), apply); + target.UpdateStatBuffMod(i); } } } @@ -3037,7 +3061,20 @@ namespace Game.Spells for (int i = (int)Stats.Strength; i < (int)Stats.Max; ++i) { if (GetMiscValue() == i || GetMiscValue() == -1) - target.HandleStatModifier(UnitMods.StatStart + i, UnitModifierType.BasePCT, (float)m_amount, apply); + { + if (apply) + target.ApplyStatPctModifier(UnitMods.StatStart + i, UnitModifierPctType.Base, m_amount); + else + { + float amount = target.GetTotalAuraMultiplier(AuraType.ModPercentStat, aurEff => + { + if (aurEff.GetMiscValue() == i || aurEff.GetMiscValue() == -1) + return true; + return false; + }); + target.SetStatPctModifier(UnitMods.StatStart + i, UnitModifierPctType.Base, amount); + } + } } } @@ -3138,22 +3175,6 @@ namespace Game.Spells return; Unit target = aurApp.GetTarget(); - int spellGroupVal = target.GetHighestExclusiveSameEffectSpellGroupValue(this, AuraType.ModTotalStatPercentage, true, -1); - if (Math.Abs(spellGroupVal) >= Math.Abs(GetAmount())) - return; - - if (spellGroupVal != 0) - { - for (int i = (int)Stats.Strength; i < (int)Stats.Max; i++) - { - if (GetMiscValue() == i || GetMiscValue() == -1) // affect the same stats - { - target.HandleStatModifier((UnitMods.StatStart + i), UnitModifierType.TotalPCT, spellGroupVal, !apply); - if (target.IsTypeId(TypeId.Player) || target.IsPet()) - target.ApplyStatPercentBuffMod((Stats)i, spellGroupVal, !apply); - } - } - } // save current health state float healthPct = target.GetHealthPct(); @@ -3169,26 +3190,25 @@ namespace Game.Spells { if (Convert.ToBoolean(GetMiscValueB() & 1 << i) || GetMiscValueB() == 0) // 0 is also used for all stats { - int spellGroupVal2 = target.GetHighestExclusiveSameEffectSpellGroupValue(this, AuraType.ModTotalStatPercentage, true, i); - if (Math.Abs(spellGroupVal2) >= Math.Abs(GetAmount())) + float amount = target.GetTotalAuraMultiplier(AuraType.ModTotalStatPercentage, aurEff => + { + if ((aurEff.GetMiscValueB() & 1 << i) != 0 || aurEff.GetMiscValueB() == 0) + return true; + return false; + }); + + if (target.GetPctModifierValue(UnitMods.StatStart + i, UnitModifierPctType.Total) == amount) continue; - if (spellGroupVal2 != 0) - { - target.HandleStatModifier(UnitMods.StatStart + i, UnitModifierType.TotalPCT, spellGroupVal2, !apply); - if (target.IsTypeId(TypeId.Player) || target.IsPet()) - target.ApplyStatPercentBuffMod((Stats)i, spellGroupVal2, !apply); - } - - target.HandleStatModifier(UnitMods.StatStart + i, UnitModifierType.TotalPCT, GetAmount(), apply); + target.SetStatPctModifier(UnitMods.StatStart + i, UnitModifierPctType.Total, amount); if (target.IsTypeId(TypeId.Player) || target.IsPet()) - target.ApplyStatPercentBuffMod((Stats)i, GetAmount(), apply); + target.UpdateStatBuffMod((Stats)i); } } - // recalculate current HP/MP after applying aura modifications (only for spells with SPELL_ATTR0_UNK4 0x00000010 flag) + // recalculate current HP/MP after applying aura modifications (only for spells with SPELL_ATTR0_ABILITY 0x00000010 flag) // this check is total bullshit i think - if (Convert.ToBoolean(GetMiscValueB() & 1 << (int)Stats.Stamina) && m_spellInfo.HasAttribute(SpellAttr0.Ability)) + if ((Convert.ToBoolean(GetMiscValueB() & 1 << (int)Stats.Stamina) || GetMiscValueB() == 0) && m_spellInfo.HasAttribute(SpellAttr0.Ability)) target.SetHealth(Math.Max(MathFunctions.CalculatePct(target.GetMaxHealth(), healthPct), (zeroHealth ? 0 : 1ul))); } @@ -3252,8 +3272,8 @@ namespace Game.Spells { if (GetMiscValue() == (int)stat || GetMiscValue() == -1) { - target.HandleStatModifier(UnitMods.StatStart + (int)stat, UnitModifierType.BasePCTExcludeCreate, m_amount, apply); - target.ApplyStatPercentBuffMod(stat, m_amount, apply); + target.HandleStatFlatModifier(UnitMods.StatStart + (int)stat, UnitModifierFlatType.BasePCTExcludeCreate, m_amount, apply); + target.UpdateStatBuffMod(stat); } } } @@ -3313,7 +3333,7 @@ namespace Game.Spells PowerType power = (PowerType)GetMiscValue(); UnitMods unitMod = (UnitMods)(UnitMods.PowerStart + (int)power); - target.HandleStatModifier(unitMod, UnitModifierType.TotalValue, GetAmount(), apply); + target.HandleStatFlatModifier(unitMod, UnitModifierFlatType.Total, GetAmount(), apply); } /********************************/ @@ -3385,7 +3405,7 @@ namespace Game.Spells if (apply) { - target.HandleStatModifier(UnitMods.Health, UnitModifierType.TotalValue, GetAmount(), apply); + target.HandleStatFlatModifier(UnitMods.Health, UnitModifierFlatType.Total, GetAmount(), apply); target.ModifyHealth(GetAmount()); } else @@ -3396,7 +3416,7 @@ namespace Game.Spells target.ModifyHealth(-value); } - target.HandleStatModifier(UnitMods.Health, UnitModifierType.TotalValue, GetAmount(), apply); + target.HandleStatFlatModifier(UnitMods.Health, UnitModifierFlatType.Total, GetAmount(), apply); } } @@ -3409,7 +3429,7 @@ namespace Game.Spells float percent = target.GetHealthPct(); - target.HandleStatModifier(UnitMods.Health, UnitModifierType.TotalValue, GetAmount(), apply); + target.HandleStatFlatModifier(UnitMods.Health, UnitModifierFlatType.Total, GetAmount(), apply); // refresh percentage if (target.GetHealth() > 0) @@ -3429,7 +3449,7 @@ namespace Game.Spells PowerType powerType = (PowerType)GetMiscValue(); UnitMods unitMod = (UnitMods.PowerStart + (int)powerType); - target.HandleStatModifier(unitMod, UnitModifierType.TotalValue, GetAmount(), apply); + target.HandleStatFlatModifier(unitMod, UnitModifierFlatType.Total, GetAmount(), apply); } [AuraEffectHandler(AuraType.ModIncreaseEnergyPercent)] @@ -3448,7 +3468,26 @@ namespace Game.Spells int oldMaxPower = target.GetMaxPower(powerType); // Handle aura effect for max power - target.HandleStatModifier(unitMod, UnitModifierType.TotalPCT, GetAmount(), apply); + if (apply) + target.ApplyStatPctModifier(unitMod, UnitModifierPctType.Total, GetAmount()); + else + { + float amount = target.GetTotalAuraMultiplier(AuraType.ModIncreaseEnergyPercent, aurEff => + { + if (aurEff.GetMiscValue() == (int)powerType) + return true; + return false; + }); + + amount *= target.GetTotalAuraMultiplier(AuraType.ModMaxPowerPct, aurEff => + { + if (aurEff.GetMiscValue() == (int)powerType) + return true; + return false; + }); + + target.SetStatPctModifier(unitMod, UnitModifierPctType.Total, amount); + } // Calculate the current power change int change = target.GetMaxPower(powerType) - oldMaxPower; @@ -3465,11 +3504,17 @@ namespace Game.Spells // Unit will keep hp% after MaxHealth being modified if unit is alive. float percent = target.GetHealthPct(); - target.HandleStatModifier(UnitMods.Health, UnitModifierType.TotalPCT, GetAmount(), apply); + if (apply) + target.ApplyStatPctModifier(UnitMods.Health, UnitModifierPctType.Total, GetAmount()); + else + { + float amount = target.GetTotalAuraMultiplier(AuraType.ModIncreaseHealthPercent); + target.SetStatPctModifier(UnitMods.Health, UnitModifierPctType.Total, amount); + } if (target.GetHealth() > 0) { - uint newHealth = (uint)Math.Max(target.CountPctFromMaxHealth((int)percent), 1); + uint newHealth = (uint)Math.Max(MathFunctions.CalculatePct(target.GetMaxHealth(), (int)percent), 1); target.SetHealth(newHealth); } } @@ -3482,7 +3527,13 @@ namespace Game.Spells Unit target = aurApp.GetTarget(); - target.HandleStatModifier(UnitMods.Health, UnitModifierType.BasePCT, GetAmount(), apply); + if (apply) + target.ApplyStatPctModifier(UnitMods.Health, UnitModifierPctType.Base, GetAmount()); + else + { + float amount = target.GetTotalAuraMultiplier(AuraType.ModBaseHealthPct); + target.SetStatPctModifier(UnitMods.Health, UnitModifierPctType.Base, amount); + } } [AuraEffectHandler(AuraType.ModBaseManaPct)] @@ -3491,7 +3542,15 @@ namespace Game.Spells if (!mode.HasAnyFlag(AuraEffectHandleModes.ChangeAmountMask | AuraEffectHandleModes.Stat)) return; - aurApp.GetTarget().HandleStatModifier(UnitMods.Mana, UnitModifierType.BasePCT, GetAmount(), apply); + Unit target = aurApp.GetTarget(); + + if (apply) + target.ApplyStatPctModifier(UnitMods.Mana, UnitModifierPctType.Base, GetAmount()); + else + { + float amount = target.GetTotalAuraMultiplier(AuraType.ModBaseManaPct); + target.SetStatPctModifier(UnitMods.Mana, UnitModifierPctType.Base, amount); + } } [AuraEffectHandler(AuraType.ModPowerDisplay)] @@ -3550,7 +3609,26 @@ namespace Game.Spells int oldMaxPower = target.GetMaxPower(powerType); // Handle aura effect for max power - target.HandleStatModifier(unitMod, UnitModifierType.TotalPCT, (float)GetAmount(), apply); + if (apply) + target.ApplyStatPctModifier(unitMod, UnitModifierPctType.Total, GetAmount()); + else + { + float amount = target.GetTotalAuraMultiplier(AuraType.ModMaxPowerPct, aurEff => + { + if (aurEff.GetMiscValue() == (int)powerType) + return true; + return false; + }); + + amount *= target.GetTotalAuraMultiplier(AuraType.ModIncreaseEnergyPercent, aurEff => + { + if (aurEff.GetMiscValue() == (int)powerType) + return true; + return false; + }); + + target.SetStatPctModifier(unitMod, UnitModifierPctType.Total, amount); + } // Calculate the current power change int change = target.GetMaxPower(powerType) - oldMaxPower; @@ -3616,13 +3694,10 @@ namespace Game.Spells return; Player target = aurApp.GetTarget().ToPlayer(); - if (!target) return; - target.HandleBaseModValue(BaseModGroup.CritPercentage, BaseModType.FlatMod, GetAmount(), apply); - target.HandleBaseModValue(BaseModGroup.OffhandCritPercentage, BaseModType.FlatMod, GetAmount(), apply); - target.HandleBaseModValue(BaseModGroup.RangedCritPercentage, BaseModType.FlatMod, GetAmount(), apply); + target.UpdateAllWeaponDependentCritAuras(); } [AuraEffectHandler(AuraType.ModHitChance)] @@ -3687,9 +3762,7 @@ namespace Game.Spells return; } - target.ToPlayer().HandleBaseModValue(BaseModGroup.CritPercentage, BaseModType.FlatMod, GetAmount(), apply); - target.ToPlayer().HandleBaseModValue(BaseModGroup.OffhandCritPercentage, BaseModType.FlatMod, GetAmount(), apply); - target.ToPlayer().HandleBaseModValue(BaseModGroup.RangedCritPercentage, BaseModType.FlatMod, GetAmount(), apply); + target.ToPlayer().UpdateAllWeaponDependentCritAuras(); // included in Player.UpdateSpellCritChance calculation target.ToPlayer().UpdateSpellCritChance(); @@ -3863,7 +3936,7 @@ namespace Game.Spells Unit target = aurApp.GetTarget(); - target.HandleStatModifier(UnitMods.AttackPower, UnitModifierType.TotalValue, GetAmount(), apply); + target.HandleStatFlatModifier(UnitMods.AttackPower, UnitModifierFlatType.Total, GetAmount(), apply); } [AuraEffectHandler(AuraType.ModRangedAttackPower)] @@ -3877,7 +3950,7 @@ namespace Game.Spells if ((target.GetClassMask() & (uint)Class.ClassMaskWandUsers) != 0) return; - target.HandleStatModifier(UnitMods.AttackPowerRanged, UnitModifierType.TotalValue, GetAmount(), apply); + target.HandleStatFlatModifier(UnitMods.AttackPowerRanged, UnitModifierFlatType.Total, GetAmount(), apply); } [AuraEffectHandler(AuraType.ModAttackPowerPct)] @@ -3889,7 +3962,13 @@ namespace Game.Spells Unit target = aurApp.GetTarget(); //UNIT_FIELD_ATTACK_POWER_MULTIPLIER = multiplier - 1 - target.HandleStatModifier(UnitMods.AttackPower, UnitModifierType.TotalPCT, GetAmount(), apply); + if (apply) + target.ApplyStatPctModifier(UnitMods.AttackPower, UnitModifierPctType.Total, GetAmount()); + else + { + float amount = target.GetTotalAuraMultiplier(AuraType.ModAttackPowerPct); + target.SetStatPctModifier(UnitMods.AttackPower, UnitModifierPctType.Total, amount); + } } [AuraEffectHandler(AuraType.ModRangedAttackPowerPct)] @@ -3904,7 +3983,13 @@ namespace Game.Spells return; //UNIT_FIELD_RANGED_ATTACK_POWER_MULTIPLIER = multiplier - 1 - target.HandleStatModifier(UnitMods.AttackPowerRanged, UnitModifierType.TotalPCT, GetAmount(), apply); + if (apply) + target.ApplyStatPctModifier(UnitMods.AttackPowerRanged, UnitModifierPctType.Total, GetAmount()); + else + { + float amount = target.GetTotalAuraMultiplier(AuraType.ModRangedAttackPowerPct); + target.SetStatPctModifier(UnitMods.AttackPowerRanged, UnitModifierPctType.Total, amount); + } } /********************************/ @@ -3919,11 +4004,7 @@ namespace Game.Spells Unit target = aurApp.GetTarget(); if ((GetMiscValue() & (int)SpellSchoolMask.Normal) != 0) - { - target.HandleStatModifier(UnitMods.DamageMainHand, UnitModifierType.TotalValue, GetAmount(), apply); - target.HandleStatModifier(UnitMods.DamageOffHand, UnitModifierType.TotalValue, GetAmount(), apply); - target.HandleStatModifier(UnitMods.DamageRanged, UnitModifierType.TotalValue, GetAmount(), apply); - } + target.UpdateAllDamageDoneMods(); // Magic damage modifiers implemented in Unit::SpellBaseDamageBonusDone // This information for client side use only @@ -3954,35 +4035,21 @@ namespace Game.Spells return; Unit target = aurApp.GetTarget(); - int spellGroupVal = target.GetHighestExclusiveSameEffectSpellGroupValue(this, AuraType.ModDamagePercentDone); - if (Math.Abs(spellGroupVal) >= Math.Abs(GetAmount())) - return; + // also handles spell group stacks if (Convert.ToBoolean(GetMiscValue() & (int)SpellSchoolMask.Normal)) - { - if (spellGroupVal != 0) - { - target.HandleStatModifier(UnitMods.DamageMainHand, UnitModifierType.TotalPCT, spellGroupVal, !apply); - target.HandleStatModifier(UnitMods.DamageOffHand, UnitModifierType.TotalPCT, spellGroupVal, !apply); - target.HandleStatModifier(UnitMods.DamageRanged, UnitModifierType.TotalPCT, spellGroupVal, !apply); - } - - target.HandleStatModifier(UnitMods.DamageMainHand, UnitModifierType.TotalPCT, GetAmount(), apply); - target.HandleStatModifier(UnitMods.DamageOffHand, UnitModifierType.TotalPCT, GetAmount(), apply); - target.HandleStatModifier(UnitMods.DamageRanged, UnitModifierType.TotalPCT, GetAmount(), apply); - } + target.UpdateAllDamagePctDoneMods(); Player thisPlayer = target.ToPlayer(); if (thisPlayer != null) { - for (int i = 0; i < (int)SpellSchools.Max; ++i) + for (var i = SpellSchools.Normal; i < SpellSchools.Max; ++i) { - if (Convert.ToBoolean(GetMiscValue() & (1 << i))) + if (Convert.ToBoolean(GetMiscValue() & (1 << (int)i))) { - if (spellGroupVal != 0) - thisPlayer.ApplyModDamageDonePercent((SpellSchools)i, spellGroupVal, !apply); - - thisPlayer.ApplyModDamageDonePercent((SpellSchools)i, GetAmount(), apply); + // only aura type modifying PLAYER_FIELD_MOD_DAMAGE_DONE_PCT + float amount = thisPlayer.GetTotalAuraMultiplierByMiscMask(AuraType.ModDamagePercentDone, 1u << (int)i); + thisPlayer.SetModDamageDonePercent(i, amount); } } } @@ -3996,18 +4063,37 @@ namespace Game.Spells Unit target = aurApp.GetTarget(); - target.HandleStatModifier(UnitMods.DamageOffHand, UnitModifierType.TotalPCT, GetAmount(), apply); + // also handles spell group stacks + target.UpdateDamagePctDoneMods(WeaponAttackType.OffAttack); + } + + void HandleShieldBlockValue(AuraApplication aurApp, AuraEffectHandleModes mode, bool apply) + { + if (!mode.HasAnyFlag(AuraEffectHandleModes.ChangeAmountMask | AuraEffectHandleModes.Stat)) + return; + + Player player = aurApp.GetTarget().ToPlayer(); + if (player != null) + player.HandleBaseModFlatValue(BaseModGroup.ShieldBlockValue, GetAmount(), apply); } [AuraEffectHandler(AuraType.ModShieldBlockvaluePct)] - void HandleShieldBlockValue(AuraApplication aurApp, AuraEffectHandleModes mode, bool apply) + void HandleShieldBlockValuePercent(AuraApplication aurApp, AuraEffectHandleModes mode, bool apply) { if (!mode.HasAnyFlag((AuraEffectHandleModes.ChangeAmountMask | AuraEffectHandleModes.Stat))) return; - Player player = aurApp.GetTarget().ToPlayer(); - if (player) - player.HandleBaseModValue(BaseModGroup.ShieldBlockValue, BaseModType.PCTmod, GetAmount(), apply); + Player target = aurApp.GetTarget().ToPlayer(); + if (!target) + return; + + if (apply) + target.ApplyBaseModPctValue(BaseModGroup.ShieldBlockValue, GetAmount()); + else + { + float amount = target.GetTotalAuraMultiplier(AuraType.ModShieldBlockvaluePct); + target.SetBaseModPctValue(BaseModGroup.ShieldBlockValue, amount); + } } /********************************/ @@ -4478,7 +4564,7 @@ namespace Game.Spells } //Adding items - uint noSpaceForCount = 0; + uint noSpaceForCount; uint count = (uint)m_amount; List dest = new List(); diff --git a/Source/Game/Spells/SpellEffects.cs b/Source/Game/Spells/SpellEffects.cs index b27364767..71e75eb1d 100644 --- a/Source/Game/Spells/SpellEffects.cs +++ b/Source/Game/Spells/SpellEffects.cs @@ -1388,8 +1388,8 @@ namespace Game.Spells Player player = m_caster.ToPlayer(); - uint lockId = 0; - ObjectGuid guid = ObjectGuid.Empty; + uint lockId; + ObjectGuid guid; // Get lockId if (gameObjTarget != null) @@ -1741,7 +1741,7 @@ namespace Game.Spells for (uint count = 0; count < numSummons; ++count) { - Position pos = new Position(); + Position pos; if (count == 0) pos = destTarget; else @@ -2657,7 +2657,7 @@ namespace Game.Spells break; } - float weapon_total_pct = m_caster.GetModifierValue(unitMod, UnitModifierType.TotalPCT); + float weapon_total_pct = m_caster.GetPctModifierValue(unitMod, UnitModifierPctType.Total); if (fixed_bonus != 0) fixed_bonus = (int)(fixed_bonus * weapon_total_pct); if (spell_bonus != 0) @@ -2725,7 +2725,7 @@ namespace Game.Spells if (unitTarget == null || !unitTarget.IsAlive()) return; - int addhealth = 0; + int addhealth; // damage == 0 - heal for caster max health if (damage == 0) @@ -2951,7 +2951,7 @@ namespace Game.Spells byte bag = 19; byte slot = 0; - Item item = null; + Item item; while (bag != 0) // 256 = 0 due to var type { @@ -3020,7 +3020,7 @@ namespace Game.Spells if (unitTarget == null || !unitTarget.IsTypeId(TypeId.Unit)) return; - uint iTmpSpellId = 0; + uint iTmpSpellId; switch (unitTarget.GetDisplayId()) { case 25369: iTmpSpellId = 51552; break; // bloodelf female @@ -3304,7 +3304,7 @@ namespace Game.Spells { if (unitTarget == null || !unitTarget.IsAlive()) return; - uint spellId1 = 0; + uint spellId1; uint spellId2 = 0; // Judgement self add switch @@ -3985,7 +3985,7 @@ namespace Game.Spells if (!m_caster.IsInWorld) return; - uint health = 0; + uint health; int mana = 0; // flat case @@ -5065,7 +5065,7 @@ namespace Game.Spells for (uint count = 0; count < numGuardians; ++count) { - Position pos = new Position(); + Position pos; if (count == 0) pos = destTarget; else