RunUO Community

This is a sample guest message. Register a free account today to become a member! Once signed in, you'll be able to participate on this site by adding your own topics and posts, as well as connect with other members through your own private inbox!

[RUO 2.0 RCX] BaseExplosionPotion with Cool-Down Delay

Vorspire

Knight
[RUO 2.0 RCX] BaseExplosionPotion with Cool-Down Delay

RunUO Versions: 2.0 RC1, 2.0 RC2, 2.0 SVN X?

Install Difficulty Rating: 2/10

I was working on such a script, when I thought, with a couple of modifications, this could be really useful to stop those players from spamming multiple explosion pots... and nuking unsuspecting players....

This version also includes a simple Notoriety-Handled system for Guarded Regions;

If you set off an Explosion Potion inside of a Guarded Region, it will only damage Mobiles that can be attacked by you; Criminals, Murderers and natural Enemies/Guilds/etc.
-This portion of the script is colored in BLUE and can be very easily removed if the lines in BLUE are simply deleted.

So here is the script for RUNUO 2.0 RCX

I don't work with RunUO 2.0 usually, so I'm not sure if this is compatible with SVN.

Anyway.. just replace the distro BaseExplosionPotion.cs with this script, let me know how it goes...
(The code that I have implimented is annotated for easy understanding.)

Code:
using System;
using System.Collections;
using System.Collections.Generic;
using Server;
using Server.Network;
using Server.Targeting;
using Server.Spells;
using Server.Regions;
using Server.Misc;

namespace Server.Items
{
[COLOR="YellowGreen"]    /// <summary>
    /// This Handler class provides the means to store delay context data in a simple cache.
    /// </summary>[/COLOR]
    public class ExplosionPotHandler
    {
        private static Hashtable m_DelayCache = new Hashtable();
        public static Hashtable DelayCache { get { return m_DelayCache; } }

[COLOR="YellowGreen"]        /// <summary>
        /// Registers a Mobile and BaseExplosionPotion with the delay cache.
        /// Overwrites delay context data if it already exists.
        /// </summary>
        /// <param name="m">Mobile</param>
        /// <param name="pot">BaseExplosionPotion</param>[/COLOR]
        public static void Register(Mobile m, BaseExplosionPotion pot)
        {
            Unregister(m);

            m_DelayCache.Add(m, new ExplosionPotDelayContext(m, pot.NextUseDelay));
        }

[COLOR="YellowGreen"]        /// <summary>
        /// Unregisters a given obile from the delay cache, if it has an entry.
        /// </summary>
        /// <param name="m">Mobile</param>[/COLOR]
        public static void Unregister(Mobile m)
        {
            if (m_DelayCache.ContainsKey(m))
                m_DelayCache.Remove(m);
        }

[COLOR="YellowGreen"]        /// <summary>
        /// Checks to see if the given Mobile can actually use a new explosion potion.
        /// </summary>
        /// <param name="m">Mobile</param>
        /// <returns>True if delay has expired or delay context is null.</returns>[/COLOR]
        public static bool AllowUse(Mobile m)
        {
            ExplosionPotDelayContext context = GetContext(m);

            if (context == null)
                return true;

            if (context.EndTime <= DateTime.Now)
            {
                Unregister(m);
                return true;
            }

            return false;
        }

[COLOR="YellowGreen"]        /// <summary>
        /// Retrieves the Delay Context for the given Mobile.
        /// </summary>
        /// <param name="m">Mobile</param>
        /// <returns>ExplosionPotDelayContext object if cache contains a delay entry.</returns>[/COLOR]
        public static ExplosionPotDelayContext GetContext(Mobile m)
        {
            if (m_DelayCache.ContainsKey(m))
                return (ExplosionPotDelayContext)m_DelayCache[m];

            return null;
        }
    }

[COLOR="YellowGreen"]    /// <summary>
    /// Class to provide the Delay Context Data.
    /// </summary>[/COLOR]
    public class ExplosionPotDelayContext
    {
        private Mobile m_Mobile;
        private DateTime m_StartTime;
        private DateTime m_EndTime;

        public Mobile Mobile { get { return m_Mobile; } }
        public DateTime StartTime { get { return m_StartTime; } }
        public DateTime EndTime { get { return m_EndTime; } }

        public TimeSpan TimeLeft { get { return GetTimeLeft(); } }

[COLOR="YellowGreen"]        /// <summary>
        /// Contruct and set the Start and End Times.
        /// </summary>
        /// <param name="m">Mobile</param>
        /// <param name="delay">TimeSpan</param>[/COLOR]
        public ExplosionPotDelayContext(Mobile m, TimeSpan delay)
        {
            m_Mobile = m;
            m_StartTime = DateTime.Now;
            m_EndTime = DateTime.Now.Add(delay);
        }

[COLOR="YellowGreen"]        /// <summary>
        /// Calculates the current amount of time left before the delay expires.
        /// </summary>
        /// <returns>TimeSpan object representing the time left until the delay expires. Zero if delay has expired.</returns>[/COLOR]
        public TimeSpan GetTimeLeft()
        {
            TimeSpan timeLeft = TimeSpan.Zero;

            if (m_EndTime > DateTime.Now)
                timeLeft = m_EndTime.Subtract(DateTime.Now);

            return timeLeft;
        }
    }


    public abstract class BaseExplosionPotion : BasePotion
    {
        public abstract int MinDamage { get; }
        public abstract int MaxDamage { get; }

        public virtual double Delay { get { return 4.0; } } // 4.0 seconds. Can be overridden in child classes.

[COLOR="YellowGreen"]        /// <summary>
        /// The delay between using potions.
        /// TimeSpan.Zero for no delay.
        /// Overridable.
        /// </summary>[/COLOR]
        public virtual TimeSpan NextUseDelay { get { return TimeSpan.FromSeconds(10.0); } }

        public override bool RequireFreeHand { get { return false; } }

        private static bool LeveledExplosion = false; // Should explosion potions explode other nearby potions?
        private static bool InstantExplosion = false; // Should explosion potions explode on impact?
        private const int ExplosionRange = 2;     // How far is the blast radius?

        public BaseExplosionPotion(PotionEffect effect)
            : base(0xF0D, effect)
        {
        }

        public BaseExplosionPotion(Serial serial)
            : base(serial)
        {
        }

        public override void Serialize(GenericWriter writer)
        {
            base.Serialize(writer);

            writer.Write((int)0); // version
        }

        public override void Deserialize(GenericReader reader)
        {
            base.Deserialize(reader);

            int version = reader.ReadInt();
        }

        private Timer m_Timer;
        private ArrayList m_Users;

        public override void Drink(Mobile from)
        {
[COLOR="YellowGreen"]            //Pseudo-setting: sendMessages
            //Default: true.
            //Set this to false to disable delay/cooldown time messages.[/COLOR]
            bool sendMessages = true;

            if (Core.AOS && (from.Paralyzed || from.Frozen || (from.Spell != null && from.Spell.IsCasting)))
            {
                from.SendLocalizedMessage(1062725); // You can not use a purple potion while paralyzed.
                return;
            }

            if (m_Timer == null)
            {
                if (ExplosionPotHandler.AllowUse(from))
                    DoThrow(from);
                else
                {
                    if (sendMessages)
                    {
                        ExplosionPotDelayContext context = ExplosionPotHandler.GetContext(from);

                        if (context == null)
                            from.LocalOverheadMessage(MessageType.Regular, 0x22, 1070772); // You must wait a few seconds before you can use that item.
                        else
                            from.SendMessage("You must wait {0} seconds before you can use that item.", (context.TimeLeft.Seconds));
                    }

                    return;
                }
            }
            else
                DoThrow(from);
        }

        public virtual object FindParent(Mobile from)
        {
            Mobile m = this.HeldBy;

            if (m != null && m.Holding == this)
                return m;

            object obj = this.RootParent;

            if (obj != null)
                return obj;

            if (Map == Map.Internal)
                return from;

            return this;
        }

        public virtual void DoThrow(Mobile from)
        {
            ThrowTarget targ = from.Target as ThrowTarget;

            if (targ != null && targ.Potion == this)
                return;

            from.RevealingAction();

            if (m_Users == null)
                m_Users = new ArrayList();

            if (!m_Users.Contains(from))
                m_Users.Add(from);

            from.Target = new ThrowTarget(this);

            if (m_Timer == null)
            {
                from.SendLocalizedMessage(500236); // You should throw it now!
                m_Timer = Timer.DelayCall(TimeSpan.FromSeconds(0.75), TimeSpan.FromSeconds(1.0), 6, new TimerStateCallback(Detonate_OnTick), new object[] { from, Utility.RandomMinMax(3, 4) });
            }
        }

        private void Detonate_OnTick(object state)
        {
            if (Deleted)
                return;

            object[] states = (object[])state;
            Mobile from = (Mobile)states[0];
            int timer = (int)states[1];

            object parent = FindParent(from);

            if (timer == 0)
            {
                Point3D loc;
                Map map;

                if (parent is Item)
                {
                    Item item = (Item)parent;

                    loc = item.GetWorldLocation();
                    map = item.Map;
                }
                else if (parent is Mobile)
                {
                    Mobile m = (Mobile)parent;

                    loc = m.Location;
                    map = m.Map;
                }
                else
                {
                    return;
                }

                Explode(from, true, loc, map);
            }
            else
            {
                if (parent is Item)
                    ((Item)parent).PublicOverheadMessage(MessageType.Regular, 0x22, false, timer.ToString());
                else if (parent is Mobile)
                    ((Mobile)parent).PublicOverheadMessage(MessageType.Regular, 0x22, false, timer.ToString());

                states[1] = timer - 1;
            }
        }

        private void Reposition_OnTick(object state)
        {
            if (Deleted)
                return;

            object[] states = (object[])state;
            Mobile from = (Mobile)states[0];
            IPoint3D p = (IPoint3D)states[1];
            Map map = (Map)states[2];

            Point3D loc = new Point3D(p);

            if (InstantExplosion)
                Explode(from, true, loc, map);
            else
                MoveToWorld(loc, map);
        }

        private class ThrowTarget : Target
        {
            private BaseExplosionPotion m_Potion;

            public BaseExplosionPotion Potion
            {
                get { return m_Potion; }
            }

            public ThrowTarget(BaseExplosionPotion potion)
                : base(12, true, TargetFlags.None)
            {
                m_Potion = potion;
            }

            protected override void OnTargetCancel(Mobile from, TargetCancelType cancelType)
            {
                base.OnTargetCancel(from, cancelType);
                from.EndAction(typeof(BaseExplosionPotion));

                ExplosionPotHandler.Unregister(from);
            }

            protected override void OnTarget(Mobile from, object targeted)
            {
                if (m_Potion.Deleted || m_Potion.Map == Map.Internal)
                    return;

                IPoint3D p = targeted as IPoint3D;

                if (p == null)
                    return;

                Map map = from.Map;

                if (map == null)
                    return;

                SpellHelper.GetSurfaceTop(ref p);

                from.RevealingAction();

                IEntity to;

                if (p is Mobile)
                    to = (Mobile)p;
                else
                    to = new Entity(Serial.Zero, new Point3D(p), map);

                Effects.SendMovingEffect(from, to, m_Potion.ItemID & 0x3FFF, 7, 0, false, false, m_Potion.Hue, 0);

                m_Potion.Internalize();
                Timer.DelayCall(TimeSpan.FromSeconds(1.0), new TimerStateCallback(m_Potion.Reposition_OnTick), new object[] { from, p, map });

                ExplosionPotHandler.Register(from, m_Potion);
            }
        }

        public void Explode(Mobile from, bool direct, Point3D loc, Map map)
        {
            if (Deleted)
                return;

            Delete();

            for (int i = 0; m_Users != null && i < m_Users.Count; ++i)
            {
                Mobile m = (Mobile)m_Users[i];
                ThrowTarget targ = m.Target as ThrowTarget;

                if (targ != null && targ.Potion == this)
                {
                    Target.Cancel(m);
                }
            }

            if (map == null)
                return;

            Effects.PlaySound(loc, map, 0x207);
            Effects.SendLocationEffect(loc, map, 0x36BD, 20);

            int alchemyBonus = 0;

            if (direct)
                alchemyBonus = (int)(from.Skills.Alchemy.Value / (Core.AOS ? 5 : 10));

            IPooledEnumerable eable = LeveledExplosion ? map.GetObjectsInRange(loc, ExplosionRange) : map.GetMobilesInRange(loc, ExplosionRange);
            ArrayList toExplode = new ArrayList();

            int toDamage = 0;

            foreach (object o in eable)
            {
                if (o is Mobile)
                {
                    [COLOR="RoyalBlue"]Mobile tag = o as Mobile;
                    Region reg = Region.Find(tag.Location, tag.Map);

                    if (tag != null && !tag.Deleted && reg != null && reg is GuardedRegion)
                    {
                        if (NotorietyHandlers.MobileNotoriety(from, tag) != (int)Notoriety.Murderer
                            && NotorietyHandlers.MobileNotoriety(from, tag) != (int)Notoriety.Criminal
                            && NotorietyHandlers.MobileNotoriety(from, tag) != (int)Notoriety.Enemy
                            && NotorietyHandlers.MobileNotoriety(from, tag) != (int)Notoriety.CanBeAttacked)
                            continue;
                    }[/COLOR]

                    toExplode.Add(o);
                    ++toDamage;
                }
                else if (o is BaseExplosionPotion && o != this)
                {
                    toExplode.Add(o);
                }
            }

            eable.Free();

            int min = Scale(from, MinDamage);
            int max = Scale(from, MaxDamage);

            for (int i = 0; i < toExplode.Count; ++i)
            {
                object o = toExplode[i];

                if (o is Mobile)
                {
                    Mobile m = (Mobile)o;

                    if (from == null || (SpellHelper.ValidIndirectTarget(from, m) && from.CanBeHarmful(m, false)))
                    {
                        if (from != null)
                            from.DoHarmful(m);

                        int damage = Utility.RandomMinMax(min, max);

                        damage += alchemyBonus;

                        if (!Core.AOS && damage > 40)
                            damage = 40;
                        else if (Core.AOS && toDamage > 2)
                            damage /= toDamage - 1;

                        AOS.Damage(m, from, damage, 0, 100, 0, 0, 0);
                    }
                }
                else if (o is BaseExplosionPotion)
                {
                    BaseExplosionPotion pot = (BaseExplosionPotion)o;

                    pot.Explode(from, false, pot.GetWorldLocation(), pot.Map);
                }
            }
        }
    }
}
 
Top