View Single Post
Old 08-18-2004, 01:29 AM   #3 (permalink)
David
Moderate
 
David's Avatar
 
Join Date: Nov 2002
Location: USA
Posts: 6,598
Default

The Magic Eight Ball script as it stands now, does its job well but could be improved with a few easy enhancements. Currently there is no range checking, a Player can activate the Eight Ball from across the room, or even from the next room. Also, it has been a lot of years since I have used my Magic Eight Ball, but it seems like you had to concentrate for a few seconds before it worked. I think at the very least we should add a three second delay before our Eight Ball can be used again.

What we need to look at first is the OnDoubleClick method at the end of the script. Currently it consists of a switch statement that makes a bunch of SendMessage calls. The beginning of the method is this:
Code:
 		public override void OnDoubleClick( Mobile from ) 
		{ 
			switch ( Utility.Random( 8 ) )
			{
				default:
				case  0: from.SendMessage( "IT IS CERTAIN" ); break;
We need to add to the method a check to limit how far a player can be from the item, say 2 tiles, and we need to make sure the player has line of sight to the item. This would go at the beginning of our method just before the switch statement.
Code:
 		public override void OnDoubleClick( Mobile from ) 
		{ 
			if ( from.InRange( this, 2 ) && from.CanSee( this ) )
			{
				switch ( Utility.Random( 8 ) )
				{
There are a few things to notice here. First is the keyword "this." The keyword this always refers to the specific instance of the Class we are working in. In other words the manifestation of this script. In this case this will mean the EightBall that has received the DoubleClick. Second, we use the InRange() and CanSee() methods of the Mobile Class. These methods and many more can be found in the RunUO Docs. Finally, notice the && operator; this is the "conditional-AND" which you will see and use often.

The AND operator (&) and the conditional-AND operator (&&) perform a boolean evaluation on two expressions. If and only if both expressions are considered to be true will the result be true. (x & y) is true only if both x AND y are both true. The conditional-AND operator is a little smarter than the regular AND operator however. If the first expression in a conditional-AND turns out to be false, the second expression is never evaluated, there is no need since the result now has to be false. This works to the scripters advantage in avoiding the dreaded null reference exception. More on this later.

Now, we have added an if statement with an opening brace to our method. Since any opening brace must have a closing brace, we still have work to do. At the end of our switch statement, after it's closing brace, we need to add a closing brace for our if statement. It wouldn't hurt to also add a message to provide a bit of feedback to the player when they are too far away. Here is the complete method at this point.
Code:
 		public override void OnDoubleClick( Mobile from ) 
		{ 
			if ( from.InRange( this, 2 ) && from.CanSee( this ) )
			{
				switch ( Utility.Random( 8 ) )
				{
					default:
					case  0: from.SendMessage( "IT IS CERTAIN" ); break;
					case  1: from.SendMessage( "WITHOUT A DOUBT" ); break;
					case  2: from.SendMessage( "MY REPLY IS NO" ); break;
					case  3: from.SendMessage( "ASK AGAIN LATER" ); break;
					case  4: from.SendMessage( "VERY DOUBTFUL" ); break;
					case  5: from.SendMessage( "CONCENTRATE AND ASK AGAIN" ); break;
					case  6: from.SendMessage( "DON'T COUNT ON IT" ); break;
					case  7: from.SendMessage( "YES" ); break;
				}
			}
			else 
			{ 
				from.SendLocalizedMessage( 500446 ); // That is too far away. 
			} 
		}
We have now used two different versions of the SendMessage method. Mobile.SendMessage() will send a system message to the player that Mobile references. Mobile.SendLocalizedMessage() will also send a system message to the player but it sends a text string from the cliloc files on the clients computer. The cliloc is the Client Localization files. There are at least eight separate files for each of eight languages that the UO client supports. You pass an integer to the SendLocalizedMessage() method and it in turn displays the appropriate message in the language of the client. It is recommended that you add the message text as a comment so you will know later what you are sending. You may find the text and index numbers using a cliloc viewer.

The method looks pretty good at this point, but what if the EightBall is in the players pack? We can add that test to the same line we test the range in, but the boolean operator must be different. In this case we want to know if the item is in the pack OR both in range and in sight. Fortunately there is an OR operator (|) and a conditional-OR operator (||). To check the pack, we will use another method from the Docs; this time from the Item Class. IsChildOf( from.Backpack ) will return a true if this item is anywhere in from's backpack. Our if statement now looks like this.
Code:
if ( IsChildOf( from.Backpack ) || from.InRange( this, 2 ) && from.CanSee( this ) )
In order to add a time delay to our EightBall, we are going to need two new variables. We will declare those variables at the Class level, meaning not inside any methods. If they were declared inside a method they would only exist for the duration of the method. We will use two time related variables; a DateTime which identifies a point in time (ex. 3:00 PM today) and a TimeSpan which identifies a length of time (ex. 15 minutes.) They will be declared at the beginning of our Class, however order is really not important.
Code:
 namespace Server.Items 
{ 
	public class EightBall : Item
	{ 
		private DateTime lastused = DateTime.Now;
		private TimeSpan delay = TimeSpan.FromSeconds( 3 );
The DateTime lastused was initialized with DateTime.Now which is always the time at that moment, in this case the moment the EightBall is created. The TimeSpan variable delay is set to 3 seconds using a method of the TimeSpan Class called FromSeconds().

Now we need to again look at the OnDoubleClick() method to add one more test. We will add delay plus lastused (adding a TimeSpan to a DateTime results in a DateTime and is similar to saying "15 minutes after 3 o'clock") then see if that time is later than it is now. If so, it has not been long enough since the last use so we will exit the entire method using a return statement. Otherwise we will update the value of lastused and continue with the rest of the method.
Code:
 		public override void OnDoubleClick( Mobile from ) 
		{ 
			if ( lastused + delay > DateTime.Now ) 
				return;
			else
				lastused = DateTime.Now;
Since the body of both the if statement and the else statement are each only one line I left out the curly braces. If they were any longer the braces would not be optional.

Here is the complete script at this point:
Code:
using System; 
using Server; 

namespace Server.Items 
{ 
	public class EightBall : Item
	{ 
		private DateTime lastused = DateTime.Now;
		private TimeSpan delay = TimeSpan.FromSeconds( 3 );

		[Constructable]
		public EightBall() : base( 0xE2F ) 
		{
			Weight = 1.0; 
			Name = "a magic eight ball"; 
		} 

		public EightBall( 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(); 
		}

		public override void OnDoubleClick( Mobile from ) 
		{ 
			if ( lastused + delay > DateTime.Now ) 
				return;
			else
				lastused = DateTime.Now;

			if ( IsChildOf( from.Backpack ) || from.InRange( this, 2 ) && from.CanSee( this ) )
			{
				switch ( Utility.Random( 8 ) )
				{
					default:
					case  0: from.SendMessage( "IT IS CERTAIN" ); break;
					case  1: from.SendMessage( "WITHOUT A DOUBT" ); break;
					case  2: from.SendMessage( "MY REPLY IS NO" ); break;
					case  3: from.SendMessage( "ASK AGAIN LATER" ); break;
					case  4: from.SendMessage( "VERY DOUBTFUL" ); break;
					case  5: from.SendMessage( "CONCENTRATE AND ASK AGAIN" ); break;
					case  6: from.SendMessage( "DON'T COUNT ON IT" ); break;
					case  7: from.SendMessage( "YES" ); break;
				}
			}
			else 
			{ 
				from.SendLocalizedMessage( 500446 ); // That is too far away. 
			} 
		}
	} 
}
__________________
David Forum Moderator
The RunUO.com Forum Moderator Team

Forum Rules and Guidelines
RunUO Forum Search Engine
Download RunUO 2.0 RC2

Last edited by David; 11-24-2007 at 12:14 AM.
David is offline