IanSmellios

Member
I am trying to make the Smithing Press work as a Smith Hammer. I have it working, I am just now trying to figure out how to make the press be usable not in the pack(house, smith shop etc.). I changed the OnDoubleClick() but it still says it has to be in the pack. Here is what I have got so far. Thank You for your time.

SmithingPress.cs
Code:
using System;
using Server.Engines.Craft;

namespace Server.Items
{
    public class SmithingPress : BaseTool
    {
        [Constructable]
        public SmithingPress()
            : base(0x9AA9)
        {
            Weight = 20.0;
            Movable = true;
        }

        [Constructable]
        public SmithingPress(int uses)
            : base(uses, 0x9AA9)
        {
            Weight = 20.0;
            Movable = true;
        }

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

        public override CraftSystem CraftSystem
        {
            get
            {
                return DefBlacksmithy.CraftSystem;
            }
        }

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

Here is the original BaseTool.cs OnDoubleClick()
Code:
        public override void OnDoubleClick(Mobile from)
        {
            if (IsChildOf(from.Backpack) || Parent == from)

This is what I added
Code:
        public override void OnDoubleClick(Mobile from)
        {
            if (IsChildOf(from.Backpack) || Parent == from || from.InRange(this, 2) && from.CanSee(this))

Again, Thank You for the assistance.
 
You shouldn't modify the BaseTool class, override the method you want to change in your child class. Then you can do your check just for range/visibility within your own method, skipping any backpack check, and show the craft menu thingy.
 
You shouldn't modify the BaseTool class, override the method you want to change in your child class. Then you can do your check just for range/visibility within your own method, skipping any backpack check, and show the craft menu thingy.
Thank You, will give that a try.
 
Okay now with this, when I put on the ground it says it must be in my pack. When I put it in the pack it says "This is too far away" I removed my edits to the BaseTool.cs as well.

SmithingPress.cs
Code:
using System;
using Server.Engines.Craft;

namespace Server.Items
{
    [Flipable(0x9AA9, 0x9AA8)]
    public class SmithingPress : BaseTool
    {
        [Constructable]
        public SmithingPress()
            : base(0x9AA9)
        {
            Weight = 20.0;
            Movable = true;
        }

        [Constructable]
        public SmithingPress(int uses)
            : base(uses, 0x9AA9)
        {
            Weight = 20.0;
            Movable = true;
        }

        public override void OnDoubleClick(Mobile from)
        {
            if (from.InRange(this, 2) && from.CanSee(this))
            {

                CraftSystem system = CraftSystem;

                int num = system.CanCraft(from, this, null);

                if (num > 0 && (num != 1044267 || !Core.SE)) // Blacksmithing shows the gump regardless of proximity of an anvil and forge after SE
                {
                    from.SendLocalizedMessage(num);
                }
                else
                {
                    CraftContext context = system.GetContext(from);

                    from.SendGump(new CraftGump(from, system, this, null));
                }
            }
            else
            {
                from.SendLocalizedMessage(500446); // That is too far away.
            }
        }

        public override CraftSystem CraftSystem
        {
            get
            {
                return DefBlacksmithy.CraftSystem;
            }
        }

        public SmithingPress(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();
        }
    }
}
 
Okay so now I added.
Code:
                base.OnDoubleClick(from);

Here
Code:
        public override void OnDoubleClick(Mobile from)
        {
            if (from.InRange(this, 1) && from.CanSee(this))
            {
                base.OnDoubleClick(from);
Now with that I can use it in my pack and if I am out of range it tells me. BUT when I am in range it still says that it has to be in my pack.

Thank You for your time and help.:):D:rolleyes:
 
Its because of the CanCraft function, it has an CanAccess function inside (I think thats how it was named) that checks if you have it in your backpack too. including the base of doubleclick will not help you either.

What you would need to do is a similar OnDoubleClick than BaseTool has but without the CanCraft or even better make an exception in the CanAccess method so that your SmithingPress will not have to be in a backpack.
 
Okay I did this
Code:
        public override void OnDoubleClick(Mobile from)
        {
            if (from.InRange(this, 1) && from.CanSee(this))
            {
                base.OnDoubleClick(from);
                CraftSystem system = CraftSystem;
                CraftContext context = system.GetContext(from);
                from.SendGump(new CraftGump(from,system, this, null));
            }
That worked, is that what you were talking about? I am just making sure I didn't just delete somethings and get lucky that it worked.

Thank You
 
yes and no, reason is if you try to craft something you will probably get the message that you need to have the item in your backpack.
 
Its because of the CanCraft function, it has an CanAccess function inside (I think thats how it was named) that checks if you have it in your backpack too. including the base of doubleclick will not help you either.

What you would need to do is a similar OnDoubleClick than BaseTool has but without the CanCraft or even better make an exception in the CanAccess method so that your SmithingPress will not have to be in a backpack.

How would I go about making an exception for it?
 
Hey Ian!

You're almost there. I think your confusion is arising from not fully understanding boolean operators and their truth tables. It's super easy!

Boolean logic has two states. True and False. A lot of things end up becoming boolean functions. Something like from.InRange(this, 1) is True when you're 1 tile away, and False when you're not. For simplicity, I'll be using 1 to signify True and 0 to signify False.

AND (&&)
The AND operator is only True when BOTH inputs are present. The truth table below shows how it works.
Code:
---------------
| X1 | X2 | Y |
|-------------|
| 0  | 0  | 0 |
| 0  | 1  | 0 |
| 1  | 0  | 0 |
| 1  | 1  | 1 |
---------------

OR (||)
The OR operator is only True when EITHER inputs are present. The truth table below shows how it works.
Code:
---------------
| X1 | X2 | Y |
|-------------|
| 0  | 0  | 0 |
| 0  | 1  | 1 |
| 1  | 0  | 1 |
| 1  | 1  | 1 |
---------------

NOT (!)
The NOT operator only has a single input. It will give its opposite as output. You place the (!) BEFORE your input in the code. The truth table below shows how it works.
Code:
----------
| X1 | Y |
|--------|
| 0  | 1 |
| 1  | 0 |
----------

XOR (^)
The XOR operator is only True when ONE of EITHER inputs are present. The truth table below shows how it works.
Code:
---------------
| X1 | X2 | Y |
|-------------|
| 0  | 0  | 0 |
| 0  | 1  | 1 |
| 1  | 0  | 1 |
| 1  | 1  | 0 |
---------------


Remember, you can use these operators like basic maths with 1's and 0's

For example, when you want something to work out of either when its in your pack or when its 1 tile away. It helps to write it out in a sentence.

This code will execute IF the player is 1 tile away from the object AND the player can see it OR it is in the player's backpack OR it is in a container located somewhere in the player's inventory

So now we can reduce it to
Code:
if((from.InRange(this, 2) && from.CanSee(this)) || IsChildOf(from.Backpack) || Parent == from)

See how that makes sense now :)

I also just noticed a second misunderstanding that you're having - and that's with object inheritance.

Imagine I have a generic car called BaseCar. In the BaseCar i have a function called OpenDoor. Here it is below.

Code:
public virtual void OpenDoors(){
    foreach (Door door in this.Doors) {
        door.SwingOut();
    }
}

Very simple and we have no problem with it. Now sometimes we don't want a BaseCar. Sometimes we want something like a BaseCar but ONLY SLIGHTLY different. For example, there's now a new model of car that's always Red. It would be tiresome to copy over the ENTIRE BaseCar database only to change the colour of it. Also what happens if I wanted to change the way doors opened, for example. I would then need to change it in two scripts rather than one.

This is where inheritance comes in. At the top of our RedCar script, we can write
Code:
public class RedCar : BaseCar
This means that a red car is an identical copy of BaseCar in every way unless otherwise stated in this file. Let's turn the car red!

Code:
[Constructable]
public RedCar()
	: base()
{
	Hue = Red;
}

So now, when a RedCar is constructed it calls base() (which in this case is BaseCar) with no arguments, and then it continues with turning the hue red. Keep in mind that sometimes you'll want to send arguments to your base model (most times in ServUO this is the ItemID). Check what arguments your parent model needs :)

NOW, here's where it starts to get interesting. We want to make a DeLorean. A very basic one. Basically we just want the doors to open upwards, rather than to swing out horizontally. But how do we do that? We've already established how doors open in BaseCar - does that mean we now have to write a whole new car script and maintain it? NOT WITH OBJECT INHERITANCE YOU DON'T!

Ok, so we get our DeLorean
Code:
public class DeLorean: BaseCar

So now our DeLorean inherits everything from BaseCar, but how do we change what BaseCar does with the OpenDoors function? If we take a look at OpenDoors, we'll see that there's a few keywords in there. Let's go over them!

public : This means that this function is able to be called from any other script that has access to the car object. So any object can say something like car.OpenDoors(); and the doors will open. The opposite of this is private. When private is used, then only functions WITHIN your object can call that function. This is to protect certain functionality in your class and not allow, say a tree object to be able to do anything to your gas level.

void : this is what gets returned. So void means nothing needs to be returned from the function. You can put any type of variable here. int will return an integer. string will give you a string. bool will give you a boolean value. You get the gist.

static : this one isn't in the basecar script, but its worth noting. When there's a static keyword, it means that the function can be called WITHOUT reference to an object. So usually you may have a BaseCar object called car which you can use to car.OpenDoors(). But you may have a static function that is for telling you something general about BaseCar objects (like who invented cars), in which case you'd call BaseCar.Inventor() and it would return a string of "Karl Benz". You don't need to have a car in order to get that information!

virtual : Now here's where it gets awesome. When you declare something as virtual, it means that the functionality can be overridden by a child class. So say we have OpenDoors in the BaseCar model, and we wanted to change the way it works in a DeLorean which is also a BaseCar - we put this in the DeLorean file.

Code:
public override void OpenDoors(){
    foreach (Door door in this.Doors) {
        door.RiseUp();
    }
}

You can also override overrides. So if there's a model that's based on a DeLorean that you want to change the OpenDoors function of, you can just insert an override function there.

You can also call the parent's function (that you have overriden) by calling base.OpenDoors(). Whenever you see base, you're dealing with the object that you inherited from (or the one that THAT one inherited from or the one that THAT one inherited from etc. etc.)

So coming back to your question, we can now try to understand why things worked or didn't work for you.

What you did first was edit BaseTool. This would have theoretically worked, but you would be changing the functionality of ALL tools on the server while you only wanted to change SmithingPress which inherits from BaseTool.

Second thing you did was create an override function in SmithingPress (on the right track!) and added checks for being in range (good!) BUT THEN you call the base.OnDoubleClick(from) function. So what's happening here is you're passing the range check, but THEN you're going to the BaseTool's double click function which checks whether or not the basetool is in your bag before working. So in this case, you would have to have the smithing press both simultaneously less than 1 tile away from you and in your LoS while also being in your bag.

Next thing you did was copy the OnDoubleClick function from BaseTool and put it in your SmithingPress script. This is on the right track! BUT within all that, you left a call to base.OnDoubleClick() (noooo!) So it'd pass the range check, fail the base.ondoubleclick check which would give you an error message, but then it would go on and continue with the rest of the script in SmithingPress. This works, but its confusing to players to get an error message and still have the thing work!

So what you want to do is check for distance OR if its in your pack (like we did earilier in this post) and then have all that functionality EXCLUSIVELY in your SmithingTool script.

Namely, just copy/paste this.

Code:
public override void OnDoubleClick(Mobile from)
{
	if ((from.InRange(this, 2) && from.CanSee(this)) || IsChildOf(from.Backpack) || Parent == from)
	{

		CraftSystem system = CraftSystem;

		int num = system.CanCraft(from, this, null);

		if (num > 0 && (num != 1044267 || !Core.SE)) // Blacksmithing shows the gump regardless of proximity of an anvil and forge after SE
		{
			from.SendLocalizedMessage(num);
		}
		else
		{
			CraftContext context = system.GetContext(from);

			from.SendGump(new CraftGump(from, system, this, null));
		}
	}
	else
	{
		from.SendMessage("This must be in your pack or 1 tile away for you to use it.");
	}
}

Sorry for the wall of text, but now you can understand what's going on :)
 
Honestly that is one the best ways I have ever seen that explained and am saving that whole thing as a reference.
 
The only persisting issue with this is that the CanCraft method still checks if the tool is in the backpack, same check happens in the crafting itself.
Therefor giving the feedback of having the item in your backpack :)
 
I can't thank you enough for that wall of text Moody!! I learned so much, you explained it all with great examples and detail.
Now I added that and like @PyrO mentioned, it still checks if its in your pack.

I am having a hard time understanding why and where it keeps checking. If you could point me in the direction of that I would love it. Please keep in mind that I am VERY new to all this. Thank You for your help and patience.

This is what I get when I tell Visual Studio to go to the CanCraft definition.
Code:
        public abstract int CanCraft(Mobile from, BaseTool tool, Type itemType);
 
Ok, so now you've run into another great keyword

abstract : Abstract basically says "Anything that inherits from me needs to have this function in it, but I'm not going to tell you what to put in it!"

The CanCraft abstract method is initially declared in the CraftSystem script. However, the actual craft systems you're interested in is not the base class, but the defined instances of them. In this case (I'm assuming) it's Blacksmithy. So open up the defBlacksmithy script which you should find in your Services/Craft folder and you'll see that CanCraft is declared in there because, as we learned before - defBlacksmithy INHERITS from CraftSystem!

This is the system that's attached to your tool. I'll leave the rest of the debugging to you because it's so very close to getting there!
 
Thank You! And Thank You even more for not just giving me the answer as well. :D I like the actuall process of doing it, usually just need a nudge in the right direction. :p
 
I did spent a little time on it and implemented it as an addon with deed (I do not have an OSI account but it did sound like it would work like this).
For that I did need to change many files to work with an altered crafting call to allow the baseaddon to work with the crafting menu.
Sofar I didnt encounter any bugs (besides that there is no message on dropping an tool onto the crates when you got no permission to) but since it required an adjustment in crafting I would need to make sure first.

(Doing the tailoring one would be rather simple after this though)
 
Well I am thinking to add
Code:
        public override int CanCraft(Mobile from, BaseTool tool, Type itemType)
        {
        }
But all that does is give me a "no suitable method to override" error.
 
So I got them working just fine by now, cant say how OSI accurate they would be.
Didnt encounter any bugs in crafting caused by this (machines with different crafting types than blacksmithy and tailoring would need a change to the def files, if not it would crash).

If anyone is interested in them I may post them to get more exposure and people that could find issues with the changed crafting (if there are any)
 
Yea I guess, just that he had a different approach in mind I think.

Will upload the files tomorrow though (late for me atm).
 
For it to work with the default crafting system and as a Addon yes. One change required the changes of the others too.

There would probably been different ways but thats the way I thought of for this :p
 
Hats off to you IanE you tried hard to get it!! :)

Thanks, I thought it was going to be a more simple script, but that ended up being WAY more things to do and change than I was thinking. I was thinking inside the box. I originally wanted to just get it to work just as an anvil/smith hammer, so I could place it at a smith shop.
 
Very nice tutorial!

Hey Ian!

You're almost there. I think your confusion is arising from not fully understanding boolean operators and their truth tables. It's super easy!

Boolean logic has two states. True and False. A lot of things end up becoming boolean functions. Something like from.InRange(this, 1) is True when you're 1 tile away, and False when you're not. For simplicity, I'll be using 1 to signify True and 0 to signify False.

AND (&&)
The AND operator is only True when BOTH inputs are present. The truth table below shows how it works.
Code:
---------------
| X1 | X2 | Y |
|-------------|
| 0  | 0  | 0 |
| 0  | 1  | 0 |
| 1  | 0  | 0 |
| 1  | 1  | 1 |
---------------

OR (||)
The OR operator is only True when EITHER inputs are present. The truth table below shows how it works.
Code:
---------------
| X1 | X2 | Y |
|-------------|
| 0  | 0  | 0 |
| 0  | 1  | 1 |
| 1  | 0  | 1 |
| 1  | 1  | 1 |
---------------

NOT (!)
The NOT operator only has a single input. It will give its opposite as output. You place the (!) BEFORE your input in the code. The truth table below shows how it works.
Code:
----------
| X1 | Y |
|--------|
| 0  | 1 |
| 1  | 0 |
----------

XOR (^)
The XOR operator is only True when ONE of EITHER inputs are present. The truth table below shows how it works.
Code:
---------------
| X1 | X2 | Y |
|-------------|
| 0  | 0  | 0 |
| 0  | 1  | 1 |
| 1  | 0  | 1 |
| 1  | 1  | 0 |
---------------


Remember, you can use these operators like basic maths with 1's and 0's

For example, when you want something to work out of either when its in your pack or when its 1 tile away. It helps to write it out in a sentence.

This code will execute IF the player is 1 tile away from the object AND the player can see it OR it is in the player's backpack OR it is in a container located somewhere in the player's inventory

So now we can reduce it to
Code:
if((from.InRange(this, 2) && from.CanSee(this)) || IsChildOf(from.Backpack) || Parent == from)

See how that makes sense now :)

I also just noticed a second misunderstanding that you're having - and that's with object inheritance.

Imagine I have a generic car called BaseCar. In the BaseCar i have a function called OpenDoor. Here it is below.

Code:
public virtual void OpenDoors(){
    foreach (Door door in this.Doors) {
        door.SwingOut();
    }
}

Very simple and we have no problem with it. Now sometimes we don't want a BaseCar. Sometimes we want something like a BaseCar but ONLY SLIGHTLY different. For example, there's now a new model of car that's always Red. It would be tiresome to copy over the ENTIRE BaseCar database only to change the colour of it. Also what happens if I wanted to change the way doors opened, for example. I would then need to change it in two scripts rather than one.

This is where inheritance comes in. At the top of our RedCar script, we can write
Code:
public class RedCar : BaseCar
This means that a red car is an identical copy of BaseCar in every way unless otherwise stated in this file. Let's turn the car red!

Code:
[Constructable]
public RedCar()
	: base()
{
	Hue = Red;
}

So now, when a RedCar is constructed it calls base() (which in this case is BaseCar) with no arguments, and then it continues with turning the hue red. Keep in mind that sometimes you'll want to send arguments to your base model (most times in ServUO this is the ItemID). Check what arguments your parent model needs :)

NOW, here's where it starts to get interesting. We want to make a DeLorean. A very basic one. Basically we just want the doors to open upwards, rather than to swing out horizontally. But how do we do that? We've already established how doors open in BaseCar - does that mean we now have to write a whole new car script and maintain it? NOT WITH OBJECT INHERITANCE YOU DON'T!

Ok, so we get our DeLorean
Code:
public class DeLorean: BaseCar

So now our DeLorean inherits everything from BaseCar, but how do we change what BaseCar does with the OpenDoors function? If we take a look at OpenDoors, we'll see that there's a few keywords in there. Let's go over them!

public : This means that this function is able to be called from any other script that has access to the car object. So any object can say something like car.OpenDoors(); and the doors will open. The opposite of this is private. When private is used, then only functions WITHIN your object can call that function. This is to protect certain functionality in your class and not allow, say a tree object to be able to do anything to your gas level.

void : this is what gets returned. So void means nothing needs to be returned from the function. You can put any type of variable here. int will return an integer. string will give you a string. bool will give you a boolean value. You get the gist.

static : this one isn't in the basecar script, but its worth noting. When there's a static keyword, it means that the function can be called WITHOUT reference to an object. So usually you may have a BaseCar object called car which you can use to car.OpenDoors(). But you may have a static function that is for telling you something general about BaseCar objects (like who invented cars), in which case you'd call BaseCar.Inventor() and it would return a string of "Karl Benz". You don't need to have a car in order to get that information!

virtual : Now here's where it gets awesome. When you declare something as virtual, it means that the functionality can be overridden by a child class. So say we have OpenDoors in the BaseCar model, and we wanted to change the way it works in a DeLorean which is also a BaseCar - we put this in the DeLorean file.

Code:
public override void OpenDoors(){
    foreach (Door door in this.Doors) {
        door.RiseUp();
    }
}

You can also override overrides. So if there's a model that's based on a DeLorean that you want to change the OpenDoors function of, you can just insert an override function there.

You can also call the parent's function (that you have overriden) by calling base.OpenDoors(). Whenever you see base, you're dealing with the object that you inherited from (or the one that THAT one inherited from or the one that THAT one inherited from etc. etc.)

So coming back to your question, we can now try to understand why things worked or didn't work for you.

What you did first was edit BaseTool. This would have theoretically worked, but you would be changing the functionality of ALL tools on the server while you only wanted to change SmithingPress which inherits from BaseTool.

Second thing you did was create an override function in SmithingPress (on the right track!) and added checks for being in range (good!) BUT THEN you call the base.OnDoubleClick(from) function. So what's happening here is you're passing the range check, but THEN you're going to the BaseTool's double click function which checks whether or not the basetool is in your bag before working. So in this case, you would have to have the smithing press both simultaneously less than 1 tile away from you and in your LoS while also being in your bag.

Next thing you did was copy the OnDoubleClick function from BaseTool and put it in your SmithingPress script. This is on the right track! BUT within all that, you left a call to base.OnDoubleClick() (noooo!) So it'd pass the range check, fail the base.ondoubleclick check which would give you an error message, but then it would go on and continue with the rest of the script in SmithingPress. This works, but its confusing to players to get an error message and still have the thing work!

So what you want to do is check for distance OR if its in your pack (like we did earilier in this post) and then have all that functionality EXCLUSIVELY in your SmithingTool script.

Namely, just copy/paste this.

Code:
public override void OnDoubleClick(Mobile from)
{
	if ((from.InRange(this, 2) && from.CanSee(this)) || IsChildOf(from.Backpack) || Parent == from)
	{

		CraftSystem system = CraftSystem;

		int num = system.CanCraft(from, this, null);

		if (num > 0 && (num != 1044267 || !Core.SE)) // Blacksmithing shows the gump regardless of proximity of an anvil and forge after SE
		{
			from.SendLocalizedMessage(num);
		}
		else
		{
			CraftContext context = system.GetContext(from);

			from.SendGump(new CraftGump(from, system, this, null));
		}
	}
	else
	{
		from.SendMessage("This must be in your pack or 1 tile away for you to use it.");
	}
}

Sorry for the wall of text, but now you can understand what's going on :)
 

Active Shards

Donations

Total amount
$0.00
Goal
$1,000.00
Back