RunUO Community

This is a sample guest message. Register a free account today to become a member! Once signed in, you'll be able to participate on this site by adding your own topics and posts, as well as connect with other members through your own private inbox!

Publish 81 Status Packet (0x11) Structure

Vorspire

Knight
Spent some time piecing together the new Extended Status Update packet for OSI Publish 81 (7.0.30.0+ Classic Clients)

Code:
		/*
		 * BYTE[1]		ID
		 * SHORT[2]		LENGTH
		 * 
		 * INT[4]		SERIAL
		 * STRING[30]	NAME
		 * 
		 * SHORT[2]		HITS
		 * SHORT[2]		HITS MAX
		 * 
		 * BOOL[1]		CAN BE RENAMED
		 * 
		 * BYTE[1]		SUPPORT FLAG <UOR=0x03, AOS=0x04, ML=0x05, HS=0x06>
		 * 
		 * BOOL[1]		FEMALE
		 * 
		 * SHORT[2]		STR
		 * SHORT[2]		DEX
		 * SHORT[2]		INT
		 * 
		 * SHORT[2]		STAM
		 * SHORT[2]		STAM MAX
		 * 
		 * SHORT[2]		MANA
		 * SHORT[2]		MANA MAX
		 * 
		 * INT[4]		TOTAL GOLD
		 * 
		 * SHORT[2]		( UOR: ARMOR RATING, AOS: PHYSICAL RESISTANCE )
		 * 
		 * SHORT[2]		TOTAL WEIGHT
		 * 
		 * SHORT[2]		( ML: WEIGHT MAX )
		 * BYTE[1]		( ML: RACE ID <NONE=0x00, HUMAN=0x01, ELF=0x02, GARGOYLE=0x03> )
		 * 
		 * SHORT[2]		STAT CAP
		 * 
		 * BYTE[1]		FOLLOWERS
		 * BYTE[1]		FOLLOWERS MAX
		 * 
		 * SHORT[2]		( AOS: FIRE RESISTANCE )
		 * SHORT[2]		( AOS: COLD RESISTANCE )
		 * SHORT[2]		( AOS: POISON RESISTANCE )
		 * SHORT[2]		( AOS: ENERGY RESISTANCE )
		 * 
		 * SHORT[2]		LUCK
		 * 
		 * SHORT[2]		DAMAGE MIN
		 * SHORT[2]		DAMAGE MAX
		 * 
		 * INT[4]		TITHING POINTS
		 * 
		 * SHORT[2]		( HS: PHYSICAL RESISTANCE MAX )
		 * SHORT[2]		( HS: FIRE RESISTANCE MAX )
		 * SHORT[2]		( HS: COLD RESISTANCE MAX )
		 * SHORT[2]		( HS: POISON RESISTANCE MAX )
		 * SHORT[2]		( HS: ENERGY RESISTANCE MAX )
		 * 
		 * SHORT[2]		( HS: DEFENSE CHANCE INCREASE )
		 * SHORT[2]		( HS: DEFENSE CHANCE INCREASE MAX )
		 * 
		 * SHORT[2]		( HS: HIT CHANCE INCREASE )
		 * SHORT[2]		( HS: SWING SPEED INCREASE )
		 * SHORT[2]		( HS: WEAPON DAMAGE INCREASE )
		 * SHORT[2]		( HS: LOWER REAGENT COST )
		 * SHORT[2]		( HS: SPELL DAMAGE INCREASE )
		 * SHORT[2]		( HS: FASTER CAST RECOVERY )
		 * SHORT[2]		( HS: FASTER CASTING )
		 * SHORT[2]		( HS: LOWER MANA COST )
		*/

[edit] FCR before FC
 

Vorspire

Knight
Seems that a substantial amount of work is needed in order to calculate the new status values, since HCI, etc, are worked out in BaseWeapon.
The easiest way to achieve it is to extract the formulas as new static methods in BaseWeapon and use a similar approach to the GetMaxResistance method in Mobile in order to get the values in the core where things like BaseWeapon can't be referenced.

So implementing this requires core mods for the extended status packet and Mobile classes, it's not an easy task.
 

Kraz

AssistUO Developer
Staff member
Seems that a substantial amount of work is needed in order to calculate the new status values, since HCI, etc, are worked out in BaseWeapon.
The easiest way to achieve it is to extract the formulas as new static methods in BaseWeapon and use a similar approach to the GetMaxResistance method in Mobile in order to get the values in the core where things like BaseWeapon can't be referenced.

So implementing this requires core mods for the extended status packet and Mobile classes, it's not an easy task.
Take a look at Misc/AOS.cs GetValue method.
I'm not sure if it does *all* the job, but worth a try:
C#:
int attackChance = AosAttributes.GetValue( from, AosAttribute.AttackChance );
 

Vorspire

Knight
Yea, of course, but even AosAttributes isn't accessible from the Core thanks to Core -> Script dependency.

I've got the new status bar fully implemented on my test centre, I was just noting how convoluted/complex the upgrade is :)

Packet -> Mobile !-> PlayerMobile

Where Mobile contains virtual methods, for example; GetStatusHCI( out short value )

- Similar to Mobile.GetMaxResistance( ResistanceType type )

Then, in order to bridge the gap between Core / Scripts, you override GetStatusHCI in PlayerMobile to return the values.

Now, AosAttributes.GetValue is useful, but that's only the first part - the status bar has to take malus in to consideration, so any modification effects, like transformation spells, etc, must also be correctly calculated for the status bar.

Since the HCI formula is done in BaseWeapon.CheckHit, a non-static method, it makes it harder to reference, since it contains a section/block/scope that specifically deals with HCI bonus, it makes sense to extract that scope in to a static method that can be called anywhere.

Now in the PlayerMobile.GetStatusHCI method, I simply call BaseWeapon.GetHCI to get the value with all mods included.

Same goes for DCI, SSI, etc.

Most of the other properties on the new status bar don't have malus or other mods, so AosAttributes.GetValue is fine by itself in that case.
 

Attachments

  • ExtendedStatus.jpg
    ExtendedStatus.jpg
    84.8 KB · Views: 127

Kraz

AssistUO Developer
Staff member
Yea, of course, but even AosAttributes isn't accessible from the Core thanks to Core -> Script dependency.
The rest of your post just show a way to solve that issue :)

I was not giving a full solution to the packet just a tip on to how to grab the properties values based on your previous inputs regarding this subject.
There are a few ways to implement that, I'd probably do the same in order to have a clean solution.

Glad to hear you managed to do that and also described a more detailed path for those going through the same situation.
 

Vorspire

Knight
Count the number of bytes of data in the list, SHORT[2] for example is a Signed 16-bit Integer -aka- 'INT16', 'SHORT' or 'WORD' - 2 bytes in length.

But the easiest way is to look at the raw data of the packet, the two bytes beginning at the index of 1 in the data stream is the length of the packet.

You can also simply use Razor's packet logging feature and sift through the logs for the packet ID you need, the length is explicitly logged for ease.
 

Vorspire

Knight
This is the extended status as it stands, for RunUO 2.3 rev 1069+

Core/Network/Packets.cs
C#:
	public sealed class MobileStatusExtended : Packet
	{
		public MobileStatusExtended( Mobile m ) : this( m, m.NetState )
		{
		}

		/*
		 * BYTE[1]		ID
		 * SHORT[2]		LENGTH
		 * 
		 * INT[4]		SERIAL
		 * STRING[30]	NAME
		 * 
		 * SHORT[2]		HITS
		 * SHORT[2]		HITS MAX
		 * 
		 * BOOL[1]		CAN BE RENAMED
		 * 
		 * BYTE[1]		SUPPORT FLAG <UOR=0x03, AOS=0x04, ML=0x05, HS=0x06>
		 * 
		 * BOOL[1]		FEMALE
		 * 
		 * SHORT[2]		STR
		 * SHORT[2]		DEX
		 * SHORT[2]		INT
		 * 
		 * SHORT[2]		STAM
		 * SHORT[2]		STAM MAX
		 * 
		 * SHORT[2]		MANA
		 * SHORT[2]		MANA MAX
		 * 
		 * INT[4]		TOTAL GOLD
		 * 
		 * SHORT[2]		( UOR: ARMOR RATING, AOS: PHYSICAL RESISTANCE )
		 * 
		 * SHORT[2]		TOTAL WEIGHT
		 * 
		 * SHORT[2]		( ML: WEIGHT MAX )
		 * BYTE[1]		( ML: RACE ID <NONE=0x00, HUMAN=0x01, ELF=0x02, GARGOYLE=0x03> )
		 * 
		 * SHORT[2]		STAT CAP
		 * 
		 * BYTE[1]		FOLLOWERS
		 * BYTE[1]		FOLLOWERS MAX
		 * 
		 * SHORT[2]		( AOS: FIRE RESISTANCE )
		 * SHORT[2]		( AOS: COLD RESISTANCE )
		 * SHORT[2]		( AOS: POISON RESISTANCE )
		 * SHORT[2]		( AOS: ENERGY RESISTANCE )
		 * 
		 * SHORT[2]		LUCK
		 * 
		 * SHORT[2]		DAMAGE MIN
		 * SHORT[2]		DAMAGE MAX
		 * 
		 * INT[4]		TITHING POINTS
		 * 
		 * SHORT[2]		( HS: PHYSICAL RESISTANCE MAX )
		 * SHORT[2]		( HS: FIRE RESISTANCE MAX )
		 * SHORT[2]		( HS: COLD RESISTANCE MAX )
		 * SHORT[2]		( HS: POISON RESISTANCE MAX )
		 * SHORT[2]		( HS: ENERGY RESISTANCE MAX )
		 * 
		 * SHORT[2]		( HS: DEFENSE CHANCE INCREASE )
		 * SHORT[2]		( HS: DEFENSE CHANCE INCREASE MAX )
		 * 
		 * SHORT[2]		( HS: HIT CHANCE INCREASE )
		 * SHORT[2]		( HS: SWING SPEED INCREASE )
		 * SHORT[2]		( HS: WEAPON DAMAGE INCREASE )
		 * SHORT[2]		( HS: LOWER REAGENT COST )
		 * SHORT[2]		( HS: SPELL DAMAGE INCREASE )
		 * SHORT[2]		( HS: FASTER CAST RECOVERY )
		 * SHORT[2]		( HS: FASTER CASTING )
		 * SHORT[2]		( HS: LOWER MANA COST )
		*/

		public MobileStatusExtended(Mobile m, NetState ns)
			: base(0x11)
		{
			string name = m.Name ?? "";

			bool pub81Extended = Core.SA && ns != null && ns.ExtendedStatus;
			bool sendMLExtended = Core.ML && ns != null && ns.SupportsExpansion(Expansion.ML);

			EnsureCapacity(pub81Extended ? 121 : (sendMLExtended ? 91 : 88));

			m_Stream.Write(m.Serial);
			m_Stream.WriteAsciiFixed(name, 30);

			m_Stream.Write((short)m.Hits);
			m_Stream.Write((short)m.HitsMax);

			m_Stream.Write(m.CanBeRenamedBy(m));

			m_Stream.Write((byte)(pub81Extended ? 0x06 : sendMLExtended ? 0x05 : Core.AOS ? 0x04 : 0x03)); // type

			m_Stream.Write(m.Female);

			m_Stream.Write((short)m.Str);
			m_Stream.Write((short)m.Dex);
			m_Stream.Write((short)m.Int);

			m_Stream.Write((short)m.Stam);
			m_Stream.Write((short)m.StamMax);

			m_Stream.Write((short)m.Mana);
			m_Stream.Write((short)m.ManaMax);

			m_Stream.Write(m.TotalGold);
			m_Stream.Write((short)(Core.AOS ? m.PhysicalResistance : (int)(m.ArmorRating + 0.5)));

			m_Stream.Write((short)(Mobile.BodyWeight + m.TotalWeight));

			if (sendMLExtended)
			{
				m_Stream.Write((short)m.MaxWeight);
				m_Stream.Write((byte)(m.Race.RaceID + 1));	// Would be 0x00 if it's a non-ML enabled account but...
			}

			m_Stream.Write((short)m.StatCap);

			m_Stream.Write((byte)m.Followers);
			m_Stream.Write((byte)m.FollowersMax);

			if (Core.AOS)
			{
				m_Stream.Write((short)m.FireResistance); // Fire
				m_Stream.Write((short)m.ColdResistance); // Cold
				m_Stream.Write((short)m.PoisonResistance); // Poison
				m_Stream.Write((short)m.EnergyResistance); // Energy

				m_Stream.Write((short)m.Luck); // Luck

				IWeapon weapon = m.Weapon;

				int min = 0;
				int max = 0;

				if (weapon != null)
				{
					weapon.GetStatusDamage(m, out min, out max);
				}

				m_Stream.Write((short)min); // Damage min
				m_Stream.Write((short)max); // Damage max

				m_Stream.Write(m.TithingPoints);
			}

			if (pub81Extended)
			{
				m_Stream.Write((short)Math.Min(Int16.MaxValue, m.GetMaxResistance(ResistanceType.Physical)));
				m_Stream.Write((short)Math.Min(Int16.MaxValue, m.GetMaxResistance(ResistanceType.Fire)));
				m_Stream.Write((short)Math.Min(Int16.MaxValue, m.GetMaxResistance(ResistanceType.Cold)));
				m_Stream.Write((short)Math.Min(Int16.MaxValue, m.GetMaxResistance(ResistanceType.Poison)));
				m_Stream.Write((short)Math.Min(Int16.MaxValue, m.GetMaxResistance(ResistanceType.Energy)));

				short val, cap;

				m.GetStatusDCI(out val, out cap);

				m_Stream.Write(Math.Min(cap, val));
				m_Stream.Write(cap);

				m.GetStatusHCI(out val);
				m_Stream.Write(val);

				m.GetStatusSSI(out val);
				m_Stream.Write(val);

				m.GetStatusWDI(out val);
				m_Stream.Write(val);

				m.GetStatusLRC(out val);
				m_Stream.Write(val);

				m.GetStatusSDI(out val);
				m_Stream.Write(val);

				m.GetStatusFCR(out val);
				m_Stream.Write(val);

				m.GetStatusFC(out val);
				m_Stream.Write(val);

				m.GetStatusLMC(out val);
				m_Stream.Write(val);
			}
		}
	}

Core/Mobile.cs
C#:
		public virtual void GetStatusDCI(out short val, out short max)
		{
			val = 0;
			max = (short)(m_Player ? 45 : 0);
		}

		public virtual void GetStatusHCI(out short val)
		{
			val = 0;
		}

		public virtual void GetStatusSSI(out short val)
		{
			val = 0;
		}

		public virtual void GetStatusWDI(out short val)
		{
			val = 0;
		}

		public virtual void GetStatusLRC(out short val)
		{
			val = 0;
		}

		public virtual void GetStatusSDI(out short val)
		{
			val = 0;
		}

		public virtual void GetStatusFC(out short val)
		{
			val = 0;
		}

		public virtual void GetStatusFCR(out short val)
		{
			val = 0;
		}

		public virtual void GetStatusLMC(out short val)
		{
			val = 0;
		}

Scripts/Mobiles/PlayerMobile.cs
C#:
		public override void GetStatusDCI(out short val, out short max)
		{
			//BaseWeapon.GetDCI(this, out val);
			val = 0;
			max = 45;
		}

		public override void GetStatusHCI(out short val)
		{
			//BaseWeapon.GetHCI(this, out val);
			val = 0;

			if (Weapon is BaseWeapon)
			{
				val += (short)((BaseWeapon)Weapon).GetHitChanceBonus();
			}
		}

		public override void GetStatusSSI(out short val)
		{
			//BaseWeapon.GetSSI(this, out val);
			val = 0;
		}

		public override void GetStatusWDI(out short val)
		{
			//BaseWeapon.GetWDI(this, out val);
			val = 0;

			if (Weapon is BaseWeapon)
			{
				val += (short)((BaseWeapon)Weapon).GetDamageBonus();
			}
		}

		public override void GetStatusLRC(out short val)
		{
			val = (short)AosAttributes.GetValue(this, AosAttribute.LowerRegCost);
		}

		public override void GetStatusSDI(out short val)
		{
			val = (short)AosAttributes.GetValue(this, AosAttribute.SpellDamage);
		}

		public override void GetStatusFC(out short val)
		{
			val = (short)AosAttributes.GetValue(this, AosAttribute.CastSpeed);
		}

		public override void GetStatusFCR(out short val)
		{
			val = (short)AosAttributes.GetValue(this, AosAttribute.CastRecovery);
		}

		public override void GetStatusLMC(out short val)
		{
			val = (short)AosAttributes.GetValue(this, AosAttribute.LowerManaCost);
		}

[user]Kraz[/user].Poke( );
 

Vorspire

Knight
The commented out parts are parts that people will have to fill in themselves when implementing this, I would have provided the code but BaseWeapon is subject to constant change, both in the RunUO SVN and on most custom shards.
 

Vorspire

Knight
C#:
	public sealed class MobileStatusExtended : Packet
	{
		public MobileStatusExtended(Mobile m)
			: this(m, m.NetState)
		{ }

		/*
		 * BYTE[1]		ID
		 * SHORT[2]		LENGTH
		 * 
		 * INT[4]		SERIAL
		 * STRING[30]	NAME
		 * 
		 * SHORT[2]		HITS
		 * SHORT[2]		HITS MAX
		 * 
		 * BOOL[1]		CAN BE RENAMED
		 * 
		 * BYTE[1]		SUPPORT FLAG <UOR=0x03, AOS=0x04, ML=0x05, HS=0x06>
		 * 
		 * BOOL[1]		FEMALE
		 * 
		 * SHORT[2]		STR
		 * SHORT[2]		DEX
		 * SHORT[2]		INT
		 * 
		 * SHORT[2]		STAM
		 * SHORT[2]		STAM MAX
		 * 
		 * SHORT[2]		MANA
		 * SHORT[2]		MANA MAX
		 * 
		 * INT[4]		TOTAL GOLD
		 * 
		 * SHORT[2]		( UOR: ARMOR RATING, AOS: PHYSICAL RESISTANCE )
		 * 
		 * SHORT[2]		TOTAL WEIGHT
		 * 
		 * SHORT[2]		( ML: WEIGHT MAX )
		 * BYTE[1]		( ML: RACE ID <NONE=0x00, HUMAN=0x01, ELF=0x02, GARGOYLE=0x03> )
		 * 
		 * SHORT[2]		STAT CAP
		 * 
		 * BYTE[1]		FOLLOWERS
		 * BYTE[1]		FOLLOWERS MAX
		 * 
		 * SHORT[2]		( AOS: FIRE RESISTANCE )
		 * SHORT[2]		( AOS: COLD RESISTANCE )
		 * SHORT[2]		( AOS: POISON RESISTANCE )
		 * SHORT[2]		( AOS: ENERGY RESISTANCE )
		 * 
		 * SHORT[2]		LUCK
		 * 
		 * SHORT[2]		DAMAGE MIN
		 * SHORT[2]		DAMAGE MAX
		 * 
		 * INT[4]		TITHING POINTS
		 * 
		 * SHORT[2]		( HS: PHYSICAL RESISTANCE MAX )
		 * SHORT[2]		( HS: FIRE RESISTANCE MAX )
		 * SHORT[2]		( HS: COLD RESISTANCE MAX )
		 * SHORT[2]		( HS: POISON RESISTANCE MAX )
		 * SHORT[2]		( HS: ENERGY RESISTANCE MAX )
		 * 
		 * SHORT[2]		( HS: DEFENSE CHANCE INCREASE )
		 * SHORT[2]		( HS: DEFENSE CHANCE INCREASE MAX )
		 * 
		 * SHORT[2]		( HS: HIT CHANCE INCREASE )
		 * SHORT[2]		( HS: SWING SPEED INCREASE )
		 * SHORT[2]		( HS: WEAPON DAMAGE INCREASE )
		 * SHORT[2]		( HS: LOWER REAGENT COST )
		 * SHORT[2]		( HS: SPELL DAMAGE INCREASE )
		 * SHORT[2]		( HS: FASTER CAST RECOVERY )
		 * SHORT[2]		( HS: FASTER CASTING )
		 * SHORT[2]		( HS: LOWER MANA COST )
		*/

		public MobileStatusExtended(Mobile m, NetState ns)
			: base(0x11)
		{
			string name = m.Name ?? String.Empty;

			bool pub81Extended = Core.SA && ns != null && ns.ExtendedStatus;
			bool sendMLExtended = Core.ML && ns != null && ns.SupportsExpansion(Expansion.ML);

			EnsureCapacity(pub81Extended ? 121 : (sendMLExtended ? 91 : 88));

			m_Stream.Write(m.Serial);
			m_Stream.WriteAsciiFixed(name, 30);

			m_Stream.Write((short)m.Hits);
			m_Stream.Write((short)m.HitsMax);

			m_Stream.Write(m.CanBeRenamedBy(m));

			m_Stream.Write((byte)(pub81Extended ? 0x06 : sendMLExtended ? 0x05 : Core.AOS ? 0x04 : 0x03)); // type

			m_Stream.Write(m.Female);

			m_Stream.Write((short)m.Str);
			m_Stream.Write((short)m.Dex);
			m_Stream.Write((short)m.Int);

			m_Stream.Write((short)m.Stam);
			m_Stream.Write((short)m.StamMax);

			m_Stream.Write((short)m.Mana);
			m_Stream.Write((short)m.ManaMax);

			m_Stream.Write(m.TotalGold);
			m_Stream.Write((short)(Core.AOS ? m.PhysicalResistance : (int)(m.ArmorRating + 0.5)));

			m_Stream.Write((short)(Mobile.BodyWeight + m.TotalWeight));

			if (sendMLExtended)
			{
				m_Stream.Write((short)m.MaxWeight);
				m_Stream.Write((byte)(m.Race.RaceID + 1));	// Would be 0x00 if it's a non-ML enabled account but...
			}

			m_Stream.Write((short)m.StatCap);

			m_Stream.Write((byte)m.Followers);
			m_Stream.Write((byte)m.FollowersMax);

			if (Core.AOS)
			{
				m_Stream.Write((short)m.FireResistance); // Fire
				m_Stream.Write((short)m.ColdResistance); // Cold
				m_Stream.Write((short)m.PoisonResistance); // Poison
				m_Stream.Write((short)m.EnergyResistance); // Energy

				m_Stream.Write((short)m.Luck); // Luck

				IWeapon weapon = m.Weapon;

				int min = 0;
				int max = 0;

				if (weapon != null)
				{
					weapon.GetStatusDamage(m, out min, out max);
				}

				m_Stream.Write((short)min); // Damage min
				m_Stream.Write((short)max); // Damage max

				m_Stream.Write(m.TithingPoints);
			}

			if (pub81Extended)
			{
				m_Stream.Write((short)Math.Min(Int16.MaxValue, m.GetMaxResistance(ResistanceType.Physical)));
				m_Stream.Write((short)Math.Min(Int16.MaxValue, m.GetMaxResistance(ResistanceType.Fire)));
				m_Stream.Write((short)Math.Min(Int16.MaxValue, m.GetMaxResistance(ResistanceType.Cold)));
				m_Stream.Write((short)Math.Min(Int16.MaxValue, m.GetMaxResistance(ResistanceType.Poison)));
				m_Stream.Write((short)Math.Min(Int16.MaxValue, m.GetMaxResistance(ResistanceType.Energy)));

				short val, cap;

				m.GetStatusDCI(out val, out cap);
				
				m_Stream.Write(Math.Min(cap, val));
				m_Stream.Write(cap);

				m.GetStatusHCI(out val);
				m_Stream.Write(val);

				m.GetStatusSSI(out val);
				m_Stream.Write(val);

				m.GetStatusWDI(out val);
				m_Stream.Write(val);

				m.GetStatusLRC(out val);
				m_Stream.Write(val);

				m.GetStatusSDI(out val);
				m_Stream.Write(val);

				m.GetStatusFCR(out val);
				m_Stream.Write(val);

				m.GetStatusFC(out val);
				m_Stream.Write(val);

				m.GetStatusLMC(out val);
				m_Stream.Write(val);
			}
		}
	}
C#:
	public sealed class MobileStatus : Packet
	{
		public MobileStatus(Mobile beholder, Mobile beheld)
			: this(beholder, beheld, beheld.NetState)
		{ }

		public MobileStatus(Mobile beholder, Mobile beheld, NetState ns)
			: base(0x11)
		{
			string name = beheld.Name ?? String.Empty;

			bool pub81Extended = Core.SA && ns != null && ns.ExtendedStatus;
			bool sendMLExtended = (Core.ML && ns != null && ns.SupportsExpansion(Expansion.ML));

			EnsureCapacity(43 + (beholder == beheld ? pub81Extended ? 78 : (sendMLExtended ? 48 : 45) : 0));

			m_Stream.Write(beheld.Serial);
			m_Stream.WriteAsciiFixed(name, 30);

			if (beholder == beheld)
				WriteAttr(beheld.Hits, beheld.HitsMax);
			else
				WriteAttrNorm(beheld.Hits, beheld.HitsMax);

			m_Stream.Write(beheld.CanBeRenamedBy(beholder));

			if (beholder == beheld)
			{
				m_Stream.Write((byte)(pub81Extended ? 0x06 : sendMLExtended ? 0x05 : Core.AOS ? 0x04 : 0x03)); // type

				m_Stream.Write(beheld.Female);

				m_Stream.Write((short)beheld.Str);
				m_Stream.Write((short)beheld.Dex);
				m_Stream.Write((short)beheld.Int);

				WriteAttr(beheld.Stam, beheld.StamMax);
				WriteAttr(beheld.Mana, beheld.ManaMax);

				m_Stream.Write(beheld.TotalGold);
				m_Stream.Write((short)(Core.AOS ? beheld.PhysicalResistance : (int)(beheld.ArmorRating + 0.5)));

				m_Stream.Write((short)(Mobile.BodyWeight + beheld.TotalWeight));

				if (sendMLExtended)
				{
					m_Stream.Write((short)beheld.MaxWeight);
					m_Stream.Write((byte)(beheld.Race.RaceID + 1));	// Would be 0x00 if it's a non-ML enabled account but...
				}

				m_Stream.Write((short)beheld.StatCap);

				m_Stream.Write((byte)beheld.Followers);
				m_Stream.Write((byte)beheld.FollowersMax);

				if (Core.AOS)
				{
					m_Stream.Write((short)beheld.FireResistance); // Fire
					m_Stream.Write((short)beheld.ColdResistance); // Cold
					m_Stream.Write((short)beheld.PoisonResistance); // Poison
					m_Stream.Write((short)beheld.EnergyResistance); // Energy

					m_Stream.Write((short)beheld.Luck); // Luck

					IWeapon weapon = beheld.Weapon;

					int min = 0;
					int max = 0;

					if (weapon != null)
					{
						weapon.GetStatusDamage(beheld, out min, out max);
					}

					m_Stream.Write((short)min); // Damage min
					m_Stream.Write((short)max); // Damage max

					m_Stream.Write(beheld.TithingPoints);
				}

				if (pub81Extended)
				{
					m_Stream.Write((short)Math.Min(Int16.MaxValue, beheld.GetMaxResistance(ResistanceType.Physical)));
					m_Stream.Write((short)Math.Min(Int16.MaxValue, beheld.GetMaxResistance(ResistanceType.Fire)));
					m_Stream.Write((short)Math.Min(Int16.MaxValue, beheld.GetMaxResistance(ResistanceType.Cold)));
					m_Stream.Write((short)Math.Min(Int16.MaxValue, beheld.GetMaxResistance(ResistanceType.Poison)));
					m_Stream.Write((short)Math.Min(Int16.MaxValue, beheld.GetMaxResistance(ResistanceType.Energy)));

					short val, cap;

					beheld.GetStatusDCI(out val, out cap);

					m_Stream.Write(Math.Min(cap, val));
					m_Stream.Write(cap);

					beheld.GetStatusHCI(out val);
					m_Stream.Write(val);

					beheld.GetStatusSSI(out val);
					m_Stream.Write(val);

					beheld.GetStatusWDI(out val);
					m_Stream.Write(val);

					beheld.GetStatusLRC(out val);
					m_Stream.Write(val);

					beheld.GetStatusSDI(out val);
					m_Stream.Write(val);

					beheld.GetStatusFCR(out val);
					m_Stream.Write(val);

					beheld.GetStatusFC(out val);
					m_Stream.Write(val);

					beheld.GetStatusLMC(out val);
					m_Stream.Write(val);
				}
			}
			else
			{
				m_Stream.Write((byte)0x00);
			}
		}

		private void WriteAttr(int current, int maximum)
		{
			m_Stream.Write((short)current);
			m_Stream.Write((short)maximum);
		}

		private void WriteAttrNorm(int current, int maximum)
		{
			AttributeNormalizer.WriteReverse(m_Stream, current, maximum);
		}
	}
 
Top