Core/Creature: Implemented sparring with max health percent thresholds
Port From (https://github.com/TrinityCore/TrinityCore/commit/0750b7f8455df39a64462636ca296c6f2aa2b048)
This commit is contained in:
@@ -490,8 +490,6 @@ namespace Game.AI
|
||||
me.ScheduleAIChange();
|
||||
}
|
||||
|
||||
public virtual bool ShouldSparWith(Unit target) { return false; }
|
||||
|
||||
public virtual void DoAction(int action) { }
|
||||
public virtual uint GetData(uint id = 0) { return 0; }
|
||||
public virtual void SetData(uint id, uint value) { }
|
||||
|
||||
@@ -30,6 +30,7 @@ namespace Game.Entities
|
||||
|
||||
uint _gossipMenuId;
|
||||
uint? _trainerId;
|
||||
float _sparringHealthPct;
|
||||
|
||||
public ulong m_PlayerDamageReq;
|
||||
public float m_SightDistance;
|
||||
|
||||
@@ -413,7 +413,7 @@ namespace Game.Entities
|
||||
InitializeMovementFlags();
|
||||
|
||||
LoadCreaturesAddon();
|
||||
|
||||
LoadCreaturesSparringHealth();
|
||||
LoadTemplateImmunities();
|
||||
GetThreatManager().EvaluateSuppressed();
|
||||
|
||||
@@ -855,7 +855,9 @@ namespace Game.Entities
|
||||
m_corpseDelay = WorldConfig.GetUIntValue(WorldCfg.CorpseDecayNormal);
|
||||
break;
|
||||
}
|
||||
|
||||
LoadCreaturesAddon();
|
||||
LoadCreaturesSparringHealth();
|
||||
|
||||
//! Need to be called after LoadCreaturesAddon - MOVEMENTFLAG_HOVER is set there
|
||||
posZ += GetHoverOffset();
|
||||
@@ -1570,6 +1572,54 @@ namespace Game.Entities
|
||||
}
|
||||
}
|
||||
|
||||
float GetSparringHealthPct() { return _sparringHealthPct; }
|
||||
|
||||
public void OverrideSparringHealthPct(List<float> healthPct)
|
||||
{
|
||||
_sparringHealthPct = healthPct.SelectRandom();
|
||||
}
|
||||
|
||||
public uint CalculateDamageForSparring(Unit attacker, uint damage)
|
||||
{
|
||||
if (GetSparringHealthPct() == 0)
|
||||
return damage;
|
||||
|
||||
if (!attacker.IsCreature() || attacker.IsCharmedOwnedByPlayerOrPlayer() || IsCharmedOwnedByPlayerOrPlayer())
|
||||
return damage;
|
||||
|
||||
if (GetHealthPct() <= GetSparringHealthPct())
|
||||
return 0;
|
||||
|
||||
uint sparringHealth = (uint)(GetMaxHealth() * GetSparringHealthPct() / 100);
|
||||
if (GetHealth() - damage <= sparringHealth)
|
||||
return (uint)(GetHealth() - sparringHealth);
|
||||
|
||||
if (damage >= GetHealth())
|
||||
return (uint)(GetHealth() - 1);
|
||||
|
||||
return damage;
|
||||
}
|
||||
|
||||
public bool ShouldFakeDamageFrom(Unit attacker)
|
||||
{
|
||||
if (GetSparringHealthPct() == 0)
|
||||
return false;
|
||||
|
||||
if (!attacker)
|
||||
return false;
|
||||
|
||||
if (!attacker.IsCreature())
|
||||
return false;
|
||||
|
||||
if (attacker.IsCharmedOwnedByPlayerOrPlayer() || IsCharmedOwnedByPlayerOrPlayer())
|
||||
return false;
|
||||
|
||||
if (GetHealthPct() > GetSparringHealthPct())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CreateFromProto(ulong guidlow, uint entry, CreatureData data = null, uint vehId = 0)
|
||||
{
|
||||
SetZoneScript();
|
||||
@@ -1960,6 +2010,7 @@ namespace Game.Entities
|
||||
InitializeMovementAI();
|
||||
base.SetDeathState(DeathState.Alive);
|
||||
LoadCreaturesAddon();
|
||||
LoadCreaturesSparringHealth();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2462,6 +2513,13 @@ namespace Game.Entities
|
||||
return true;
|
||||
}
|
||||
|
||||
public void LoadCreaturesSparringHealth()
|
||||
{
|
||||
var templateValues = Global.ObjectMgr.GetCreatureTemplateSparringValues(GetCreatureTemplate().Entry);
|
||||
if (!templateValues.Empty())
|
||||
_sparringHealthPct = templateValues.SelectRandom();
|
||||
}
|
||||
|
||||
// Send a message to LocalDefense channel for players opposition team in the zone
|
||||
public void SendZoneUnderAttackMessage(Player attacker)
|
||||
{
|
||||
|
||||
@@ -564,6 +564,15 @@ namespace Game.Entities
|
||||
CalculateMeleeDamage(victim, out damageInfo, attType);
|
||||
// Send log damage message to client
|
||||
DealDamageMods(damageInfo.Attacker, victim, ref damageInfo.Damage, ref damageInfo.Absorb);
|
||||
|
||||
// sparring
|
||||
Creature victimCreature = victim.ToCreature();
|
||||
if (victimCreature != null)
|
||||
{
|
||||
if (victimCreature.ShouldFakeDamageFrom(damageInfo.Attacker))
|
||||
damageInfo.HitInfo |= HitInfo.FakeDamage;
|
||||
}
|
||||
|
||||
SendAttackStateUpdate(damageInfo);
|
||||
|
||||
_lastDamagedTargetGuid = victim.GetGUID();
|
||||
|
||||
@@ -2489,6 +2489,11 @@ namespace Game.Entities
|
||||
{
|
||||
uint tmpDamage = damageTaken;
|
||||
|
||||
// sparring
|
||||
Creature victimCreature = victim.ToCreature();
|
||||
if (victimCreature != null)
|
||||
tmpDamage = victimCreature.CalculateDamageForSparring(attacker, tmpDamage);
|
||||
|
||||
victim.GetAI()?.DamageTaken(attacker, ref tmpDamage, damagetype, spellProto);
|
||||
|
||||
attacker?.GetAI()?.DamageDealt(victim, ref tmpDamage, damagetype);
|
||||
@@ -3652,6 +3657,14 @@ namespace Game.Entities
|
||||
uint split_absorb = 0;
|
||||
DealDamageMods(damageInfo.GetAttacker(), caster, ref splitDamage, ref split_absorb);
|
||||
|
||||
// sparring
|
||||
Creature victimCreature = damageInfo.GetVictim().ToCreature();
|
||||
if (victimCreature != null)
|
||||
{
|
||||
if (victimCreature.ShouldFakeDamageFrom(damageInfo.GetAttacker()))
|
||||
damageInfo.ModifyDamage((int)(damageInfo.GetDamage() * -1));
|
||||
}
|
||||
|
||||
SpellNonMeleeDamage log = new(damageInfo.GetAttacker(), caster, itr.GetSpellInfo(), itr.GetBase().GetSpellVisual(), damageInfo.GetSchoolMask(), itr.GetBase().GetCastId());
|
||||
CleanDamage cleanDamage = new(splitDamage, 0, WeaponAttackType.BaseAttack, MeleeHitOutcome.Normal);
|
||||
DealDamage(damageInfo.GetAttacker(), caster, splitDamage, cleanDamage, DamageEffectType.Direct, damageInfo.GetSchoolMask(), itr.GetSpellInfo(), false);
|
||||
|
||||
@@ -2691,6 +2691,44 @@ namespace Game
|
||||
|
||||
Log.outInfo(LogFilter.ServerLoading, "Loaded {0} creature model based info in {1} ms", count, Time.GetMSTimeDiffToNow(time));
|
||||
}
|
||||
|
||||
public void LoadCreatureTemplateSparring()
|
||||
{
|
||||
uint oldMSTime = Time.GetMSTime();
|
||||
|
||||
// 0 1
|
||||
SQLResult result = DB.World.Query("SELECT Entry, NoNPCDamageBelowHealthPct FROM creature_template_sparring");
|
||||
if (result.IsEmpty())
|
||||
{
|
||||
Log.outInfo(LogFilter.ServerLoading, "Loaded 0 creature template sparring definitions. DB table `creature_template_sparring` is empty.");
|
||||
return;
|
||||
}
|
||||
|
||||
uint count = 0;
|
||||
do
|
||||
{
|
||||
uint entry = result.Read<uint>(0);
|
||||
float noNPCDamageBelowHealthPct = result.Read<float>(1);
|
||||
|
||||
if (GetCreatureTemplate(entry) == null)
|
||||
{
|
||||
Log.outError(LogFilter.Sql, $"Creature template (Entry: {entry}) does not exist but has a record in `creature_template_sparring`");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (noNPCDamageBelowHealthPct <= 0 || noNPCDamageBelowHealthPct > 100)
|
||||
{
|
||||
Log.outError(LogFilter.Sql, $"Creature (Entry: {entry}) has invalid NoNPCDamageBelowHealthPct ({noNPCDamageBelowHealthPct}) defined in `creature_template_sparring`. Skipping");
|
||||
continue;
|
||||
}
|
||||
_creatureTemplateSparringStorage.Add(entry, noNPCDamageBelowHealthPct);
|
||||
|
||||
++count;
|
||||
} while (result.NextRow());
|
||||
|
||||
Log.outInfo(LogFilter.ServerLoading, $"Loaded {count} creature template sparring rows in {Time.GetMSTimeDiffToNow(oldMSTime)} ms");
|
||||
}
|
||||
|
||||
public void LoadCreatureScalingData()
|
||||
{
|
||||
uint oldMSTime = Time.GetMSTime();
|
||||
@@ -5345,14 +5383,22 @@ namespace Game
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public VendorItemData GetNpcVendorItemList(uint entry)
|
||||
{
|
||||
return cacheVendorItemStorage.LookupByKey(entry);
|
||||
}
|
||||
|
||||
public List<float> GetCreatureTemplateSparringValues(uint entry)
|
||||
{
|
||||
return _creatureTemplateSparringStorage.LookupByKey(entry);
|
||||
}
|
||||
|
||||
public CreatureMovementData GetCreatureMovementOverride(ulong spawnId)
|
||||
{
|
||||
return creatureMovementOverrides.LookupByKey(spawnId);
|
||||
}
|
||||
|
||||
public EquipmentInfo GetEquipmentInfo(uint entry, int id)
|
||||
{
|
||||
var equip = equipmentInfoStorage.LookupByKey(entry);
|
||||
@@ -11022,6 +11068,7 @@ namespace Game
|
||||
Dictionary<ulong, CreatureAddon> creatureAddonStorage = new();
|
||||
MultiMap<uint, uint> creatureQuestItemStorage = new();
|
||||
Dictionary<uint, CreatureAddon> creatureTemplateAddonStorage = new();
|
||||
MultiMap<uint, float> _creatureTemplateSparringStorage = new();
|
||||
Dictionary<ulong, CreatureMovementData> creatureMovementOverrides = new();
|
||||
MultiMap<uint, Tuple<uint, EquipmentInfo>> equipmentInfoStorage = new();
|
||||
Dictionary<ObjectGuid, ObjectGuid> linkedRespawnStorage = new();
|
||||
|
||||
@@ -65,6 +65,7 @@ namespace Game.AI
|
||||
|
||||
owner.SetSpawnHealth();
|
||||
owner.LoadCreaturesAddon();
|
||||
owner.LoadCreaturesSparringHealth();
|
||||
if (owner.IsVehicle())
|
||||
owner.GetVehicleKit().Reset(true);
|
||||
|
||||
|
||||
@@ -8475,6 +8475,11 @@ namespace Game.Spells
|
||||
|
||||
spell.m_damage = (int)damageInfo.damage;
|
||||
|
||||
// sparring
|
||||
Creature victimCreature = damageInfo.target.ToCreature();
|
||||
if (victimCreature != null)
|
||||
damageInfo.damage = victimCreature.CalculateDamageForSparring(damageInfo.attacker, damageInfo.damage);
|
||||
|
||||
caster.DealSpellDamage(damageInfo, true);
|
||||
|
||||
// Send log damage message to client
|
||||
|
||||
@@ -606,6 +606,9 @@ namespace Game
|
||||
Log.outInfo(LogFilter.ServerLoading, "Loading Creature template addons...");
|
||||
Global.ObjectMgr.LoadCreatureTemplateAddons();
|
||||
|
||||
Log.outInfo(LogFilter.ServerLoading, "Loading Creature template sparring...");
|
||||
Global.ObjectMgr.LoadCreatureTemplateSparring();
|
||||
|
||||
Log.outInfo(LogFilter.ServerLoading, "Loading Creature template scaling...");
|
||||
Global.ObjectMgr.LoadCreatureScalingData();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user