From 00c25cd506677ee38644159d5ce0d08e16711af9 Mon Sep 17 00:00:00 2001 From: WarrentyExpired Date: Fri, 30 Oct 2026 21:04:26 -0400 Subject: [PATCH] #W# New: Portal Spawner that spawns a mob. if the mob doesn't have a combatant after 3 mins it despawns. --- Projects/UOContent/Engines/PortalSpawner.cs | 89 +++++++++++++++++++ .../Custom/PortalSpawns/PortalDaemon.cs | 45 ++++++++++ .../Custom/PortalSpawns/PortalWyvern.cs | 41 +++++++++ 3 files changed, 175 insertions(+) create mode 100644 Projects/UOContent/Engines/PortalSpawner.cs create mode 100644 Projects/UOContent/Mobiles/Monsters/Custom/PortalSpawns/PortalDaemon.cs create mode 100644 Projects/UOContent/Mobiles/Monsters/Custom/PortalSpawns/PortalWyvern.cs diff --git a/Projects/UOContent/Engines/PortalSpawner.cs b/Projects/UOContent/Engines/PortalSpawner.cs new file mode 100644 index 000000000..c4df887b3 --- /dev/null +++ b/Projects/UOContent/Engines/PortalSpawner.cs @@ -0,0 +1,89 @@ +using System; +using ModernUO.Serialization; +using Server.Items; +using Server.Mobiles; +using Server.Network; + +namespace Server.Custom.Events +{ + [SerializationGenerator(0)] + public partial class PortalSpawner : Item + { + private bool _triggered; + + [Constructible] + public PortalSpawner() : base(0x0F6C) + { + Name = "a horrific tear"; + Hue = 1157; + Movable = false; + Light = LightType.Circle300; + } + + public PortalSpawner(Serial serial) : base(serial) + { + } + + public override bool HandlesOnMovement => true; + + public override void OnMovement(Mobile m, Point3D oldLocation) + { + base.OnMovement(m, oldLocation); + + if (_triggered || Deleted) + return; + + if (m.AccessLevel > AccessLevel.Player || !m.Alive || !(m is PlayerMobile)) + return; + + if (Utility.InRange(Location, m.Location, 8)) + { + TriggerAmbush(m); + } + } + + private void TriggerAmbush(Mobile target) + { + _triggered = true; + + Effects.PlaySound(Location, Map, 0x20F); + Effects.SendLocationEffect(Location, Map, 0x3728, 10, 10); + Effects.SendLocationEffect(Location, Map, 0x3709, 10, 30, 1153, 0); + + try + { + BaseCreature ambushMonster = null; + string ambushMessage = "A terrifying creature bursts from the portal!"; + + switch (Utility.Random(2)) + { + case 0: + ambushMonster = new PortalDaemon(); + ambushMessage = "A daemon tears through the portal to ambush you!"; + break; + case 1: + ambushMonster = new PortalWyvern(); + ambushMessage = "A furious dragon bursts through the portal to ambush you!"; + break; + } + + if (ambushMonster != null) + { + ambushMonster.MoveToWorld(Location, Map); + ambushMonster.PlaySound(ambushMonster.GetAngerSound()); + + ambushMonster.Combatant = target; + + target.SendMessage(89, ambushMessage); + } + } + catch (Exception e) + { + Console.WriteLine($"Error spawning portal monster: {e.Message}"); + } + + Delete(); + } + + } +} diff --git a/Projects/UOContent/Mobiles/Monsters/Custom/PortalSpawns/PortalDaemon.cs b/Projects/UOContent/Mobiles/Monsters/Custom/PortalSpawns/PortalDaemon.cs new file mode 100644 index 000000000..9d614730f --- /dev/null +++ b/Projects/UOContent/Mobiles/Monsters/Custom/PortalSpawns/PortalDaemon.cs @@ -0,0 +1,45 @@ +using System; +using Server; +using ModernUO.Serialization; +using Server.Items; + +namespace Server.Mobiles +{ + [SerializationGenerator(0, false)] + public partial class PortalDaemon : Daemon + { + // Removed [Save] - it will now self-clean automatically on server restarts! + private DateTime _DespawnTime; + + [Constructible] + public PortalDaemon() : base() + { + // Give it 3 minutes of inactivity before it despawns + _DespawnTime = DateTime.UtcNow + TimeSpan.FromMinutes(3); + } + + public override void OnThink() + { + base.OnThink(); + + if (Deleted) return; + + if (Combatant != null) + { + // As long as it is fighting a player or pet, constantly reset the clock + _DespawnTime = DateTime.UtcNow + TimeSpan.FromMinutes(3); + } + else if (DateTime.UtcNow > _DespawnTime) + { + // The clock ran out and it has no combatant. Clean it up! + Effects.SendLocationEffect(Location, Map, 0x3728, 10, 10); // Smoke puff + PlaySound(0x1FD); // Eerie fade out sound + Delete(); + } + } + + public PortalDaemon(Serial serial) : base(serial) + { + } + } +} diff --git a/Projects/UOContent/Mobiles/Monsters/Custom/PortalSpawns/PortalWyvern.cs b/Projects/UOContent/Mobiles/Monsters/Custom/PortalSpawns/PortalWyvern.cs new file mode 100644 index 000000000..f910fc232 --- /dev/null +++ b/Projects/UOContent/Mobiles/Monsters/Custom/PortalSpawns/PortalWyvern.cs @@ -0,0 +1,41 @@ +using System; +using Server; +using ModernUO.Serialization; +using Server.Items; + +namespace Server.Mobiles +{ + [SerializationGenerator(0, false)] + public partial class PortalWyvern : AncientWyvern + { + private DateTime _DespawnTime; + + [Constructible] + public PortalWyvern() : base() + { + _DespawnTime = DateTime.UtcNow + TimeSpan.FromMinutes(3); + } + + public override void OnThink() + { + base.OnThink(); + + if (Deleted) return; + + if (Combatant != null) + { + _DespawnTime = DateTime.UtcNow + TimeSpan.FromMinutes(3); + } + else if (DateTime.UtcNow > _DespawnTime) + { + Effects.SendLocationEffect(Location, Map, 0x3728, 10, 10); + PlaySound(0x1FD); + Delete(); + } + } + + public PortalWyvern(Serial serial) : base(serial) + { + } + } +}