Implemented Basic Creature Scaling
Trainer followup Misc Fixes
This commit is contained in:
@@ -52,7 +52,7 @@ namespace Framework.Constants
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Lfg Const
|
/// Lfg Const
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const uint LFGTimeRolecheck = 45 * Time.InMilliseconds;
|
public const uint LFGTimeRolecheck = 45;
|
||||||
public const uint LFGTimeBoot = 120;
|
public const uint LFGTimeBoot = 120;
|
||||||
public const uint LFGTimeProposal = 45;
|
public const uint LFGTimeProposal = 45;
|
||||||
public const uint LFGQueueUpdateInterval = 15 * Time.InMilliseconds;
|
public const uint LFGQueueUpdateInterval = 15 * Time.InMilliseconds;
|
||||||
|
|||||||
@@ -1840,7 +1840,7 @@ namespace Framework.Constants
|
|||||||
Threat = 63,
|
Threat = 63,
|
||||||
TriggerSpell = 64,
|
TriggerSpell = 64,
|
||||||
ApplyAreaAuraRaid = 65,
|
ApplyAreaAuraRaid = 65,
|
||||||
CreateManaGem = 66,
|
RechargeItem = 66,
|
||||||
HealMaxHealth = 67,
|
HealMaxHealth = 67,
|
||||||
InterruptCast = 68,
|
InterruptCast = 68,
|
||||||
Distract = 69,
|
Distract = 69,
|
||||||
|
|||||||
@@ -1263,7 +1263,7 @@ namespace Game.Achievements
|
|||||||
return false;
|
return false;
|
||||||
break;
|
break;
|
||||||
case CriteriaAdditionalCondition.TargetLevel: // 40
|
case CriteriaAdditionalCondition.TargetLevel: // 40
|
||||||
if (unit == null || unit.getLevel() != reqValue)
|
if (unit == null || unit.GetLevelForTarget(referencePlayer) != reqValue)
|
||||||
return false;
|
return false;
|
||||||
break;
|
break;
|
||||||
case CriteriaAdditionalCondition.TargetZone: // 41
|
case CriteriaAdditionalCondition.TargetZone: // 41
|
||||||
@@ -2035,7 +2035,7 @@ namespace Game.Achievements
|
|||||||
case CriteriaDataType.TLevel:
|
case CriteriaDataType.TLevel:
|
||||||
if (target == null)
|
if (target == null)
|
||||||
return false;
|
return false;
|
||||||
return target.getLevel() >= Level.Min;
|
return target.GetLevelForTarget(source) >= Level.Min;
|
||||||
case CriteriaDataType.TGender:
|
case CriteriaDataType.TGender:
|
||||||
if (target == null)
|
if (target == null)
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -288,8 +288,8 @@ namespace Game.Entities
|
|||||||
|
|
||||||
if (updateLevel)
|
if (updateLevel)
|
||||||
SelectLevel();
|
SelectLevel();
|
||||||
|
else
|
||||||
UpdateLevelDependantStats();
|
UpdateLevelDependantStats(); // We still re-initialize level dependant stats on entry update
|
||||||
|
|
||||||
SetMeleeDamageSchool((SpellSchools)cInfo.DmgSchool);
|
SetMeleeDamageSchool((SpellSchools)cInfo.DmgSchool);
|
||||||
SetModifierValue(UnitMods.ResistanceHoly, UnitModifierType.BaseValue, cInfo.Resistance[(int)SpellSchools.Holy]);
|
SetModifierValue(UnitMods.ResistanceHoly, UnitModifierType.BaseValue, cInfo.Resistance[(int)SpellSchools.Holy]);
|
||||||
@@ -1090,11 +1090,23 @@ namespace Game.Entities
|
|||||||
{
|
{
|
||||||
CreatureTemplate cInfo = GetCreatureTemplate();
|
CreatureTemplate cInfo = GetCreatureTemplate();
|
||||||
|
|
||||||
// level
|
if (!HasScalableLevels())
|
||||||
byte minlevel = (byte)Math.Min(cInfo.Maxlevel, cInfo.Minlevel);
|
{
|
||||||
byte maxlevel = (byte)Math.Max(cInfo.Maxlevel, cInfo.Minlevel);
|
// level
|
||||||
byte level = (byte)(minlevel == maxlevel ? minlevel : RandomHelper.URand(minlevel, maxlevel));
|
byte minlevel = (byte)Math.Min(cInfo.Maxlevel, cInfo.Minlevel);
|
||||||
SetLevel(level);
|
byte maxlevel = (byte)Math.Max(cInfo.Maxlevel, cInfo.Minlevel);
|
||||||
|
byte level = (byte)(minlevel == maxlevel ? minlevel : RandomHelper.URand(minlevel, maxlevel));
|
||||||
|
SetLevel(level);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SetLevel(cInfo.levelScaling.Value.MaxLevel);
|
||||||
|
SetUInt32Value(UnitFields.ScalingLevelMin, cInfo.levelScaling.Value.MinLevel);
|
||||||
|
SetUInt32Value(UnitFields.ScalingLevelMax, cInfo.levelScaling.Value.MaxLevel);
|
||||||
|
SetUInt32Value(UnitFields.ScalingLevelDelta, (uint)cInfo.levelScaling.Value.DeltaLevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateLevelDependantStats();
|
||||||
}
|
}
|
||||||
|
|
||||||
void UpdateLevelDependantStats()
|
void UpdateLevelDependantStats()
|
||||||
@@ -1663,8 +1675,8 @@ namespace Game.Entities
|
|||||||
|
|
||||||
if (m_originalEntry != GetEntry())
|
if (m_originalEntry != GetEntry())
|
||||||
UpdateEntry(m_originalEntry);
|
UpdateEntry(m_originalEntry);
|
||||||
|
else
|
||||||
SelectLevel();
|
SelectLevel();
|
||||||
|
|
||||||
setDeathState(DeathState.JustRespawned);
|
setDeathState(DeathState.JustRespawned);
|
||||||
|
|
||||||
@@ -2344,17 +2356,91 @@ namespace Game.Entities
|
|||||||
m_respawnTime = m_corpseRemoveTime + m_respawnDelay;
|
m_respawnTime = m_corpseRemoveTime + m_respawnDelay;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool HasScalableLevels()
|
||||||
|
{
|
||||||
|
CreatureTemplate cinfo = GetCreatureTemplate();
|
||||||
|
return cinfo.levelScaling.HasValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong GetMaxHealthByLevel(uint level)
|
||||||
|
{
|
||||||
|
CreatureTemplate cInfo = GetCreatureTemplate();
|
||||||
|
CreatureBaseStats stats = Global.ObjectMgr.GetCreatureBaseStats(level, cInfo.UnitClass);
|
||||||
|
return stats.GenerateHealth(cInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
float GetHealthMultiplierForTarget(WorldObject target)
|
||||||
|
{
|
||||||
|
if (!HasScalableLevels())
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
uint levelForTarget = GetLevelForTarget(target);
|
||||||
|
if (getLevel() < levelForTarget)
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
return GetMaxHealthByLevel(levelForTarget) / GetCreateHealth();
|
||||||
|
}
|
||||||
|
|
||||||
|
float GetBaseDamageForLevel(uint level)
|
||||||
|
{
|
||||||
|
CreatureTemplate cInfo = GetCreatureTemplate();
|
||||||
|
CreatureBaseStats stats = Global.ObjectMgr.GetCreatureBaseStats(level, cInfo.UnitClass);
|
||||||
|
return stats.GenerateBaseDamage(cInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
float GetDamageMultiplierForTarget(WorldObject target)
|
||||||
|
{
|
||||||
|
if (!HasScalableLevels())
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
uint levelForTarget = GetLevelForTarget(target);
|
||||||
|
|
||||||
|
return GetBaseDamageForLevel(levelForTarget) / GetBaseDamageForLevel(getLevel());
|
||||||
|
}
|
||||||
|
|
||||||
|
float GetBaseArmorForLevel(uint level)
|
||||||
|
{
|
||||||
|
CreatureTemplate cInfo = GetCreatureTemplate();
|
||||||
|
CreatureBaseStats stats = Global.ObjectMgr.GetCreatureBaseStats(level, cInfo.UnitClass);
|
||||||
|
return stats.GenerateArmor(cInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
float GetArmorMultiplierForTarget(WorldObject target)
|
||||||
|
{
|
||||||
|
if (!HasScalableLevels())
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
uint levelForTarget = GetLevelForTarget(target);
|
||||||
|
|
||||||
|
return GetBaseArmorForLevel(levelForTarget) / GetBaseArmorForLevel(getLevel());
|
||||||
|
}
|
||||||
|
|
||||||
public override uint GetLevelForTarget(WorldObject target)
|
public override uint GetLevelForTarget(WorldObject target)
|
||||||
{
|
{
|
||||||
if (!isWorldBoss() || !target.IsTypeId(TypeId.Unit))
|
Unit unitTarget = target.ToUnit();
|
||||||
return base.GetLevelForTarget(target);
|
if (unitTarget)
|
||||||
|
{
|
||||||
|
if (isWorldBoss())
|
||||||
|
{
|
||||||
|
int level = (int)(unitTarget.getLevel() + WorldConfig.GetIntValue(WorldCfg.WorldBossLevelDiff));
|
||||||
|
return (uint)MathFunctions.RoundToInterval(ref level, 1u, 255u);
|
||||||
|
}
|
||||||
|
|
||||||
uint level = target.ToUnit().getLevel() + WorldConfig.GetUIntValue(WorldCfg.WorldBossLevelDiff);
|
// If this creature should scale level, adapt level depending of target level
|
||||||
if (level < 1)
|
// between UNIT_FIELD_SCALING_LEVEL_MIN and UNIT_FIELD_SCALING_LEVEL_MAX
|
||||||
return 1;
|
if (HasScalableLevels())
|
||||||
if (level > 255)
|
{
|
||||||
return 255;
|
uint targetLevelWithDelta = (uint)(unitTarget.getLevel() + GetCreatureTemplate().levelScaling.Value.DeltaLevel);
|
||||||
return level;
|
|
||||||
|
if (target.IsPlayer())
|
||||||
|
targetLevelWithDelta += target.GetUInt32Value(PlayerFields.ScalingLevelDelta);
|
||||||
|
|
||||||
|
return MathFunctions.RoundToInterval(ref targetLevelWithDelta, GetUInt32Value(UnitFields.ScalingLevelMin), GetUInt32Value(UnitFields.ScalingLevelMax));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return base.GetLevelForTarget(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GetAIName()
|
public string GetAIName()
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ using Framework.Collections;
|
|||||||
using Framework.Constants;
|
using Framework.Constants;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using Framework.Dynamic;
|
||||||
|
|
||||||
namespace Game.Entities
|
namespace Game.Entities
|
||||||
{
|
{
|
||||||
@@ -37,6 +38,7 @@ namespace Game.Entities
|
|||||||
public string IconName;
|
public string IconName;
|
||||||
public uint GossipMenuId;
|
public uint GossipMenuId;
|
||||||
public short Minlevel;
|
public short Minlevel;
|
||||||
|
public Optional<CreatureLevelScaling> levelScaling;
|
||||||
public short Maxlevel;
|
public short Maxlevel;
|
||||||
public int HealthScalingExpansion;
|
public int HealthScalingExpansion;
|
||||||
public uint RequiredExpansion;
|
public uint RequiredExpansion;
|
||||||
@@ -384,4 +386,11 @@ namespace Game.Entities
|
|||||||
m_items.Clear();
|
m_items.Clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public struct CreatureLevelScaling
|
||||||
|
{
|
||||||
|
public ushort MinLevel;
|
||||||
|
public ushort MaxLevel;
|
||||||
|
public short DeltaLevel;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,8 @@ namespace Game.Entities
|
|||||||
public Array<uint> ReqAbility = new Array<uint>(3);
|
public Array<uint> ReqAbility = new Array<uint>(3);
|
||||||
public byte ReqLevel;
|
public byte ReqLevel;
|
||||||
|
|
||||||
public uint CastSpellId;
|
public uint LearnedSpellId;
|
||||||
|
public bool IsCastable() { return LearnedSpellId != SpellId; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Trainer
|
public class Trainer
|
||||||
@@ -84,8 +85,8 @@ namespace Game.Entities
|
|||||||
player.SendPlaySpellVisualKit(362, 1, 0); // 113 EmoteSalute
|
player.SendPlaySpellVisualKit(362, 1, 0); // 113 EmoteSalute
|
||||||
|
|
||||||
// learn explicitly or cast explicitly
|
// learn explicitly or cast explicitly
|
||||||
if (trainerSpell.CastSpellId != 0)
|
if (trainerSpell.IsCastable())
|
||||||
player.CastSpell(player, trainerSpell.CastSpellId, true);
|
player.CastSpell(player, trainerSpell.SpellId, true);
|
||||||
else
|
else
|
||||||
player.LearnSpell(trainerSpell.SpellId, false);
|
player.LearnSpell(trainerSpell.SpellId, false);
|
||||||
}
|
}
|
||||||
@@ -129,6 +130,12 @@ namespace Game.Entities
|
|||||||
if (player.getLevel() < trainerSpell.ReqLevel)
|
if (player.getLevel() < trainerSpell.ReqLevel)
|
||||||
return TrainerSpellState.Unavailable;
|
return TrainerSpellState.Unavailable;
|
||||||
|
|
||||||
|
// check ranks
|
||||||
|
uint previousRankSpellId = Global.SpellMgr.GetPrevSpellInChain(trainerSpell.LearnedSpellId);
|
||||||
|
if (previousRankSpellId != 0)
|
||||||
|
if (!player.HasSpell(previousRankSpellId))
|
||||||
|
return TrainerSpellState.Unavailable;
|
||||||
|
|
||||||
// check additional spell requirement
|
// check additional spell requirement
|
||||||
foreach (var spellId in Global.SpellMgr.GetSpellsRequiredForSpellBounds(trainerSpell.SpellId))
|
foreach (var spellId in Global.SpellMgr.GetSpellsRequiredForSpellBounds(trainerSpell.SpellId))
|
||||||
if (!player.HasSpell(spellId))
|
if (!player.HasSpell(spellId))
|
||||||
|
|||||||
@@ -850,7 +850,7 @@ namespace Game.Entities
|
|||||||
DynamicUpdateMask arrayMask = new DynamicUpdateMask((uint)values.Length);
|
DynamicUpdateMask arrayMask = new DynamicUpdateMask((uint)values.Length);
|
||||||
arrayMask.EncodeDynamicFieldChangeType(_dynamicChangesMask[index], updateType);
|
arrayMask.EncodeDynamicFieldChangeType(_dynamicChangesMask[index], updateType);
|
||||||
if (updateType == UpdateType.Values && _dynamicChangesMask[index] == DynamicFieldChangeType.ValueAndSizeChanged)
|
if (updateType == UpdateType.Values && _dynamicChangesMask[index] == DynamicFieldChangeType.ValueAndSizeChanged)
|
||||||
arrayMask.SetCount(values.Length);
|
arrayMask.SetCount(values.Length);
|
||||||
|
|
||||||
for (var v = 0; v < values.Length; ++v)
|
for (var v = 0; v < values.Length; ++v)
|
||||||
{
|
{
|
||||||
@@ -2029,7 +2029,7 @@ namespace Game.Entities
|
|||||||
|
|
||||||
Map map = GetMap();
|
Map map = GetMap();
|
||||||
GameObject go = new GameObject();
|
GameObject go = new GameObject();
|
||||||
if (!go.Create(entry, map, GetPhaseMask(), pos, rotation, 255, GameObjectState.Ready))
|
if (!go.Create(entry, map, GetPhaseMask(), pos, rotation, 255, GameObjectState.Ready))
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
go.CopyPhaseFrom(this);
|
go.CopyPhaseFrom(this);
|
||||||
@@ -2498,14 +2498,23 @@ namespace Game.Entities
|
|||||||
public void SetFieldNotifyFlag(uint flag) { _fieldNotifyFlags |= flag; }
|
public void SetFieldNotifyFlag(uint flag) { _fieldNotifyFlags |= flag; }
|
||||||
public void RemoveFieldNotifyFlag(uint flag) { _fieldNotifyFlags &= ~flag; }
|
public void RemoveFieldNotifyFlag(uint flag) { _fieldNotifyFlags &= ~flag; }
|
||||||
|
|
||||||
public Creature ToCreature() { return IsTypeId(TypeId.Unit) ? (this as Creature) : null; }
|
bool IsCreature() { return GetTypeId() == TypeId.Unit; }
|
||||||
public Player ToPlayer() { return IsTypeId(TypeId.Player) ? (this as Player) : null; }
|
public bool IsPlayer() { return GetTypeId() == TypeId.Player; }
|
||||||
public GameObject ToGameObject() { return IsTypeId(TypeId.GameObject) ? (this as GameObject) : null; }
|
bool IsGameObject() { return GetTypeId() == TypeId.GameObject; }
|
||||||
public Unit ToUnit() { return IsTypeId(TypeId.Unit) || IsTypeId(TypeId.Player) ? (this as Unit) : null; }
|
bool IsUnit() { return isTypeMask(TypeMask.Unit); }
|
||||||
public Corpse ToCorpse() { return IsTypeId(TypeId.Corpse) ? (this as Corpse) : null; }
|
bool IsCorpse() { return GetTypeId() == TypeId.Corpse; }
|
||||||
public DynamicObject ToDynamicObject() { return IsTypeId(TypeId.DynamicObject) ? (this as DynamicObject) : null; }
|
bool IsDynObject() { return GetTypeId() == TypeId.DynamicObject; }
|
||||||
public AreaTrigger ToAreaTrigger() { return IsTypeId(TypeId.AreaTrigger) ? (this as AreaTrigger) : null; }
|
bool IsAreaTrigger() { return GetTypeId() == TypeId.AreaTrigger; }
|
||||||
public Conversation ToConversation() { return IsTypeId(TypeId.Conversation) ? (this as Conversation) : null; }
|
bool IsConversation() { return GetTypeId() == TypeId.Conversation; }
|
||||||
|
|
||||||
|
public Creature ToCreature() { return IsCreature() ? (this as Creature) : null; }
|
||||||
|
public Player ToPlayer() { return IsPlayer() ? (this as Player) : null; }
|
||||||
|
public GameObject ToGameObject() { return IsGameObject() ? (this as GameObject) : null; }
|
||||||
|
public Unit ToUnit() { return IsUnit() ? (this as Unit) : null; }
|
||||||
|
public Corpse ToCorpse() { return IsCorpse() ? (this as Corpse) : null; }
|
||||||
|
public DynamicObject ToDynamicObject() { return IsDynObject() ? (this as DynamicObject) : null; }
|
||||||
|
public AreaTrigger ToAreaTrigger() { return IsAreaTrigger() ? (this as AreaTrigger) : null; }
|
||||||
|
public Conversation ToConversation() { return IsConversation() ? (this as Conversation) : null; }
|
||||||
|
|
||||||
public virtual void Update(uint diff) { }
|
public virtual void Update(uint diff) { }
|
||||||
|
|
||||||
|
|||||||
@@ -117,7 +117,7 @@ namespace Game.Entities
|
|||||||
// 2.4. _maxNotGrayMember - maximum level of alive group member within reward distance,
|
// 2.4. _maxNotGrayMember - maximum level of alive group member within reward distance,
|
||||||
// for whom victim is not gray;
|
// for whom victim is not gray;
|
||||||
uint grayLevel = Formulas.GetGrayLevel(lvl);
|
uint grayLevel = Formulas.GetGrayLevel(lvl);
|
||||||
if (_victim.getLevel() > grayLevel && (!_maxNotGrayMember || _maxNotGrayMember.getLevel() < lvl))
|
if (_victim.GetLevelForTarget(member) > grayLevel && (!_maxNotGrayMember || _maxNotGrayMember.getLevel() < lvl))
|
||||||
_maxNotGrayMember = member;
|
_maxNotGrayMember = member;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2363,7 +2363,7 @@ namespace Game.Entities
|
|||||||
}
|
}
|
||||||
|
|
||||||
SetUInt32Value(UnitFields.Level, result.Read<uint>(6));
|
SetUInt32Value(UnitFields.Level, result.Read<uint>(6));
|
||||||
SetUInt32Value(PlayerFields.Xp, result.Read<uint>(7));
|
SetXP(result.Read<uint>(7));
|
||||||
|
|
||||||
_LoadIntoDataField(result.Read<string>(65), (int)PlayerFields.ExploredZones1, PlayerConst.ExploredZonesSize);
|
_LoadIntoDataField(result.Read<string>(65), (int)PlayerFields.ExploredZones1, PlayerConst.ExploredZonesSize);
|
||||||
_LoadIntoDataField(result.Read<string>(66), (int)PlayerFields.KnownTitles, PlayerConst.KnowTitlesSize * 2);
|
_LoadIntoDataField(result.Read<string>(66), (int)PlayerFields.KnownTitles, PlayerConst.KnowTitlesSize * 2);
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ namespace Game.Entities
|
|||||||
|
|
||||||
byte k_level = (byte)getLevel();
|
byte k_level = (byte)getLevel();
|
||||||
byte k_grey = (byte)Formulas.GetGrayLevel(k_level);
|
byte k_grey = (byte)Formulas.GetGrayLevel(k_level);
|
||||||
byte v_level = (byte)victim.getLevel();
|
byte v_level = (byte)victim.GetLevelForTarget(this);
|
||||||
|
|
||||||
if (v_level <= k_grey)
|
if (v_level <= k_grey)
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -1646,7 +1646,7 @@ namespace Game.Entities
|
|||||||
|
|
||||||
if (Rep.RepFaction1 != 0 && (!Rep.TeamDependent || team == Team.Alliance))
|
if (Rep.RepFaction1 != 0 && (!Rep.TeamDependent || team == Team.Alliance))
|
||||||
{
|
{
|
||||||
int donerep1 = CalculateReputationGain(ReputationSource.Kill, victim.getLevel(), Rep.RepValue1, (int)(ChampioningFaction != 0 ? ChampioningFaction : Rep.RepFaction1));
|
int donerep1 = CalculateReputationGain(ReputationSource.Kill, victim.GetLevelForTarget(this), Rep.RepValue1, (int)(ChampioningFaction != 0 ? ChampioningFaction : Rep.RepFaction1));
|
||||||
donerep1 = (int)(donerep1 * rate);
|
donerep1 = (int)(donerep1 * rate);
|
||||||
|
|
||||||
FactionRecord factionEntry1 = CliDB.FactionStorage.LookupByKey(ChampioningFaction != 0 ? ChampioningFaction : Rep.RepFaction1);
|
FactionRecord factionEntry1 = CliDB.FactionStorage.LookupByKey(ChampioningFaction != 0 ? ChampioningFaction : Rep.RepFaction1);
|
||||||
@@ -1657,7 +1657,7 @@ namespace Game.Entities
|
|||||||
|
|
||||||
if (Rep.RepFaction2 != 0 && (!Rep.TeamDependent || team == Team.Horde))
|
if (Rep.RepFaction2 != 0 && (!Rep.TeamDependent || team == Team.Horde))
|
||||||
{
|
{
|
||||||
int donerep2 = CalculateReputationGain(ReputationSource.Kill, victim.getLevel(), Rep.RepValue2, (int)(ChampioningFaction != 0 ? ChampioningFaction : Rep.RepFaction2));
|
int donerep2 = CalculateReputationGain(ReputationSource.Kill, victim.GetLevelForTarget(this), Rep.RepValue2, (int)(ChampioningFaction != 0 ? ChampioningFaction : Rep.RepFaction2));
|
||||||
donerep2 = (int)(donerep2 * rate);
|
donerep2 = (int)(donerep2 * rate);
|
||||||
|
|
||||||
FactionRecord factionEntry2 = CliDB.FactionStorage.LookupByKey(ChampioningFaction != 0 ? ChampioningFaction : Rep.RepFaction2);
|
FactionRecord factionEntry2 = CliDB.FactionStorage.LookupByKey(ChampioningFaction != 0 ? ChampioningFaction : Rep.RepFaction2);
|
||||||
@@ -4116,9 +4116,12 @@ namespace Game.Entities
|
|||||||
if (!IsSectionFlagValid(hair, class_, create))
|
if (!IsSectionFlagValid(hair, class_, create))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
CharSectionsRecord facialHair = Global.DB2Mgr.GetCharSectionEntry(race, CharSectionType.Hair, gender, facialHairId, hairColor);
|
if (facialHairId != 0)
|
||||||
if (facialHair == null)
|
{
|
||||||
return false;
|
CharSectionsRecord facialHair = Global.DB2Mgr.GetCharSectionEntry(race, CharSectionType.FacialHair, gender, facialHairId, hairColor);
|
||||||
|
if (facialHair == null)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = 0; i < PlayerConst.CustomDisplaySize; ++i)
|
for (int i = 0; i < PlayerConst.CustomDisplaySize; ++i)
|
||||||
{
|
{
|
||||||
@@ -5064,7 +5067,7 @@ namespace Game.Entities
|
|||||||
// Used in triggers for check "Only to targets that grant experience or honor" req
|
// Used in triggers for check "Only to targets that grant experience or honor" req
|
||||||
public bool isHonorOrXPTarget(Unit victim)
|
public bool isHonorOrXPTarget(Unit victim)
|
||||||
{
|
{
|
||||||
uint v_level = victim.getLevel();
|
uint v_level = victim.GetLevelForTarget(this);
|
||||||
uint k_grey = Formulas.GetGrayLevel(getLevel());
|
uint k_grey = Formulas.GetGrayLevel(getLevel());
|
||||||
|
|
||||||
// Victim level less gray level
|
// Victim level less gray level
|
||||||
@@ -6412,6 +6415,19 @@ namespace Game.Entities
|
|||||||
SendMovementSetCollisionHeight(scale * GetCollisionHeight(IsMounted()));
|
SendMovementSetCollisionHeight(scale * GetCollisionHeight(IsMounted()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SetXP(uint xp)
|
||||||
|
{
|
||||||
|
SetUInt32Value(PlayerFields.Xp, xp);
|
||||||
|
|
||||||
|
int playerLevelDelta = 0;
|
||||||
|
|
||||||
|
// If XP < 50%, player should see scaling creature with -1 level except for level max
|
||||||
|
if (getLevel() < SharedConst.MaxLevel && xp < (GetUInt32Value(PlayerFields.NextLevelXp) / 2))
|
||||||
|
playerLevelDelta = -1;
|
||||||
|
|
||||||
|
SetInt32Value(PlayerFields.ScalingLevelDelta, playerLevelDelta);
|
||||||
|
}
|
||||||
|
|
||||||
public void GiveXP(uint xp, Unit victim, float group_rate = 1.0f)
|
public void GiveXP(uint xp, Unit victim, float group_rate = 1.0f)
|
||||||
{
|
{
|
||||||
if (xp < 1)
|
if (xp < 1)
|
||||||
@@ -6467,7 +6483,7 @@ namespace Game.Entities
|
|||||||
nextLvlXP = GetUInt32Value(PlayerFields.NextLevelXp);
|
nextLvlXP = GetUInt32Value(PlayerFields.NextLevelXp);
|
||||||
}
|
}
|
||||||
|
|
||||||
SetUInt32Value(PlayerFields.Xp, newXP);
|
SetXP(newXP);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void HandleBaseModValue(BaseModGroup modGroup, BaseModType modType, float amount, bool apply)
|
public void HandleBaseModValue(BaseModGroup modGroup, BaseModType modType, float amount, bool apply)
|
||||||
|
|||||||
@@ -420,6 +420,10 @@ namespace Game.Entities
|
|||||||
public ulong CountPctFromMaxHealth(int pct) { return MathFunctions.CalculatePct(GetMaxHealth(), pct); }
|
public ulong CountPctFromMaxHealth(int pct) { return MathFunctions.CalculatePct(GetMaxHealth(), pct); }
|
||||||
ulong CountPctFromCurHealth(int pct) { return MathFunctions.CalculatePct(GetHealth(), pct); }
|
ulong CountPctFromCurHealth(int pct) { return MathFunctions.CalculatePct(GetHealth(), pct); }
|
||||||
|
|
||||||
|
public virtual float GetHealthMultiplierForTarget(WorldObject target) { return 1.0f; }
|
||||||
|
public virtual float GetDamageMultiplierForTarget(WorldObject target) { return 1.0f; }
|
||||||
|
public virtual float GetArmorMultiplierForTarget(WorldObject target) { return 1.0f; }
|
||||||
|
|
||||||
//Powers
|
//Powers
|
||||||
public PowerType getPowerType()
|
public PowerType getPowerType()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -724,7 +724,10 @@ namespace Game.Entities
|
|||||||
{
|
{
|
||||||
absorb += damage;
|
absorb += damage;
|
||||||
damage = 0;
|
damage = 0;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
damage *= (uint)GetDamageMultiplierForTarget(victim);
|
||||||
}
|
}
|
||||||
void DealMeleeDamage(CalcDamageInfo damageInfo, bool durabilityLoss)
|
void DealMeleeDamage(CalcDamageInfo damageInfo, bool durabilityLoss)
|
||||||
{
|
{
|
||||||
@@ -787,7 +790,7 @@ namespace Game.Entities
|
|||||||
|
|
||||||
// there is a newbie protection, at level 10 just 7% base chance; assuming linear function
|
// there is a newbie protection, at level 10 just 7% base chance; assuming linear function
|
||||||
if (victim.getLevel() < 30)
|
if (victim.getLevel() < 30)
|
||||||
Probability = 0.65f * victim.getLevel() + 0.5f;
|
Probability = 0.65f * victim.GetLevelForTarget(this) + 0.5f;
|
||||||
|
|
||||||
uint VictimDefense = victim.GetMaxSkillValueForLevel(this);
|
uint VictimDefense = victim.GetMaxSkillValueForLevel(this);
|
||||||
uint AttackerMeleeSkill = GetMaxSkillValueForLevel();
|
uint AttackerMeleeSkill = GetMaxSkillValueForLevel();
|
||||||
@@ -1018,6 +1021,8 @@ namespace Game.Entities
|
|||||||
victim.ToCreature().LowerPlayerDamageReq(health < damage ? health : damage);
|
victim.ToCreature().LowerPlayerDamageReq(health < damage ? health : damage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
damage /= (uint)victim.GetHealthMultiplierForTarget(this);
|
||||||
|
|
||||||
if (health <= damage)
|
if (health <= damage)
|
||||||
{
|
{
|
||||||
Log.outDebug(LogFilter.Unit, "DealDamage: victim just died");
|
Log.outDebug(LogFilter.Unit, "DealDamage: victim just died");
|
||||||
@@ -1221,6 +1226,10 @@ namespace Game.Entities
|
|||||||
packet.BlockAmount = (int)damageInfo.blocked_amount;
|
packet.BlockAmount = (int)damageInfo.blocked_amount;
|
||||||
packet.LogData.Initialize(damageInfo.attacker);
|
packet.LogData.Initialize(damageInfo.attacker);
|
||||||
|
|
||||||
|
SandboxScalingData sandboxScalingData = new SandboxScalingData();
|
||||||
|
if (sandboxScalingData.GenerateDataForUnits(damageInfo.attacker, damageInfo.target))
|
||||||
|
packet.SandboxScaling = sandboxScalingData;
|
||||||
|
|
||||||
SendCombatLogMessage(packet);
|
SendCombatLogMessage(packet);
|
||||||
}
|
}
|
||||||
public void CombatStart(Unit target, bool initialAggro = true)
|
public void CombatStart(Unit target, bool initialAggro = true)
|
||||||
@@ -2289,7 +2298,7 @@ namespace Game.Entities
|
|||||||
byte bossLevel = 83;
|
byte bossLevel = 83;
|
||||||
uint bossResistanceConstant = 510;
|
uint bossResistanceConstant = 510;
|
||||||
uint resistanceConstant = 0;
|
uint resistanceConstant = 0;
|
||||||
uint level = victim.getLevel();
|
uint level = victim.GetLevelForTarget(this);
|
||||||
|
|
||||||
if (level == bossLevel)
|
if (level == bossLevel)
|
||||||
resistanceConstant = bossResistanceConstant;
|
resistanceConstant = bossResistanceConstant;
|
||||||
@@ -2626,6 +2635,8 @@ namespace Game.Entities
|
|||||||
{
|
{
|
||||||
float armor = victim.GetArmor();
|
float armor = victim.GetArmor();
|
||||||
|
|
||||||
|
armor *= victim.GetArmorMultiplierForTarget(attacker);
|
||||||
|
|
||||||
// bypass enemy armor by SPELL_AURA_BYPASS_ARMOR_FOR_CASTER
|
// bypass enemy armor by SPELL_AURA_BYPASS_ARMOR_FOR_CASTER
|
||||||
int armorBypassPct = 0;
|
int armorBypassPct = 0;
|
||||||
var reductionAuras = victim.GetAuraEffectsByType(AuraType.BypassArmorForCaster);
|
var reductionAuras = victim.GetAuraEffectsByType(AuraType.BypassArmorForCaster);
|
||||||
@@ -2655,10 +2666,10 @@ namespace Game.Entities
|
|||||||
if (IsTypeId(TypeId.Player))
|
if (IsTypeId(TypeId.Player))
|
||||||
{
|
{
|
||||||
float maxArmorPen = 0;
|
float maxArmorPen = 0;
|
||||||
if (victim.getLevel() < 60)
|
if (victim.GetLevelForTarget(attacker) < 60)
|
||||||
maxArmorPen = 400 + 85 * victim.getLevel();
|
maxArmorPen = 400 + 85 * victim.GetLevelForTarget(attacker);
|
||||||
else
|
else
|
||||||
maxArmorPen = 400 + 85 * victim.getLevel() + 4.5f * 85 * (victim.getLevel() - 59);
|
maxArmorPen = 400 + 85 * victim.GetLevelForTarget(attacker) + 4.5f * 85 * (victim.GetLevelForTarget(attacker) - 59);
|
||||||
|
|
||||||
// Cap armor penetration to this number
|
// Cap armor penetration to this number
|
||||||
maxArmorPen = Math.Min((armor + maxArmorPen) / 3, armor);
|
maxArmorPen = Math.Min((armor + maxArmorPen) / 3, armor);
|
||||||
@@ -2671,7 +2682,7 @@ namespace Game.Entities
|
|||||||
if (MathFunctions.fuzzyLe(armor, 0.0f))
|
if (MathFunctions.fuzzyLe(armor, 0.0f))
|
||||||
return damage;
|
return damage;
|
||||||
|
|
||||||
uint attackerLevel = attacker.getLevel();
|
uint attackerLevel = attacker.GetLevelForTarget(victim);
|
||||||
if (attackerLevel > CliDB.ArmorMitigationByLvlGameTable.GetTableRowCount())
|
if (attackerLevel > CliDB.ArmorMitigationByLvlGameTable.GetTableRowCount())
|
||||||
attackerLevel = (uint)CliDB.ArmorMitigationByLvlGameTable.GetTableRowCount();
|
attackerLevel = (uint)CliDB.ArmorMitigationByLvlGameTable.GetTableRowCount();
|
||||||
|
|
||||||
|
|||||||
@@ -745,7 +745,7 @@ namespace Game.Entities
|
|||||||
if (!pet.CreateBaseAtCreature(creatureTarget))
|
if (!pet.CreateBaseAtCreature(creatureTarget))
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
uint level = creatureTarget.getLevel() + 5 < getLevel() ? (getLevel() - 5) : creatureTarget.getLevel();
|
uint level = creatureTarget.GetLevelForTarget(this) + 5 < getLevel() ? (getLevel() - 5) : creatureTarget.GetLevelForTarget(this);
|
||||||
|
|
||||||
InitTamedPet(pet, level, spell_id);
|
InitTamedPet(pet, level, spell_id);
|
||||||
|
|
||||||
|
|||||||
@@ -3079,6 +3079,11 @@ namespace Game.Entities
|
|||||||
packet.Absorbed = (int)log.absorb;
|
packet.Absorbed = (int)log.absorb;
|
||||||
packet.Periodic = log.periodicLog;
|
packet.Periodic = log.periodicLog;
|
||||||
packet.Flags = (int)log.HitInfo;
|
packet.Flags = (int)log.HitInfo;
|
||||||
|
|
||||||
|
SandboxScalingData sandboxScalingData = new SandboxScalingData();
|
||||||
|
if (sandboxScalingData.GenerateDataForUnits(log.attacker, log.target))
|
||||||
|
packet.SandboxScaling.Set(sandboxScalingData);
|
||||||
|
|
||||||
SendCombatLogMessage(packet);
|
SendCombatLogMessage(packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3102,6 +3107,12 @@ namespace Game.Entities
|
|||||||
spellLogEffect.Crit = info.critical;
|
spellLogEffect.Crit = info.critical;
|
||||||
/// @todo: implement debug info
|
/// @todo: implement debug info
|
||||||
|
|
||||||
|
SandboxScalingData sandboxScalingData = new SandboxScalingData();
|
||||||
|
Unit caster = Global.ObjAccessor.GetUnit(this, aura.GetCasterGUID());
|
||||||
|
if (caster)
|
||||||
|
if (sandboxScalingData.GenerateDataForUnits(caster, this))
|
||||||
|
spellLogEffect.SandboxScaling.Set(sandboxScalingData);
|
||||||
|
|
||||||
data.Effects.Add(spellLogEffect);
|
data.Effects.Add(spellLogEffect);
|
||||||
|
|
||||||
SendCombatLogMessage(data);
|
SendCombatLogMessage(data);
|
||||||
|
|||||||
@@ -2127,6 +2127,7 @@ namespace Game.Entities
|
|||||||
{
|
{
|
||||||
return GetUInt32Value(UnitFields.Level);
|
return GetUInt32Value(UnitFields.Level);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override uint GetLevelForTarget(WorldObject target)
|
public override uint GetLevelForTarget(WorldObject target)
|
||||||
{
|
{
|
||||||
return getLevel();
|
return getLevel();
|
||||||
|
|||||||
@@ -2280,6 +2280,41 @@ namespace Game
|
|||||||
|
|
||||||
Log.outInfo(LogFilter.ServerLoading, "Loaded {0} creature model based info in {1} ms", count, Time.GetMSTimeDiffToNow(time));
|
Log.outInfo(LogFilter.ServerLoading, "Loaded {0} creature model based info in {1} ms", count, Time.GetMSTimeDiffToNow(time));
|
||||||
}
|
}
|
||||||
|
public void LoadCreatureScalingData()
|
||||||
|
{
|
||||||
|
uint oldMSTime = Time.GetMSTime();
|
||||||
|
|
||||||
|
// 0 1 2 3
|
||||||
|
SQLResult result = DB.World.Query("SELECT Entry, LevelScalingMin, LevelScalingMax, LevelScalingDelta FROM creature_template_scaling");
|
||||||
|
if (result.IsEmpty())
|
||||||
|
{
|
||||||
|
Log.outInfo(LogFilter.ServerLoading, "Loaded 0 creature template scaling definitions. DB table `creature_template_scaling` is empty.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint count = 0;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
uint entry = result.Read<uint>(0);
|
||||||
|
|
||||||
|
var template = creatureTemplateStorage.LookupByKey(entry);
|
||||||
|
if (template == null)
|
||||||
|
{
|
||||||
|
Log.outError(LogFilter.Sql, $"Creature template (Entry: {entry}) does not exist but has a record in `creature_template_scaling`");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
CreatureLevelScaling creatureLevelScaling;
|
||||||
|
creatureLevelScaling.MinLevel = result.Read<ushort>(1);
|
||||||
|
creatureLevelScaling.MaxLevel = result.Read<ushort>(2);
|
||||||
|
creatureLevelScaling.DeltaLevel = result.Read<short>(3);
|
||||||
|
template.levelScaling.Set(creatureLevelScaling);
|
||||||
|
|
||||||
|
++count;
|
||||||
|
} while (result.NextRow());
|
||||||
|
|
||||||
|
Log.outInfo(LogFilter.ServerLoading, $"Loaded {count} creature template scaling data in {Time.GetMSTimeDiffToNow(oldMSTime)} ms");
|
||||||
|
}
|
||||||
public void CheckCreatureTemplate(CreatureTemplate cInfo)
|
public void CheckCreatureTemplate(CreatureTemplate cInfo)
|
||||||
{
|
{
|
||||||
if (cInfo == null)
|
if (cInfo == null)
|
||||||
@@ -2952,12 +2987,13 @@ namespace Game
|
|||||||
if (!allReqValid)
|
if (!allReqValid)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
spell.LearnedSpellId = spell.SpellId;
|
||||||
foreach (SpellEffectInfo spellEffect in spellInfo.GetEffectsForDifficulty(Difficulty.None))
|
foreach (SpellEffectInfo spellEffect in spellInfo.GetEffectsForDifficulty(Difficulty.None))
|
||||||
{
|
{
|
||||||
if (spellEffect.IsEffect(SpellEffectName.LearnSpell))
|
if (spellEffect != null && spellEffect.IsEffect(SpellEffectName.LearnSpell))
|
||||||
{
|
{
|
||||||
spell.CastSpellId = spell.SpellId;
|
Contract.Assert(spell.LearnedSpellId == spell.SpellId, $"Only one learned spell is currently supported - spell {spell.SpellId} already teaches {spell.LearnedSpellId} but it tried to overwrite it with {spellEffect.TriggerSpell}");
|
||||||
spell.SpellId = spellEffect.TriggerSpell;
|
spell.LearnedSpellId = spellEffect.TriggerSpell;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -292,7 +292,7 @@ namespace Game
|
|||||||
// auto-selection buff level base at target level (in spellInfo)
|
// auto-selection buff level base at target level (in spellInfo)
|
||||||
if (targets.GetUnitTarget() != null)
|
if (targets.GetUnitTarget() != null)
|
||||||
{
|
{
|
||||||
SpellInfo actualSpellInfo = spellInfo.GetAuraRankForLevel(targets.GetUnitTarget().getLevel());
|
SpellInfo actualSpellInfo = spellInfo.GetAuraRankForLevel(targets.GetUnitTarget().GetLevelForTarget(caster));
|
||||||
|
|
||||||
// if rank not found then function return NULL but in explicit cast case original spell can be casted and later failed with appropriate error message
|
// if rank not found then function return NULL but in explicit cast case original spell can be casted and later failed with appropriate error message
|
||||||
if (actualSpellInfo != null)
|
if (actualSpellInfo != null)
|
||||||
|
|||||||
@@ -149,7 +149,7 @@ namespace Game
|
|||||||
{
|
{
|
||||||
float xpMod = 1.0f;
|
float xpMod = 1.0f;
|
||||||
|
|
||||||
gain = BaseGain(player.getLevel(), u.getLevel());
|
gain = BaseGain(player.getLevel(), u.GetLevelForTarget(player));
|
||||||
|
|
||||||
if (gain != 0 && creature)
|
if (gain != 0 && creature)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ namespace Game.Network.Packets
|
|||||||
public int Absorbed;
|
public int Absorbed;
|
||||||
public int Flags;
|
public int Flags;
|
||||||
// Optional<SpellNonMeleeDamageLogDebugInfo> DebugInfo;
|
// Optional<SpellNonMeleeDamageLogDebugInfo> DebugInfo;
|
||||||
Optional<SandboxScalingData> SandboxScaling = new Optional<SandboxScalingData>();
|
public Optional<SandboxScalingData> SandboxScaling = new Optional<SandboxScalingData>();
|
||||||
}
|
}
|
||||||
|
|
||||||
class EnvironmentalDamageLog : CombatLogServerPacket
|
class EnvironmentalDamageLog : CombatLogServerPacket
|
||||||
@@ -310,7 +310,7 @@ namespace Game.Network.Packets
|
|||||||
public uint Resisted;
|
public uint Resisted;
|
||||||
public bool Crit;
|
public bool Crit;
|
||||||
public Optional<PeriodicalAuraLogEffectDebugInfo> DebugInfo;
|
public Optional<PeriodicalAuraLogEffectDebugInfo> DebugInfo;
|
||||||
Optional<SandboxScalingData> SandboxScaling = new Optional<SandboxScalingData>();
|
public Optional<SandboxScalingData> SandboxScaling = new Optional<SandboxScalingData>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -593,7 +593,7 @@ namespace Game.Network.Packets
|
|||||||
public int RageGained;
|
public int RageGained;
|
||||||
public UnkAttackerState UnkState;
|
public UnkAttackerState UnkState;
|
||||||
public float Unk;
|
public float Unk;
|
||||||
SandboxScalingData SandboxScaling = new SandboxScalingData();
|
public SandboxScalingData SandboxScaling = new SandboxScalingData();
|
||||||
}
|
}
|
||||||
|
|
||||||
//Structs
|
//Structs
|
||||||
|
|||||||
@@ -1146,6 +1146,86 @@ namespace Game.Network.Packets
|
|||||||
|
|
||||||
class SandboxScalingData
|
class SandboxScalingData
|
||||||
{
|
{
|
||||||
|
public bool GenerateDataForUnits(Creature attacker, Player target)
|
||||||
|
{
|
||||||
|
CreatureTemplate creatureTemplate = attacker.GetCreatureTemplate();
|
||||||
|
|
||||||
|
Type = SandboxScalingDataType.CreatureToPlayerDamage;
|
||||||
|
PlayerLevelDelta = (short)target.GetInt32Value(PlayerFields.ScalingLevelDelta);
|
||||||
|
PlayerItemLevel = (ushort)target.GetAverageItemLevel();
|
||||||
|
TargetLevel = (byte)target.getLevel();
|
||||||
|
Expansion = (byte)creatureTemplate.RequiredExpansion;
|
||||||
|
Class = (byte)creatureTemplate.UnitClass;
|
||||||
|
TargetMinScalingLevel = (byte)creatureTemplate.levelScaling.Value.MinLevel;
|
||||||
|
TargetMaxScalingLevel = (byte)creatureTemplate.levelScaling.Value.MaxLevel;
|
||||||
|
TargetScalingLevelDelta = (sbyte)creatureTemplate.levelScaling.Value.DeltaLevel;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool GenerateDataForUnits(Player attacker, Creature target)
|
||||||
|
{
|
||||||
|
CreatureTemplate creatureTemplate = target.GetCreatureTemplate();
|
||||||
|
|
||||||
|
Type = SandboxScalingDataType.PlayerToCreatureDamage;
|
||||||
|
PlayerLevelDelta = (short)attacker.GetInt32Value(PlayerFields.ScalingLevelDelta);
|
||||||
|
PlayerItemLevel = (ushort)attacker.GetAverageItemLevel();
|
||||||
|
TargetLevel = (byte)target.getLevel();
|
||||||
|
Expansion = (byte)creatureTemplate.RequiredExpansion;
|
||||||
|
Class = (byte)creatureTemplate.UnitClass;
|
||||||
|
TargetMinScalingLevel = (byte)creatureTemplate.levelScaling.Value.MinLevel;
|
||||||
|
TargetMaxScalingLevel = (byte)creatureTemplate.levelScaling.Value.MaxLevel;
|
||||||
|
TargetScalingLevelDelta = (sbyte)creatureTemplate.levelScaling.Value.DeltaLevel;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool GenerateDataForUnits(Creature attacker, Creature target)
|
||||||
|
{
|
||||||
|
CreatureTemplate creatureTemplate = target.HasScalableLevels() ? target.GetCreatureTemplate() : attacker.GetCreatureTemplate();
|
||||||
|
|
||||||
|
Type = SandboxScalingDataType.CreatureToCreatureDamage;
|
||||||
|
PlayerLevelDelta = 0;
|
||||||
|
PlayerItemLevel = 0;
|
||||||
|
TargetLevel = (byte)target.getLevel();
|
||||||
|
Expansion = (byte)creatureTemplate.RequiredExpansion;
|
||||||
|
Class = (byte)creatureTemplate.UnitClass;
|
||||||
|
TargetMinScalingLevel = (byte)creatureTemplate.levelScaling.Value.MinLevel;
|
||||||
|
TargetMaxScalingLevel = (byte)creatureTemplate.levelScaling.Value.MaxLevel;
|
||||||
|
TargetScalingLevelDelta = (sbyte)creatureTemplate.levelScaling.Value.DeltaLevel;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool GenerateDataForUnits(Unit attacker, Unit target)
|
||||||
|
{
|
||||||
|
Player playerAttacker = attacker.ToPlayer();
|
||||||
|
Creature creatureAttacker = attacker.ToCreature();
|
||||||
|
if (playerAttacker)
|
||||||
|
{
|
||||||
|
Player playerTarget = target.ToPlayer();
|
||||||
|
Creature creatureTarget = target.ToCreature();
|
||||||
|
if (playerTarget)
|
||||||
|
return GenerateDataForUnits(playerAttacker, playerTarget);
|
||||||
|
else if (creatureTarget)
|
||||||
|
{
|
||||||
|
if (creatureTarget.HasScalableLevels())
|
||||||
|
return GenerateDataForUnits(playerAttacker, creatureTarget);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (creatureAttacker)
|
||||||
|
{
|
||||||
|
Player playerTarget = target.ToPlayer();
|
||||||
|
Creature creatureTarget = target.ToCreature();
|
||||||
|
if (playerTarget)
|
||||||
|
return GenerateDataForUnits(creatureAttacker, playerTarget);
|
||||||
|
else if (creatureTarget)
|
||||||
|
{
|
||||||
|
if (creatureAttacker.HasScalableLevels() || creatureTarget.HasScalableLevels())
|
||||||
|
return GenerateDataForUnits(creatureAttacker, creatureTarget);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public void Write(WorldPacket data)
|
public void Write(WorldPacket data)
|
||||||
{
|
{
|
||||||
data.WriteBits(Type, 3);
|
data.WriteBits(Type, 3);
|
||||||
@@ -1159,15 +1239,23 @@ namespace Game.Network.Packets
|
|||||||
data.WriteInt8(TargetScalingLevelDelta);
|
data.WriteInt8(TargetScalingLevelDelta);
|
||||||
}
|
}
|
||||||
|
|
||||||
public uint Type;
|
public SandboxScalingDataType Type;
|
||||||
public short PlayerLevelDelta;
|
public short PlayerLevelDelta;
|
||||||
public ushort PlayerItemLevel;
|
public ushort PlayerItemLevel;
|
||||||
public byte TargetLevel;
|
public byte TargetLevel;
|
||||||
public byte Expansion;
|
public byte Expansion;
|
||||||
public byte Class = 1;
|
public byte Class;
|
||||||
public byte TargetMinScalingLevel = 1;
|
public byte TargetMinScalingLevel;
|
||||||
public byte TargetMaxScalingLevel = 1;
|
public byte TargetMaxScalingLevel;
|
||||||
public sbyte TargetScalingLevelDelta = 1;
|
public sbyte TargetScalingLevelDelta;
|
||||||
|
|
||||||
|
public enum SandboxScalingDataType
|
||||||
|
{
|
||||||
|
PlayerToPlayer = 1, // NYI
|
||||||
|
CreatureToPlayerDamage = 2,
|
||||||
|
PlayerToCreatureDamage = 3,
|
||||||
|
CreatureToCreatureDamage = 4
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class AuraDataInfo
|
public class AuraDataInfo
|
||||||
|
|||||||
@@ -478,6 +478,9 @@ namespace Game
|
|||||||
Log.outInfo(LogFilter.ServerLoading, "Loading Creature template addons...");
|
Log.outInfo(LogFilter.ServerLoading, "Loading Creature template addons...");
|
||||||
Global.ObjectMgr.LoadCreatureTemplateAddons();
|
Global.ObjectMgr.LoadCreatureTemplateAddons();
|
||||||
|
|
||||||
|
Log.outInfo(LogFilter.ServerLoading, "Loading Creature template scaling...");
|
||||||
|
Global.ObjectMgr.LoadCreatureScalingData();
|
||||||
|
|
||||||
Log.outInfo(LogFilter.ServerLoading, "Loading Reputation Reward Rates...");
|
Log.outInfo(LogFilter.ServerLoading, "Loading Reputation Reward Rates...");
|
||||||
Global.ObjectMgr.LoadReputationRewardRate();
|
Global.ObjectMgr.LoadReputationRewardRate();
|
||||||
|
|
||||||
|
|||||||
@@ -150,6 +150,7 @@ namespace Game.Spells
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (GetBase().GetSpellInfo().HasAttribute(SpellAttr8.AuraSendAmount) ||
|
if (GetBase().GetSpellInfo().HasAttribute(SpellAttr8.AuraSendAmount) ||
|
||||||
|
GetBase().HasEffectType(AuraType.ModSpellCategoryCooldown) ||
|
||||||
GetBase().HasEffectType(AuraType.ModMaxCharges) ||
|
GetBase().HasEffectType(AuraType.ModMaxCharges) ||
|
||||||
GetBase().HasEffectType(AuraType.ChargeRecoveryMod) ||
|
GetBase().HasEffectType(AuraType.ChargeRecoveryMod) ||
|
||||||
GetBase().HasEffectType(AuraType.ChargeRecoveryMultiplier))
|
GetBase().HasEffectType(AuraType.ChargeRecoveryMultiplier))
|
||||||
|
|||||||
+11
-11
@@ -1625,7 +1625,7 @@ namespace Game.Spells
|
|||||||
if (m_auraScaleMask != 0 && ihit.effectMask == m_auraScaleMask && m_caster != target)
|
if (m_auraScaleMask != 0 && ihit.effectMask == m_auraScaleMask && m_caster != target)
|
||||||
{
|
{
|
||||||
SpellInfo auraSpell = Global.SpellMgr.GetSpellInfo(Global.SpellMgr.GetFirstSpellInChain(m_spellInfo.Id));
|
SpellInfo auraSpell = Global.SpellMgr.GetSpellInfo(Global.SpellMgr.GetFirstSpellInChain(m_spellInfo.Id));
|
||||||
if (target.getLevel() + 10 >= auraSpell.SpellLevel)
|
if (target.GetLevelForTarget(m_caster) + 10 >= auraSpell.SpellLevel)
|
||||||
ihit.scaleAura = true;
|
ihit.scaleAura = true;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@@ -1646,7 +1646,7 @@ namespace Game.Spells
|
|||||||
if (m_auraScaleMask != 0 && targetInfo.effectMask == m_auraScaleMask && m_caster != target)
|
if (m_auraScaleMask != 0 && targetInfo.effectMask == m_auraScaleMask && m_caster != target)
|
||||||
{
|
{
|
||||||
SpellInfo auraSpell = Global.SpellMgr.GetSpellInfo(Global.SpellMgr.GetFirstSpellInChain(m_spellInfo.Id));
|
SpellInfo auraSpell = Global.SpellMgr.GetSpellInfo(Global.SpellMgr.GetFirstSpellInChain(m_spellInfo.Id));
|
||||||
if (target.getLevel() + 10 >= auraSpell.SpellLevel)
|
if (target.GetLevelForTarget(m_caster) + 10 >= auraSpell.SpellLevel)
|
||||||
targetInfo.scaleAura = true;
|
targetInfo.scaleAura = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4874,7 +4874,7 @@ namespace Game.Spells
|
|||||||
SkillType skill = creature.GetCreatureTemplate().GetRequiredLootSkill();
|
SkillType skill = creature.GetCreatureTemplate().GetRequiredLootSkill();
|
||||||
|
|
||||||
ushort skillValue = m_caster.ToPlayer().GetSkillValue(skill);
|
ushort skillValue = m_caster.ToPlayer().GetSkillValue(skill);
|
||||||
uint TargetLevel = m_targets.GetUnitTarget().getLevel();
|
uint TargetLevel = m_targets.GetUnitTarget().GetLevelForTarget(m_caster);
|
||||||
int ReqValue = (int)(skillValue < 100 ? (TargetLevel - 10) * 10 : TargetLevel * 5);
|
int ReqValue = (int)(skillValue < 100 ? (TargetLevel - 10) * 10 : TargetLevel * 5);
|
||||||
if (ReqValue > skillValue)
|
if (ReqValue > skillValue)
|
||||||
return SpellCastResult.LowCastlevel;
|
return SpellCastResult.LowCastlevel;
|
||||||
@@ -5199,7 +5199,7 @@ namespace Game.Spells
|
|||||||
return SpellCastResult.TargetIsPlayerControlled;
|
return SpellCastResult.TargetIsPlayerControlled;
|
||||||
|
|
||||||
int damage = CalculateDamage(effect.EffectIndex, target1);
|
int damage = CalculateDamage(effect.EffectIndex, target1);
|
||||||
if (damage != 0 && target1.getLevel() > damage)
|
if (damage != 0 && target1.GetLevelForTarget(m_caster) > damage)
|
||||||
return SpellCastResult.Highlevel;
|
return SpellCastResult.Highlevel;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -6118,19 +6118,19 @@ namespace Game.Spells
|
|||||||
return SpellCastResult.EquippedItem;
|
return SpellCastResult.EquippedItem;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SpellEffectName.CreateManaGem:
|
case SpellEffectName.RechargeItem:
|
||||||
{
|
{
|
||||||
uint item_id = effect.ItemType;
|
uint itemId = effect.ItemType;
|
||||||
ItemTemplate proto = Global.ObjectMgr.GetItemTemplate(item_id);
|
|
||||||
|
|
||||||
|
ItemTemplate proto = Global.ObjectMgr.GetItemTemplate(itemId);
|
||||||
if (proto == null)
|
if (proto == null)
|
||||||
return SpellCastResult.ItemAtMaxCharges;
|
return SpellCastResult.ItemAtMaxCharges;
|
||||||
|
|
||||||
Item pitem = player.GetItemByEntry(item_id);
|
Item item = player.GetItemByEntry(itemId);
|
||||||
if (pitem != null)
|
if (item != null)
|
||||||
{
|
{
|
||||||
for (int x = 0; x < proto.Effects.Count && x < 5; ++x)
|
for (int x = 0; x < proto.Effects.Count && x < 5; ++x)
|
||||||
if (proto.Effects[x].Charges != 0 && pitem.GetSpellCharges(x) == proto.Effects[x].Charges)
|
if (proto.Effects[x].Charges != 0 && item.GetSpellCharges(x) == proto.Effects[x].Charges)
|
||||||
return SpellCastResult.ItemAtMaxCharges;
|
return SpellCastResult.ItemAtMaxCharges;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -6343,7 +6343,7 @@ namespace Game.Spells
|
|||||||
return false;
|
return false;
|
||||||
int damage = CalculateDamage(effect.EffectIndex, target);
|
int damage = CalculateDamage(effect.EffectIndex, target);
|
||||||
if (damage != 0)
|
if (damage != 0)
|
||||||
if (target.getLevel() > damage)
|
if (target.GetLevelForTarget(m_caster) > damage)
|
||||||
return false;
|
return false;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|||||||
+12
-20
@@ -2464,7 +2464,7 @@ namespace Game.Spells
|
|||||||
// "kill" original creature
|
// "kill" original creature
|
||||||
creatureTarget.DespawnOrUnsummon();
|
creatureTarget.DespawnOrUnsummon();
|
||||||
|
|
||||||
uint level = (creatureTarget.getLevel() < (m_caster.getLevel() - 5)) ? (m_caster.getLevel() - 5) : creatureTarget.getLevel();
|
uint level = (creatureTarget.GetLevelForTarget(m_caster) < (m_caster.GetLevelForTarget(creatureTarget) - 5)) ? (m_caster.GetLevelForTarget(creatureTarget) - 5) : creatureTarget.GetLevelForTarget(m_caster);
|
||||||
|
|
||||||
// prepare visual effect for levelup
|
// prepare visual effect for levelup
|
||||||
pet.SetUInt32Value(UnitFields.Level, level - 1);
|
pet.SetUInt32Value(UnitFields.Level, level - 1);
|
||||||
@@ -4169,7 +4169,7 @@ namespace Game.Spells
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
Creature creature = unitTarget.ToCreature();
|
Creature creature = unitTarget.ToCreature();
|
||||||
int targetLevel = (int)creature.getLevel();
|
int targetLevel = (int)creature.GetLevelForTarget(m_caster);
|
||||||
|
|
||||||
SkillType skill = creature.GetCreatureTemplate().GetRequiredLootSkill();
|
SkillType skill = creature.GetCreatureTemplate().GetRequiredLootSkill();
|
||||||
|
|
||||||
@@ -5376,35 +5376,27 @@ namespace Game.Spells
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[SpellEffectHandler(SpellEffectName.CreateManaGem)]
|
[SpellEffectHandler(SpellEffectName.RechargeItem)]
|
||||||
void EffectRechargeManaGem(uint effIndex)
|
void EffectRechargeItem(uint effIndex)
|
||||||
{
|
{
|
||||||
if (effectHandleMode != SpellEffectHandleMode.HitTarget)
|
if (effectHandleMode != SpellEffectHandleMode.HitTarget)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (unitTarget == null || !unitTarget.IsTypeId(TypeId.Player))
|
if (unitTarget == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Player player = m_caster.ToPlayer();
|
Player player = unitTarget.ToPlayer();
|
||||||
|
|
||||||
if (player == null)
|
if (player == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
uint item_id = GetEffect(0).ItemType;
|
Item item = player.GetItemByEntry(effectInfo.ItemType);
|
||||||
|
if (item != null)
|
||||||
ItemTemplate proto = Global.ObjectMgr.GetItemTemplate(item_id);
|
|
||||||
if (proto == null)
|
|
||||||
{
|
|
||||||
player.SendEquipError(InventoryResult.ItemNotFound);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Item pItem = player.GetItemByEntry(item_id);
|
|
||||||
if (pItem != null)
|
|
||||||
{
|
{
|
||||||
|
ItemTemplate proto = item.GetTemplate();
|
||||||
for (int x = 0; x < proto.Effects.Count && x < 5; ++x)
|
for (int x = 0; x < proto.Effects.Count && x < 5; ++x)
|
||||||
pItem.SetSpellCharges(x, proto.Effects[x].Charges);
|
item.SetSpellCharges(x, proto.Effects[x].Charges);
|
||||||
pItem.SetState(ItemUpdateState.Changed, player);
|
|
||||||
|
item.SetState(ItemUpdateState.Changed, player);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3124,7 +3124,7 @@ namespace Game.Spells
|
|||||||
new StaticData(SpellEffectImplicitTargetTypes.Explicit, SpellTargetObjectTypes.Unit), // 63 SPELL_EFFECT_THREAT
|
new StaticData(SpellEffectImplicitTargetTypes.Explicit, SpellTargetObjectTypes.Unit), // 63 SPELL_EFFECT_THREAT
|
||||||
new StaticData(SpellEffectImplicitTargetTypes.None, SpellTargetObjectTypes.None), // 64 SPELL_EFFECT_TRIGGER_SPELL
|
new StaticData(SpellEffectImplicitTargetTypes.None, SpellTargetObjectTypes.None), // 64 SPELL_EFFECT_TRIGGER_SPELL
|
||||||
new StaticData(SpellEffectImplicitTargetTypes.Explicit, SpellTargetObjectTypes.Unit), // 65 SPELL_EFFECT_APPLY_AREA_AURA_RAID
|
new StaticData(SpellEffectImplicitTargetTypes.Explicit, SpellTargetObjectTypes.Unit), // 65 SPELL_EFFECT_APPLY_AREA_AURA_RAID
|
||||||
new StaticData(SpellEffectImplicitTargetTypes.Explicit, SpellTargetObjectTypes.Unit), // 66 SPELL_EFFECT_CREATE_MANA_GEM
|
new StaticData(SpellEffectImplicitTargetTypes.Explicit, SpellTargetObjectTypes.Unit), // 66 SPELL_EFFECT_RECHARGE_ITEM
|
||||||
new StaticData(SpellEffectImplicitTargetTypes.Explicit, SpellTargetObjectTypes.Unit), // 67 SPELL_EFFECT_HEAL_MAX_HEALTH
|
new StaticData(SpellEffectImplicitTargetTypes.Explicit, SpellTargetObjectTypes.Unit), // 67 SPELL_EFFECT_HEAL_MAX_HEALTH
|
||||||
new StaticData(SpellEffectImplicitTargetTypes.Explicit, SpellTargetObjectTypes.Unit), // 68 SPELL_EFFECT_INTERRUPT_CAST
|
new StaticData(SpellEffectImplicitTargetTypes.Explicit, SpellTargetObjectTypes.Unit), // 68 SPELL_EFFECT_INTERRUPT_CAST
|
||||||
new StaticData(SpellEffectImplicitTargetTypes.Explicit, SpellTargetObjectTypes.UnitAndDest), // 69 SPELL_EFFECT_DISTRACT
|
new StaticData(SpellEffectImplicitTargetTypes.Explicit, SpellTargetObjectTypes.UnitAndDest), // 69 SPELL_EFFECT_DISTRACT
|
||||||
|
|||||||
Reference in New Issue
Block a user