zerodowned

Moderator
To explain, you can't take say a Runic Hammer and put it on a vendor stone because you have to identify the resource and use amount.
For some reason beyond my understanding, duping doesn't capture those properties so the item cannot be duped.

Is there any known work around to this?

To be clear, I'm not talking about JUST runic tools, but any and all items.
 
OR is hooking onto a method within xmlspawner and using that to generate a new item the only way to do it?
obviously that wouldn't be duping, but that seems like the next best option if there isn't a workaround
 
No matter which way you do it, the item has to be created with a constructor, and if there isn't a default constructor (zero arguments), then it can't construct it.

I was actually working on this issue the other day because I wanted to improve the support for my Advanced Vendors' dynamic stock feature and I did come up with something, but decided it would be better to not implement it and just add the default constructors to the items I needed support for - Runic Tools and Champion Skulls.

The solution I came up with was to instantiate the new item using the Serial constructor, then utilising a MemoryStream to perform a Serialize operation on the source item and a Deserialize operation on the target (new) item.

Issues with this method;

* It does a deep copy, meaning items like containers will have their Items (contents) list references copied too, resulting in weird parent-hierarchy behaviour, and that's the tip of the iceberg really.

* The new item isn't instantiated with the usual default values from the Item() base constructor.

* There are too many convoluted scenarios that need to be compensated for, such as resetting the parent and aforementioned list references.

Here is a sample of the entire method I was using to achieve this (it will be included in the next Vita-Nex: Core release);
Code:
		public static T BinaryClone<T>(T item)
			where T : Item
		{
			var t = item.GetType();
			var o = t.CreateInstanceSafe<T>(Serial.NewItem); // Activator.CreateInstance(typeof(T), new object[]{ Serial.NewItem });

			if (o != null)
			{
				try
				{
					using (var ms = new MemoryStream())
					{
						var w = ms.GetBinaryWriter(); // new BinaryFileWriter( ... )

						item.Serialize(w);

						ms.Position = 0;

						var r = ms.GetBinaryReader(); // new BinaryFileReader( ... )

						o.Deserialize(r);

						w.Close();
						r.Close();
					}

					var m = o.Parent as Mobile;

					o.Amount = 1;

					if (o.Items != null)
					{
						o.Items.Clear();
					}

					o.Internalize();
					o.Parent = null;

					if (m != null)
					{
						o.OnRemoved(o.Parent);
					}
				}
				catch
				{
					o.Delete();
					o = null;
				}
			}

			return o;
		}
 
I like that!

For now I think what i'll do is go with a text input gump and convert that string to the add command.
It makes a bit more sense with what I'm trying to do but I'd still like to test out your version when its available
 
You can try use FormatterServices.GetUninitializedObject() - it will create an instance without calling a constructor and sets default values for item properties. Then you can try using CopyProperties methods of Dupe class.
 
You can try use FormatterServices.GetUninitializedObject() - it will create an instance without calling a constructor and sets default values for item properties. Then you can try using CopyProperties methods of Dupe class.

https://msdn.microsoft.com/en-us/li...tion.formatterservices.getuninitializedobject
Because the new instance of the object is initialized to zero and no constructors are run, the object might not represent a state that is regarded as valid by that object. The current method should only be used for deserialization when the user intends to immediately populate all fields.

It's basically the same scenario as using the Serial constructor.
 
First attempt, isn't working

Code:
Type t = copy.GetType();
                    ConstructorInfo c = t.GetConstructor(Type.EmptyTypes);

                    try
                    {
                        object o = c.Invoke(null);

                        if (o != null && o is Item)
                        {
                            Item newItem = (Item)o;

                            newItem = (Item)FormatterServices.GetUninitializedObject(t);
                            c.Invoke(newItem, null);
                            pack.DropItem(newItem);

                        }
                    }
                    catch{}
 
If you want to play with a bit of dark magic...

var ctors = typeof(__Class__To__Instantiate__).GetConstructors();
iterate through
var ctor = ctors[ *iteration* ];
iterate through params:
var param in ctor.GetParameters()
Gives you
param.Position {0,1,2... you already know that through iteration, though}
param.Name {important for some dark magic, but passing params in the right orders usually nullify that. Still, two instances of a similar parameterType could require that to differentiate them, for example, a player type}
param.ParameterType {this is what mostly matters. Here you'll have a lot of enum, numbers and the like. Combo with the name to identify everything}

Now, from there, you can, for example, try to grab the simplest constructor. Or, one with parameters you can easily create (aka, only built from enum, which, in turn, you can iterate through to get any). If you have a parameter named, say, width, try to see if the item you dupe doesn't have a Width(), getWidth() function or a width or _width inner variable. Even if those are private, you can easily grab them through reflection. Still, try to use the constructor that is the simplest for your dupe. Also, you might want to pre-answer common parameters if you see the same name and type in multiple instances of those failed dupes. Rather than try to guess an answer from the type and name, if you *know* the answer ahead of time, just hardcode it.

Best of luck!
 
Back