/// <summary>
			///     Setter function (can use vars like THIS().xmlobjs.test can be accessed with XMLOBJS(THIS(), xmlobjs.varName) with this function, which you can't do with with THIS().xmlobjs._____ alone
			/// </summary>
			/// <param name="trigObject"></param>
			/// <param name="o"></param>
			/// <param name="name"></param>
			/// <param name="value"></param>
			/// <returns></returns>
			public static void XMLOBJS(TriggerObject trigObject, IEntity o, string name, object value)
			{
				if (!(o is Mobile || o is Item))
				{
					throw new UberScriptException("Can't set xmlobjs on anything but Mobile or Item");
				}

				var arglist = name.Split('.');
				XmlObject xmlObject = XmlAttach.GetObjectAttachment(o, arglist[0]);

				if (xmlObject == null)
				{
					// arglist should only have [xmlints, name], nothing more
					if (arglist.Length > 1)
					{
						throw new UberScriptException("Attempted to set property on a not-yet-existant attachment!:" + name);
					}

					xmlObject = new XmlObject(arglist[0], value);

					XmlAttach.AttachTo(null, o, xmlObject);
				}
				else if (arglist.Length == 1)
				{
					xmlObject.Value = value;
				}
				else if (arglist.Length > 1)
				{
					// XmlObject only contains a few properties that
					// can be accessed through statements like THIS().xmlobjs.test._____
					// since there is a potential conflict between the developer wanting access
					// to the properties on the object contained in the XmlObject.Value (most likely)
					// or the properties on the XmlObject itself (far less likely)
					string testPropName = arglist[1].ToLower();

					// to access properties on the xmlobject itself (e.g. expiration), one must do this:
					//  THIS().xmlobjs.test.xmlobject.expiration
					if (testPropName == "xmlobject")
					{
						if (arglist.Length < 3)
						{
							throw new UberScriptException("Can't set an xmlobject directly, use ATTACH function!");
						}

						string propLookup = arglist[2]; // add this first so later additions all prepended with '.'

						for (int i = 3; i < arglist.Length; i++)
						{
							propLookup += "." + arglist[i];
						}

						PropertySetters.SetPropertyValue(trigObject, xmlObject, propLookup, value);
					}
					else
					{
						string propLookup = arglist[1]; // add this first so later additions all prepended with '.'

						for (int i = 2; i < arglist.Length; i++)
						{
							propLookup += "." + arglist[i];
						}

						PropertySetters.SetPropertyValue(trigObject, xmlObject.Value, propLookup, value);
					}
				}
			}
        // These functions are borrowed heavily from ArteGordon's BaseXmlSpawner class

        // set property values with support for nested attributes
        public static void SetPropertyValue(TriggerObject trigObj, object o, string name, object value)
        {
            if (o == null)
            {
                throw new UberScriptException("Null object");
            }

            //Type ptype = null;
            object po = null;
            Type type = o.GetType();

            PropertyInfo[] props = type.GetProperties(BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public);

            // parse the strings of the form property.attribute into two parts
            // first get the property
            List<string> arglist = PropertyGetters.PropSplitter(name);
            
            string propname = arglist[0];
            if (propname.Length == 0) throw new UberScriptException("Empty string property!");

            // Check whether they are trying to set an xml attachment through the
            // shortcut e.g. TRIGMOB().xmlints.score = TRIGMOB().xmlints.score + 1
            
            if (arglist.Count > 1 && (propname[0] == 'x' || propname[0] == 'X'))
            {
                // NEED TO HANDLE SETTING PROPERTIES ON THE ATTACHMENTS!
                string lowerPropName = propname.ToLower();
                if (lowerPropName == "xmlints")
                {
                    IEntity entity = o as IEntity;
                    if (entity == null) throw new UberScriptException("Can't set xmlints on anything but Mobile or Item");
                    // check for existing xmlValue attachment or create a new one
                    XmlValue xmlValue = XmlAttach.GetValueAttachment(entity, arglist[1]);
                    if (xmlValue == null)
                    {
                        // arglist should only have [xmlints, name], nothing more
                        if (arglist.Count > 2) throw new UberScriptException("Attempted to set property on a not-yet-existant attachment!:" + name );
                        xmlValue = new XmlValue(arglist[1], (int)value);
                        XmlAttach.AttachTo(null, entity, xmlValue);
                    }
                    else if (arglist.Count == 2)
                    {
                        if (value == null)
                        {
                            xmlValue.Delete();
                        }
                        else
                        {
                            xmlValue.Value = (int)value;
                        }
                    }
                    else if (arglist.Count > 2) 
                    {
                        // could be setting a property on an existing XmlAttachment!
                        // e.g. xmlints.test.expiration = 0:0:1
                        SetPropertyValue(trigObj, xmlValue, arglist[2], value);
                    }
                    return;
                }
                else if (lowerPropName == "xmlstrings")
                {
                    IEntity entity = o as IEntity;
                    if (entity == null) throw new UberScriptException("Can't set xmlstrings on anything but Mobile or Item");
                    XmlLocalVariable xmlLocalVariable = XmlAttach.GetStringAttachment(entity, arglist[1]);
                    if (xmlLocalVariable == null)
                    {
                        // arglist should only have [xmlints, name], nothing more
                        if (arglist.Count > 2) throw new UberScriptException("Attempted to set property on a not-yet-existant attachment!:" + name);
                        xmlLocalVariable = new XmlLocalVariable(arglist[1], (string)value);
                        XmlAttach.AttachTo(null, entity, xmlLocalVariable);
                    }
                    else if (arglist.Count == 2)
                    {
                        if (value == null)
                        {
                            xmlLocalVariable.Delete();
                        }
                        else
                        {
                            xmlLocalVariable.Data = (string)value;
                        }
                    }
                    else if (arglist.Count > 2)
                    {
                        // could be setting a property on an existing XmlAttachment!
                        // e.g. xmlints.test.expiration = 0:0:1
                        SetPropertyValue(trigObj, xmlLocalVariable, arglist[2], value);
                    }
                    return;
                }
                else if (lowerPropName == "xmldoubles")
                {
                    IEntity entity = o as IEntity;
                    if (entity == null) throw new UberScriptException("Can't set xmldoubles on anything but Mobile or Item");
                    XmlDouble xmlDouble = XmlAttach.GetDoubleAttachment(entity, arglist[1]);
                    if (xmlDouble == null)
                    {
                        // arglist should only have [xmlints, name], nothing more
                        if (arglist.Count > 2) throw new UberScriptException("Attempted to set property on a not-yet-existant attachment!:" + name);
                        xmlDouble = new XmlDouble(arglist[1], (double)value);
                        XmlAttach.AttachTo(null, entity, xmlDouble);
                    }
                    else if (arglist.Count == 2)
                    {
                        if (value == null)
                        {
                            xmlDouble.Delete();
                        }
                        else
                        {
                            xmlDouble.Value = (double)value;
                        }
                    }
                    else if (arglist.Count > 2)
                    {
                        // could be setting a property on an existing XmlAttachment!
                        // e.g. xmlints.test.expiration = 0:0:1
                        SetPropertyValue(trigObj, xmlDouble, arglist[2], value);
                    }
                    return;
                }
                else if (lowerPropName == "xmlobjs")
                {
                    IEntity entity = o as IEntity;
                    if (entity == null) throw new UberScriptException("Can't set xmlobjs on anything but Mobile or Item");
                    XmlObject xmlObject = XmlAttach.GetObjectAttachment(entity, arglist[1]);
                    if (xmlObject == null)
                    {
                        // arglist should only have [xmlints, name], nothing more
                        if (arglist.Count > 2) throw new UberScriptException("Attempted to set property on a not-yet-existant attachment!:" + name);
                        xmlObject = new XmlObject(arglist[1], value);
                        XmlAttach.AttachTo(null, entity, xmlObject);
                    }
                    else if (arglist.Count == 2)
                    {
                        if (value == null)
                        {
                            xmlObject.Delete();
                        }
                        else
                        {
                            xmlObject.Value = value;
                        }
                    }
                    else if (arglist.Count > 2)
                    {
                        // XmlObject only contains a few properties that
                        // can be accessed through statements like THIS().xmlobjs.test._____
                        // since there is a potential conflict between the developer wanting access
                        // to the properties on the object contained in the XmlObject.Value (most likely)
                        // or the properties on the XmlObject itself (far less likely)
                        string testPropName = arglist[2].ToLower();
                        // to access properties on the xmlobject itself (e.g. expiration), one must do this:
                        //  THIS().xmlobjs.test.xmlobject.expiration
                        if (testPropName == "xmlobject")
                        {
                            if (arglist.Count < 4)
                            {
                                throw new UberScriptException("Can't set an xmlobject directly, use ATTACH function!");
                            }
                            else
                            {
                                string propLookup = arglist[3]; // add this first so later additions all prepended with '.'
                                for (int i = 4; i < arglist.Count; i++)
                                {
                                    propLookup += "." + arglist[i];
                                }
                                SetPropertyValue(trigObj, xmlObject, propLookup, value);
                            }
                        }
                        else
                        {
                            string propLookup = arglist[2]; // add this first so later additions all prepended with '.'
                            for (int i = 3; i < arglist.Count; i++)
                            {
                                propLookup += "." + arglist[i];
                            }
                            SetPropertyValue(trigObj, xmlObject.Value, propLookup, value);
                        }
                    }
                    
                    //else if (arglist.Count > 2)
                    //{
                        // could be setting a property on an existing XmlAttachment!
                        // e.g. xmlints.test.expiration = 0:0:1
                    //    SetPropertyValue(trigObject, xmlObject, arglist[2], value);
                    //}
                    return;
                }
            }

            /*
            string[] keywordargs = ParseString(propname, 4, ",");

            // check for special keywords
            if (keywordargs[0] == "ATTACHMENT")
            {
                if (keywordargs.Length < 4)
                {
                    return "Invalid ATTACHMENT format";
                }
                // syntax is ATTACHMENT,type,name,propname

                string apropname = keywordargs[3];
                string aname = keywordargs[2];
                Type attachtype = SpawnerType.GetType(keywordargs[1]);

                // allow empty string specifications to be used to indicate a null string which will match any name
                if (aname == "") aname = null;

                ArrayList attachments = XmlAttach.FindAttachments(o, attachtype, aname);

                if (attachments != null && attachments.Count > 0)
                {
                    // change the object, object type, and propname to refer to the attachment
                    o = attachments[0];
                    propname = apropname;

                    if (o == null)
                    {
                        return "Null object";
                    }

                    type = o.GetType();
                    props = type.GetProperties(BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public);
                }
                else
                    return "Attachment not found";

            }
            else if (keywordargs[0] == "SKILL")
            {
                if (keywordargs.Length < 2)
                {
                    return "Invalid SKILL format";
                }
                bool found = true;
                try
                {
                    SkillName skillname = (SkillName)Enum.Parse(typeof(SkillName), keywordargs[1], true);
                    if (o is Mobile)
                    {

                        Skill skill = ((Mobile)o).Skills[skillname];

                        skill.Base = double.Parse(value);

                        return "Property has been set.";
                    }
                    else
                        return "Object is not mobile";
                }
                catch { found = false; }

                if (!found)
                    return "Invalid SKILL reference.";
            }
            else if (keywordargs[0] == "STEALABLE")
            {

                bool found = true;
                try
                {
                    if (o is Item)
                    {

                        ItemFlags.SetStealable(o as Item, bool.Parse(value));

                        return "Property has been set.";
                    }
                    else
                        return "Object is not an item";
                }
                catch { found = false; }

                if (!found)
                    return "Invalid Stealable assignment.";
            }
             * */

            // do a bit of parsing to handle array references
            string[] arraystring = propname.Split('[');
            int index = 0;
            if (arraystring.Length > 1)
            {
                // parse the property name from the indexing
                propname = arraystring[0];

                // then parse to get the index value
                string[] arrayvalue = arraystring[1].Split(']');

                if (arrayvalue.Length > 0)
                {
                    try
                    {
                        index = int.Parse(arrayvalue[0]);
                    }
                    catch 
                    {
                        try
                        {
                            index = (int)(new MathTree(null, arrayvalue[0])).Calculate(trigObj);
                        }
                        catch
                        {
                            throw new UberScriptException("Could not get int array value from: " + arrayvalue[0] + " for prop of " + name + " on object " + o);
                        }
                    }
                }
            }
            PropertyInfo propInfoToSet = null;
            string lowerCasePropName = propname.ToLower();

            // optimization to find propertyInfo without looping through
            if (o is BaseCreature)
            {
                if (BaseCreatureProperties.ContainsKey(lowerCasePropName)) propInfoToSet = BaseCreatureProperties[lowerCasePropName];
            }
            else if (o is PlayerMobile)
            {
                if (PlayerMobileProperties.ContainsKey(lowerCasePropName)) propInfoToSet = PlayerMobileProperties[lowerCasePropName];
            }
            else if (o is Item)
            {
                if (ItemProperties.ContainsKey(lowerCasePropName)) propInfoToSet = ItemProperties[lowerCasePropName];
            }
            // is a nested property with attributes so first get the property
            if (propInfoToSet == null) foreach (PropertyInfo p in props)
                {
                    if (Insensitive.Equals(p.Name, propname))
                    {
                        propInfoToSet = p;
                        break;
                    }
                }
            if (propInfoToSet != null)
            {
                //if (IsProtected(type, propname))
                //    return "Property is protected.";

                if (arglist.Count > 1)
                {
                    //ptype = propInfoToSet.PropertyType;
                    po = propInfoToSet.GetValue(o, null);
                    // now set the nested attribute using the new property list
                    string propLookup = arglist[1];
                    for (int i = 2; i < arglist.Count; i++)
                    {
                        propLookup += "." + arglist[i];
                    }
                    SetPropertyValue(trigObj, po, propLookup, value);
                    return;
                    // otherwise let it roll through and throw an exception
                }
                else // arglist.Count == 1
                {
                    //if (IsProtected(type, propname))
                    //    return "Property is protected.";
                    if (!propInfoToSet.CanWrite)
                        throw new UberScriptException("Property is read only.");

                    InternalSetValue(o, propInfoToSet, value, false, index);
                    return;
                }
            }

            throw new UberScriptException("Property " + name + " not found on " + o + ".");
        }