using System; namespace Server.Items { public enum ECEffectType { None, Moving, Location, Target, Lightning } public enum EffectTriggerType { None, Sequenced, DoubleClick, InRange } public class EffectController : Item { private IEntity m_Source; private IEntity m_Target; [Constructible] public EffectController() : base(0x1B72) { Movable = false; Visible = false; TriggerType = EffectTriggerType.Sequenced; EffectLayer = (EffectLayer)255; } public EffectController(Serial serial) : base(serial) { } [CommandProperty(AccessLevel.GameMaster)] public ECEffectType EffectType { get; set; } [CommandProperty(AccessLevel.GameMaster)] public EffectTriggerType TriggerType { get; set; } [CommandProperty(AccessLevel.GameMaster)] public EffectLayer EffectLayer { get; set; } [CommandProperty(AccessLevel.GameMaster)] public TimeSpan EffectDelay { get; set; } [CommandProperty(AccessLevel.GameMaster)] public TimeSpan TriggerDelay { get; set; } [CommandProperty(AccessLevel.GameMaster)] public TimeSpan SoundDelay { get; set; } [CommandProperty(AccessLevel.GameMaster)] public Item SourceItem { get => m_Source as Item; set => m_Source = value; } [CommandProperty(AccessLevel.GameMaster)] public Mobile SourceMobile { get => m_Source as Mobile; set => m_Source = value; } [CommandProperty(AccessLevel.GameMaster)] public bool SourceNull { get => m_Source == null; set { if (value) { m_Source = null; } } } [CommandProperty(AccessLevel.GameMaster)] public Item TargetItem { get => m_Target as Item; set => m_Target = value; } [CommandProperty(AccessLevel.GameMaster)] public Mobile TargetMobile { get => m_Target as Mobile; set => m_Target = value; } [CommandProperty(AccessLevel.GameMaster)] public bool TargetNull { get => m_Target == null; set { if (value) { m_Target = null; } } } [CommandProperty(AccessLevel.GameMaster)] public EffectController Sequence { get; set; } [CommandProperty(AccessLevel.GameMaster)] private bool FixedDirection { get; set; } [CommandProperty(AccessLevel.GameMaster)] private bool Explodes { get; set; } [CommandProperty(AccessLevel.GameMaster)] private bool PlaySoundAtTrigger { get; set; } [CommandProperty(AccessLevel.GameMaster)] public int EffectItemID { get; set; } [CommandProperty(AccessLevel.GameMaster)] public int EffectHue { get; set; } [CommandProperty(AccessLevel.GameMaster)] public int RenderMode { get; set; } [CommandProperty(AccessLevel.GameMaster)] public int Speed { get; set; } [CommandProperty(AccessLevel.GameMaster)] public int Duration { get; set; } [CommandProperty(AccessLevel.GameMaster)] public int ParticleEffect { get; set; } [CommandProperty(AccessLevel.GameMaster)] public int ExplodeParticleEffect { get; set; } [CommandProperty(AccessLevel.GameMaster)] public int ExplodeSound { get; set; } [CommandProperty(AccessLevel.GameMaster)] public int Unknown { get; set; } [CommandProperty(AccessLevel.GameMaster)] public int SoundID { get; set; } [CommandProperty(AccessLevel.GameMaster)] public int TriggerRange { get; set; } public override string DefaultName => "Effect Controller"; public override bool HandlesOnMovement => TriggerType == EffectTriggerType.InRange; public override void OnDoubleClick(Mobile from) { if (TriggerType == EffectTriggerType.DoubleClick) { DoEffect(from); } } public override void OnMovement(Mobile m, Point3D oldLocation) { if (m.Location != oldLocation && TriggerType == EffectTriggerType.InRange && Utility.InRange(GetWorldLocation(), m.Location, TriggerRange) && !Utility.InRange(GetWorldLocation(), oldLocation, TriggerRange)) { DoEffect(m); } } public override void Serialize(IGenericWriter writer) { base.Serialize(writer); writer.Write(0); // version writer.Write(EffectDelay); writer.Write(TriggerDelay); writer.Write(SoundDelay); if (m_Source is Item srcItem) { writer.Write(srcItem); } else { writer.Write(m_Source as Mobile); } if (m_Target is Item targItem) { writer.Write(targItem); } else { writer.Write(m_Target as Mobile); } writer.Write(Sequence); writer.Write(FixedDirection); writer.Write(Explodes); writer.Write(PlaySoundAtTrigger); writer.WriteEncodedInt((int)EffectType); writer.WriteEncodedInt((int)EffectLayer); writer.WriteEncodedInt((int)TriggerType); writer.WriteEncodedInt(EffectItemID); writer.WriteEncodedInt(EffectHue); writer.WriteEncodedInt(RenderMode); writer.WriteEncodedInt(Speed); writer.WriteEncodedInt(Duration); writer.WriteEncodedInt(ParticleEffect); writer.WriteEncodedInt(ExplodeParticleEffect); writer.WriteEncodedInt(ExplodeSound); writer.WriteEncodedInt(Unknown); writer.WriteEncodedInt(SoundID); writer.WriteEncodedInt(TriggerRange); } private IEntity ReadEntity(IGenericReader reader) => World.FindEntity(reader.ReadUInt()); public override void Deserialize(IGenericReader reader) { base.Deserialize(reader); var version = reader.ReadInt(); switch (version) { case 0: { EffectDelay = reader.ReadTimeSpan(); TriggerDelay = reader.ReadTimeSpan(); SoundDelay = reader.ReadTimeSpan(); m_Source = ReadEntity(reader); m_Target = ReadEntity(reader); Sequence = reader.ReadEntity(); FixedDirection = reader.ReadBool(); Explodes = reader.ReadBool(); PlaySoundAtTrigger = reader.ReadBool(); EffectType = (ECEffectType)reader.ReadEncodedInt(); EffectLayer = (EffectLayer)reader.ReadEncodedInt(); TriggerType = (EffectTriggerType)reader.ReadEncodedInt(); EffectItemID = reader.ReadEncodedInt(); EffectHue = reader.ReadEncodedInt(); RenderMode = reader.ReadEncodedInt(); Speed = reader.ReadEncodedInt(); Duration = reader.ReadEncodedInt(); ParticleEffect = reader.ReadEncodedInt(); ExplodeParticleEffect = reader.ReadEncodedInt(); ExplodeSound = reader.ReadEncodedInt(); Unknown = reader.ReadEncodedInt(); SoundID = reader.ReadEncodedInt(); TriggerRange = reader.ReadEncodedInt(); break; } } } public void PlaySound(IEntity trigger) { var ent = PlaySoundAtTrigger ? trigger : this; Effects.PlaySound((ent as Item)?.GetWorldLocation() ?? ent.Location, ent.Map, SoundID); } public void DoEffect(IEntity trigger) { if (Deleted || TriggerType == EffectTriggerType.None) { return; } if (trigger is Mobile { Hidden: true } mobile && mobile.AccessLevel > AccessLevel.Player) { return; } if (SoundID > 0) { Timer.StartTimer(SoundDelay, () => PlaySound(trigger)); } if (Sequence != null) { Timer.StartTimer(TriggerDelay, () => Sequence.DoEffect(trigger)); } if (EffectType != ECEffectType.None) { Timer.StartTimer(EffectDelay, () => InternalDoEffect(trigger)); } } public void InternalDoEffect(IEntity trigger) { var from = m_Source ?? trigger; var to = m_Target ?? trigger; switch (EffectType) { case ECEffectType.Lightning: { Effects.SendBoltEffect(from, false, EffectHue); break; } case ECEffectType.Location: { Effects.SendLocationParticles( EffectItem.Create(from.Location, from.Map, EffectItem.DefaultDuration), EffectItemID, Speed, Duration, EffectHue, RenderMode, ParticleEffect, Unknown ); break; } case ECEffectType.Moving: { if (from == this) { from = EffectItem.Create(from.Location, from.Map, EffectItem.DefaultDuration); } if (to == this) { to = EffectItem.Create(to.Location, to.Map, EffectItem.DefaultDuration); } Effects.SendMovingParticles( from, to, EffectItemID, Speed, Duration, FixedDirection, Explodes, EffectHue, RenderMode, ParticleEffect, ExplodeParticleEffect, ExplodeSound, EffectLayer, Unknown ); break; } case ECEffectType.Target: { Effects.SendTargetParticles( from, EffectItemID, Speed, Duration, EffectHue, RenderMode, ParticleEffect, EffectLayer, Unknown ); break; } } } } }