Discussion in   Coding Corner   started     10 years ago   August 19, 2013, 06:18:10 PM   by   Keldon

What I learned this week(timers and displaying time left)

Keldon
Offline
223 Posts
Topic :   What I learned this week(timers and displaying time left)
10 years ago  August 19, 2013, 06:18:10 PM

This week I wanted to design an item that is meant to be used post res to refill the users hit points, mana, and stamina.  I put this on a 10 minute lock out and if the item is locked out it gives the user a message with about how long until they can use it again.  I know my tutorials are probably not as easy to follow as Kyn's tutorials but I figure I would throw the stuff up here incase anyone else is playing around with C#.  Here is how I accomplished said feat:


We start out with the usings, namespace, and constructable as we do in any program with an item.  I am going to assume at this point anyone reading these posts knows the general setup for this section:


Code: [Select]

using System;
using Server;
using Server.Items;
using Server.Mobiles;


namespace Server.Items
{
    public class AHealersTouch : Item
    {
        public override string DefaultName{get {return ("A Healer's Touch");}}


        [Constructable]
        public AHealersTouch() : base(0x1F1C)
        {
            this.Weight = 1.0;
            LootType = LootType.Blessed;
            this.Hue = 37;
        }




        public AHealersTouch( Serial serial ) : base( serial )
{
}
        public override void Serialize(GenericWriter writer)
        {
            base.Serialize(writer);


            writer.Write((int)0);
        }


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


            int version = reader.ReadInt();
        }
    }
}


This generates an item that is blessed and is a red hued powercrystal.  I have added my serialize and deserialize.  Now I want this item to do something when double clicked from the players back pack.  If the user is less then full of any of the 3 stats it will heal that stat for 300 and give the user a message that they have been healed in that stat.  Here is how I performed that function:
Code: [Select]

        public override void OnDoubleClick(Mobile m)
        {
            if (IsChildOf(m.Backpack))//this is to allow the item to only be used from the backpack
            {
                    if (m.Hits < m.HitsMax)//this heals the user for 300 hit points if they are not already full and gives a message saying they were healed.
                    {
                        m.Hits += 300;
                        m.SendMessage("You feel your life return!");
                    }
                    if (m.Stam < m.StamMax)//this heals the user for 300 stam if they are not already full and gives a message saying they were healed.
                    {
                        m.Stam += 300;
                        m.SendMessage("You feel your vigor return!");
                    }
                    if (m.Mana < m.ManaMax)//this heals the user for 300 mana if they are not already full and gives a message saying they were healed.
                    {
                        m.Mana += 300;
                        m.SendMessage("You feel your magical prowess return!");
                    }
            }
        }


The way the item is setup now it could be spammed as fast as a player could double click the item.  This is not something I wanted so I added a 10 minute timer and had the double click check to see if the timer state was true.  If it is false (default state when declaring a bool variable) it will perform the heals.  If it is true, meaning the item is on lock out it returns a message to the user telling them they need to wait.


Code: [Select]


        public static bool m_Used; //Declare a bool for a timer.  This is to keep the item from being spammed


        public override void OnDoubleClick(Mobile m)
        {
            if (IsChildOf(m.Backpack))//this is to allow the item to only be used from the backpack
            {
                if (m_Used == false)//this allows the item to only be used if not on timer lock out
                {
                    if (m.Hits < m.HitsMax)//this heals the user for 300 hit points if they are not already full and gives a message saying they were healed.
                    {
                        m.Hits += 300;
                        m.SendMessage("You feel your life return!");
                    }
                    if (m.Stam < m.StamMax)//this heals the user for 300 stam if they are not already full and gives a message saying they were healed.
                    {
                        m.Stam += 300;
                        m.SendMessage("You feel your vigor return!");
                    }
                    if (m.Mana < m.ManaMax)//this heals the user for 300 mana if they are not already full and gives a message saying they were healed.
                    {
                        m.Mana += 300;
                        m.SendMessage("You feel your magical prowess return!");
                    }
                    m_Used = true;//this sets the timer tick to a lock out state
                    UsedTimer used = new UsedTimer();//this declares the timer
                    used.Start();//this starts the timer
                }
                else
                    m.SendMessage("You must wait awhile for the crystals power to return");
            }
        }


        public class UsedTimer : Timer//this starts the timer, Timer is set to 10 mintues in an seconds interval to prevent issues with the display of time left.
        {
            public UsedTimer() : base(TimeSpan.FromSeconds(600))
            {
                Priority = TimerPriority.OneSecond;
            }
            protected override void  OnTick()//when the timer is up it resets the reuse so the player can use the item again.
            {
                m_Used = false;
            }
        }


Now if we put that all together we would have a functioning item that will heal its user once every 10 minutes.  Lets take a look at how this looks


Code: [Select]


using System;
using Server;
using Server.Items;
using Server.Mobiles;


namespace Server.Items
{
    public class AHealersTouch : Item
    {
        public override string DefaultName{get {return ("A Healer's Touch");}}


        [Constructable]
        public AHealersTouch() : base(0x1F1C)
        {
            this.Weight = 1.0;
            LootType = LootType.Blessed;
            this.Hue = 37;
        }


        public static bool m_Used; //Declare a bool for a timer.  This is to keep the item from being spammed


        public override void OnDoubleClick(Mobile m)
        {
            if (IsChildOf(m.Backpack))//this is to allow the item to only be used from the backpack
            {
                if (m_Used == false)//this allows the item to only be used if not on timer lock out
                {
                    if (m.Hits < m.HitsMax)//this heals the user for 300 hit points if they are not already full and gives a message saying they were healed.
                    {
                        m.Hits += 300;
                        m.SendMessage("You feel your life return!");
                    }
                    if (m.Stam < m.StamMax)//this heals the user for 300 stam if they are not already full and gives a message saying they were healed.
                    {
                        m.Stam += 300;
                        m.SendMessage("You feel your vigor return!");
                    }
                    if (m.Mana < m.ManaMax)//this heals the user for 300 mana if they are not already full and gives a message saying they were healed.
                    {
                        m.Mana += 300;
                        m.SendMessage("You feel your magical prowess return!");
                    }
                    m_Used = true;//this sets the timer tick to a lock out state
                    UsedTimer used = new UsedTimer();//this declares the timer
                    used.Start();//this starts the timer
                }
                else
                    m.SendMessage("You must wait awhile for the crystals power to return");
            }
        }


        public class UsedTimer : Timer//this starts the timer, Timer is set to 10 mintues in an seconds interval to prevent issues with the display of time left.
        {
            public UsedTimer() : base(TimeSpan.FromSeconds(600))
            {
                Priority = TimerPriority.OneSecond;
            }
            protected override void  OnTick()//when the timer is up it resets the reuse so the player can use the item again.
            {
                m_Used = false;
            }
        }




        public AHealersTouch( Serial serial ) : base( serial )
{
}
        public override void Serialize(GenericWriter writer)
        {
            base.Serialize(writer);


            writer.Write((int)0);
        }


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


            int version = reader.ReadInt();
        }
    }
}


That would give us a fully functional item but I wanted a little bit more out of it.  I do not like when I go to use something in a game and it just tells me to wait.  Do I wait a minute, a day, a month.  Some times an item just does not tell you.  For this I decided I would declare a DateTime variable.


Code: [Select]
        public DateTime NextUse;


This allows me the ability to perform a math function to get variable that stores the time of when the item is usable again and I can perform a math function off of that so I can display that to the user.


Code: [Select]
                    NextUse = DateTime.Now + TimeSpan.FromSeconds(600);
                    TimeSpan NextUseInterval = NextUse - DateTime.Now;


The NextUse is put in the double click section.  When the user double clicks the item it sets the NextUse to current time + 600 seconds since this is the length of our timer.  The TimeSpan NextUseInterval is in the section of the double click if the timer is running, m_Used = true.  This TimeSpan variable takes our NextUse variable that we set at the DateTime that our timer would expire and subtract the current DateTime from it.  With this we can perform the Math.Truncate function to provide the user with feed back on how long until the timer resets.  We are going to place our NextUseInterval into our m.SendMessage line.


Code: [Select]
                        m.SendMessage("You must wait " + Math.Truncate(NextUseInterval.TotalMinutes) + " minutes longer for the crystal's power to return");


Now in this basic form we will a message but when the timer is less then 1 minute it will tell us we must wait 0 minutes to use the item again.  Also at 1 minute left it will tell us we have 1 minutes remaining.  To clean this up I used an inbedded if series.  If the time left is > 1 minute give a message otherwise if it is > 0 give a different message otherwise give a 3rd message.  This allows the giving of 3 different messages for 2-10 minutes, 1 minute, and finally seconds remaining.  This looks like this


Code: [Select]

                    if (Math.Truncate(NextUseInterval.TotalMinutes) > 1)
                        m.SendMessage("You must wait " + Math.Truncate(NextUseInterval.TotalMinutes) + " minutes longer for the crystal's power to return");
                    else
                        if (Math.Truncate(NextUseInterval.TotalMinutes) > 0)
                            m.SendMessage("You must wait " + Math.Truncate(NextUseInterval.TotalMinutes) + " minute longer for the crystal's power to return");
                        else
                            m.SendMessage("You must wait " + Math.Truncate(NextUseInterval.TotalSeconds) + " seconds longer for the crystal's power to return");


Now we put all of this together and we end up with an item that heals the users hit points, stamina, and mana for 300 points each once every 10 minutes.  I have added comments to all of my code which is the stuff following the // on any line.  This should help you understand what each section is and why it is there.


Code: [Select]

using System;
using Server;
using Server.Items;
using Server.Mobiles;


namespace Server.Items
{
    public class AHealersTouch : Item
    {
        public override string DefaultName{get {return ("A Healer's Touch");}}


        [Constructable]
        public AHealersTouch() : base(0x1F1C)
        {
            this.Weight = 1.0;
            LootType = LootType.Blessed;
            this.Hue = 37;
        }


        public static bool m_Used; //Declare a bool for a timer.  This is to keep the item from being spammed


        public DateTime NextUse;//This is to give the user feedback as to about how long until the item can be used again.


        public override void OnDoubleClick(Mobile m)
        {
            if (IsChildOf(m.Backpack))//this is to allow the item to only be used from the backpack
            {
                if (m_Used == false)//this allows the item to only be used if not on timer lock out
                {
                    if (m.Hits < m.HitsMax)//this heals the user for 300 hit points if they are not already full and gives a message saying they were healed.
                    {
                        m.Hits += 300;
                        m.SendMessage("You feel your life return!");
                    }
                    if (m.Stam < m.StamMax)//this heals the user for 300 stam if they are not already full and gives a message saying they were healed.
                    {
                        m.Stam += 300;
                        m.SendMessage("You feel your vigor return!");
                    }
                    if (m.Mana < m.ManaMax)//this heals the user for 300 mana if they are not already full and gives a message saying they were healed.
                    {
                        m.Mana += 300;
                        m.SendMessage("You feel your magical prowess return!");
                    }
                    m_Used = true;//this sets the timer tick to a lock out state
                    UsedTimer used = new UsedTimer();//this declares the timer
                    used.Start();//this starts the timer
                    NextUse = DateTime.Now + TimeSpan.FromSeconds(600);//this declares the time until the item is useable again.
                }
                else
                {
                    TimeSpan NextUseInterval = NextUse - DateTime.Now;//this calculates how long until the player has to wait until the item is usable again.


                    if (Math.Truncate(NextUseInterval.TotalMinutes) > 1)
                        m.SendMessage("You must wait " + Math.Truncate(NextUseInterval.TotalMinutes) + " minutes longer for the crystal's power to return");//This gives the user the lock out message if the time left is well over 1 minute
                    else
                        if (Math.Truncate(NextUseInterval.TotalMinutes) > 0)
                            m.SendMessage("You must wait " + Math.Truncate(NextUseInterval.TotalMinutes) + " minute longer for the crystal's power to return");//This gives the user a lock out message if the time left is about 1 minute
                        else
                            m.SendMessage("You must wait " + Math.Truncate(NextUseInterval.TotalSeconds) + " seconds longer for the crystal's power to return");//this gives the user a lock out message if the time left is less then 1 minute.  the message is in seconds
                }
            }
        }


        public class UsedTimer : Timer//this starts the timer, Timer is set to 10 mintues in an seconds interval to prevent issues with the display of time left.
        {
            public UsedTimer() : base(TimeSpan.FromSeconds(600))
            {
                Priority = TimerPriority.OneSecond;
            }
            protected override void  OnTick()//when the timer is up it resets the reuse so the player can use the item again.
            {
                m_Used = false;
            }
        }




        public AHealersTouch( Serial serial ) : base( serial )
{
}
        public override void Serialize(GenericWriter writer)
        {
            base.Serialize(writer);


            writer.Write((int)0);
        }


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


            int version = reader.ReadInt();
        }
    }
}

SirGuybrush
Offline
33 Posts
#1 Re :   What I learned this week(timers and displaying time left)
10 years ago  August 20, 2013, 03:20:47 AM

Thanks sir! But i think you missed something (not sure).
If the players HP/M/S are full, he/she can use the potion. I think you can code something like when players hp/m/s are full game gives "You don't need to use this potion!" message etc. Just asking :)

Thanks for the tutorial, +Karma ^^

Keldon
Offline
223 Posts
#2 Re :   What I learned this week(timers and displaying time left)
10 years ago  August 20, 2013, 03:29:47 AM

Yes I thought about adding that but decided against it.  If a player burns something with a 10 minute lock out when they don't need it I feel like it is a natural selection methodology.


that being said adding another layer to the ifs inside of on double click after the ischildof check to see if it is in the players back pack this could be added:


Code: [Select]
if(m.Hits = m.HitsMax && m.Stam = m.StamMax && m.Mana = m.ManaMax)
    m.SendMessage("You are full and have no need of this infusion");
else
{
all of the other stuff for adding hp and such, starting timer
}

good catch and thinking though.  I just try to encourage a smarter player base who thinks before they blindly click on things.

SirGuybrush
Offline
33 Posts
#3 Re :   What I learned this week(timers and displaying time left)
10 years ago  August 20, 2013, 08:40:58 AM

" I just try to encourage a smarter player base who thinks before they blindly click on things. "

 Lol ok :) Just think that player use that potion accidently (with macro or wrong double-clicking), this 'if' line will block him to use the potion and saves his/her 10 minute + 1 potion. This is why i suggested :)

"if(m.Hits = m.HitsMax && m.Stam = m.StamMax && m.Mana = m.ManaMax)
    m.SendMessage("You are full and have no need of this infusion");
else
{
all of the other stuff for adding hp and such, starting timer
} "

Thanks for the codes!

Kyn
Offline
336 Posts
#4 Re :   What I learned this week(timers and displaying time left)
10 years ago  August 20, 2013, 10:01:39 AM

Interesting. :)

Keldon
Offline
223 Posts
#5 Re :   What I learned this week(timers and displaying time left)
10 years ago  August 20, 2013, 12:15:56 PM

This item is intended as a post rez, get you back in the fight quickly item.  Think of this item in this manner.  You just used your self rez, you hit your dress macro, you double click this item.  In about 15 seconds your back full and ready to fight, pvm or pvp, your back in it before the post rez invul wear off.


The 10 minute timer is at that length to prevent people from using it freely to save their butts in a fight.

Keldon
Offline
223 Posts
#6 Re :   What I learned this week(timers and displaying time left)
10 years ago  August 20, 2013, 12:28:55 PM

Also SirGuybrush, this is actually an item and not a potion.  Once you get one its with you but just not spam able due to the lock out.


If along with the timer start I added this line of code then the item would be consumed:


Code: [Select]
this.Delete();


that would make the item destroy itself upon use.  Double clicking on a second one of these will still have the lock out but displayed time left would be very strange.  If I were going to make a new potion line that dropped I would make a base item that had a timer on double click and not much else.  Then each item would call back to that base item and modify what is going to happen.


C# builds upon the base object.


Code: [Select]
public class AHealersTouch : Item


That is where I called to the base "Item".  If I were making a new potion line I would delete the stuff about healing from this and basically just have it start a timer.  I would then create a series of items that used "AHealersTouch" instead of "Item" like so


Code: [Select]
public class AdvancedAHealersTouch : AHealersTouch


This means the AdvancedAHealersTouch would gain everything coded in its own file as well as inherit all the stuff from AHealersTouch, Item, and so forth until you get all the way back to the original object file.


This is something that is completely possible but was not in the scope of what I was trying to do.