using System; using System.Collections.Generic; using ModernUO.Serialization; using Server.Items; using Server.Utilities; namespace Server.Engines.Doom; public enum GauntletSpawnerState { InSequence, InProgress, Completed } [SerializationGenerator(2, false)] public partial class GauntletSpawner : Item { public const int PlayersPerSpawn = 5; public const int InSequenceItemHue = 0x000; public const int InProgressItemHue = 0x676; public const int CompletedItemHue = 0x455; [SerializableField(0)] [SerializedCommandProperty(AccessLevel.GameMaster)] private Rectangle2D _regionBounds; [SerializableField(1)] [SerializedCommandProperty(AccessLevel.GameMaster)] private List _traps; [SerializableField(2)] [SerializedCommandProperty(AccessLevel.GameMaster)] private List _creatures; [SerializableField(3)] [SerializedCommandProperty(AccessLevel.GameMaster)] private string _typeName; [SerializableField(4)] [SerializedCommandProperty(AccessLevel.GameMaster)] private BaseDoor _door; [SerializableField(5)] [SerializedCommandProperty(AccessLevel.GameMaster)] private BaseAddon _addon; [SerializableField(6)] [SerializedCommandProperty(AccessLevel.GameMaster)] private GauntletSpawner _sequence; private TimerExecutionToken _timerToken; [Constructible] public GauntletSpawner(string typeName = null) : base(0x36FE) { Visible = false; Movable = false; TypeName = typeName; Creatures = new List(); Traps = new List(); } [CommandProperty(AccessLevel.GameMaster)] public bool HasCompleted { get { if (Creatures.Count == 0) { return false; } for (var i = 0; i < Creatures.Count; ++i) { var mob = Creatures[i]; if (!mob.Deleted) { return false; } } return true; } } [SerializableProperty(7)] [CommandProperty(AccessLevel.GameMaster)] public GauntletSpawnerState State { get => _state; set { if (_state != value) { _state = value; Update(); } this.MarkDirty(); } } public Region Region { get; set; } private void Update(bool shouldSpawn = true) { var lockDoors = _state == GauntletSpawnerState.InProgress; var hue = _state switch { GauntletSpawnerState.InSequence => InSequenceItemHue, GauntletSpawnerState.InProgress => InProgressItemHue, GauntletSpawnerState.Completed => CompletedItemHue, _ => 0 }; if (Door != null) { Door.Hue = hue; Door.Locked = lockDoors; if (lockDoors) { Door.KeyValue = Key.RandomValue(); Door.Open = false; } if (Door.Link?.Deleted == false) { Door.Link.Hue = hue; Door.Link.Locked = lockDoors; if (lockDoors) { Door.Link.KeyValue = Key.RandomValue(); Door.Open = false; } } } if (Addon != null) { Addon.Hue = hue; } if (_state == GauntletSpawnerState.InProgress) { CreateRegion(); if (shouldSpawn) { FullSpawn(); } Timer.StartTimer(TimeSpan.FromSeconds(1.0), TimeSpan.FromSeconds(1.0), Slice, out _timerToken); } else { Stop(); } } public override string DefaultName => "doom spawner"; public virtual void CreateRegion() { if (Region != null) { return; } var map = Map; if (map == null || map == Map.Internal) { return; } Region = new GauntletRegion(this, map); } public void Stop() { ClearCreatures(); ClearTraps(); DestroyRegion(); _timerToken.Cancel(); } public override void OnDelete() { base.OnDelete(); Stop(); Door?.Link?.Delete(); Door?.Delete(); Addon?.Delete(); } public virtual void DestroyRegion() { Region?.Unregister(); Region = null; } public virtual int ComputeTrapCount() { var area = RegionBounds.Width * RegionBounds.Height; return area / 100; } public virtual void RemoveAllTraps() { for (var i = 0; i < Traps.Count; ++i) { Traps[i].Delete(); } ClearTraps(); } public virtual void SpawnTrap() { var map = Map; if (map == null) { return; } var random = Utility.Random(100); BaseTrap trap = random switch { < 22 => new SawTrap(Utility.RandomBool() ? SawTrapType.WestFloor : SawTrapType.NorthFloor), < 44 => new SpikeTrap(Utility.RandomBool() ? SpikeTrapType.WestFloor : SpikeTrapType.NorthFloor), < 66 => new GasTrap(Utility.RandomBool() ? GasTrapType.NorthWall : GasTrapType.WestWall), < 88 => new FireColumnTrap(), _ => new MushroomTrap() }; if (trap is FireColumnTrap or MushroomTrap) { trap.Hue = 0x451; } // try 10 times to find a valid location for (var i = 0; i < 10; ++i) { var x = Utility.Random(RegionBounds.X, RegionBounds.Width); var y = Utility.Random(RegionBounds.Y, RegionBounds.Height); var z = Z; if (!map.CanFit(x, y, z, 16, false, false)) { z = map.GetAverageZ(x, y); } if (!map.CanFit(x, y, z, 16, false, false)) { continue; } trap.MoveToWorld(new Point3D(x, y, z), map); Traps.Add(trap); return; } trap.Delete(); } public virtual int ComputeSpawnCount() { var playerCount = 0; var map = Map; if (map != null) { var loc = GetWorldLocation(); var reg = Region.Find(loc, map).GetRegion("Doom Gauntlet"); if (reg != null) { playerCount = reg.GetPlayerCount(); } } if (playerCount == 0 && Region != null) { playerCount = Region.GetPlayerCount(); } return Math.Max((playerCount + PlayersPerSpawn - 1) / PlayersPerSpawn, 1); } public virtual void RemoveAllCreatures() { for (var i = 0; i < Creatures.Count; ++i) { Creatures[i].Delete(); } ClearCreatures(); } public virtual void FullSpawn() { RemoveAllCreatures(); var count = ComputeSpawnCount(); for (var i = 0; i < count; ++i) { Spawn(); } RemoveAllTraps(); count = ComputeTrapCount(); for (var i = 0; i < count; ++i) { SpawnTrap(); } } public virtual void Spawn() { try { if (TypeName == null) { return; } var type = AssemblyHandler.FindTypeByName(TypeName); if (type == null) { return; } var mob = type.CreateEntityInstance(); if (mob != null) { mob.MoveToWorld(GetWorldLocation(), Map); Creatures.Add(mob); } } catch { // ignored } } public virtual void RecurseReset() { if (_state != GauntletSpawnerState.InSequence) { State = GauntletSpawnerState.InSequence; if (Sequence?.Deleted == false) { Sequence.RecurseReset(); } } } public virtual void Slice() { if (_state != GauntletSpawnerState.InProgress) { return; } var count = ComputeSpawnCount(); for (var i = Creatures.Count; i < count; ++i) { Spawn(); } if (HasCompleted) { State = GauntletSpawnerState.Completed; if (Sequence?.Deleted == false) { if (Sequence.State == GauntletSpawnerState.Completed) { RecurseReset(); } Sequence.State = GauntletSpawnerState.InProgress; } } } private void Deserialize(IGenericReader reader, int version) { RegionBounds = reader.ReadRect2D(); Traps = reader.ReadEntityList(); Creatures = reader.ReadEntityList(); TypeName = reader.ReadString(); Door = reader.ReadEntity(); Addon = reader.ReadEntity(); Sequence = reader.ReadEntity(); _state = (GauntletSpawnerState)reader.ReadInt(); } [AfterDeserialization(false)] private void AfterDeserialization() { Update(false); } }