ModernUO/ToAdd/Traps/LandMobTrap.cs

167 lines
No EOL
4.5 KiB
C#

/**********************************
* Author: Expa *
* Optimized ModernUO .Net 10/C#14 *
**********************************/
using System;
using ModernUO.Serialization;
using Server;
using Server.Mobiles;
namespace Server.Items;
[SerializationGenerator(0, false)]
public partial class LandMobTrap : Item
{
[SerializableField(0)] private int _minSpawn = 3;
[SerializableField(1)] private int _maxSpawn = 6;
[SerializableField(2)] private double _triggerDelaySeconds = 0.5;
[SerializableField(3)] private int _soundID = 0x1C6;
[SerializableField(4)] private int _particleItemId = 0x373A;
[SerializableField(5)] private byte _particleCount = 20;
[SerializableField(6)] private byte _particleSpeed = 10;
[SerializableField(7)] private byte _flags = 1;
private const int MaxAllowedSpawn = 12;
private TimerExecutionToken _spawnToken;
private bool Armed
{
get => (_flags & 1) != 0;
set => _flags = (byte)(value ? (_flags | 1) : (_flags & ~1));
}
private bool BlockMovement
{
get => (_flags & 2) != 0;
set => _flags = (byte)(value ? (_flags | 2) : (_flags & ~2));
}
[Constructible]
public LandMobTrap() : base(0x1B7A)
{
Movable = false;
Hue = 1058;
Visible = false;
Name = "land mob trap";
}
public override bool HandlesOnMovement => true;
public override bool OnMoveOver(Mobile m)
{
var map = Map;
if (Deleted || map == null || map == Map.Internal)
return true;
if (!Armed || !m.Player || m.AccessLevel > AccessLevel.Player)
return true;
foreach (var bc in GetMobilesInRange<BaseCreature>(1))
{
if (bc is Skeleton or SkeletalMage or SkeletalKnight)
return true;
}
Armed = false;
InvalidateProperties();
var min = Math.Max(1, _minSpawn);
var max = Math.Clamp(_maxSpawn, min, MaxAllowedSpawn);
if (_triggerDelaySeconds <= 0.0)
DoSpawn(m, min, max);
else
Timer.StartTimer(TimeSpan.FromSeconds(_triggerDelaySeconds), () => DoSpawn(m, min, max), out _spawnToken);
return !BlockMovement;
}
private void DoSpawn(Mobile trigger, int minSpawn, int maxSpawn)
{
try
{
var map = Map;
if (Deleted || map == null || map == Map.Internal)
return;
var count = Utility.RandomMinMax(minSpawn, maxSpawn);
for (var i = 0; i < count; ++i)
{
var cr = CreateRandomCreature();
if (cr is null)
continue;
try
{
if (TryFindSpawnLocation(map, out var loc))
{
cr.MoveToWorld(loc, map);
cr.Combatant = trigger;
}
else
{
cr.Delete();
}
}
catch
{
cr?.Delete();
}
}
Effects.SendLocationParticles(
EffectItem.Create(Location, Map, EffectItem.DefaultDuration),
_particleItemId, _particleCount, _particleSpeed, 5044
);
Effects.PlaySound(Location, Map, _soundID);
}
finally
{
Delete();
}
}
private static BaseCreature CreateRandomCreature() =>
Utility.Random(5) switch
{
0 or 1 => new Skeleton(),
2 or 3 => new SkeletalMage(),
_ => new SkeletalKnight()
};
private bool TryFindSpawnLocation(Map map, out Point3D loc)
{
loc = Location;
for (var i = 0; i < 10; ++i)
{
var x = X + Utility.Random(3) - 1;
var y = Y + Utility.Random(3) - 1;
var z = map.GetAverageZ(x, y);
if (map.CanSpawnMobile(new Point2D(x, y), z))
{
loc = new Point3D(x, y, z);
return true;
}
if (map.CanFit(x, y, Z, 16, false, false))
{
loc = new Point3D(x, y, Z);
return true;
}
}
return false;
}
public override void OnAfterDelete()
{
_spawnToken.Cancel();
base.OnAfterDelete();
}
[AfterDeserialization]
private void AfterDeserialization() { }
}