fix: Changes Map.Sector.Mobiles to link list & Fixes various related crash bugs (#1553)
### Summary
Eliminates `IPooledEnumerable<T>` and `eable.Free()` from `Map` for mobiles. This drastically simplifies code that iterates in range, for example:
```cs
foreach (var m in m.GetMobilesInRange(5))
{
}
```
The code above no longer requires an eable and calling `Free()`.
- [X] Fixed several locations where an NPC that was damaged would cause a server crash.
- [X] Removed an unnecessary allocation in guard fake calls (NPCs calling guards on you)
- [X] Fixes damage precision loss in Poison Strike Spell
- [X] BogThing no longer attempts to "search" for boglings to eat when it is at full health
This commit is contained in:
parent
27f0cec1fa
commit
28c06c1cc0
71 changed files with 796 additions and 591 deletions
|
|
@ -21,9 +21,8 @@ namespace Server.Spells.Bushido
|
|||
|
||||
var weapon = attacker.Weapon;
|
||||
|
||||
var eable = attacker.GetMobilesInRange(weapon.MaxRange);
|
||||
using var queue = PooledRefQueue<Mobile>.Create();
|
||||
foreach (var m in eable)
|
||||
foreach (var m in attacker.GetMobilesInRange(weapon.MaxRange))
|
||||
{
|
||||
if (m != defender && m.Combatant == attacker)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -46,9 +46,8 @@ namespace Server.Spells.Chivalry
|
|||
|
||||
var chiv = Caster.Skills.Chivalry.Value;
|
||||
|
||||
var eable = Caster.GetMobilesInRange(8);
|
||||
using var queue = PooledRefQueue<Mobile>.Create();
|
||||
foreach (var m in eable)
|
||||
foreach (var m in Caster.GetMobilesInRange(8))
|
||||
{
|
||||
if (Caster != m && SpellHelper.ValidIndirectTarget(Caster, m) && Caster.CanBeHarmful(m, false))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using Server.Collections;
|
||||
using Server.Items;
|
||||
|
||||
namespace Server.Spells.Chivalry
|
||||
|
|
@ -54,6 +55,7 @@ namespace Server.Spells.Chivalry
|
|||
0
|
||||
);
|
||||
|
||||
using var queue = PooledRefQueue<Mobile>.Create();
|
||||
foreach (var m in Caster.GetMobilesInRange(3))
|
||||
{
|
||||
if (Caster == m || !SpellHelper.ValidIndirectTarget(Caster, m) || !Caster.CanBeHarmful(m, false) ||
|
||||
|
|
@ -62,6 +64,13 @@ namespace Server.Spells.Chivalry
|
|||
continue;
|
||||
}
|
||||
|
||||
queue.Enqueue(m);
|
||||
}
|
||||
|
||||
while (queue.Count > 0)
|
||||
{
|
||||
var m = queue.Dequeue();
|
||||
|
||||
var damage = Math.Clamp(ComputePowerValue(10) + Utility.RandomMinMax(0, 2), 8, 24);
|
||||
|
||||
Caster.DoHarmful(m);
|
||||
|
|
|
|||
|
|
@ -34,10 +34,9 @@ namespace Server.Spells.Chivalry
|
|||
{
|
||||
if (CheckSequence())
|
||||
{
|
||||
var eable = Caster.GetMobilesInRange(3);
|
||||
using var pool = PooledRefQueue<Mobile>.Create();
|
||||
|
||||
foreach (var m in eable)
|
||||
foreach (var m in Caster.GetMobilesInRange(3))
|
||||
{
|
||||
if (m is not BaseCreature { IsAnimatedDead: true } && Caster != m && m.InLOS(Caster) &&
|
||||
Caster.CanBeBeneficial(m, false, true) && m is not Golem)
|
||||
|
|
|
|||
|
|
@ -37,9 +37,8 @@ namespace Server.Spells.Eighth
|
|||
return;
|
||||
}
|
||||
|
||||
var eable = Caster.GetMobilesInRange(1 + (int)(Caster.Skills.Magery.Value / 15.0));
|
||||
using var queue = PooledRefQueue<Mobile>.Create();
|
||||
foreach (var m in eable)
|
||||
foreach (var m in Caster.GetMobilesInRange(1 + (int)(Caster.Skills.Magery.Value / 15.0)))
|
||||
{
|
||||
if (Caster != m && SpellHelper.ValidIndirectTarget(Caster, m) && Caster.CanBeHarmful(m, false) &&
|
||||
(!Core.AOS || Caster.InLOS(m)))
|
||||
|
|
|
|||
|
|
@ -243,17 +243,15 @@ namespace Server.Spells.Fifth
|
|||
if (map != null && caster != null)
|
||||
{
|
||||
var eastToWest = m_Item.ItemID == 0x3915;
|
||||
var eable = map.GetMobilesInBounds(
|
||||
new Rectangle2D(
|
||||
m_Item.X - (eastToWest ? 0 : 1),
|
||||
m_Item.Y - (eastToWest ? 1 : 0),
|
||||
eastToWest ? 1 : 2,
|
||||
eastToWest ? 2 : 1
|
||||
)
|
||||
var bounds = new Rectangle2D(
|
||||
m_Item.X - (eastToWest ? 0 : 1),
|
||||
m_Item.Y - (eastToWest ? 1 : 0),
|
||||
eastToWest ? 1 : 2,
|
||||
eastToWest ? 2 : 1
|
||||
);
|
||||
|
||||
using var queue = PooledRefQueue<Mobile>.Create();
|
||||
foreach (var m in eable)
|
||||
foreach (var m in map.GetMobilesInBounds(bounds))
|
||||
{
|
||||
if (m.Z + 16 > m_Item.Z && m_Item.Z + 12 > m.Z && (!Core.AOS || m != caster) &&
|
||||
SpellHelper.ValidIndirectTarget(caster, m) && caster.CanBeHarmful(m, false))
|
||||
|
|
|
|||
|
|
@ -48,8 +48,7 @@ namespace Server.Spells.Fourth
|
|||
pool.Enqueue(directTarget);
|
||||
}
|
||||
|
||||
var eable = map.GetMobilesInRange(loc, 2);
|
||||
foreach (var m in eable)
|
||||
foreach (var m in map.GetMobilesInRange(loc, 2))
|
||||
{
|
||||
if (m != directTarget && AreaCanTarget(m, feluccaRules))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -48,10 +48,8 @@ namespace Server.Spells.Fourth
|
|||
return;
|
||||
}
|
||||
|
||||
var eable = Caster.Map.GetMobilesInRange(loc, Core.AOS ? 2 : 3);
|
||||
using var targets = PooledRefQueue<Mobile>.Create();
|
||||
|
||||
foreach (var m in eable)
|
||||
foreach (var m in Caster.Map.GetMobilesInRange(loc, Core.AOS ? 2 : 3))
|
||||
{
|
||||
if (Caster.CanBeBeneficial(m, false))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -244,7 +244,7 @@ namespace Server.Spells.Fourth
|
|||
}
|
||||
|
||||
using var queue = PooledRefQueue<Mobile>.Create();
|
||||
foreach (var m in m_Item.GetMobilesInRange(0))
|
||||
foreach (var m in m_Item.GetMobilesAt())
|
||||
{
|
||||
if (m.Z + 16 > m_Item.Z && m_Item.Z + 12 > m.Z && (!Core.AOS || m != caster) &&
|
||||
SpellHelper.ValidIndirectTarget(caster, m) && caster.CanBeHarmful(m, false))
|
||||
|
|
@ -256,10 +256,6 @@ namespace Server.Spells.Fourth
|
|||
while (queue.Count > 0)
|
||||
{
|
||||
var m = queue.Dequeue();
|
||||
if (m == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (SpellHelper.CanRevealCaster(m))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -41,6 +41,10 @@ namespace Server.Spells.Mysticism
|
|||
|
||||
Caster.PlaySound(0x64C);
|
||||
|
||||
var primarySkill = GetBaseSkill(Caster);
|
||||
var secondarySkill = GetDamageSkill(Caster);
|
||||
var cureChance = 10000 + (int)((primarySkill + secondarySkill) / 2 * 75);
|
||||
|
||||
using var pool = PooledRefQueue<Mobile>.Create();
|
||||
pool.Enqueue(m);
|
||||
|
||||
|
|
@ -49,12 +53,7 @@ namespace Server.Spells.Mysticism
|
|||
{
|
||||
foreach (Mobile mob in Caster.Map.GetMobilesInRange(m.Location, 2))
|
||||
{
|
||||
if (mob == m)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (casterParty.Contains(mob) && Caster.CanBeBeneficial(mob, false))
|
||||
if (mob != m && casterParty.Contains(mob) && Caster.CanBeBeneficial(mob, false))
|
||||
{
|
||||
pool.Enqueue(mob);
|
||||
if (pool.Count == 4)
|
||||
|
|
@ -65,9 +64,6 @@ namespace Server.Spells.Mysticism
|
|||
}
|
||||
}
|
||||
|
||||
var primarySkill = GetBaseSkill(Caster);
|
||||
var secondarySkill = GetDamageSkill(Caster);
|
||||
|
||||
var toHeal = ((int)((primarySkill + secondarySkill) / 4.0) + Utility.RandomMinMax(-3, 3)) / pool.Count;
|
||||
|
||||
while (pool.Count > 0)
|
||||
|
|
@ -102,9 +98,9 @@ namespace Server.Spells.Mysticism
|
|||
if (target.Poisoned)
|
||||
{
|
||||
var poisonLevel = target.Poison.Level + 1;
|
||||
var chanceToCure = 10000 + (int)((primarySkill + secondarySkill) / 2 * 75) - poisonLevel * 1750;
|
||||
var chanceToCure = cureChance - poisonLevel * 1750;
|
||||
|
||||
if (chanceToCure > Utility.Random(10000) && target.CurePoison(Caster))
|
||||
if (chanceToCure > 10000 || chanceToCure > Utility.Random(10000) && target.CurePoison(Caster))
|
||||
{
|
||||
toHealMod -= (int)(toHeal * poisonLevel * 0.15);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using Server.Collections;
|
||||
using Server.Engines.CannedEvil;
|
||||
using Server.Engines.PartySystem;
|
||||
using Server.Factions;
|
||||
|
|
@ -94,14 +95,23 @@ namespace Server.Spells.Necromancy
|
|||
|
||||
if (map != null)
|
||||
{
|
||||
// Surprisingly, no sparkle type effects
|
||||
// Cannot move a mobile while iterating mobiles in range, so use a queue
|
||||
using var queue = PooledRefQueue<Mobile>.Create();
|
||||
foreach (var m in r.Spawn.GetMobilesInRange(Range))
|
||||
{
|
||||
if (IsValidTarget(m))
|
||||
{
|
||||
m.Location = GetNearestShrine(m);
|
||||
queue.Enqueue(m);
|
||||
}
|
||||
}
|
||||
|
||||
while (queue.Count > 0)
|
||||
{
|
||||
var m = queue.Dequeue();
|
||||
|
||||
// Surprisingly, no sparkle type effects
|
||||
m.Location = GetNearestShrine(m);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -80,8 +80,7 @@ namespace Server.Spells.Necromancy
|
|||
var cbc = Caster as BaseCreature;
|
||||
var isMonster = cbc?.Controlled == false && (cbc.IsAnimatedDead || !cbc.Summoned);
|
||||
|
||||
var eable = m.GetMobilesInRange(2);
|
||||
foreach (Mobile targ in eable)
|
||||
foreach (Mobile targ in m.GetMobilesInRange(2))
|
||||
{
|
||||
if (targ == Caster || m == targ || !SpellHelper.ValidIndirectTarget(Caster, targ)
|
||||
|| !Caster.CanBeHarmful(targ, false))
|
||||
|
|
|
|||
|
|
@ -51,8 +51,7 @@ namespace Server.Spells.Necromancy
|
|||
var cbc = Caster as BaseCreature;
|
||||
var isMonster = cbc?.Controlled == false && (cbc.IsAnimatedDead || !cbc.Summoned);
|
||||
|
||||
var eable = Caster.GetMobilesInRange(Core.ML ? 4 : 5);
|
||||
foreach (var targ in eable)
|
||||
foreach (var targ in Caster.GetMobilesInRange(Core.ML ? 4 : 5))
|
||||
{
|
||||
if (targ == Caster
|
||||
|| !Caster.InLOS(targ)
|
||||
|
|
@ -106,7 +105,7 @@ namespace Server.Spells.Necromancy
|
|||
|
||||
double damage = Utility.RandomMinMax(30, 35);
|
||||
|
||||
damage *= 300 + m.Karma / 100 + GetDamageSkill(Caster) * 10;
|
||||
damage *= 300 + m.Karma / 100.0 + GetDamageSkill(Caster) * 10;
|
||||
damage /= 1000;
|
||||
|
||||
var sdiBonus = AosAttributes.GetValue(Caster, AosAttribute.SpellDamage);
|
||||
|
|
|
|||
|
|
@ -35,11 +35,9 @@ namespace Server.Spells.Seventh
|
|||
|
||||
if (map != null)
|
||||
{
|
||||
using var pool = PooledRefQueue<Mobile>.Create();
|
||||
var pvp = false;
|
||||
|
||||
var eable = map.GetMobilesInRange(loc, 2);
|
||||
foreach (var m in eable)
|
||||
using var pool = PooledRefQueue<Mobile>.Create();
|
||||
foreach (var m in map.GetMobilesInRange(loc, 2))
|
||||
{
|
||||
if (Core.AOS && (m == Caster || !Caster.InLOS(m)) ||
|
||||
!SpellHelper.ValidIndirectTarget(Caster, m) ||
|
||||
|
|
|
|||
|
|
@ -35,11 +35,8 @@ namespace Server.Spells.Seventh
|
|||
|
||||
if (map != null)
|
||||
{
|
||||
var eable = map.GetMobilesInRange<BaseCreature>(new Point3D(p), 8);
|
||||
|
||||
using var queue = PooledRefQueue<Mobile>.Create();
|
||||
|
||||
foreach (var bc in eable)
|
||||
foreach (var bc in map.GetMobilesInRange<BaseCreature>(new Point3D(p), 8))
|
||||
{
|
||||
if (!(bc.IsDispellable && Caster.CanBeHarmful(bc, false)))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -42,9 +42,8 @@ namespace Server.Spells.Seventh
|
|||
|
||||
if (map != null)
|
||||
{
|
||||
var eable = map.GetMobilesInRange(loc, 2);
|
||||
using var queue = PooledRefQueue<Mobile>.Create();
|
||||
foreach (var m in eable)
|
||||
foreach (var m in map.GetMobilesInRange(loc, 2))
|
||||
{
|
||||
if (Caster == m || !SpellHelper.ValidIndirectTarget(Caster, m) ||
|
||||
!Caster.CanBeHarmful(m, false) || Core.AOS && !Caster.InLOS(m))
|
||||
|
|
|
|||
|
|
@ -32,9 +32,7 @@ namespace Server.Spells.Sixth
|
|||
|
||||
if (map != null)
|
||||
{
|
||||
var eable = map.GetMobilesInRange(new Point3D(p), 2);
|
||||
|
||||
foreach (var m in eable)
|
||||
foreach (var m in map.GetMobilesInRange(new Point3D(p), 2))
|
||||
{
|
||||
if (Core.AOS && (m == Caster || !SpellHelper.ValidIndirectTarget(Caster, m) || !Caster.CanSee(m) ||
|
||||
!Caster.CanBeHarmful(m, false)))
|
||||
|
|
|
|||
|
|
@ -29,12 +29,8 @@ namespace Server.Spells.Sixth
|
|||
|
||||
if (map != null)
|
||||
{
|
||||
var eable = map.GetMobilesInRange(
|
||||
new Point3D(p),
|
||||
1 + (int)(Caster.Skills.Magery.Value / 20.0)
|
||||
);
|
||||
|
||||
foreach (var m in eable)
|
||||
var range = 1 + (int)(Caster.Skills.Magery.Value / 20.0);
|
||||
foreach (var m in map.GetMobilesInRange(new Point3D(p), range))
|
||||
{
|
||||
if (m is ShadowKnight && (m.X != p.X || m.Y != p.Y))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -114,8 +114,7 @@ namespace Server.Spells.Spellweaving
|
|||
private bool CheckArcanists()
|
||||
{
|
||||
var spellWeaving = Caster.Skills.Spellweaving.Value;
|
||||
var eable = Caster.GetMobilesInRange(1);
|
||||
foreach (var m in eable)
|
||||
foreach (var m in Caster.GetMobilesInRange(1))
|
||||
{
|
||||
if (m != Caster && m is PlayerMobile && Caster.CanBeBeneficial(m, false) &&
|
||||
Math.Abs(spellWeaving - m.Skills.Spellweaving.Value) <= 20)
|
||||
|
|
@ -132,9 +131,7 @@ namespace Server.Spells.Spellweaving
|
|||
// Everyone gets the Arcane Focus, power capped elsewhere
|
||||
|
||||
var pool = PooledRefQueue<Mobile>.Create();
|
||||
var eable = Caster.GetMobilesInRange(1);
|
||||
|
||||
foreach (var m in eable)
|
||||
foreach (var m in Caster.GetMobilesInRange(1))
|
||||
{
|
||||
if (m != Caster && m is PlayerMobile && Caster.CanBeBeneficial(m, false) &&
|
||||
Math.Abs(spellWeaving - m.Skills.Spellweaving.Value) <= 20)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Server.Collections;
|
||||
|
||||
namespace Server.Spells.Spellweaving
|
||||
{
|
||||
|
|
@ -24,7 +25,6 @@ namespace Server.Spells.Spellweaving
|
|||
{
|
||||
Caster.PlaySound(0x5C6);
|
||||
|
||||
var range = 5 + FocusLevel;
|
||||
var damage = 25 + FocusLevel;
|
||||
|
||||
var skill = Caster.Skills.Spellweaving.Value;
|
||||
|
|
@ -34,9 +34,8 @@ namespace Server.Spells.Spellweaving
|
|||
var fcMalus = FocusLevel + 1;
|
||||
var ssiMalus = 2 * (FocusLevel + 1);
|
||||
|
||||
var eable = Caster.GetMobilesInRange(range);
|
||||
|
||||
foreach (var m in eable)
|
||||
using var queue = PooledRefQueue<Mobile>.Create();
|
||||
foreach (var m in Caster.GetMobilesInRange(5 + FocusLevel))
|
||||
{
|
||||
if (Caster == m || !Caster.InLOS(m) || !SpellHelper.ValidIndirectTarget(Caster, m) ||
|
||||
!Caster.CanBeHarmful(m, false))
|
||||
|
|
@ -44,6 +43,12 @@ namespace Server.Spells.Spellweaving
|
|||
continue;
|
||||
}
|
||||
|
||||
queue.Enqueue(m);
|
||||
}
|
||||
|
||||
while (queue.Count > 0)
|
||||
{
|
||||
var m = queue.Dequeue();
|
||||
Caster.DoHarmful(m);
|
||||
|
||||
SpellHelper.Damage(this, m, damage, 0, 0, 100, 0, 0);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Server.Collections;
|
||||
|
||||
namespace Server.Spells.Spellweaving
|
||||
{
|
||||
|
|
@ -38,12 +39,10 @@ namespace Server.Spells.Spellweaving
|
|||
|
||||
var pvpDamage = damage * (100 + Math.Min(sdiBonus, 15)) / 100;
|
||||
|
||||
var range = 2 + FocusLevel;
|
||||
var duration = TimeSpan.FromSeconds(5 + FocusLevel);
|
||||
|
||||
var eable = Caster.GetMobilesInRange(range);
|
||||
|
||||
foreach (var m in eable)
|
||||
using var queue = PooledRefQueue<Mobile>.Create();
|
||||
foreach (var m in Caster.GetMobilesInRange(2 + FocusLevel))
|
||||
{
|
||||
if (Caster == m || !SpellHelper.ValidIndirectTarget(Caster, m) || !Caster.CanBeHarmful(m, false) ||
|
||||
!Caster.InLOS(m))
|
||||
|
|
@ -51,6 +50,12 @@ namespace Server.Spells.Spellweaving
|
|||
continue;
|
||||
}
|
||||
|
||||
queue.Enqueue(m);
|
||||
}
|
||||
|
||||
while (queue.Count > 0)
|
||||
{
|
||||
var m = queue.Dequeue();
|
||||
Caster.DoHarmful(m);
|
||||
|
||||
var oldSpell = m.Spell as Spell;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue