Full Version : Scripting Properties
xmlspawner >>Scripting Support >>Scripting Properties


<< Prev | Next >>

HellRazor- 07-23-2007
Hi Arte!

I'm trying to figure out how to create a property on a playermobile that has a category on the main props list - and when you click that category you get a list of properties under that category that can each be set to true or false.

I've been looking at baseweapon and basearmor as examples of this but I can't figure out how the main category knows what its properties are.

Can you give me a quick example of how to script this? I could put all these properties on the main props screen but there are a lot of them and it would be much better organization if I can figure out how to put them under their own category!

ArteGordon- 07-24-2007
you are probably thinking of things like the Attributes property, which is displayed that way because the type of that property is a class (AosAttributes) that also has properties.
The properties display gump does all of that formatting automatically based on the property type.

For example in baseweapon.cs

CODE

 [CommandProperty( AccessLevel.GameMaster )]
 public AosAttributes Attributes
 {
  get{ return m_AosAttributes; }
  set{}
 }


and to it is initialized by creating a new instance of the class in the constructor and during deserialization

CODE

m_AosAttributes = new AosAttributes( this );


and the AosAttributes class is defined in Aos.cs

CODE

   public sealed class AosAttributes : BaseAttributes
   {
       public AosAttributes(Item owner)
           : base(owner)
       {
       }

       public AosAttributes(Item owner, GenericReader reader)
           : base(owner, reader)
       {
       }

       public static int GetValue(Mobile m, AosAttribute attribute)
       {
           if (!Core.AOS)
               return 0;

           List<Item> items = m.Items;
           int value = 0;

           for (int i = 0; i < items.Count; ++i)
           {
               Item obj = items[i];

               if (obj is BaseWeapon)
               {
                   AosAttributes attrs = ((BaseWeapon)obj).Attributes;

                   if (attrs != null)
                       value += attrs[attribute];

                   if (attribute == AosAttribute.Luck)
                       value += ((BaseWeapon)obj).GetLuckBonus();
               }
               else if (obj is BaseArmor)
               {
                   AosAttributes attrs = ((BaseArmor)obj).Attributes;

                   if (attrs != null)
                       value += attrs[attribute];

                   if (attribute == AosAttribute.Luck)
                       value += ((BaseArmor)obj).GetLuckBonus();
               }
               else if (obj is BaseJewel)
               {
                   AosAttributes attrs = ((BaseJewel)obj).Attributes;

                   if (attrs != null)
                       value += attrs[attribute];
               }
               else if (obj is BaseClothing)
               {
                   AosAttributes attrs = ((BaseClothing)obj).Attributes;

                   if (attrs != null)
                       value += attrs[attribute];
               }
               else if (obj is Spellbook)
               {
                   AosAttributes attrs = ((Spellbook)obj).Attributes;

                   if (attrs != null)
                       value += attrs[attribute];
               }
           }

           return value;
       }

       public int this[AosAttribute attribute]
       {
           //ARTEGORDONMOD
           // add support for XmlAosAttributes attachment
           get { return ExtendedGetValue((int)attribute); }
           set { SetValue((int)attribute, value); }
       }

       //ARTEGORDONMOD
       // add support for XmlAosAttributes attachment
       public int ExtendedGetValue(int bitmask)
       {
           int value = GetValue(bitmask);

           //ARTEGORDONMOD
           // add support for multiple XmlAosAttributes attachments
           ArrayList xalist = XmlAttach.FindAttachments(Owner, typeof(XmlAosAttributes));
           if (xalist != null)
           {
               foreach (XmlAosAttributes xa in xalist)
               {
                   if (xa != null)
                       value += xa.GetValue(bitmask);
               }
           }

           return (value);
       }

       public override string ToString()
       {
           return "...";
       }

       [CommandProperty(AccessLevel.GameMaster)]
       public int RegenHits { get { return this[AosAttribute.RegenHits]; } set { this[AosAttribute.RegenHits] = value; } }

       [CommandProperty(AccessLevel.GameMaster)]
       public int RegenStam { get { return this[AosAttribute.RegenStam]; } set { this[AosAttribute.RegenStam] = value; } }

       [CommandProperty(AccessLevel.GameMaster)]
       public int RegenMana { get { return this[AosAttribute.RegenMana]; } set { this[AosAttribute.RegenMana] = value; } }

       [CommandProperty(AccessLevel.GameMaster)]
       public int DefendChance { get { return this[AosAttribute.DefendChance]; } set { this[AosAttribute.DefendChance] = value; } }

       [CommandProperty(AccessLevel.GameMaster)]
       public int AttackChance { get { return this[AosAttribute.AttackChance]; } set { this[AosAttribute.AttackChance] = value; } }

       [CommandProperty(AccessLevel.GameMaster)]
       public int BonusStr { get { return this[AosAttribute.BonusStr]; } set { this[AosAttribute.BonusStr] = value; } }

       [CommandProperty(AccessLevel.GameMaster)]
       public int BonusDex { get { return this[AosAttribute.BonusDex]; } set { this[AosAttribute.BonusDex] = value; } }

       [CommandProperty(AccessLevel.GameMaster)]
       public int BonusInt { get { return this[AosAttribute.BonusInt]; } set { this[AosAttribute.BonusInt] = value; } }

       [CommandProperty(AccessLevel.GameMaster)]
       public int BonusHits { get { return this[AosAttribute.BonusHits]; } set { this[AosAttribute.BonusHits] = value; } }

       [CommandProperty(AccessLevel.GameMaster)]
       public int BonusStam { get { return this[AosAttribute.BonusStam]; } set { this[AosAttribute.BonusStam] = value; } }

       [CommandProperty(AccessLevel.GameMaster)]
       public int BonusMana { get { return this[AosAttribute.BonusMana]; } set { this[AosAttribute.BonusMana] = value; } }

       [CommandProperty(AccessLevel.GameMaster)]
       public int WeaponDamage { get { return this[AosAttribute.WeaponDamage]; } set { this[AosAttribute.WeaponDamage] = value; } }

       [CommandProperty(AccessLevel.GameMaster)]
       public int WeaponSpeed { get { return this[AosAttribute.WeaponSpeed]; } set { this[AosAttribute.WeaponSpeed] = value; } }

       [CommandProperty(AccessLevel.GameMaster)]
       public int SpellDamage { get { return this[AosAttribute.SpellDamage]; } set { this[AosAttribute.SpellDamage] = value; } }

       [CommandProperty(AccessLevel.GameMaster)]
       public int CastRecovery { get { return this[AosAttribute.CastRecovery]; } set { this[AosAttribute.CastRecovery] = value; } }

       [CommandProperty(AccessLevel.GameMaster)]
       public int CastSpeed { get { return this[AosAttribute.CastSpeed]; } set { this[AosAttribute.CastSpeed] = value; } }

       [CommandProperty(AccessLevel.GameMaster)]
       public int LowerManaCost { get { return this[AosAttribute.LowerManaCost]; } set { this[AosAttribute.LowerManaCost] = value; } }

       [CommandProperty(AccessLevel.GameMaster)]
       public int LowerRegCost { get { return this[AosAttribute.LowerRegCost]; } set { this[AosAttribute.LowerRegCost] = value; } }

       [CommandProperty(AccessLevel.GameMaster)]
       public int ReflectPhysical { get { return this[AosAttribute.ReflectPhysical]; } set { this[AosAttribute.ReflectPhysical] = value; } }

       [CommandProperty(AccessLevel.GameMaster)]
       public int EnhancePotions { get { return this[AosAttribute.EnhancePotions]; } set { this[AosAttribute.EnhancePotions] = value; } }

       [CommandProperty(AccessLevel.GameMaster)]
       public int Luck { get { return this[AosAttribute.Luck]; } set { this[AosAttribute.Luck] = value; } }

       [CommandProperty(AccessLevel.GameMaster)]
       public int SpellChanneling { get { return this[AosAttribute.SpellChanneling]; } set { this[AosAttribute.SpellChanneling] = value; } }

       [CommandProperty(AccessLevel.GameMaster)]
       public int NightSight { get { return this[AosAttribute.NightSight]; } set { this[AosAttribute.NightSight] = value; } }
   }



HellRazor- 07-24-2007
AHA! That's what was throwing me, some of the relevent info was in a different script (Aos.cs)! I couldn't figure out how the program knew which properties went under the heading!

Thank you Arte!



HellRazor- 07-24-2007
I'm having one other problem with this.

I'm trying to put this on NewMobile (custom playermobile) instead of an item as in the example. Basically there is a main category, and then the sub categories are flags.

I've made my changes to NewMobile.cs and tried to create a custom MyClass to hold the individual properties that will go under the main category.

Given the AosAttributes class, what do I need to change to make it apply to a mobile instead of an item? In...

public sealed class AosAttributes : BaseAttributes

...BaseAttributes seems to apply specifically to items but I'm not sure how to format it all to apply to a mobile instead of an item. I played around with it a bit bit there seems to be a few other item-specific things in the code as well.

Any help will be appreciated! (Or if there is a better example in the default scripts of what I am trying to do feel free to point me in that direction!)

ArteGordon- 07-24-2007
you would just define the new property like this

CODE

private MyClass m_NewAttributes;

[CommandProperty( AccessLevel.GameMaster )]
public MyClass NewAttributes
{
 get{ return m_NewAttributes; }
 set{}
}


and you would also have to assign the m_NewAttributes property to an instance of your MyClass, as in the constructor and deserialization for baseweapon.

HellRazor- 07-25-2007
Still having problems (and I haven't even started on serialization/deserialzation yet). Think you can point me in the right direction?

Here is my playermobile code:

CODE

using System;
using System.Collections;
using System.Collections.Generic;
using Server;
using Server.ContextMenus;
using Server.Mobiles;
using Server.Items;
using Server.Gumps;
using Server.Custom;

namespace Server.Custom
{

public class NewMobile2 : PlayerMobile
{
 private Feats m_Feats;
 
 [CommandProperty( AccessLevel.GameMaster) ]
 public Feats PlayerFeats
 {
  get{ return m_Feats; }
  set{}
 }
}

public class Feats
{
 [Flags]
 public enum Feat
 {
  HairStyling=0x0001,
  HorseRiding=0x0002
 }
 
 [CommandProperty( AccessLevel.GameMaster )]
 public int HairStyling
 {
  get{ return this [Feat.HairStyling]; }
  set{ this [Feat.HairStyling] = value; }
 }
 
 [CommandProperty( AccessLevel.GameMaster )]
 public int HorseRiding
 {
  get{ return this [Feat.HorseRiding]; }
  set{ this [Feat.HorseRiding] = value; }
 }
 
}



}
 


I am getting this error:

Cannot apply indexing with [] to an expression of Server.Custom.Feats.

ArteGordon- 07-25-2007
what are you trying to do here?

CODE

[CommandProperty( AccessLevel.GameMaster )]
public int HairStyling
{
 get{ return this [Feat.HairStyling]; }
 set{ this [Feat.HairStyling] = value; }
}


that is not valid syntax.

HellRazor- 07-25-2007
From my playermobile's props, I'm trying to get an option called Feats. Clicking that category should open up a sub-category of individual flags, of which HairStyling and HorseRiding are part of. The flags should be able to be set true or false so I can check for them from other scripts.

The syntax was copied from Aos.cs but they are doing a few different things there too (there is a GetValue and SetValue I didn't think I needed for what I was trying to do).

I'm sure once I fully get this it will probably end up being painfully easy. I'm just not sure how to piece it together from the few examples I have been able to find.



ArteGordon- 07-25-2007
ah, ok. You are trying to refer to an array entry. In AOS.cs the Attributes class has an accessor explicitly defined for that syntax.

CODE

       public int this[AosAttribute attribute]
       {
           get { return GetValue((int)attribute); }
           set { SetValue((int)attribute, value); }
       }


and the GetValue and SetValue methods that do the bit flag translation.

The reason for that is to allow the use of space-optimized bit flags for all of the various attribute settings.
You dont have to get that fancy with only two attributes. I would just use good old bools for your flags.
Also, you are going to need a constructor, and you are going to have to serialize/deserialize these flag settings in your playermobile.cs

CODE

public class Feats
{

private bool m_hairStyling;
private bool m_horseRiding;

[CommandProperty( AccessLevel.GameMaster )]
public bool HairStyling
{
 get{ return m_hairStyling; }
 set{ m_hairStyling = value; }
}

[CommandProperty( AccessLevel.GameMaster )]
public bool HorseRiding
{
 get{ return m_horseRiding; }
 set{ m_horseRiding= value; }
}

public Feats()
{
}

}

ArteGordon- 07-25-2007
If you really wanted to implement your feats in the same way that the other AOS attributes are implemented, it would look something like this

CODE

   [Flags]
   public enum Feat
   {
       HairStyling = 0x0001,
       HorseRiding = 0x0002
   }

   public class Feats : BaseAttributes
   {
       public Feats(Item owner)
           : base(owner)
       {
       }

       public Feats(Item owner, GenericReader reader)
           : base(owner, reader)
       {
       }

       public int this[Feat attribute]
       {
           get { return GetValue((int)attribute); }
           set { SetValue((int)attribute, value); }
       }

       public override string ToString()
       {
           return "...";
       }

       [CommandProperty(AccessLevel.GameMaster)]
       public int HairStyling
       {
           get { return this[Feat.HairStyling]; }
           set { this[Feat.HairStyling] = value; }
       }

       [CommandProperty(AccessLevel.GameMaster)]
       public int HorseRiding
       {
           get { return this[Feat.HorseRiding]; }
           set { this[Feat.HorseRiding] = value; }
       }
   }

HellRazor- 07-25-2007
Thank you for all your help and insight Arte!

I just threw in two to get start with and to get the framework into place - I will actually end up having quite a few of these properties in the end (dozens).

But I don't anticipate having to check for multiple properties in most scripts. Given that, is there any real advantage to using flags rather than bools?

ArteGordon- 07-26-2007
QUOTE (HellRazor @ July 26, 2007 01:52 am)
Thank you for all your help and insight Arte!

I just threw in two to get start with and to get the framework into place - I will actually end up having quite a few of these properties in the end (dozens). 

But I don't anticipate having to check for multiple properties in most scripts.  Given that, is there any real advantage to using flags rather than bools?

If you use the bit flags it will take up less memory and saves will be smaller. It is space, that's all.

What happens is that the bit flags indicate which of your custom attributes have actually been set to something, and only those get stored and saved. This works well for AOS attributes because in general, you might have a few dozen available, but only one or two might actually be set, and in many cases, none.

If you are going to have 'dozens' and decide to go the BaseAttributes route you are going to have to consider the organization since you are limited to what you can fit in a single int (32 bits)

HellRazor- 07-26-2007
I think I am on the home stretch (can't really check the code in-game until I get home tonight but I am checking for compile errors).

I am having trouble with my deserialization method for the flags, particularly this piece:

CODE

if ( GetSaveFlag( flags, SaveFlag.PlayerFeats ) )
 m_Feats = new Feats( this, reader );
else
 m_Feats = new Feats( this );


I get this error on both of the m_Feats = lines:

Arguement 1: Cannot convert from Server.Custom.NewMobile to Server.Item

Not surprising since I am stealing all of this from BaseArmor. But I'm not sure how to fix it.

Other than this piece my playermobile at least compiles correctly (including the serialization part which is):

CODE

SaveFlag flags = SaveFlag.None;
SetSaveFlag( ref flags, SaveFlag.PlayerFeats, !m_Feats.IsEmpty );
writer.WriteEncodedInt( (int) flags );
if ( GetSaveFlag( flags, SaveFlag.PlayerFeats ) )
 m_Feats.Serialize( writer );


ArteGordon- 07-26-2007
the problem here is that the AOS BaseAttribute system is designed to work with items, not mobiles.
You are going to have to make a few changes in AOS.cs to allow it to work.

Basically you are going to have to pass it a null item argument,

QUOTE

if ( GetSaveFlag( flags, SaveFlag.PlayerFeats ) )
m_Feats = new Feats( null, reader );
else
m_Feats = new Feats( null );


and then you are going to have to add a null check into the SetValue method around line 1000 in AOS.cs

QUOTE

           else if ((m_Names & mask) != 0)
           {
               int index = GetIndex(mask);

               if (index >= 0 && index < m_Values.Length)
               {
                   m_Names &= ~mask;

                   if (m_Values.Length == 1)
                   {
                       m_Values = m_Empty;
                   }
                   else
                   {
                       int[] old = m_Values;
                       m_Values = new int[old.Length - 1];

                       for (int i = 0; i < index; ++i)
                           m_Values[i] = old[i];

                       for (int i = index + 1; i < old.Length; ++i)
                           m_Values[i - 1] = old[i];
                   }
               }
           }


if(m_Owner == null) return;

           if ((bitmask == (int)AosWeaponAttribute.DurabilityBonus) && (this is AosWeaponAttributes))
           {
               if (m_Owner is BaseWeapon)
                   ((BaseWeapon)m_Owner).ScaleDurability();
           }
           else if ((bitmask == (int)AosArmorAttribute.DurabilityBonus) && (this is AosArmorAttributes))
           {
               if (m_Owner is BaseArmor)
                   ((BaseArmor)m_Owner).ScaleDurability();
           }

           if (m_Owner.Parent is Mobile)
           {
               Mobile m = (Mobile)m_Owner.Parent;

               m.CheckStatTimers();
               m.UpdateResistances();
               m.Delta(MobileDelta.Stat | MobileDelta.WeaponDamage | MobileDelta.Hits | MobileDelta.Stam | MobileDelta.Mana);

               if (this is AosSkillBonuses)
               {
                   ((AosSkillBonuses)this).Remove();
                   ((AosSkillBonuses)this).AddTo(m);
               }
           }

           m_Owner.InvalidateProperties();
       }

HellRazor- 07-26-2007
Ack. More troubles.

In-game, I get the "PlayerFeats" category, but under that it says "-null-" and clicking it displays nothing.

Upon worldsave, the shard crashes (System.NullReferenceException: Object reference not set to an instance of an object) and afterwards it won't restart (System.IO.EndofStream Exception: unable to read beyond the end of the stream) - so obviously there are still serialization/deserialization issues.

Here is the complete playermobile script - for SetSaveFlag and GetSaveFlag I just duplicated the ones from basearmor and placed them in my player mobile script.

Can I impose on you to straighten me out again? (And THANK YOU for your patience with a newbie C# coder).

QUOTE

using System;
using System.Collections;
using System.Collections.Generic;
using Server;
using Server.ContextMenus;
using Server.Mobiles;
using Server.Items;
using Server.Gumps;
using Server.Custom;

namespace Server.Custom
{
public class NewMobile : PlayerMobile
{
  private LevelEntry m_Level;
  /// <summary>
  /// NewLevel is the LevelEntry for the NewMobile class.
  /// It keeps track of the Rank, Experience until next Level, etc.
  /// </summary>
  public LevelEntry NewLevel { get { return m_Level; } set { m_Level = value; } }

  private Stats m_Stats;
  /// <summary>
  /// NewStats is the Stats for the NewMobile class.
  /// It keeps track of Race, Class, Stats, Bonuses, Caps, etc.
  /// </summary>
  public Stats NewStats { get { return m_Stats; } set { m_Stats = value; } }

  private Feats m_Feats;

  private NewSkillNameValue[] m_Skills;
  /// <summary>
  /// These are the NewSkillNameValue's for the NewMobile class.
  /// They include ClassSkill_1 through ClassSkill_5.
  /// </summary>
  public NewSkillNameValue[] NewSkills { get { return m_Skills; } set { m_Skills = value; } }
  public NewSkillNameValue ClassSkill_1 { get { return m_Skills[0]; } set { m_Skills[0] = value; } }
  public NewSkillNameValue ClassSkill_2 { get { return m_Skills[1]; } set { m_Skills[1] = value; } }
  public NewSkillNameValue ClassSkill_3 { get { return m_Skills[2]; } set { m_Skills[2] = value; } }
  public NewSkillNameValue ClassSkill_4 { get { return m_Skills[3]; } set { m_Skills[3] = value; } }
  public NewSkillNameValue ClassSkill_5 { get { return m_Skills[4]; } set { m_Skills[4] = value; } }

  [CommandProperty( AccessLevel.GameMaster )]
  public int BaseStrength { get { return m_Stats.Strength; } set { m_Stats.Strength = value; } }
  [CommandProperty( AccessLevel.GameMaster )]
  public int BaseDexterity { get { return m_Stats.Dexterity; } set { m_Stats.Dexterity = value; } }
  [CommandProperty( AccessLevel.GameMaster )]
  public int BaseIntelligence { get { return m_Stats.Intelligence; } set { m_Stats.Intelligence = value; } }
  [CommandProperty( AccessLevel.GameMaster )]
  public int BaseConstitution { get { return m_Stats.Constitution; } set { m_Stats.Constitution = value; } }
  [CommandProperty( AccessLevel.GameMaster )]
  public int BaseCharisma { get { return m_Stats.Charisma; } set { m_Stats.Charisma = value; } }
  [CommandProperty( AccessLevel.GameMaster )]
  public int BaseWisdom { get { return m_Stats.Wisdom; } set { m_Stats.Wisdom = value; } }


  [CommandProperty( AccessLevel.GameMaster )]
  public Feats PlayerFeats
  {
  get{ return m_Feats; }
  set{}
  }




  public int ActualStrength { get { return BaseStrength + RaceEntry.GetEntry( _Race ).RaceMods[Stat.Strength]; } }
  public int ActualDexterity { get { return BaseDexterity + RaceEntry.GetEntry( _Race ).RaceMods[Stat.Dexterity]; } }
  public int ActualIntelligence { get { return BaseIntelligence + RaceEntry.GetEntry( _Race ).RaceMods[Stat.Intelligence]; } }
  public int ActualConstitution { get { return BaseConstitution + RaceEntry.GetEntry( _Race ).RaceMods[Stat.Constitution]; } }
  public int ActualCharisma { get { return BaseCharisma + RaceEntry.GetEntry( _Race ).RaceMods[Stat.Charisma]; } }
  public int ActualWisdom { get { return BaseWisdom + RaceEntry.GetEntry( _Race ).RaceMods[Stat.Wisdom]; } }

  public int GetActual( Stat name )
  {
  switch ( name )
  {
    case Stat.Strength: return ActualStrength;
    case Stat.Dexterity: return ActualDexterity;
    case Stat.Intelligence: return ActualIntelligence;
    case Stat.Constitution: return ActualConstitution;
    case Stat.Charisma: return ActualCharisma;
    case Stat.Wisdom: return ActualWisdom;
    default: return 0;
  }
  }

  [CommandProperty( AccessLevel.GameMaster )]
  public int StrengthBonus { get { return m_Stats.StrengthBonus; } }
  [CommandProperty( AccessLevel.GameMaster )]
  public int DexterityBonus { get { return m_Stats.DexterityBonus; } }
  [CommandProperty( AccessLevel.GameMaster )]
  public int IntelligenceBonus { get { return m_Stats.IntelligenceBonus; } }
  [CommandProperty( AccessLevel.GameMaster )]
  public int ConstitutionBonus { get { return m_Stats.ConstitutionBonus; } }
  [CommandProperty( AccessLevel.GameMaster )]
  public int CharismaBonus { get { return m_Stats.CharismaBonus; } }
  [CommandProperty( AccessLevel.GameMaster )]
  public int WisdomBonus { get { return m_Stats.WisdomBonus; } }

  [CommandProperty( AccessLevel.GameMaster )]
  public int StrengthCap { get { return m_Stats.StrengthCap; } }
  [CommandProperty( AccessLevel.GameMaster )]
  public int DexterityCap { get { return m_Stats.DexterityCap; } }
  [CommandProperty( AccessLevel.GameMaster )]
  public int IntelligenceCap { get { return m_Stats.IntelligenceCap; } }
  [CommandProperty( AccessLevel.GameMaster )]
  public int ConstitutionCap { get { return m_Stats.ConstitutionCap; } }
  [CommandProperty( AccessLevel.GameMaster )]
  public int CharismaCap { get { return m_Stats.CharismaCap; } }
  [CommandProperty( AccessLevel.GameMaster )]
  public int WisdomCap { get { return m_Stats.WisdomCap; } }

 
  [CommandProperty( AccessLevel.GameMaster )]
  public Race _Race { get { return m_Stats.Race; } set { m_Stats.Race = value; } }
  [CommandProperty( AccessLevel.GameMaster )]
  public Class _Class { get { return m_Stats.Class; } set { m_Stats.Class = value; } }
  [CommandProperty( AccessLevel.GameMaster )]
  public int _Experience { get { return m_Stats.Experience; } set { m_Stats.Experience = value; } }
  [CommandProperty( AccessLevel.GameMaster )]
  public int _ClassLevel { get { return m_Level.CurrentLevel; } }
  [CommandProperty( AccessLevel.GameMaster )]
  public int _NextLevelPointsNeeded { get { return m_Level.PointsNeeded(); } }
  [CommandProperty( AccessLevel.GameMaster )]
  public string _ClassRank { get { return m_Level.GetRank(); } }
 
  public NewMobile()
  {
  m_Stats = new Stats( this, true );
  m_Skills = new NewSkillNameValue[5];
  m_Level = new LevelEntry( this );
  }
 
  public NewMobile( Serial s ) : base( s )
  {
  }

  public bool UseNewSkill( NewSkillName name )
  {
  return UseNewSkill( (int)name );
  }

  public bool UseNewSkill( int newSkillID )
  {
  NewSkillInfo info;
  if (!this.CheckAlive())
  {
    return false;
  }

  if (!NewSkillInfo.Table[newSkillID].ClickToUse)
  {
    this.SendMessage("You may not call this skill directly.");
    return false;
  }
 
  if ((newSkillID >= 0) && (newSkillID < NewSkillInfo.Table.Length))
  {
    info = NewSkillInfo.Table[newSkillID];
    if (this.Spell == null)
    {
    if (this.Meditating)
    {
      this.Meditating = false;
      this.SendLocalizedMessage(500134);
    }

    if ( info.Callback != null )
    {
      this.NextSkillTime = DateTime.Now + info.Callback( this );
      Console.WriteLine( this.NextSkillTime.ToString() );
    }
   
    this.SendMessage("Now using skill: {0}.", info.Name);
    return true;
    }
  }

  return false;
  }

  public bool CheckNewSkill( NewSkillName skill, double min, double max )
  {
  int num = (int)skill;
  if ( NewSkills[num%5].Name != skill )
    return false;
  double value = NewSkills[num%5].Value;
  if ( value < min )
    return false; // Too difficult
  else if ( value >= max )
    return true; // No challenge

  double chance = (value - min) / (max - min);

  return CheckNewSkill( skill, chance );
  }

  public bool CheckNewSkill( NewSkillName skill, double chance )
  {
  bool success = ( chance >= Utility.RandomDouble() );

  return success;
  }



  public override void GetContextMenuEntries( Mobile from, List<ContextMenuEntry> list )
  {
  base.GetContextMenuEntries( from, list );

  if ( from.Alive )
  {
    list.Add( new ContextMenus.NewSkillsEntry( from ) );
    list.Add( new ContextMenus.NewStatsEntry( from ) );
  }
  }
 
  [Flags]
  private enum SaveFlag
  {
  None    = 0x00000000,
  PlayerFeats  = 0x00000001
  }

  private static void SetSaveFlag( ref SaveFlag flags, SaveFlag toSet, bool setIf )
  {
  if ( setIf )
    flags |= toSet;
  }

  private static bool GetSaveFlag( SaveFlag flags, SaveFlag toGet )
  {
  return ( (flags & toGet) != 0 );
  }


  public override void Deserialize( GenericReader reader )
  {
  base.Deserialize( reader );
  int version = reader.ReadInt();

  switch ( version )
  {
    case 2:
    {
    SaveFlag flags = (SaveFlag)reader.ReadEncodedInt();

    if ( GetSaveFlag( flags, SaveFlag.PlayerFeats ) )
      m_Feats = new Feats( null, reader );
    else
      m_Feats = new Feats( null );

    m_Stats = new Stats( this, false );
    m_Stats.Deserialize( this, reader );

    m_Skills = new NewSkillNameValue[5];

    m_Level = new LevelEntry( this );
    m_Level.Deserialize( this, reader );
    goto case 1;
    }

    case 1:
    {
    for (int x=0; x<5; x++)
    {
      m_Skills[x].Name = (NewSkillName)reader.ReadInt();
      m_Skills[x].Value = (int)reader.ReadInt();
    }
    goto case 0;
    }
    case 0:
    {
    break;
    }
  }
  }
 
  public override void Serialize( GenericWriter writer )
  {
  base.Serialize( writer );
 
  writer.Write( (int) 2 ); // version

  SaveFlag flags = SaveFlag.None;

  SetSaveFlag( ref flags, SaveFlag.PlayerFeats, !m_Feats.IsEmpty );

  writer.WriteEncodedInt( (int) flags );

  if ( GetSaveFlag( flags, SaveFlag.PlayerFeats ) )
    m_Feats.Serialize( writer );

  m_Stats.Serialize( this, writer );

  m_Level.Serialize( this, writer );
 

  for (int x=0; x<5; x++)
  {
    writer.Write( (int) m_Skills[x].Name );
    writer.Write( (int) m_Skills[x].Value );
  }
  }
 
  public int Between( int number, int lower, int upper )
  {
  if ( number < lower ) return lower;
  else if ( number > upper ) return upper;
  else return number;
  }
 
  public int Lesser( int first, int second )
  {
  if ( first < second ) return first; else return second;
  }
 
  public int Greater( int first, int second )
  {
  if ( first > second ) return first; else return second;
  }
 
#region New additions to Override PlayerMobile defaults...
 
  public override int MaxWeight { get { return Between( ( ( this.ActualStrength + 5 ) * ( this.ActualStrength + 2 ) ), 40, 800 ); } }

  ///TODO: find a better place to adjust the FollowersMax...
  public override void ValidateSkillMods()
  {
  try
  {
    int mod = (int)( ( this.ActualCharisma - 12 ) / 3 );
    this.FollowersMax = Between( 5 + mod, 2, 10 );
  }
  catch { }
  base.ValidateSkillMods();
  }

  ///TODO: Add code to adjust resistances based on Class or Race abilities.
  public override int BasePhysicalResistance { get { return 0; } }
  public override int BaseFireResistance { get { return 0; } }
  public override int BaseColdResistance { get { return 0; } }
  public override int BasePoisonResistance { get { return 0; } }
  public override int BaseEnergyResistance { get { return 0; } }

  public override int GetMaxResistance( ResistanceType type )
  {
  int max = base.GetMaxResistance( type );

  return max;
  }

  public override int GetResistance( ResistanceType type )
  {
  return base.GetResistance( type );
  }

  public override int GetMinResistance( ResistanceType type )
  {
  return base.GetMinResistance( type );
  }

  public override void OnItemAdded( Item item )
  {
  base.OnItemAdded( item );
  }

  [CommandProperty( AccessLevel.GameMaster )]
  public override int HitsMax
  {
  get
  {
    try {
    int baseHits = 50;
    int thisHits = baseHits + (int)( ( this.ActualStrength - 10 ) * 10 );
    return Greater( thisHits, 10 ); }
    catch{ return base.HitsMax; }
  }
  }

  [CommandProperty( AccessLevel.GameMaster )]
  public override int StamMax
  {
  get
  {
    try {
    int baseStam = 50;
    int thisStam = baseStam + (int)( ( this.ActualConstitution - 10 ) * 10 );
    return Greater( thisStam, 10 ); }
    catch { return base.StamMax; }
  }
  }

  [CommandProperty( AccessLevel.GameMaster )]
  public override int ManaMax
  {
  get
  {
    try {
    int baseMana = 50;
    int thisMana = baseMana + (int)( ( this.ActualIntelligence + this.ActualWisdom - 20 ) * 5 );
    return Greater( thisMana, 10 );  }
    catch { return base.ManaMax; }
  }
  }

  public override void Damage( int amount, Mobile from )
  {
  ///TODO: Add code to determine how STR and certain skills affect Damage, like Warrior's DoubleDamage skill
  base.Damage( amount, from );
  }

  public override void GetProperties( ObjectPropertyList list )
  {
  ///TODO: Add code to display Race or Class titles
  base.GetProperties( list );
  }

  public override void OnSingleClick( Mobile from )
  {
  ///TODO: Add code to display Race or Class titles
  base.OnSingleClick( from );
  }

  public override string ApplyNameSuffix( string suffix )
  {
  ///TODO: Add code to display Race or Class titles
  return base.ApplyNameSuffix( suffix );
  }
 
#endregion
// End of New additions to Override PlayerMobile defaults...
}

[Flags]
public enum Feat
{
HairStyling = 0x0001,
HorseRiding = 0x0002
}

public class Feats : BaseAttributes
{
public Feats(Item owner)
: base(owner)
{
}

public Feats(Item owner, GenericReader reader)
: base(owner, reader)
{
}

public int this[Feat attribute]
{
get { return GetValue((int)attribute); }
set { SetValue((int)attribute, value); }
}

public override string ToString()
{
return "...";
}

[CommandProperty(AccessLevel.GameMaster)]
public int HairStyling
{
get { return this[Feat.HairStyling]; }
set { this[Feat.HairStyling] = value; }
}

[CommandProperty(AccessLevel.GameMaster)]
public int HorseRiding
{
get { return this[Feat.HorseRiding]; }
set { this[Feat.HorseRiding] = value; }
}
}


}