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:
Kamron Batman 2023-10-29 22:42:46 -07:00 • committed by GitHub
parent 27f0cec1fa
commit 28c06c1cc0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
71 changed files with 796 additions and 591 deletions

View file

@ -1175,10 +1175,8 @@ namespace Server.Commands
if (item is BaseDoor door)
{
var eable = maps[j].GetItemsInRange<BaseDoor>(loc, 1);
var itemType = door.GetType();
foreach (var link in eable)
foreach (var link in maps[j].GetItemsInRange<BaseDoor>(loc, 1))
{
if (link != item && link.Z == door.Z && link.GetType() == itemType)
{

View file

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using ModernUO.Serialization;
using Server.Collections;
using Server.Mobiles;
using Server.Network;
using Server.Spells;
@ -605,30 +606,35 @@ public partial class LeverPuzzleController : Item
PlayerSendASCII(m_Player, 1); // A speeding rock ...
PlaySounds(m_Player.Location, !m_Player.Female ? fs2 : ms2);
using var queue = PooledRefQueue<Mobile>.Create();
var j = Utility.Random(6, 10);
for (var i = 0; i < j; i++)
{
IEntity m_IEntity = new Entity(Serial.Zero, RandomPointIn(m_Player.Location, 10), m_Player.Map);
var entity = new Entity(Serial.Zero, RandomPointIn(m_Player.Location, 10), m_Player.Map);
var eable = m_IEntity.Map.GetMobilesInRange(m_IEntity.Location, 2);
var mobiles = new List<Mobile>();
mobiles.AddRange(eable);
for (var k = 0; k < mobiles.Count; k++)
// Queue the mobiles because we cannot modify while iterating through mobiles in range
foreach (var m in entity.Map.GetMobilesInRange(entity.Location, 2))
{
if (IsValidDamagable(mobiles[k]) && mobiles[k] != m_Player)
if (IsValidDamagable(m) && m != m_Player)
{
PlayEffect(m_Player, mobiles[k], Rock(), 8, true);
DoDamage(mobiles[k], 25, 30, false);
if (mobiles[k].Player)
{
POHMessage(mobiles[k], 2); // OUCH!
}
queue.Enqueue(m);
}
}
PlayEffect(m_Player, m_IEntity, Rock(), 8, false);
while (queue.Count > 0)
{
var m = queue.Dequeue();
PlayEffect(m_Player, m, Rock(), 8, true);
DoDamage(m, 25, 30, false);
if (m.Player)
{
POHMessage(m, 2); // OUCH!
}
}
PlayEffect(m_Player, entity, Rock(), 8, false);
}
}
}

View file

@ -1,5 +1,5 @@
using System;
using System.Collections.Generic;
using Server.Collections;
using Server.Factions;
using Server.Spells;
using Server.Targeting;
@ -90,20 +90,21 @@ namespace Server
private static void OnHit(Mobile from, Point3D origin, Map facet)
{
var eable = facet.GetMobilesInRange(origin, 12);
var targets = new List<Mobile>();
foreach (var m in eable)
using var queue = PooledRefQueue<Mobile>.Create();
foreach (var m in facet.GetMobilesInRange(origin, 12))
{
if (from.CanBeHarmful(m, false) &&
m.InLOS(new Point3D(origin.X, origin.Y, origin.Z + 1)) &&
Faction.Find(m) != null)
{
targets.Add(from);
queue.Enqueue(from);
}
}
foreach (var mob in targets)
while (queue.Count > 0)
{
var mob = queue.Dequeue();
var damage = mob.Hits * 6 / 10;
if (!mob.Player && damage < 10)

View file

@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using ModernUO.Serialization;
using Server.Collections;
using Server.Targeting;
namespace Server.Items;
@ -343,28 +344,13 @@ public abstract partial class BaseDoor : Item, ILockable, ITelekinesisable
}
}
var mobs = map.GetSector(x, y).Mobiles;
for (var i = 0; i < mobs.Count; ++i)
foreach (var m in map.GetMobilesAt(x, y))
{
var m = mobs[i];
if (m.Location.X == x && m.Location.Y == y)
// At the same location, not hidden, or is a player, alive, and within z-bounds - then cannot fit
if (m.Location.X == x && m.Location.Y == y &&
(!m.Hidden || m.AccessLevel == AccessLevel.Player) && m.Alive && m.Z + 16 > z && z + height > m.Z)
{
if (m.Hidden && m.AccessLevel > AccessLevel.Player)
{
continue;
}
if (!m.Alive)
{
continue;
}
if (m.Z + 16 > z && z + height > m.Z)
{
return false;
}
return false;
}
}

View file

@ -64,7 +64,7 @@ public partial class Acid : Item
using var queue = PooledRefQueue<Mobile>.Create();
foreach (var m in GetMobilesInRange(0))
foreach (var m in GetMobilesAt())
{
if (m.AccessLevel == AccessLevel.Player &&
m.Alive && !m.IsDeadBondedPet && (m is not BaseCreature bc || bc.Controlled || bc.Summoned))

View file

@ -122,9 +122,8 @@ public partial class Firebomb : Item
}
else if (RootParent == null)
{
var eable = Map.GetMobilesInRange(Location, 1);
using var targets = PooledRefQueue<Mobile>.Create();
foreach (var m in eable)
foreach (var m in Map.GetMobilesInRange(Location, 1))
{
if (m_LitBy == null || SpellHelper.ValidIndirectTarget(m_LitBy, m) &&
m_LitBy.CanBeHarmful(m, false))

View file

@ -78,8 +78,7 @@ public partial class MorphItem : Item
public void Refresh()
{
var found = false;
var eable = GetMobilesInRange(CurrentRange);
foreach (var mob in eable)
foreach (var mob in GetMobilesInRange(CurrentRange))
{
if (!mob.Hidden || mob.AccessLevel <= AccessLevel.Player)
{

View file

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using ModernUO.Serialization;
using Server.Collections;
using Server.Spells;
using Server.Targeting;
@ -275,17 +276,24 @@ public abstract partial class BaseConflagrationPotion : BasePotion
return;
}
foreach (var m in _item.GetMobilesInRange(0))
using var queue = PooledRefQueue<Mobile>.Create();
foreach (var m in _item.GetMobilesAt())
{
if (m.Z + 16 > _item.Z && _item.Z + 12 > m.Z && (!Core.AOS || m != from) &&
SpellHelper.ValidIndirectTarget(from, m) && from.CanBeHarmful(m, false))
{
from.DoHarmful(m);
AOS.Damage(m, from, _item.GetDamage(), 0, 100, 0, 0, 0);
m.PlaySound(0x208);
queue.Enqueue(m);
}
}
while (queue.Count > 0)
{
var m = queue.Dequeue();
from.DoHarmful(m);
AOS.Damage(m, from, _item.GetDamage(), 0, 100, 0, 0, 0);
m.PlaySound(0x208);
}
}
}
}

View file

@ -82,13 +82,8 @@ public abstract partial class BaseConfusionBlastPotion : BasePotion
foreach (var mobile in map.GetMobilesInRange(loc, Radius))
{
if (mobile is BaseCreature mon)
if (mobile is BaseCreature { Controlled: false, Summoned: false } mon)
{
if (mon.Controlled || mon.Summoned)
{
continue;
}
mon.Pacify(from, Core.Now + TimeSpan.FromSeconds(5.0)); // TODO check
}
}

View file

@ -98,10 +98,9 @@ public partial class FireHorn : Item
);
var playerVsPlayer = false;
var eable = from.Map.GetMobilesInRange(loc, 2);
using var targets = PooledRefQueue<Mobile>.Create();
foreach (var m in eable)
foreach (var m in from.Map.GetMobilesInRange(loc, 2))
{
if (from != m && SpellHelper.ValidIndirectTarget(from, m) && from.CanBeHarmful(m, false) &&
(!Core.AOS || from.InLOS(m)))

View file

@ -90,6 +90,8 @@ public partial class FlameSpurtTrap : BaseTrap
if (_spurt?.Deleted != false)
{
_spurt = new Static(0x3709);
// This MoveToWorld is generally not safe in a range loop, except we return after this on L100
_spurt.MoveToWorld(Location, Map);
Effects.PlaySound(GetWorldLocation(), Map, 0x309);

View file

@ -1,5 +1,6 @@
using System;
using ModernUO.Serialization;
using Server.Collections;
using Server.Spells;
namespace Server.Items;
@ -92,14 +93,21 @@ public partial class SpikeTrap : BaseTrap
Effects.SendLocationEffect(Location, Map, GetBaseID(Type) + 1, 18, 3, GetEffectHue());
Effects.PlaySound(Location, Map, 0x22C);
foreach (var mob in GetMobilesInRange(0))
var queue = PooledRefQueue<Mobile>.Create();
foreach (var mob in GetMobilesAt())
{
if (mob.Alive && !mob.IsDeadBondedPet)
if (mob.Alive && !mob.IsDeadBondedPet && mob.AccessLevel == AccessLevel.Player)
{
SpellHelper.Damage(TimeSpan.FromTicks(1), mob, mob, Utility.RandomMinMax(1, 6) * 6);
queue.Enqueue(mob);
}
}
while (queue.Count > 0)
{
var mob = queue.Dequeue();
SpellHelper.Damage(TimeSpan.FromTicks(1), mob, mob, Utility.RandomMinMax(1, 6) * 6);
}
Timer.StartTimer(TimeSpan.FromSeconds(1.0), OnSpikeExtended);
from.LocalOverheadMessage(MessageType.Regular, 0x22, 500852); // You stepped onto a spike trap!

View file

@ -1,5 +1,6 @@
using System;
using ModernUO.Serialization;
using Server.Collections;
using Server.Spells;
namespace Server.Items;
@ -94,13 +95,20 @@ public partial class StoneFaceTrap : BaseTrap
public virtual void TriggerDamage()
{
var queue = PooledRefQueue<Mobile>.Create();
foreach (var mob in GetMobilesInRange(1))
{
if (mob.Alive && !mob.IsDeadBondedPet && mob.AccessLevel == AccessLevel.Player)
{
SpellHelper.Damage(TimeSpan.FromTicks(1), mob, mob, Utility.Dice(3, 15, 0));
queue.Enqueue(mob);
}
}
while (queue.Count > 0)
{
var mob = queue.Dequeue();
SpellHelper.Damage(TimeSpan.FromTicks(1), mob, mob, Utility.Dice(3, 15, 0));
}
}
[AfterDeserialization]

View file

@ -39,8 +39,7 @@ namespace Server.Items
bool didEffect = false;
var eable = attacker.GetMobilesInRange(1);
foreach (var m in eable)
foreach (var m in attacker.GetMobilesInRange(1))
{
if (m?.Deleted == false && m != defender && m != attacker &&
SpellHelper.ValidIndirectTarget(attacker, m) &&

View file

@ -36,10 +36,9 @@ namespace Server.Items
attacker.FixedEffect(0x3728, 10, 15);
attacker.PlaySound(0x2A1);
var eable = attacker.GetMobilesInRange(1);
using var queue = PooledRefQueue<Mobile>.Create();
foreach (var m in eable)
foreach (var m in attacker.GetMobilesInRange(1))
{
if (m?.Deleted == false && m != defender && m != attacker &&
m.Map == attacker.Map && m.Alive &&

View file

@ -1718,9 +1718,8 @@ public abstract partial class BaseWeapon : Item, IWeapon, IFactionItem, ICraftab
return 0;
}
var eable = defender.GetMobilesInRange<BaseCreature>(1);
var inPack = 1;
foreach (var m in eable)
foreach (var m in defender.GetMobilesInRange<BaseCreature>(1))
{
if (m != attacker && (m.PackInstinct & bc.PackInstinct) != 0 && (m.Controlled || m.Summoned) &&
master == (m.ControlMaster ?? m.SummonMaster) && m.Combatant == defender)
@ -1743,8 +1742,7 @@ public abstract partial class BaseWeapon : Item, IWeapon, IFactionItem, ICraftab
{
if (MirrorImage.HasClone(defender) && defender.Skills.Ninjitsu.Value / 150.0 > Utility.RandomDouble())
{
var eable = defender.GetMobilesInRange<Clone>(4);
foreach (var m in eable)
foreach (var m in defender.GetMobilesInRange<Clone>(4))
{
if (m?.Summoned == true && m.SummonMaster == defender)
{
@ -3570,11 +3568,8 @@ public abstract partial class BaseWeapon : Item, IWeapon, IFactionItem, ICraftab
return;
}
var range = Core.ML ? 5 : 10;
var eable = from.GetMobilesInRange(range);
using var queue = PooledRefQueue<Mobile>.Create();
foreach (var m in eable)
foreach (var m in from.GetMobilesInRange(Core.ML ? 5 : 10))
{
if (from != m && defender != m && SpellHelper.ValidIndirectTarget(from, m)
&& from.CanBeHarmful(m, false) && (!Core.ML || from.InLOS(m)))

View file

@ -2604,9 +2604,7 @@ public abstract class BaseAI
Mobile enemySummonMob = null;
var enemySummonVal = double.MinValue;
var eable = map.GetMobilesInRange(m_Mobile.Location, iRange);
foreach (var m in eable)
foreach (var m in map.GetMobilesInRange(m_Mobile.Location, iRange))
{
if (m.Deleted || m.Blessed)
{
@ -2808,9 +2806,7 @@ public abstract class BaseAI
return;
}
var eable = m_Mobile.GetMobilesInRange(m_Mobile.RangePerception);
foreach (var trg in eable)
foreach (var trg in m_Mobile.GetMobilesInRange(m_Mobile.RangePerception))
{
if (trg != m_Mobile && trg.Player && trg.Alive && trg.Hidden && trg.AccessLevel == AccessLevel.Player &&
m_Mobile.InLOS(trg))

View file

@ -151,21 +151,21 @@ public class HealerAI : BaseAI
foreach (var m in m_Mobile.GetMobilesInRange(m_Mobile.RangePerception))
{
if (!m_Mobile.CanSee(m) || !(m is BaseCreature) || ((BaseCreature)m).Team != m_Mobile.Team)
if (!m_Mobile.CanSee(m) || m is not BaseCreature bc || bc.Team != m_Mobile.Team)
{
continue;
}
for (var i = 0; i < funcs.Length; ++i)
{
if (funcs[i](m))
if (funcs[i](bc))
{
var val = -m_Mobile.GetDistanceToSqrt(m);
var val = -m_Mobile.GetDistanceToSqrt(bc);
if (found == null || val > prio)
{
prio = val;
found = m;
found = bc;
}
break;

View file

@ -8,9 +8,8 @@ public abstract class AreaEffectMonsterAbility : MonsterAbility
public override void Trigger(MonsterAbilityTrigger trigger, BaseCreature source, Mobile target)
{
var eable = source.GetMobilesInRange(AreaRange);
using var queue = PooledRefQueue<Mobile>.Create();
foreach (var m in eable)
foreach (var m in source.GetMobilesInRange(AreaRange))
{
if (CanEffectTarget(source, m))
{

View file

@ -3800,8 +3800,7 @@ namespace Server.Mobiles
}
using var queue = PooledRefQueue<Mobile>.Create();
var eable = master.GetMobilesInRange(3);
foreach (var m in eable)
foreach (var m in master.GetMobilesInRange(3))
{
if (m is BaseCreature
{ Controlled: true, ControlOrder: OrderType.Guard or OrderType.Follow or OrderType.Come } pet &&
@ -5346,9 +5345,8 @@ namespace Server.Mobiles
return;
}
var eable = GetMobilesInRange(AuraRange);
using var queue = PooledRefQueue<Mobile>.Create();
foreach (var m in eable)
foreach (var m in GetMobilesInRange(AuraRange))
{
if (m != this && CanBeHarmful(m, false) && (Core.AOS || InLOS(m)) &&
(m is BaseCreature bc && (bc.Controlled || bc.Summoned || bc.Team != Team) || m.Player))

View file

@ -1,6 +1,5 @@
using System;
using ModernUO.Serialization;
using Server.Collections;
namespace Server.Mobiles;
@ -68,19 +67,13 @@ public partial class ShadowWispFamiliar : BaseFamiliar
return;
}
var eable = GetMobilesInRange(5);
using var queue = PooledRefQueue<Mobile>.Create();
foreach (var m in eable)
foreach (var m in GetMobilesInRange(5))
{
if (m.Player && m.Alive && !m.IsDeadBondedPet && m.Karma <= 0 && m.AccessLevel < AccessLevel.Counselor)
if (!m.Player || !m.Alive || m.IsDeadBondedPet || m.Karma > 0 || m.AccessLevel >= AccessLevel.Counselor)
{
queue.Enqueue(m);
continue;
}
}
while (queue.Count > 0)
{
var m = queue.Dequeue();
var friendly = true;
for (var j = 0; friendly && j < caster.Aggressors.Count; ++j)

View file

@ -1,6 +1,7 @@
using ModernUO.Serialization;
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using Server.Collections;
using Server.Items;
using Server.Spells;
@ -131,13 +132,13 @@ namespace Server.Mobiles
return;
}
var list = new List<SavageShaman>();
using var queue = PooledRefQueue<BaseCreature>.Create();
foreach (var m in GetMobilesInRange(8))
{
if (m != this && m is SavageShaman ss)
{
list.Add(ss);
queue.Enqueue(ss);
}
}
@ -148,22 +149,104 @@ namespace Server.Mobiles
AIObject.NextMove = Core.TickCount + 1000;
}
if (list.Count >= 3)
if (queue.Count < 3)
{
for (var i = 0; i < list.Count; ++i)
{
var dancer = list[i];
dancer.Animate(111, 5, 1, true, false, 0); // Get down tonight...
if (dancer.AIObject != null)
{
dancer.AIObject.NextMove = Core.TickCount + 1000;
}
}
Timer.StartTimer(TimeSpan.FromSeconds(1.0), EndSavageDance);
return;
}
while (queue.Count > 0)
{
var dancer = queue.Dequeue();
dancer.Animate(111, 5, 1, true, false, 0); // Get down tonight...
if (dancer.AIObject != null)
{
dancer.AIObject.NextMove = Core.TickCount + 1000;
}
}
Timer.StartTimer(TimeSpan.FromSeconds(1.0), EndSavageDance);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private bool CanDoGreaterHeal(Mobile m, bool isFriendly) =>
isFriendly && !m.Poisoned && !MortalStrike.IsWounded(m) && CanBeBeneficial(m);
private void DoGreaterHeal(Mobile m)
{
DoBeneficial(m);
// Algorithm: (40% of magery) + (1-10)
var toHeal = (int)(Skills.Magery.Value * 0.4);
toHeal += Utility.Random(1, 10);
m.Heal(toHeal, this);
m.FixedParticles(0x376A, 9, 32, 5030, EffectLayer.Waist);
m.PlaySound(0x202);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private bool CanDoLightning(Mobile m, bool isFriendly) =>
!isFriendly && CanBeHarmful(m) && (!m.Hidden || m.AccessLevel == AccessLevel.Player);
private void DoLightning(Mobile m)
{
DoHarmful(m);
double damage;
if (Core.AOS)
{
var baseDamage = 6 + (int)(Skills.EvalInt.Value / 5.0);
damage = Utility.RandomMinMax(baseDamage, baseDamage + 3);
}
else
{
damage = Utility.Random(12, 9);
}
m.BoltEffect(0);
SpellHelper.Damage(TimeSpan.FromSeconds(0.25), m, this, damage, 0, 0, 0, 0, 100);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private bool CanDoPoison(Mobile m, bool isFriendly) =>
!isFriendly && CanBeHarmful(m) && (!m.Hidden || m.AccessLevel == AccessLevel.Player);
private void DoPoison(Mobile m)
{
DoHarmful(m);
m.Spell?.OnCasterHurt();
m.Paralyzed = false;
var total = Skills.Magery.Value + Skills.Poisoning.Value;
var dist = GetDistanceToSqrt(m);
if (dist >= 3.0)
{
total -= (dist - 3.0) * 10.0;
}
int level = total switch
{
>= 200.0 => Utility.Random(10) == 0 ? 3 : 2,
> 170.0 => 2,
> 130.0 => 1,
_ => 0
};
m.ApplyPoison(this, Poison.GetPoison(level));
m.FixedParticles(0x374A, 10, 15, 5021, EffectLayer.Waist);
m.PlaySound(0x474);
}
public void EndSavageDance()
@ -173,137 +256,47 @@ namespace Server.Mobiles
return;
}
var eable = GetMobilesInRange(8);
var rnd = Utility.Random(3);
switch (Utility.Random(3))
using var queue = PooledRefQueue<Mobile>.Create();
foreach (var m in GetMobilesInRange(8))
{
case 0: /* greater heal */
{
foreach (var m in eable)
var isFriendly = m is Savage or SavageRider or SavageShaman or SavageRidgeback;
var shouldApply = rnd switch
{
0 => CanDoGreaterHeal(m, isFriendly),
1 => CanDoLightning(m, isFriendly),
2 => CanDoPoison(m, isFriendly),
};
if (shouldApply)
{
queue.Enqueue(m);
}
}
while (queue.Count > 0)
{
var m = queue.Dequeue();
switch (rnd)
{
case 0:
{
var isFriendly = m is Savage or SavageRider or SavageShaman or SavageRidgeback;
if (!isFriendly)
{
continue;
}
if (m.Poisoned || MortalStrike.IsWounded(m) || !CanBeBeneficial(m))
{
continue;
}
DoBeneficial(m);
// Algorithm: (40% of magery) + (1-10)
var toHeal = (int)(Skills.Magery.Value * 0.4);
toHeal += Utility.Random(1, 10);
m.Heal(toHeal, this);
m.FixedParticles(0x376A, 9, 32, 5030, EffectLayer.Waist);
m.PlaySound(0x202);
DoGreaterHeal(m);
break;
}
break;
}
case 1: /* lightning */
{
foreach (var m in eable)
case 1:
{
var isFriendly = m is Savage or SavageRider or SavageShaman or SavageRidgeback;
if (isFriendly)
{
continue;
}
if (!CanBeHarmful(m))
{
continue;
}
DoHarmful(m);
double damage;
if (Core.AOS)
{
var baseDamage = 6 + (int)(Skills.EvalInt.Value / 5.0);
damage = Utility.RandomMinMax(baseDamage, baseDamage + 3);
}
else
{
damage = Utility.Random(12, 9);
}
m.BoltEffect(0);
SpellHelper.Damage(TimeSpan.FromSeconds(0.25), m, this, damage, 0, 0, 0, 0, 100);
DoLightning(m);
break;
}
break;
}
case 2: /* poison */
{
foreach (var m in eable)
case 2:
{
var isFriendly = m is Savage or SavageRider or SavageShaman or SavageRidgeback;
if (isFriendly)
{
continue;
}
if (!CanBeHarmful(m))
{
continue;
}
DoHarmful(m);
m.Spell?.OnCasterHurt();
m.Paralyzed = false;
var total = Skills.Magery.Value + Skills.Poisoning.Value;
var dist = GetDistanceToSqrt(m);
if (dist >= 3.0)
{
total -= (dist - 3.0) * 10.0;
}
int level;
if (total >= 200.0 && Utility.Random(1, 100) <= 10)
{
level = 3;
}
else if (total > 170.0)
{
level = 2;
}
else if (total > 130.0)
{
level = 1;
}
else
{
level = 0;
}
m.ApplyPoison(this, Poison.GetPoison(level));
m.FixedParticles(0x374A, 10, 15, 5021, EffectLayer.Waist);
m.PlaySound(0x474);
DoPoison(m);
break;
}
break;
}
}
}
}
}

View file

@ -107,23 +107,19 @@ namespace Server.Mobiles
return;
}
var eable = GetMobilesInRange<OrcishLord>(10);
var count = 0;
foreach (var m in eable)
foreach (var m in GetMobilesInRange<OrcishLord>(10))
{
if (++count == 10)
{
break;
BaseCreature orc = new SpawnedOrcishLord { Team = Team };
// This MoveToWorld is safe since we return after executing and do not keep looping.
orc.MoveToWorld(map.GetRandomNearbyLocation(target.Location), map);
orc.Combatant = target;
return;
}
}
if (count < 10)
{
BaseCreature orc = new SpawnedOrcishLord { Team = Team };
orc.MoveToWorld(map.GetRandomNearbyLocation(target.Location), map);
orc.Combatant = target;
}
}
}
}

View file

@ -118,9 +118,7 @@ namespace Server.Mobiles
{
m_NextAbilityTime = Core.Now + TimeSpan.FromSeconds(Utility.RandomMinMax(10, 15));
var eable = GetMobilesInRange(8);
foreach (var m in eable)
foreach (var m in GetMobilesInRange(8))
{
if (m is not MeerWarrior || !IsFriend(m) || !CanBeBeneficial(m) || m.Hits >= m.HitsMax || m.Poisoned ||
MortalStrike.IsWounded(m))

View file

@ -81,9 +81,8 @@ namespace Server.Mobiles
private void DoAreaLeech_Finish()
{
var eable = GetMobilesInRange(6);
using var queue = PooledRefQueue<Mobile>.Create();
foreach (var m in eable)
foreach (var m in GetMobilesInRange(6))
{
if (CanBeHarmful(m) && IsEnemy(m))
{

View file

@ -122,16 +122,15 @@ namespace Server.Mobiles
UndressItem(m, Layer.Pants);
UndressItem(m, Layer.Shirt);
m.SendLocalizedMessage(
1072197
); // The dryad's beauty makes your blood race. Your clothing is too confining.
// The dryad's beauty makes your blood race. Your clothing is too confining.
m.SendLocalizedMessage(1072197);
}
}
m_NextUndress = Core.Now + TimeSpan.FromMinutes(1);
}
public void UndressItem(Mobile m, Layer layer)
public static void UndressItem(Mobile m, Layer layer)
{
var item = m.FindItemOnLayer(layer);

View file

@ -1,6 +1,7 @@
using ModernUO.Serialization;
using System;
using System.Collections.Generic;
using Server.Collections;
using Server.Engines.CannedEvil;
using Server.Items;
using Server.Network;
@ -311,31 +312,19 @@ namespace Server.Mobiles
private void OnTick()
{
var toDamage = new List<Mobile>();
foreach (var m in GetMobilesInRange(0))
using var queue = PooledRefQueue<Mobile>.Create();
foreach (var m in GetMobilesAt())
{
if (m is BaseCreature bc)
if ((m.Player || m is BaseCreature bc && (bc.Controlled || bc.Summoned))
&& m.Alive && !m.IsDeadBondedPet && m.CanBeDamaged())
{
if (!bc.Controlled && !bc.Summoned)
{
continue;
}
}
else if (!m.Player)
{
continue;
}
if (m.Alive && !m.IsDeadBondedPet && m.CanBeDamaged())
{
toDamage.Add(m);
queue.Enqueue(m);
}
}
for (var i = 0; i < toDamage.Count; ++i)
while (queue.Count > 0)
{
Damage(toDamage[i]);
Damage(queue.Dequeue());
}
++m_Ticks;

View file

@ -2,6 +2,7 @@ using System;
using Server.Engines.CannedEvil;
using Server.Items;
using ModernUO.Serialization;
using Server.Collections;
namespace Server.Mobiles;
@ -214,30 +215,30 @@ public partial class Meraktus : BaseChampion
public void Earthquake()
{
var eable = GetMobilesInRange(8);
foreach (var m in eable)
using var queue = PooledRefQueue<Mobile>.Create();
foreach (var m in GetMobilesInRange(8))
{
if (m == this || !CanBeHarmful(m) || m.Deleted || !m.Player &&
!(m is BaseCreature creature && (creature.Controlled || creature.Summoned || creature.Team != Team)))
if (m.Deleted || m == this || !CanBeHarmful(m))
{
continue;
}
if (m.Player || m is BaseCreature bc && (bc.Controlled || bc.Summoned || bc.Team != Team))
{
queue.Enqueue(m);
}
}
while (queue.Count > 0)
{
var m = queue.Dequeue();
if (m is PlayerMobile pm && pm.Mounted)
{
pm.Mount.Rider = null;
}
var damage = (int)(m.Hits * 0.6);
if (damage < 10)
{
damage = 10;
}
else if (damage > 75)
{
damage = 75;
}
var damage = Math.Clamp((int)(m.Hits * 0.6), 10, 75);
DoHarmful(m);
AOS.Damage(m, this, damage, 100, 0, 0, 0, 0);

View file

@ -1,6 +1,5 @@
using ModernUO.Serialization;
using System;
using Server.Buffers;
using Server.Collections;
namespace Server.Mobiles
@ -69,28 +68,24 @@ namespace Server.Mobiles
{
if (Core.SE && Summoned)
{
var eable = GetMobilesInRange(5);
using var queue = PooledRefQueue<Mobile>.Create();
foreach (var m in eable)
using var list = PooledRefList<Mobile>.Create();
foreach (var m in GetMobilesInRange(5))
{
if (m is EnergyVortex or BladeSpirits && ((BaseCreature)m).Summoned)
{
queue.Enqueue(m);
list.Add(m);
}
}
var amount = queue.Count - 6;
var amount = list.Count - 6;
if (amount > 0)
{
var mobs = queue.ToPooledArray();
mobs.Shuffle();
list.Shuffle();
while (amount > 0)
{
Dispel(mobs[amount--]);
Dispel(list[amount--]);
}
STArrayPool<Mobile>.Shared.Return(mobs, true);
}
}

View file

@ -1,6 +1,5 @@
using ModernUO.Serialization;
using System;
using Server.Buffers;
using Server.Collections;
namespace Server.Mobiles
@ -75,28 +74,24 @@ namespace Server.Mobiles
{
if (Core.SE && Summoned)
{
var eable = GetMobilesInRange(5);
using var queue = PooledRefQueue<Mobile>.Create();
foreach (var m in eable)
using var list = PooledRefList<Mobile>.Create();
foreach (var m in GetMobilesInRange(5))
{
if (m is EnergyVortex or BladeSpirits && ((BaseCreature)m).Summoned)
{
queue.Enqueue(m);
list.Add(m);
}
}
var amount = queue.Count - 6;
var amount = list.Count - 6;
if (amount > 0)
{
var mobs = queue.ToPooledArray();
mobs.Shuffle();
list.Shuffle();
while (amount > 0)
{
Dispel(mobs[amount--]);
Dispel(list[amount--]);
}
STArrayPool<Mobile>.Shared.Return(mobs, true);
}
}

View file

@ -1,4 +1,5 @@
using ModernUO.Serialization;
using Server.Collections;
using Server.Engines.Plants;
using Server.Items;
@ -81,24 +82,33 @@ namespace Server.Mobiles
public void EatBoglings()
{
var eable = GetMobilesInRange<Bogling>(2);
var sound = true;
if (Hits >= HitsMax)
{
return;
}
foreach (var bogling in eable)
using var queue = PooledRefQueue<Mobile>.Create();
foreach (var bogling in GetMobilesInRange<Bogling>(2))
{
if (Hits >= HitsMax)
{
break;
}
if (sound)
{
PlaySound(Utility.Random(0x3B, 2)); // Eat sound
sound = false;
}
Hits += bogling.Hits / 2;
bogling.Delete();
queue.Enqueue(bogling);
}
if (queue.Count <= 0)
{
return;
}
PlaySound(Utility.Random(0x3B, 2)); // Eat sound
while (queue.Count > 0)
{
queue.Dequeue().Delete();
}
}

View file

@ -1,4 +1,5 @@
using ModernUO.Serialization;
using Server.Collections;
using Server.Engines.Plants;
using Server.Items;
@ -120,9 +121,8 @@ namespace Server.Mobiles
Animate(10, 4, 1, true, false, 0);
var eable = target.GetMobilesInRange<Mobile>(8);
foreach (var m in eable)
using var queue = PooledRefQueue<Mobile>.Create();
foreach (var m in target.GetMobilesInRange<Mobile>(8))
{
if (m == this || !(CanBeHarmful(m) || m.Player && m.Alive))
{
@ -134,6 +134,12 @@ namespace Server.Mobiles
continue;
}
queue.Enqueue(m);
}
while (queue.Count > 0)
{
var m = queue.Dequeue();
DoHarmful(m);
AOS.Damage(m, this, Utility.RandomMinMax(20, 25), true, 0, 0, 0, 100, 0);

View file

@ -856,9 +856,7 @@ namespace Server.Mobiles
if (Core.AOS)
{
var mobiles = Map.GetMobilesInRange(location, 0);
foreach (Mobile m in mobiles)
foreach (Mobile m in Map.GetMobilesAt(location))
{
if (m.Z >= location.Z && m.Z < location.Z + 16 && (!m.Hidden || m.AccessLevel == AccessLevel.Player))
{

View file

@ -138,18 +138,13 @@ public partial class Barracoon : BaseChampion
return;
}
var eable = GetMobilesInRange<BaseCreature>(10);
var rats = 0;
foreach (var m in eable)
foreach (var m in GetMobilesInRange<BaseCreature>(10))
{
if (m is Ratman or RatmanArcher or RatmanMage)
if (m is Ratman or RatmanArcher or RatmanMage && ++rats >= 16)
{
rats++;
if (rats >= 16)
{
return;
}
return;
}
}

View file

@ -529,9 +529,8 @@ public partial class Harrower : BaseCreature
return;
}
var eable = m_Owner.GetMobilesInRange(16);
Mobile toTeleport = null;
foreach (var m in eable)
foreach (var m in m_Owner.GetMobilesInRange(16))
{
if (m != m_Owner && m.Player && m_Owner.CanBeHarmful(m) && m_Owner.CanSee(m))
{

View file

@ -1,5 +1,6 @@
using System;
using ModernUO.Serialization;
using Server.Collections;
namespace Server.Mobiles;
@ -119,9 +120,8 @@ public partial class HarrowerTentacles : BaseCreature
return;
}
var eable = m_Owner.GetMobilesInRange<Mobile>(9);
foreach (var m in eable)
using var queue = PooledRefQueue<Mobile>.Create();
foreach (var m in m_Owner.GetMobilesInRange<Mobile>(9))
{
if (m == m_Owner || !m_Owner.CanBeHarmful(m))
{
@ -138,6 +138,12 @@ public partial class HarrowerTentacles : BaseCreature
continue;
}
queue.Enqueue(m);
}
while (queue.Count > 0)
{
var m = queue.Dequeue();
m_Owner.DoHarmful(m);
m.FixedParticles(0x374A, 10, 15, 5013, 0x455, 0, EffectLayer.Waist);

View file

@ -1,5 +1,6 @@
using System;
using ModernUO.Serialization;
using Server.Collections;
using Server.Engines.CannedEvil;
using Server.Items;
@ -98,9 +99,8 @@ public partial class Rikktor : BaseChampion
PlaySound(0x2F3);
var eable = GetMobilesInRange<Mobile>(8);
foreach (var m in eable)
using var queue = PooledRefQueue<Mobile>.Create();
foreach (var m in GetMobilesInRange<Mobile>(8))
{
if (m == this || !(CanBeHarmful(m) || m.Player && m.Alive))
{
@ -112,20 +112,18 @@ public partial class Rikktor : BaseChampion
continue;
}
var damage = m.Hits * 0.6;
queue.Enqueue(m);
}
if (damage < 10.0)
{
damage = 10.0;
}
else if (damage > 75.0)
{
damage = 75.0;
}
while (queue.Count > 0)
{
var m = queue.Dequeue();
var damage = Math.Clamp((int)(m.Hits * 0.6), 10, 75);
DoHarmful(m);
AOS.Damage(m, this, (int)damage, 100, 0, 0, 0, 0);
AOS.Damage(m, this, damage, 100, 0, 0, 0, 0);
if (m.Alive && m.Body.IsHuman && !m.Mounted)
{

View file

@ -1,5 +1,6 @@
using System;
using ModernUO.Serialization;
using Server.Collections;
using Server.Engines.CannedEvil;
using Server.Engines.Plants;
using Server.Items;
@ -152,9 +153,8 @@ public partial class Serado : BaseChampion
Animate(10, 4, 1, true, false, 0);
var eable = target.GetMobilesInRange<Mobile>(8);
foreach (var m in eable)
using var queue = PooledRefQueue<Mobile>.Create();
foreach (var m in target.GetMobilesInRange<Mobile>(8))
{
if (m == this || !(CanBeHarmful(m) || m.Player && m.Alive))
{
@ -166,6 +166,12 @@ public partial class Serado : BaseChampion
continue;
}
queue.Enqueue(m);
}
while (queue.Count > 0)
{
var m = queue.Dequeue();
DoHarmful(m);
AOS.Damage(m, this, Utility.RandomMinMax(20, 25), true, 0, 0, 0, 100, 0);

View file

@ -1268,9 +1268,7 @@ namespace Server.Mobiles
}
else
{
var mobiles = e.Mobile.GetMobilesInRange<PlayerVendor>(2);
foreach (var m in mobiles)
foreach (var m in e.Mobile.GetMobilesInRange<PlayerVendor>(2))
{
if (m.CanSee(e.Mobile) && m.InLOS(e.Mobile))
{

View file

@ -151,12 +151,8 @@ namespace Server.Multis
mobiles.Clear();
var sector = map.GetSector(tileX, tileY);
for (var i = 0; i < sector.Mobiles.Count; ++i)
foreach (var m in map.GetMobilesAt(tileX, tileY))
{
var m = sector.Mobiles[i];
if (m.X == tileX && m.Y == tileY)
{
mobiles.Add(m);

View file

@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json.Serialization;
using Server.Mobiles;
using Server.Utilities;
@ -153,8 +152,15 @@ public class GuardedRegion : BaseRegion
public override void MakeGuard(Mobile focus)
{
var eable = focus.GetMobilesInRange<BaseGuard>(8);
var useGuard = eable.FirstOrDefault(m => m.Focus == null);
BaseGuard useGuard = null;
foreach (var m in focus.GetMobilesInRange<BaseGuard>(8))
{
if (m.Focus == null)
{
useGuard = m;
break;
}
}
if (useGuard == null)
{
@ -286,19 +292,8 @@ public class GuardedRegion : BaseRegion
if (fakeCall != null)
{
fakeCall.Say(
Utility.RandomList(
1007037,
501603,
1013037,
1013038,
1013039,
1013041,
1013042,
1013043,
1013052
)
);
fakeCall.Say(Utility.Random(1013037, 16));
MakeGuard(m);
timer.Stop();
m_GuardCandidates.Remove(m);
@ -319,9 +314,7 @@ public class GuardedRegion : BaseRegion
return;
}
var eable = Map.GetMobilesInRange(p, 14);
foreach (var m in eable)
foreach (var m in Map.GetMobilesInRange(p, 14))
{
if (IsGuardCandidate(m) &&
(!AllowReds && m.Kills >= 5 && m.Region.IsPartOf(this) || m_GuardCandidates.ContainsKey(m)))

View file

@ -58,27 +58,29 @@ namespace Server.SkillHandlers
if (range > 0)
{
var inRange = src.Map.GetMobilesInRange(p, range);
foreach (var trg in inRange)
foreach (var trg in src.Map.GetMobilesInRange(p, range))
{
if (trg.Hidden && src != trg)
if (!trg.Hidden || src == trg)
{
var ss = srcSkill + Utility.Random(21) - 10;
var ts = trg.Skills.Hiding.Value + Utility.Random(21) - 10;
if (src.AccessLevel >= trg.AccessLevel && (ss >= ts || inHouse && house.IsInside(trg)))
{
if (trg is ShadowKnight && (trg.X != p.X || trg.Y != p.Y))
{
continue;
}
trg.RevealingAction();
trg.SendLocalizedMessage(500814); // You have been revealed!
foundAnyone = true;
}
continue;
}
var ss = srcSkill + Utility.Random(21) - 10;
var ts = trg.Skills.Hiding.Value + Utility.Random(21) - 10;
if (src.AccessLevel < trg.AccessLevel || ss < ts && (!inHouse || !house.IsInside(trg)))
{
continue;
}
if (trg is ShadowKnight && (trg.X != p.X || trg.Y != p.Y))
{
continue;
}
trg.RevealingAction();
trg.SendLocalizedMessage(500814); // You have been revealed!
foundAnyone = true;
}
if (Faction.Find(src) != null)

View file

@ -60,8 +60,7 @@ namespace Server.SkillHandlers
{
if (!CombatOverride)
{
var eable = m.GetMobilesInRange(range);
foreach (var check in eable)
foreach (var check in m.GetMobilesInRange(range))
{
if (check.InLOS(m) && check.Combatant == m)
{

View file

@ -118,9 +118,8 @@ namespace Server.SkillHandlers
calmed = true;
m.SendLocalizedMessage(
500616
); // You hear lovely music, and forget to continue battling!
// You hear lovely music, and forget to continue battling!
m.SendLocalizedMessage(500616);
m.Combatant = null;
m.Warmode = false;
@ -132,9 +131,8 @@ namespace Server.SkillHandlers
if (!calmed)
{
from.SendLocalizedMessage(
1049648
); // You play hypnotic music, but there is nothing in range for you to calm.
// You play hypnotic music, but there is nothing in range for you to calm.
from.SendLocalizedMessage(1049648);
}
else
{
@ -216,9 +214,8 @@ namespace Server.SkillHandlers
{
from.SendLocalizedMessage(1049532); // You play hypnotic music, calming your target.
targ.SendLocalizedMessage(
500616
); // You hear lovely music, and forget to continue battling!
// You hear lovely music, and forget to continue battling!
targ.SendLocalizedMessage(500616);
}
}
}

View file

@ -210,20 +210,17 @@ namespace Server.SkillHandlers
from.SendGump(new TrackWhoGump(from, mobs, range));
from.SendLocalizedMessage(1018093); // Select the one you would like to track.
}
else if (type == 0)
{
from.SendLocalizedMessage(502991); // You see no evidence of animals in the area.
}
else if (type == 1)
{
from.SendLocalizedMessage(502993); // You see no evidence of creatures in the area.
}
else
{
if (type == 0)
{
from.SendLocalizedMessage(502991); // You see no evidence of animals in the area.
}
else if (type == 1)
{
from.SendLocalizedMessage(502993); // You see no evidence of creatures in the area.
}
else
{
from.SendLocalizedMessage(502995); // You see no evidence of people in the area.
}
from.SendLocalizedMessage(502995); // You see no evidence of people in the area.
}
}

View file

@ -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)
{

View file

@ -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))
{

View file

@ -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);

View file

@ -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)

View file

@ -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)))

View file

@ -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))

View file

@ -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))
{

View file

@ -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))
{

View file

@ -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))
{

View file

@ -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);
}

View file

@ -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);
}
}
}

View file

@ -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))

View file

@ -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);

View file

@ -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) ||

View file

@ -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)))
{

View file

@ -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))

View file

@ -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)))

View file

@ -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))
{

View file

@ -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)

View file

@ -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);

View file

@ -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;