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!

Interception Mobile Packets - Hallucinations

Terrormaster

Wanderer
Intercepting Mobile Packets - Hallucination Effect Redeaux

Hi all, this started off on another topic from Starbucks but tought I'd post it here since it's more of a coding support topic:

Essentially what I'm doing is coding a sanity system for a gothic/lovecraftian themed shard. One of the side effect of sanity loss are phobias and hallucinations. So for instance if your character has a fear of spiders and they fail a sanity check they should start seeing all human mobiles as spiders for a fixed period of time.

Now I've pretty much done my due diligence here and read through all the topics pertaining to hallucination effects with little to know resolution - it was determined it can be done but the concensus was that in order to do it right it would require a modification to the core.

So I got the source code and started digging through it. I have a lot of programming background so I'm not afraid to mess around with the core. However, since there's no documentation for the source (and nor am I asking for any - the core team has enough on their plates) I'm not sure WHERE I'll need to make the necessary changes to implement the sanity hallucination effects. I know HOW to do it, just not WHERE to do it.

On the script side I have flags in a custommobile script for different phobias and a sanity amount. In addition I have two properties: SeeMobilesAsBody and SeeMobilesAsHue.

So, when a phobia kicks in the two above values are set to non-zero values (setting hue to a -1 to generate a random hue).

Then on the core side I would look at these two values before sending out mobile packets. If SeeMobileAsBody is set to a value > 0 then all mobile packets destined to the infected player's client would have the body and hue set accordingly before the packet goes out.

The process is pretty straight forward. I just can't figure out where I need to code it. I've narrowed my search to the packets.cs file but I'm having difficulty decyphering which function needs to be changed. If anyone from the core team (or anyone else in the know) can just point me to the right function, I can drive from there. I'm pretty sure it's one of the functions that accepts beholder and beholdee.

The original topic on starbucks can be found here:
http://www.runuo.com/forum/showthread.php?t=60012

Thanks in advance for any and all help,
-TM
 

Arahil

Sorceror
well, first of all: you can not access your custom mobile script from the core, except you do it with reflection (which i absolutely do not advise you because it is really very, very slow). so in order to make this change you have to modify the mobile.cs and put your two fields there - or messing around with some interfaces.
then, to send the right packets at the right time, you have to understand the uo packet protocol. there are several sites dedicated to it, for example http://necrotoolz.sourceforge.net/kairpacketguide/index.htm, http://gonzo.kiev.ua/guide/guide.html or http://uopackets.emuresource.com/packets.php?style= (not complete, but a good start). you have to take a look where the body id and hue is sended and replace the correct values with your halluzination stuff. there will be several places to change this, i think.
maybe an easy way to find those code pieces is to follow the way the server goes if the body or the hue is changed, but you won't learn that much about the uo protocol that way :)
 

noobie

Wanderer
you dont need to deal with uo protocols to do such a thing.

here is a starting point for you:

a) add a variable to Mobile/PlayerMobile for hallucination. (implement getter/setter)
b) in Mobile.cs (core), there is a method called ProcessDelta. This is where the packets are sent to the clients. First it updates the client itself, then sends new packets the other clients around.

some code from core:
Code:
IPooledEnumerable eable = m.Map.GetClientsInRange( m.m_Location );
Mobile beholder;
foreach ( NetState state in eable )
{
  beholder = state.Mobile;
  if ( beholder != m && beholder.CanSee( m ) )
  {
      //it sends some packets to clients
     //this is where you need to check whether if 
     //beholder is hallucinating. 
  }
 }

there are some packets that is sent to the clients: RemovePacket, MobileIncoming,MobileMoving etc..

you need to change some of these packets' constructors.
ex)
from
public MobileIncoming( Mobile beholder, Mobile beheld ) : base( 0x78 )
to
public MobileIncoming( Mobile beholder, Mobile beheld, bool hallucination ) : base( 0x78 )

or just add another constructor..

if the boolean variable is true, then you need to pick a random hue for each item.
see the whole code for MobileIncoming, you will get the idea. you might wanna choose a random hue range first, then pick a random hue for each item if you want colors not to be so much different.

you might not need to modify every packet in there.
 

Terrormaster

Wanderer
Thanks Noobie, thats exactly what I was looking for - the WHERE. Gonna hit this evening.

you need to change some of these packets' constructors.
ex)
from
public MobileIncoming( Mobile beholder, Mobile beheld ) : base( 0x78 )
to
public MobileIncoming( Mobile beholder, Mobile beheld, bool hallucination ) : base( 0x78 )

Might do that a little different. since I don't want to have to deal with Reflections. Gonna add m_SeeBodyAs into mobile.cs with its get/set. Then change the logic in

public MobileIncoming( Mobile beholder, Mobile beheld ) : base( 0x78 )

to something like

Code:
if ( beholder.SeeBodyAs == 0 )
    m_Stream.Write( (short) beheld.Body );
else
    m_Stream.Write( (short) beholder.SeeBodyAs );

I'll try that and see how that works out.

Thanks Noobie,
-TM
 

noobie

Wanderer
hehe, thats not reflection, its overloading (defining extra constructor/method) :)

btw, you could use some kind of your own method to pick a random hue.
lets say ProcessDelta method is called 5 times in a second.
for each call, you dont want to pick a new hue for the same player. so you could use a random "seed" for some period (use a seed for randomization), and change it after a while. otherwise, client would see some kind of chameleon which changes its color so rapidly ;)
 

Terrormaster

Wanderer
hehe, thats not reflection, its overloading (defining extra constructor/method)

Sorry, my reply was a bit confusing - was in a rush to pick up my vidcam, was a guy outside the office cutting a car in half Knoxville style to demonstrate Lennox tools.

But yeah, I knew what you mean by overloading. What I meant by Reflection was the defining of props in the scripts and trying to access them from the core. Haven't done any reflections stuff yet but I know at a high-level what it is kinda. So, I want to put the SeeBodyAs and SeeHueAs in the core instead of the scripts.

Anyways, since I stopped in at home fer lunch (to get the vidcam) I decided to do a recompile on clean unaltered core code... Compiles fine but won't run - gets to verifying and throws an exception. I always test original source compile before making any changes to make sure I have a good point of reference for debugging. I'll post the specifics of the exception when I get back home. But anyone know why a fresh core recompile would crap out when ran?

-TM
 

Atomic

Wanderer
Some times when I compile the core with a differently named .exe I get, this, try to rename the .exe or the project to the same name as the default one (Server.exe)
 

Terrormaster

Wanderer
Atomic said:
Some times when I compile the core with a differently named .exe I get, this, try to rename the .exe or the project to the same name as the default one (Server.exe)

BINGO - Renamed the executable to server.exe and it worked like a charm - thanks Atomic! I'll start implementing tonight now that I know any problems resulting will be because of code I introduce. Once I get it working, I'll put together a tutorial to share here.

-TM
 

Terrormaster

Wanderer
WOOHOO! Got this to work, yehah! Had some interesting hickups on the way. For some reason the first iteration behaved rather bizaar - everyone within range of the affected player saw the effects as well.

After commenting out a particular piece of core code in two spots I got the sucker to work. However, I'm not 100% certain as to why it created the above behavior nor do I completely understand the logic behind its inclusion. The code in question is as follows:

Code:
if ( p == null )
    p = cache[noto] = new MobileMoving( m, noto );

What is the reasoning behind caching preset packets sorted by notoriety? I'd be eternally greatful if someone could explain this one - I hate commenting out original code without knowing why I'm doing so.

-TM
 

noobie

Wanderer
thats a cache mechanism. instead of re-evaluate and creating the same packet, it sends same value from the cache.

that should has nothing to do with the error.
 

Terrormaster

Wanderer
Is it possible that the cache[] array is not localized? Because when I comment out the conditional and just force a value into it and P the error goes away. Here's the full extent of the code I modified in mobile.cs:

move() method:
Code:
		public virtual bool Move( Direction d )
		{
			if ( m_Deleted )
				return false;

			BankBox box = FindBankNoCreate();

			if ( box != null && box.Opened )
				box.Close();

			Point3D newLocation = Location;
			Point3D oldLocation = newLocation;

			if ( (m_Direction & Direction.Mask) == (d & Direction.Mask) )
			{
				// We are actually moving (not just a direction change)

				if ( m_Spell != null && !m_Spell.OnCasterMoving( d ) )
					return false;

				if ( m_Paralyzed || m_Frozen )
				{
					SendLocalizedMessage( 500111 ); // You are frozen and can not move.

					return false;
				}

				int newZ;

				if ( CheckMovement( d, out newZ ) )
				{
					int x = m_Location.m_X, y = m_Location.m_Y;
					int oldX = x, oldY = y;
					int oldZ = m_Location.m_Z;

					switch ( d & Direction.Mask )
					{
						case Direction.North:      --y; break;
						case Direction.Right: ++x; --y; break;
						case Direction.East:  ++x;      break;
						case Direction.Down:  ++x; ++y; break;
						case Direction.South:      ++y; break;
						case Direction.Left:  --x; ++y; break;
						case Direction.West:  --x;      break;
						case Direction.Up:    --x; --y; break;
					}

					m_Pushing = false;

					Map map = m_Map;

					if ( map != null )
					{
						Sector oldSector = map.GetSector( oldX, oldY );
						Sector newSector = map.GetSector( x, y );
						ArrayList list;

						if ( oldSector != newSector )
						{
							list = oldSector.Mobiles;

							for ( int i = 0; i < list.Count; ++i )
							{
								Mobile m = (Mobile)list[i];

								if ( m != this && m.X == oldX && m.Y == oldY && (m.Z + 15) > oldZ && (oldZ + 15) > m.Z && !m.OnMoveOff( this ) )
									return false;
							}

							list = oldSector.Items;

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

								if ( item.AtWorldPoint( oldX, oldY ) && (item.Z == oldZ || ((item.Z + item.ItemData.Height) > oldZ && (oldZ + 15) > item.Z)) && !item.OnMoveOff( this ) )
									return false;
							}

							list = newSector.Mobiles;

							for ( int i = 0; i < list.Count; ++i )
							{
								Mobile m = (Mobile)list[i];

								if ( m.X == x && m.Y == y && (m.Z + 15) > newZ && (newZ + 15) > m.Z && !m.OnMoveOver( this ) )
									return false;
							}

							list = newSector.Items;

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

								if ( item.AtWorldPoint( x, y ) && (item.Z == newZ || ((item.Z + item.ItemData.Height) > newZ && (newZ + 15) > item.Z)) && !item.OnMoveOver( this ) )
									return false;
							}
						}
						else
						{
							list = oldSector.Mobiles;

							for ( int i = 0; i < list.Count; ++i )
							{
								Mobile m = (Mobile)list[i];

								if ( m != this && m.X == oldX && m.Y == oldY && (m.Z + 15) > oldZ && (oldZ + 15) > m.Z && !m.OnMoveOff( this ) )
									return false;
								else if ( m.X == x && m.Y == y && (m.Z + 15) > newZ && (newZ + 15) > m.Z && !m.OnMoveOver( this ) )
									return false;
							}

							list = oldSector.Items;

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

								if ( item.AtWorldPoint( oldX, oldY ) && (item.Z == oldZ || ((item.Z + item.ItemData.Height) > oldZ && (oldZ + 15) > item.Z)) && !item.OnMoveOff( this ) )
									return false;
								else if ( item.AtWorldPoint( x, y ) && (item.Z == newZ || ((item.Z + item.ItemData.Height) > newZ && (newZ + 15) > item.Z)) && !item.OnMoveOver( this ) )
									return false;
							}
						}

						Region region = Region.Find( new Point3D( x, y, newZ ), m_Map );

						if ( region != null && !region.OnMoveInto( this, d, new Point3D( x, y, newZ ), oldLocation ) )
							return false;
					}
					else
					{
						return false;
					}

					if ( !InternalOnMove( d ) )
						return false;

					if ( m_FwdEnabled && m_NetState != null && m_AccessLevel < m_FwdAccessOverride && (!m_FwdUOTDOverride || (m_NetState.Version != null && m_NetState.Version.Type != ClientType.UOTD)) )
					{
						if ( m_MoveRecords == null )
							m_MoveRecords = new Queue( 6 );

						while ( m_MoveRecords.Count > 0 )
						{
							MovementRecord r = (MovementRecord)m_MoveRecords.Peek();

							if ( r.Expired() )
								m_MoveRecords.Dequeue();
							else
								break;
						}

						if ( m_MoveRecords.Count >= m_FwdMaxSteps )
						{
							FastWalkEventArgs fw = new FastWalkEventArgs( m_NetState );
							EventSink.InvokeFastWalk( fw );

							if ( fw.Blocked )
								return false;
						}

						TimeSpan delay;

						if ( Mounted )
							delay = (d & Direction.Running) != 0 ? m_RunMount : m_WalkMount;
						else
							delay = (d & Direction.Running) != 0 ? m_RunFoot : m_WalkFoot;

						DateTime end;

						if ( m_MoveRecords.Count > 0 )
							end = m_EndQueue + delay;
						else
							end = DateTime.Now + delay;

						m_MoveRecords.Enqueue( MovementRecord.NewInstance( end ) );

						m_EndQueue = end;
					}

					m_LastMoveTime = DateTime.Now;
					newLocation = new Point3D( x, y, newZ );
				}
				else
				{
					return false;
				}

				DisruptiveAction();
			}

			if ( m_NetState != null )
				m_NetState.Send( MovementAck.Instantiate( m_NetState.Sequence, this ) );//new MovementAck( m_NetState.Sequence, this ) );

			SetLocation( newLocation, false );
			SetDirection( d );

			if ( m_Map != null )
			{
				MobileMoving[] cache = m_MovingPacketCache;

				for ( int i = 0; i < cache.Length; ++i )
					cache[i] = null;

				IPooledEnumerable eable = m_Map.GetObjectsInRange( m_Location, Core.GlobalMaxUpdateRange );

				foreach ( object o in eable )
				{
					if ( o == this )
						continue;

					if ( o is Mobile )
					{
						m_MoveList.Add( o );
					}
					else if ( o is Item )
					{
						Item item = (Item)o;

						if ( item.HandlesOnMovement )
							m_MoveList.Add( item );
					}
				}

				eable.Free();

				for ( int i = 0; i < m_MoveList.Count; ++i )
				{
					object o = m_MoveList[i];

					if ( o is Mobile )
					{
						Mobile m = (Mobile)m_MoveList[i];
						NetState ns = m.NetState;

						if ( ns != null && Utility.InUpdateRange( m_Location, m.m_Location ) && m.CanSee( this ) )
						{
							int noto = Notoriety.Compute( m, this );
							MobileMoving p = cache[noto];

[COLOR="Red"]							/// NECROPOLIS: removed cache conditional to correct bug with hallucination code - TM, 08-19-05
							//if ( p == null ) //** ORIGINAL CORE **//
							//	p = cache[noto] = new MobileMoving( m, noto ); //** ORIGINAL CORE **//
							p = cache[noto] = new MobileMoving( this, noto, m );
[/COLOR]
							ns.Send( p );
						}

						m.OnMovement( this, oldLocation );
					}
					else if ( o is Item )
					{
						((Item)o).OnMovement( this, oldLocation );
					}
				}

				if ( m_MoveList.Count > 0 )
					m_MoveList.Clear();
			}

			return true;
		}

ProcessDelta() method
Code:
		public virtual void ProcessDelta()
		{
			Mobile m = this;
			MobileDelta delta;
			MobileDelta attrs;

			delta = m.m_DeltaFlags;

			if ( delta == MobileDelta.None )
				return;

			attrs = delta & MobileDelta.Attributes;

			m.m_DeltaFlags = MobileDelta.None;
			m.m_InDeltaQueue = false;

			bool sendHits = false, sendStam = false, sendMana = false, sendAll = false, sendAny = false;
			bool sendIncoming = false, sendNonlocalIncoming = false;
			bool sendUpdate = false, sendRemove = false;
			bool sendPublicStats = false, sendPrivateStats = false;
			bool sendMoving = false, sendNonlocalMoving = false;
			bool sendOPLUpdate = Core.AOS && (delta & MobileDelta.Properties) != 0;

			if ( attrs != MobileDelta.None )
			{
				sendAny = true;

				if ( attrs == MobileDelta.Attributes )
				{
					sendAll = true;
				}
				else
				{
					sendHits = ( (attrs & MobileDelta.Hits) != 0 );
					sendStam = ( (attrs & MobileDelta.Stam) != 0 );
					sendMana = ( (attrs & MobileDelta.Mana) != 0 );
				}
			}

			if ( (delta & MobileDelta.GhostUpdate) != 0 )
			{
				sendNonlocalIncoming = true;
			}

			if ( (delta & MobileDelta.Hue) != 0 )
			{
				sendNonlocalIncoming = true;
				sendUpdate = true;
				sendRemove = true;
			}

			if ( (delta & MobileDelta.Direction) != 0 )
			{
				sendNonlocalMoving = true;
				sendUpdate = true;
			}

			if ( (delta & MobileDelta.Body) != 0 )
			{
				sendUpdate = true;
				sendIncoming = true;
			}

			/*if ( (delta & MobileDelta.Hue) != 0 )
				{
					sendNonlocalIncoming = true;
					sendUpdate = true;
				}
				else if ( (delta & (MobileDelta.Direction | MobileDelta.Body)) != 0 )
				{
					sendNonlocalMoving = true;
					sendUpdate = true;
				}
				else*/ if ( (delta & (MobileDelta.Flags | MobileDelta.Noto)) != 0 )
					   {
						   sendMoving = true;
					   }

			if ( (delta & MobileDelta.Name) != 0 )
			{
				sendAll = false;
				sendHits = false;
				sendAny = sendStam || sendMana;
				sendPublicStats = true;
			}

			if ( (delta & (MobileDelta.WeaponDamage | MobileDelta.Resistances | MobileDelta.Stat | MobileDelta.Weight | MobileDelta.Gold | MobileDelta.Armor | MobileDelta.StatCap | MobileDelta.Followers | MobileDelta.TithingPoints)) != 0 )
			{
				sendPrivateStats = true;
			}

			MobileMoving[] cache = m_MovingPacketCache;

			if ( sendMoving || sendNonlocalMoving )
			{
				for ( int i = 0; i < cache.Length; ++i )
					cache[i] = null;
			}

			NetState ourState = m.m_NetState;

			if ( ourState != null )
			{
				if ( sendUpdate )
				{
					ourState.Sequence = 0;
					ourState.Send( new MobileUpdate( m ) );
					ClearFastwalkStack();
				}

				if ( sendIncoming )
					ourState.Send( new MobileIncoming( m, m ) );

				if ( sendMoving )
				{
					int noto = Notoriety.Compute( m, m );

					ourState.Send( cache[noto] = new MobileMoving( m, noto ) );
				}

				if ( sendPublicStats || sendPrivateStats )
				{
					ourState.Send( new MobileStatusExtended( m ) );
				}
				else if ( sendAll )
				{
					ourState.Send( new MobileAttributes( m ) );
				}
				else if ( sendAny )
				{
					if ( sendHits )
						ourState.Send( new MobileHits( m ) );

					if ( sendStam )
						ourState.Send( new MobileStam( m ) );

					if ( sendMana )
						ourState.Send( new MobileMana( m ) );
				}

				if ( sendStam || sendMana )
				{
					IParty ip = m_Party as IParty;

					if ( ip != null && sendStam )
						ip.OnStamChanged( this );

					if ( ip != null && sendMana )
						ip.OnManaChanged( this );
				}

				if ( sendOPLUpdate )
					ourState.Send( OPLPacket );
			}

			sendMoving = sendMoving || sendNonlocalMoving;
			sendIncoming = sendIncoming || sendNonlocalIncoming;
			sendHits = sendHits || sendAll;

			if ( m.m_Map != null && (sendRemove || sendIncoming || sendPublicStats || sendHits || sendMoving || sendOPLUpdate) )
			{
				Mobile beholder;

				IPooledEnumerable eable = m.Map.GetClientsInRange( m.m_Location );

				Packet hitsPacket = null;
				Packet statPacketTrue = null, statPacketFalse = null;
				Packet deadPacket = null;

				foreach ( NetState state in eable )
				{
					beholder = state.Mobile;

					if ( beholder != m && beholder.CanSee( m ) )
					{
						if ( sendRemove )
							state.Send( m.RemovePacket );

						if ( sendIncoming )
						{
							state.Send( new MobileIncoming( beholder, m ) );

							if ( m.IsDeadBondedPet )
							{
								if ( deadPacket == null )
									deadPacket = new BondedStatus( 0, m.m_Serial, 1 );

								state.Send( deadPacket );
							}
						}

						if ( sendMoving )
						{
							int noto = Notoriety.Compute( beholder, m );

							MobileMoving p = cache[noto];

[COLOR="Red"]
					/// NECROPOLIS: removed cache conditional to correct bug with hallucination code - TM, 08-19-05
							//if ( p == null ) //** ORIGINAL CORE **//
							//	cache[noto] = p = new MobileMoving( m, noto ); //** ORIGINAL CORE **//
							cache[noto] = p = new MobileMoving( m, noto, beholder );
[/color]
							state.Send( p );
						}

						if ( sendPublicStats )
						{
							if ( m.CanBeRenamedBy( beholder ) )
							{
								if ( statPacketTrue == null )
									statPacketTrue = new MobileStatusCompact( true, m );

								state.Send( statPacketTrue );
							}
							else
							{
								if ( statPacketFalse == null )
									statPacketFalse = new MobileStatusCompact( false, m );

								state.Send( statPacketFalse );
							}
						}
						else if ( sendHits )
						{
							if ( hitsPacket == null )
								hitsPacket = new MobileHitsN( m );

							state.Send( hitsPacket );
						}

						if ( sendOPLUpdate )
							state.Send( OPLPacket );
					}
				}

				eable.Free();
			}
		}

SeeBodyAs(get|set) property <-- appended to the end of the mobile class
Code:
[color="red"]
		/// NECROPOLIS: ==============================================================
		/// Begin modifications for Necropolis Sanity system
		/// ==========================================================================

		private short m_SeeBodyAs = 0;

		[CommandProperty( AccessLevel.GameMaster )]
		public short SeeBodyAs
		{
			get{ return m_SeeBodyAs; }
			set{ m_SeeBodyAs = value; }
		}
		/// ==========================================================================
[/color]

The following are the changes I made to the packets.cs file:

MobileIncoming() class
Code:
	public sealed class MobileIncoming : Packet
	{
		private static int[] m_DupedLayers = new int[256];
		private static int m_Version;

		public Mobile m_Beheld;

		public MobileIncoming( Mobile beholder, Mobile beheld ) : base( 0x78 )
		{
			m_Beheld = beheld;
			++m_Version;

			ArrayList eq = beheld.Items;
			int count = eq.Count;

			this.EnsureCapacity( 23 + (count * 9) );

			int hue = beheld.Hue;

			if ( beheld.SolidHueOverride >= 0 )
				hue = beheld.SolidHueOverride;

			m_Stream.Write( (int) beheld.Serial );
[color="red"]
			/// NECROPOLIS: Added conditional for Hallucination support - TM, 08-19-05
			if ( beholder.SeeBodyAs == 0 || beholder == beheld )[/color]
				m_Stream.Write( (short) beheld.Body );[color="red"]
			else
				m_Stream.Write( (short) beholder.SeeBodyAs );
			/// NECROPOLIS ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^[/color]
			m_Stream.Write( (short) beheld.X );
			m_Stream.Write( (short) beheld.Y );
			m_Stream.Write( (sbyte) beheld.Z );
			m_Stream.Write( (byte) beheld.Direction );
			m_Stream.Write( (short) hue );
			m_Stream.Write( (byte) beheld.GetPacketFlags() );
			m_Stream.Write( (byte) Notoriety.Compute( beholder, beheld ) );

			for ( int i = 0; i < count; ++i )
			{
				Item item = (Item)eq[i];

				byte layer = (byte) item.Layer;

				if ( !item.Deleted && beholder.CanSee( item ) && m_DupedLayers[layer] != m_Version )
				{
					m_DupedLayers[layer] = m_Version;

					hue = item.Hue;

					if ( beheld.SolidHueOverride >= 0 )
						hue = beheld.SolidHueOverride;

					int itemID = item.ItemID & 0x3FFF;
					bool writeHue = ( hue != 0 );

					if ( writeHue )
						itemID |= 0x8000;

					m_Stream.Write( (int) item.Serial );
					m_Stream.Write( (short) itemID );
					m_Stream.Write( (byte) layer );

					if ( writeHue )
						m_Stream.Write( (short) hue );
				}
			}

			m_Stream.Write( (int) 0 ); // terminate
		}
	}

MobileMoving() class <- basically added an overload to pass in beholder
Code:
	public sealed class MobileMoving : Packet
	{
		public MobileMoving( Mobile m, int noto/*Mobile beholder, Mobile beheld*/ ) : base( 0x77, 17 )
		{
			Point3D loc = m.Location;

			int hue = m.Hue;

			if ( m.SolidHueOverride >= 0 )
				hue = m.SolidHueOverride;

			m_Stream.Write( (int) m.Serial );
			m_Stream.Write( (short) m.Body );
			m_Stream.Write( (short) loc.m_X );
			m_Stream.Write( (short) loc.m_Y );
			m_Stream.Write( (sbyte) loc.m_Z );
			m_Stream.Write( (byte) m.Direction );
			m_Stream.Write( (short) hue );
			m_Stream.Write( (byte) m.GetPacketFlags() );
			m_Stream.Write( (byte) noto );//Notoriety.Compute( beholder, beheld ) );
		}
[color="red"]
		/// NECROPOLIS: Added overload for Hallucination support - TM, 08-19-05
		public MobileMoving( Mobile m, int noto, Mobile beholder ) : base( 0x77, 17 )
		{
			Point3D loc = m.Location;

			int hue = m.Hue;

			if ( m.SolidHueOverride >= 0 )
				hue = m.SolidHueOverride;

			m_Stream.Write( (int) m.Serial );
			if ( beholder.SeeBodyAs == 0 || m == beholder )
				m_Stream.Write( (short) m.Body );
			else
				m_Stream.Write( (short) beholder.SeeBodyAs );
			m_Stream.Write( (short) loc.m_X );
			m_Stream.Write( (short) loc.m_Y );
			m_Stream.Write( (sbyte) loc.m_Z );
			m_Stream.Write( (byte) m.Direction );
			m_Stream.Write( (short) hue );
			m_Stream.Write( (byte) m.GetPacketFlags() );
			m_Stream.Write( (byte) noto );
		}[/color]
	}

Thats pretty much the extent of everything I changed or added at this point. Then I loaded up two clients with two different accounts as well as having a friend login to test. Then I used [set seebodyas 28 (spider) and target only ONE of the three players.

The SeeBodyAs will be managed script side from the sanity system to keep all the sanity rules out of the core.

Also, I had to mod both Move() and ProcessDelta() for this to work - modding ProcessDelta() only resulted in bizaar flickering effects where the mobile would occasionally toggle back to their normal state.

-TM

FYI: ALPHA VERSION / PROOF OF CONCEPT
This core modification is alpha only as proof of concept. Install into the core at your own risk. Once I have finished testing, and am confident the code is pretty solid I will release it for public consumption with support and detailed installation instructions.
 

Terrormaster

Wanderer
I didn't overload on MobileIncoming() as it already accepted Beholder and Beheld, just added logic around the stream for Body. As a result I didn't need to modify any of the existing calls.

-TM
 

Terrormaster

Wanderer
noobie said:
upss, my bad sry :)

NP :)

Now all is left is to find out what the reprecussions of commenting out those IFs are and I'll be set to roll forward. Gonna wrap up the remainder of the design for the sanity system and begin implementation.

Thanks again for all the help everyone,
-TM
 

NicoTM

Wanderer
You can mantain the caching system for non-Hallucinated mobile.

when the code send the Mobilemoving packet you can check if the beholding mobile are hallucinated or not. Then you can sent the cached packet (if the mob aren't hallucinated), or a new one for the hallucination.
 
Top