fix: Cleans up potential memory leak in monster abilities. (#2185)
This commit is contained in:
parent
f2526c82f3
commit
91b2912ee4
4 changed files with 52 additions and 33 deletions
|
|
@ -1,12 +1,13 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using ModernUO.CodeGeneratedEvents;
|
||||||
|
|
||||||
namespace Server.Mobiles;
|
namespace Server.Mobiles;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Abstract class used to build singletons for managing a specific monster ability.
|
/// Abstract class used to build singletons for managing a specific monster ability.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract partial class MonsterAbility
|
public abstract class MonsterAbility
|
||||||
{
|
{
|
||||||
private Dictionary<BaseCreature, long> _nextTriggerTicks;
|
private Dictionary<BaseCreature, long> _nextTriggerTicks;
|
||||||
|
|
||||||
|
|
@ -18,39 +19,34 @@ public abstract partial class MonsterAbility
|
||||||
public virtual TimeSpan MinTriggerCooldown => TimeSpan.Zero;
|
public virtual TimeSpan MinTriggerCooldown => TimeSpan.Zero;
|
||||||
public virtual TimeSpan MaxTriggerCooldown => TimeSpan.Zero;
|
public virtual TimeSpan MaxTriggerCooldown => TimeSpan.Zero;
|
||||||
|
|
||||||
public bool WillTrigger(MonsterAbilityTrigger trigger) => (AbilityTrigger & trigger) != 0;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns true if ability is not on cooldown, and the chance to trigger succeeds.
|
/// Returns true if ability is not on cooldown, and the chance to trigger succeeds.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>Boolean indicating the ability can trigger.</returns>
|
/// <returns>Boolean indicating the ability can trigger.</returns>
|
||||||
public virtual bool CanTrigger(BaseCreature source, MonsterAbilityTrigger trigger)
|
public virtual bool CanTrigger(BaseCreature source, MonsterAbilityTrigger trigger)
|
||||||
{
|
{
|
||||||
if (source is not { Alive: true, Deleted: false })
|
if ((AbilityTrigger & trigger) == 0 || source is not { Alive: true, Deleted: false })
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_nextTriggerTicks?.TryGetValue(source, out var nextTrigger) == true && nextTrigger - Core.TickCount > 0)
|
if (_nextTriggerTicks?.TryGetValue(source, out var nextTrigger) == true)
|
||||||
{
|
{
|
||||||
return false;
|
if (nextTrigger - Core.TickCount > 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_nextTriggerTicks.Remove(source);
|
||||||
|
if (_nextTriggerTicks.Count == 0)
|
||||||
|
{
|
||||||
|
_nextTriggerTicks = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var c = ChanceToTrigger;
|
var c = ChanceToTrigger;
|
||||||
|
|
||||||
if (c >= 1)
|
return c >= 1 || c > 0 && c > Utility.RandomDouble();
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (c <= 0)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var rnd = Utility.RandomDouble();
|
|
||||||
|
|
||||||
return c > rnd;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -99,7 +95,23 @@ public abstract partial class MonsterAbility
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual void Move(BaseCreature creature, Direction d)
|
public virtual void Move(BaseCreature source, Direction d)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[OnEvent(nameof(BaseCreature.CreatureDeathEvent))]
|
||||||
|
[OnEvent(nameof(BaseCreature.CreatureDeletedEvent))]
|
||||||
|
public static void InvalidateNextAbilityTriggers(BaseCreature source)
|
||||||
|
{
|
||||||
|
var abilities = source.GetMonsterAbilities();
|
||||||
|
if (abilities == null || abilities.Length == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < abilities.Length; i++)
|
||||||
|
{
|
||||||
|
abilities[i]._nextTriggerTicks?.Remove(source);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,14 @@
|
||||||
using System;
|
using System;
|
||||||
using Server.Random;
|
|
||||||
using WeightedMonsterAbility = Server.Random.WeightedValue<Server.Mobiles.MonsterAbility>;
|
using WeightedMonsterAbility = Server.Random.WeightedValue<Server.Mobiles.MonsterAbility>;
|
||||||
|
|
||||||
namespace Server.Mobiles;
|
namespace Server.Mobiles;
|
||||||
|
|
||||||
public class MonsterAbilityGroup : MonsterAbility
|
public class MonsterAbilityGroup : MonsterAbility
|
||||||
{
|
{
|
||||||
private WeightedMonsterAbility[] _weightedAbilities;
|
private readonly WeightedMonsterAbility[] _weightedAbilities;
|
||||||
private WeightedMonsterAbility[] _availableToTrigger;
|
private readonly WeightedMonsterAbility[] _availableToTrigger;
|
||||||
|
private readonly MonsterAbilityTrigger _triggers;
|
||||||
private int _availableToTriggerCount;
|
private int _availableToTriggerCount;
|
||||||
private MonsterAbilityTrigger _triggers;
|
|
||||||
|
|
||||||
public MonsterAbilityGroup(params WeightedMonsterAbility[] weightedAbilities)
|
public MonsterAbilityGroup(params WeightedMonsterAbility[] weightedAbilities)
|
||||||
{
|
{
|
||||||
|
|
@ -18,8 +17,7 @@ public class MonsterAbilityGroup : MonsterAbility
|
||||||
|
|
||||||
for (var i = 0; i < _weightedAbilities.Length; i++)
|
for (var i = 0; i < _weightedAbilities.Length; i++)
|
||||||
{
|
{
|
||||||
var weightedAbility = _weightedAbilities[i];
|
_triggers |= _weightedAbilities[i].Value.AbilityTrigger;
|
||||||
_triggers |= weightedAbility.Value.AbilityTrigger;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -66,7 +64,7 @@ public class MonsterAbilityGroup : MonsterAbility
|
||||||
for (var i = 0; i < _weightedAbilities.Length; i++)
|
for (var i = 0; i < _weightedAbilities.Length; i++)
|
||||||
{
|
{
|
||||||
var weightedAbility = _weightedAbilities[i];
|
var weightedAbility = _weightedAbilities[i];
|
||||||
if (weightedAbility.Value.WillTrigger(trigger) && weightedAbility.Value.CanTrigger(source, trigger))
|
if (weightedAbility.Value.CanTrigger(source, trigger))
|
||||||
{
|
{
|
||||||
_availableToTrigger[_availableToTriggerCount++] = weightedAbility;
|
_availableToTrigger[_availableToTriggerCount++] = weightedAbility;
|
||||||
}
|
}
|
||||||
|
|
@ -83,7 +81,7 @@ public class MonsterAbilityGroup : MonsterAbility
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var slice = new ReadOnlySpan<WeightedValue<MonsterAbility>>(_availableToTrigger, 0, _availableToTriggerCount);
|
var slice = new ReadOnlySpan<WeightedMonsterAbility>(_availableToTrigger, 0, _availableToTriggerCount);
|
||||||
var chosenAbility = slice.RandomWeightedElement().Value;
|
var chosenAbility = slice.RandomWeightedElement().Value;
|
||||||
|
|
||||||
// Just in case?
|
// Just in case?
|
||||||
|
|
|
||||||
|
|
@ -8,4 +8,8 @@ public class ReflectPhysicalDamage : MonsterAbility
|
||||||
public override MonsterAbilityType AbilityType => MonsterAbilityType.ReflectPhysicalDamage;
|
public override MonsterAbilityType AbilityType => MonsterAbilityType.ReflectPhysicalDamage;
|
||||||
|
|
||||||
public virtual int PercentReflected => 10;
|
public virtual int PercentReflected => 10;
|
||||||
|
|
||||||
|
public override void Trigger(MonsterAbilityTrigger trigger, BaseCreature source, Mobile target)
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1216,7 +1216,7 @@ namespace Server.Mobiles
|
||||||
for (var i = 0; i < abilities.Length; i++)
|
for (var i = 0; i < abilities.Length; i++)
|
||||||
{
|
{
|
||||||
var ability = abilities[i];
|
var ability = abilities[i];
|
||||||
if (ability.WillTrigger(trigger) && ability.CanTrigger(this, trigger))
|
if (ability.CanTrigger(this, trigger))
|
||||||
{
|
{
|
||||||
ability.Trigger(trigger, this, defender);
|
ability.Trigger(trigger, this, defender);
|
||||||
triggered = true;
|
triggered = true;
|
||||||
|
|
@ -1238,7 +1238,7 @@ namespace Server.Mobiles
|
||||||
for (var i = 0; i < abilities.Length; i++)
|
for (var i = 0; i < abilities.Length; i++)
|
||||||
{
|
{
|
||||||
var ability = abilities[i];
|
var ability = abilities[i];
|
||||||
if (ability.WillTrigger(trigger) && ability.CanTrigger(this, trigger))
|
if (ability.CanTrigger(this, trigger))
|
||||||
{
|
{
|
||||||
ability.Move(this, d);
|
ability.Move(this, d);
|
||||||
}
|
}
|
||||||
|
|
@ -1257,7 +1257,7 @@ namespace Server.Mobiles
|
||||||
for (var i = 0; i < abilities.Length; i++)
|
for (var i = 0; i < abilities.Length; i++)
|
||||||
{
|
{
|
||||||
var ability = abilities[i];
|
var ability = abilities[i];
|
||||||
if (ability.WillTrigger(trigger) && ability.CanTrigger(this, trigger))
|
if (ability.CanTrigger(this, trigger))
|
||||||
{
|
{
|
||||||
if ((trigger & MonsterAbilityTrigger.GiveMeleeDamage) != 0)
|
if ((trigger & MonsterAbilityTrigger.GiveMeleeDamage) != 0)
|
||||||
{
|
{
|
||||||
|
|
@ -1298,7 +1298,7 @@ namespace Server.Mobiles
|
||||||
for (var i = 0; i < abilities.Length; i++)
|
for (var i = 0; i < abilities.Length; i++)
|
||||||
{
|
{
|
||||||
var ability = abilities[i];
|
var ability = abilities[i];
|
||||||
if (ability.WillTrigger(trigger) && ability.CanTrigger(this, trigger))
|
if (ability.CanTrigger(this, trigger))
|
||||||
{
|
{
|
||||||
if ((trigger & MonsterAbilityTrigger.GiveSpellDamage) != 0)
|
if ((trigger & MonsterAbilityTrigger.GiveSpellDamage) != 0)
|
||||||
{
|
{
|
||||||
|
|
@ -3263,7 +3263,7 @@ namespace Server.Mobiles
|
||||||
}
|
}
|
||||||
|
|
||||||
[GeneratedEvent(nameof(CreatureDeathEvent))]
|
[GeneratedEvent(nameof(CreatureDeathEvent))]
|
||||||
public static partial void CreatureDeathEvent(Mobile m);
|
public static partial void CreatureDeathEvent(BaseCreature bc);
|
||||||
|
|
||||||
public override void OnDeath(Container c)
|
public override void OnDeath(Container c)
|
||||||
{
|
{
|
||||||
|
|
@ -3454,8 +3454,13 @@ namespace Server.Mobiles
|
||||||
CreatureDeathEvent(this);
|
CreatureDeathEvent(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[GeneratedEvent(nameof(CreatureDeletedEvent))]
|
||||||
|
public static partial void CreatureDeletedEvent(BaseCreature bc);
|
||||||
|
|
||||||
public override void OnDelete()
|
public override void OnDelete()
|
||||||
{
|
{
|
||||||
|
CreatureDeletedEvent(this);
|
||||||
|
|
||||||
var m = m_ControlMaster;
|
var m = m_ControlMaster;
|
||||||
SetControlMaster(null);
|
SetControlMaster(null);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue