context: using System;
  using System.Collections;
  using System.Collections.Generic;
  using System.IO;
  using System.Threading;
  using Server.Gumps;
  using Server.Items;
  using Server.Mobiles;
  using Server.Network;
  using Server.Spells;
  
  namespace Server.Multis
  {
      public enum FoundationType
  	{
  		Stone,
  		DarkWood,
  		LightWood,
  		Dungeon,
  		Brick,
  		ElvenGrey,
  		ElvenNatural,
  		Crystal,
  		Shadow
  	}
  
  	public class HouseFoundation : BaseHouse
  	{
  		private DesignState m_Current; // State which is currently visible.
  		private DesignState m_Design;  // State of current design.
  		private DesignState m_Backup;  // State at last user backup.
  		private Item m_SignHanger;     // Item hanging the sign.
  		private Item m_Signpost;       // Item supporting the hanger.
  		private int m_SignpostGraphic; // ItemID number of the chosen signpost.
  		private int m_LastRevision;    // Latest revision number.
  		private List<Item> m_Fixtures; // List of fixtures (teleporters and doors) associated with this house.
  		private FoundationType m_Type; // Graphic type of this foundation.
  		private Mobile m_Customizer;   // Who is currently customizing this -or- null if not customizing.
  
  		public FoundationType Type { get { return m_Type; } set { m_Type = value; } }
  		public int LastRevision { get { return m_LastRevision; } set { m_LastRevision = value; } }
  		public List<Item> Fixtures { get { return m_Fixtures; } }
  		public Item SignHanger { get { return m_SignHanger; } }
  		public Item Signpost { get { return m_Signpost; } }
  		public int SignpostGraphic { get { return m_SignpostGraphic; } set { m_SignpostGraphic = value; } }
  		public Mobile Customizer { get { return m_Customizer; } set { m_Customizer = value; } }
  
  		public override bool IsAosRules { get { return true; } }
  
  		public override bool IsActive { get { return Customizer == null; } }
  
  		public virtual int CustomizationCost { get { return (Core.AOS ? 0 : 10000); } }
  
  		public bool IsFixture( Item item )
  		{
  			return (m_Fixtures != null && m_Fixtures.Contains( item ));
  		}
  
  		public override MultiComponentList Components
  		{
  			get
  			{
  				if( m_Current == null )
  					SetInitialState();
  
  				return m_Current.Components;
  			}
  		}
  
  		public override int GetMaxUpdateRange()
  		{
  			return 24;
  		}
  
  		public override int GetUpdateRange( Mobile m )
  		{
  			int w = CurrentState.Components.Width;
  			int h = CurrentState.Components.Height-1;
  			int v = 18 + ((w > h ? w : h) / 2);
  
  			if( v > 24 )
  				v = 24;
  			else if( v < 18 )
  				v = 18;
  
  			return v;
  		}
  
  		public DesignState CurrentState
  		{
  			get { if( m_Current == null ) SetInitialState(); return m_Current; }
  			set { m_Current = value; }
  		}
  
  		public DesignState DesignState
  		{
  			get { if( m_Design == null ) SetInitialState(); return m_Design; }
  			set { m_Design = value; }
  		}
  
  		public DesignState BackupState
  		{
  			get { if( m_Backup == null ) SetInitialState(); return m_Backup; }
  			set { m_Backup = value; }
  		}
  
  		public void SetInitialState()
  		{
  			// This is a new house, it has not yet loaded a design state
  			m_Current = new DesignState( this, GetEmptyFoundation() );
  			m_Design = new DesignState( m_Current );
  			m_Backup = new DesignState( m_Current );
  		}
  
  		public override void OnAfterDelete()
  		{
  			base.OnAfterDelete();
  
  			if( m_SignHanger != null )
  				m_SignHanger.Delete();
  
  			if( m_Signpost != null )
  				m_Signpost.Delete();
  
  			if( m_Fixtures == null )
  				return;
  
  			for( int i = 0; i < m_Fixtures.Count; ++i )
  			{
  				Item item = m_Fixtures[i];
  
  				if( item != null )
  					item.Delete();
  			}
  
  			m_Fixtures.Clear();
  		}
  
  		public override void OnLocationChange( Point3D oldLocation )
  		{
  			base.OnLocationChange( oldLocation );
  
  			int x = Location.X - oldLocation.X;
  			int y = Location.Y - oldLocation.Y;
  			int z = Location.Z - oldLocation.Z;
  
  			if( m_SignHanger != null )
  				m_SignHanger.MoveToWorld( new Point3D( m_SignHanger.X + x, m_SignHanger.Y + y, m_SignHanger.Z + z ), Map );
  
  			if( m_Signpost != null )
  				m_Signpost.MoveToWorld( new Point3D( m_Signpost.X + x, m_Signpost.Y + y, m_Signpost.Z + z ), Map );
  
  			if( m_Fixtures == null )
  				return;
  
  			for( int i = 0; i < m_Fixtures.Count; ++i )
  			{
  				Item item = m_Fixtures[i];
  
  				if( Doors.Contains( item ) )
  					continue;
  
  				item.MoveToWorld( new Point3D( item.X + x, item.Y + y, item.Z + z ), Map );
  			}
  		}
  
  		public override void OnMapChange()
  		{
  			base.OnMapChange();
  
  			if( m_SignHanger != null )
  				m_SignHanger.Map = this.Map;
  
  			if( m_Signpost != null )
  				m_Signpost.Map = this.Map;
  
  			if( m_Fixtures == null )
  				return;
  
  			for( int i = 0; i < m_Fixtures.Count; ++i )
  				m_Fixtures[i].Map = this.Map;
  		}
  
  		public void ClearFixtures( Mobile from )
  		{
  			if( m_Fixtures == null )
  				return;
  
  			RemoveKeys( from );
  
  			for( int i = 0; i < m_Fixtures.Count; ++i )
  			{
  				m_Fixtures[i].Delete();
  				Doors.Remove( m_Fixtures[i] );
  			}
  
  			m_Fixtures.Clear();
  		}
  
  		public void AddFixtures( Mobile from, MultiTileEntry[] list )
  		{
  			if( m_Fixtures == null )
  				m_Fixtures = new List<Item>();
  
  			uint keyValue = 0;
  
  			for( int i = 0; i < list.Length; ++i )
  			{
  				MultiTileEntry mte = list[i];
  				int itemID = mte.m_ItemID;
  
  				if( itemID >= 0x181D && itemID < 0x1829 )
  				{
  					HouseTeleporter tp = new HouseTeleporter( itemID );
  
  					AddFixture( tp, mte );
  				}
  				else
  				{
  					BaseDoor door = null;
  
  					if( itemID >= 0x675 && itemID < 0x6F5 )
  					{
  						int type = (itemID - 0x675) / 16;
  						DoorFacing facing = (DoorFacing)(((itemID - 0x675) / 2) % 8);
  
  						switch( type )
  						{
  							case 0: door = new GenericHouseDoor( facing, 0x675, 0xEC, 0xF3 ); break;
  							case 1: door = new GenericHouseDoor( facing, 0x685, 0xEC, 0xF3 ); break;
  							case 2: door = new GenericHouseDoor( facing, 0x695, 0xEB, 0xF2 ); break;
  							case 3: door = new GenericHouseDoor( facing, 0x6A5, 0xEA, 0xF1 ); break;
  							case 4: door = new GenericHouseDoor( facing, 0x6B5, 0xEA, 0xF1 ); break;
  							case 5: door = new GenericHouseDoor( facing, 0x6C5, 0xEC, 0xF3 ); break;
  							case 6: door = new GenericHouseDoor( facing, 0x6D5, 0xEA, 0xF1 ); break;
  							case 7: door = new GenericHouseDoor( facing, 0x6E5, 0xEA, 0xF1 ); break;
  						}
  					}
  					else if( itemID >= 0x314 && itemID < 0x364 )
  					{
  						int type = (itemID - 0x314) / 16;
  						DoorFacing facing = (DoorFacing)(((itemID - 0x314) / 2) % 8);
  						door = new GenericHouseDoor( facing, 0x314 + ( type * 16 ), 0xED, 0xF4 );
  					}
  					else if( itemID >= 0x824 && itemID < 0x834 )
  					{
  						DoorFacing facing = (DoorFacing)(((itemID - 0x824) / 2) % 8);
  						door = new GenericHouseDoor( facing, 0x824, 0xEC, 0xF3 );
  					}
  					else if( itemID >= 0x839 && itemID < 0x849 )
  					{
  						DoorFacing facing = (DoorFacing)(((itemID - 0x839) / 2) % 8);
  						door = new GenericHouseDoor( facing, 0x839, 0xEB, 0xF2 );
  					}
  					else if( itemID >= 0x84C && itemID < 0x85C )
  					{
  						DoorFacing facing = (DoorFacing)(((itemID - 0x84C) / 2) % 8);
  						door = new GenericHouseDoor( facing, 0x84C, 0xEC, 0xF3 );
  					}
  					else if( itemID >= 0x866 && itemID < 0x876 )
  					{
  						DoorFacing facing = (DoorFacing)(((itemID - 0x866) / 2) % 8);
  						door = new GenericHouseDoor( facing, 0x866, 0xEB, 0xF2 );
  					}
  					else if( itemID >= 0xE8 && itemID < 0xF8 )
  					{
  						DoorFacing facing = (DoorFacing)(((itemID - 0xE8) / 2) % 8);
  						door = new GenericHouseDoor( facing, 0xE8, 0xED, 0xF4 );
  					}
  					else if( itemID >= 0x1FED && itemID < 0x1FFD )
  					{
  						DoorFacing facing = (DoorFacing)(((itemID - 0x1FED) / 2) % 8);
  						door = new GenericHouseDoor( facing, 0x1FED, 0xEC, 0xF3 );
  					}
  					else if( itemID >= 0x241F && itemID < 0x2421 )
  					{
  						//DoorFacing facing = (DoorFacing)(((itemID - 0x241F) / 2) % 8);
  						door = new GenericHouseDoor( DoorFacing.NorthCCW, 0x2415, -1, -1 );
  					}
  					else if( itemID >= 0x2423 && itemID < 0x2425 )
  					{
  						//DoorFacing facing = (DoorFacing)(((itemID - 0x241F) / 2) % 8);
  						//This one and the above one are 'special' cases, ie: OSI had the ItemID pattern discombobulated for these
  						door = new GenericHouseDoor( DoorFacing.WestCW, 0x2423, -1, -1 );
  					}
  					else if( itemID >= 0x2A05 && itemID < 0x2A1D )
  					{
  						DoorFacing facing = (DoorFacing)((((itemID - 0x2A05) / 2) % 4) + 8);
  
  						int sound = (itemID >= 0x2A0D && itemID < 0x2a15) ? 0x539 : -1;
  
  						door = new GenericHouseDoor( facing, 0x29F5 + (8 * ((itemID - 0x2A05) / 8)), sound, sound );
  					}
  					else if( itemID == 0x2D46 )
  					{
  						door = new GenericHouseDoor( DoorFacing.NorthCW, 0x2D46, 0xEA, 0xF1, false );
  					}
  					else if( itemID == 0x2D48 || itemID == 0x2FE2 )
  					{
  						door = new GenericHouseDoor( DoorFacing.SouthCCW, itemID, 0xEA, 0xF1, false );
  					}
  					else if( itemID >= 0x2D63 && itemID < 0x2D70 )
  					{
  						int mod = (itemID - 0x2D63)/2%2;
  						DoorFacing facing = ( ( mod == 0 ) ? DoorFacing.SouthCCW : DoorFacing.WestCCW );
  
  						int type = (itemID - 0x2D63) / 4;
  
  						door = new GenericHouseDoor( facing, 0x2D63 + 4*type + mod*2, 0xEA, 0xF1, false );
  					}
  					else if( itemID == 0x2FE4 || itemID == 0x31AE )
  					{
  						door = new GenericHouseDoor( DoorFacing.WestCCW, itemID, 0xEA, 0xF1, false );
  					}
  					else if( itemID >= 0x319C && itemID < 0x31AE )
  					{
  						//special case for 0x31aa <-> 0x31a8 (a9)
  
  						int mod = (itemID - 0x319C) / 2 % 2;
  
  						bool specialCase = (itemID == 0x31AA || itemID == 0x31A8);
  
  						DoorFacing facing;
  
  						if ( itemID == 0x31AA || itemID == 0x31A8 )
  							facing = ((mod == 0) ? DoorFacing.NorthCW : DoorFacing.EastCW);
  						else
  							facing = ((mod == 0) ? DoorFacing.EastCW : DoorFacing.NorthCW);
  
  						int type = (itemID - 0x319C) / 4;
  
  						door = new GenericHouseDoor( facing, 0x319C + 4 * type + mod * 2, 0xEA, 0xF1, false );
  					}
  					else if( itemID >= 0x367B && itemID < 0x369B )
  					{
  						int type = (itemID - 0x367B) / 16;
  						DoorFacing facing = (DoorFacing)(((itemID - 0x367B) / 2) % 8);
  
  						switch( type )
  						{
  							case 0: door = new GenericHouseDoor( facing, 0x367B, 0xED, 0xF4 ); break;	//crystal
  							case 1: door = new GenericHouseDoor( facing, 0x368B, 0xEC, 0x3E7 ); break;	//shadow
  						}
  					}
  					else if( itemID >= 0x409B && itemID < 0x40A3 )
  					{
  						door = new GenericHouseDoor( GetSADoorFacing( itemID - 0x409B ), itemID, 0xEA, 0xF1, false );
  					}
  					else if( itemID >= 0x410C && itemID < 0x4114 )
  					{
  						door = new GenericHouseDoor( GetSADoorFacing( itemID - 0x410C ), itemID, 0xEA, 0xF1, false );
  					}
  					else if( itemID >= 0x41C2 && itemID < 0x41CA )
  					{
  						door = new GenericHouseDoor( GetSADoorFacing( itemID - 0x41C2 ), itemID, 0xEA, 0xF1, false );
  					}
  					else if( itemID >= 0x41CF && itemID < 0x41D7 )
  					{
  						door = new GenericHouseDoor( GetSADoorFacing( itemID - 0x41CF ), itemID, 0xEA, 0xF1, false );
  					}
  					else if( itemID >= 0x436E && itemID < 0x437E )
  					{
  						/* These ones had to be different...
  						 * Offset		0	2	4	6	8	10	12	14
  						 * DoorFacing	2	3	2	3	6	7	6	7
  						 */
  						int offset = itemID - 0x436E;
  						DoorFacing facing = (DoorFacing)( ( offset / 2 + 2 * ( ( 1 + offset / 4 ) % 2 ) ) % 8 );
  						door = new GenericHouseDoor( facing, itemID, 0xEA, 0xF1, false );
  					}
  					else if( itemID >= 0x46DD && itemID < 0x46E5 )
  					{
  						door = new GenericHouseDoor( GetSADoorFacing( itemID - 0x46DD ), itemID, 0xEB, 0xF2, false );
  					}
  					else if( itemID >= 0x4D22 && itemID < 0x4D2A )
  					{
  						door = new GenericHouseDoor( GetSADoorFacing( itemID - 0x4D22 ), itemID, 0xEA, 0xF1, false );
  					}
  					else if( itemID >= 0x50C8 && itemID < 0x50D0 )
  					{
  						door = new GenericHouseDoor( GetSADoorFacing( itemID - 0x50C8 ), itemID, 0xEA, 0xF1, false );
  					}
  					else if( itemID >= 0x50D0 && itemID < 0x50D8 )
  					{
  						door = new GenericHouseDoor( GetSADoorFacing( itemID - 0x50D0 ), itemID, 0xEA, 0xF1, false );
  					}
  					else if( itemID >= 0x5142 && itemID < 0x514A )
  					{
  						door = new GenericHouseDoor( GetSADoorFacing( itemID - 0x5142 ), itemID, 0xF0, 0xEF, false );
  					}
  
  					if( door != null )
  					{
  						if( keyValue == 0 )
  							keyValue = CreateKeys( from );
  
  						door.Locked = true;
  						door.KeyValue = keyValue;
  
  						AddDoor( door, mte.m_OffsetX, mte.m_OffsetY, mte.m_OffsetZ );
  						m_Fixtures.Add( door );
  					}
  				}
  			}
  
  			for( int i = 0; i < m_Fixtures.Count; ++i )
  			{
  				Item fixture = m_Fixtures[i];
  
  				if( fixture is HouseTeleporter )
  				{
  					HouseTeleporter tp = (HouseTeleporter)fixture;
  
  					for( int j = 1; j <= m_Fixtures.Count; ++j )
  					{
  						HouseTeleporter check = m_Fixtures[(i + j) % m_Fixtures.Count] as HouseTeleporter;
  
  						if( check != null && check.ItemID == tp.ItemID )
  						{
  							tp.Target = check;
  							break;
  						}
  					}
  				}
  				else if( fixture is BaseHouseDoor )
  				{
  					BaseHouseDoor door = (BaseHouseDoor)fixture;
  
  					if( door.Link != null )
  						continue;
  
  					DoorFacing linkFacing;
  					int xOffset, yOffset;
  
  					switch( door.Facing )
  					{
  						default:
  						case DoorFacing.WestCW: linkFacing = DoorFacing.EastCCW; xOffset = 1; yOffset = 0; break;
  						case DoorFacing.EastCCW: linkFacing = DoorFacing.WestCW; xOffset = -1; yOffset = 0; break;
  						case DoorFacing.WestCCW: linkFacing = DoorFacing.EastCW; xOffset = 1; yOffset = 0; break;
  						case DoorFacing.EastCW: linkFacing = DoorFacing.WestCCW; xOffset = -1; yOffset = 0; break;
  						case DoorFacing.SouthCW: linkFacing = DoorFacing.NorthCCW; xOffset = 0; yOffset = -1; break;
  						case DoorFacing.NorthCCW: linkFacing = DoorFacing.SouthCW; xOffset = 0; yOffset = 1; break;
  						case DoorFacing.SouthCCW: linkFacing = DoorFacing.NorthCW; xOffset = 0; yOffset = -1; break;
  						case DoorFacing.NorthCW: linkFacing = DoorFacing.SouthCCW; xOffset = 0; yOffset = 1; break;
  						case DoorFacing.SouthSW: linkFacing = DoorFacing.SouthSE; xOffset = 1; yOffset = 0; break;
  						case DoorFacing.SouthSE: linkFacing = DoorFacing.SouthSW; xOffset = -1; yOffset = 0; break;
  						case DoorFacing.WestSN: linkFacing = DoorFacing.WestSS; xOffset = 0; yOffset = 1; break;
  						case DoorFacing.WestSS: linkFacing = DoorFacing.WestSN; xOffset = 0; yOffset = -1; break;
  					}
  
  					for( int j = i + 1; j < m_Fixtures.Count; ++j )
  					{
  						BaseHouseDoor check = m_Fixtures[j] as BaseHouseDoor;
  
  						if( check != null && check.Link == null && check.Facing == linkFacing && (check.X - door.X) == xOffset && (check.Y - door.Y) == yOffset && (check.Z == door.Z) )
  						{
  							check.Link = door;
  							door.Link = check;
  							break;
  						}
  					}
  				}
  			}
  		}
  
  		private static DoorFacing GetSADoorFacing( int offset )
  		{
  			/* Offset		0	2	4	6
  			 * DoorFacing	2	3	6	7
  			 */
  			return (DoorFacing)( ( offset / 2 + 2 * ( 1 + offset / 4 ) ) % 8 );
  		}
  
  		public void AddFixture( Item item, MultiTileEntry mte )
  		{
  			m_Fixtures.Add( item );
  			item.MoveToWorld( new Point3D( X + mte.m_OffsetX, Y + mte.m_OffsetY, Z + mte.m_OffsetZ ), Map );
  		}
  
  		public static void GetFoundationGraphics( FoundationType type, out int east, out int south, out int post, out int corner )
  		{
  			switch( type )
  			{
  				default:
  				case FoundationType.DarkWood: corner = 0x0014; east = 0x0015; south = 0x0016; post = 0x0017; break;
  				case FoundationType.LightWood: corner = 0x00BD; east = 0x00BE; south = 0x00BF; post = 0x00C0; break;
  				case FoundationType.Dungeon: corner = 0x02FD; east = 0x02FF; south = 0x02FE; post = 0x0300; break;
  				case FoundationType.Brick: corner = 0x0041; east = 0x0043; south = 0x0042; post = 0x0044; break;
  				case FoundationType.Stone: corner = 0x0065; east = 0x0064; south = 0x0063; post = 0x0066; break;
  
  				case FoundationType.ElvenGrey: corner = 0x2DF7; east = 0x2DF9; south = 0x2DFA; post = 0x2DF8; break;
  				case FoundationType.ElvenNatural: corner = 0x2DFB; east = 0x2DFD; south = 0x2DFE; post = 0x2DFC; break;
  
  				case FoundationType.Crystal: corner = 0x3672; east = 0x3671; south = 0x3670; post = 0x3673; break;
  				case FoundationType.Shadow: corner = 0x3676; east = 0x3675; south = 0x3674; post = 0x3677; break;
  			}
  		}
  
  		public static void ApplyFoundation( FoundationType type, MultiComponentList mcl )
  		{
  			int east, south, post, corner;
  
  			GetFoundationGraphics( type, out east, out south, out post, out corner );
  
  			int xCenter = mcl.Center.X;
  			int yCenter = mcl.Center.Y;
  
  			mcl.Add( post, 0 - xCenter, 0 - yCenter, 0 );
  			mcl.Add( corner, mcl.Width - 1 - xCenter, mcl.Height - 2 - yCenter, 0 );
  
  			for( int x = 1; x < mcl.Width; ++x )
  			{
  				mcl.Add( south, x - xCenter, 0 - yCenter, 0 );
  
  				if( x < mcl.Width-1 )
  					mcl.Add( south, x - xCenter, mcl.Height - 2 - yCenter, 0 );
  			}
  
  			for( int y = 1; y < mcl.Height - 1; ++y )
  			{
  				mcl.Add( east, 0 - xCenter, y - yCenter, 0 );
  
  				if( y < mcl.Height - 2 )
  					mcl.Add( east, mcl.Width - 1 - xCenter, y - yCenter, 0 );
  			}
  		}
  
  		public static void AddStairsTo( ref MultiComponentList mcl )
  		{
  			// copy the original..
  			mcl = new MultiComponentList( mcl );
  
  			mcl.Resize( mcl.Width, mcl.Height + 1 );
  
  			int xCenter = mcl.Center.X;
  			int yCenter = mcl.Center.Y;
  			int y = mcl.Height - 1;
  
  			for( int x = 0; x < mcl.Width; ++x )
  				mcl.Add( 0x63, x - xCenter, y - yCenter, 0 );
  		}
  
  		public MultiComponentList GetEmptyFoundation()
  		{
  			// Copy original foundation layout
  			MultiComponentList mcl = new MultiComponentList( MultiData.GetComponents( ItemID ) );
  
  			mcl.Resize( mcl.Width, mcl.Height + 1 );
  
  			int xCenter = mcl.Center.X;
  			int yCenter = mcl.Center.Y;
  			int y = mcl.Height - 1;
  
  			ApplyFoundation( m_Type, mcl );
  
  			for( int x = 1; x < mcl.Width; ++x )
  				mcl.Add( 0x751, x - xCenter, y - yCenter, 0 );
  
  			return mcl;
  		}
  
  		public override Rectangle2D[] Area
  		{
  			get
  			{
  				MultiComponentList mcl = Components;
  
  				return new Rectangle2D[] { new Rectangle2D( mcl.Min.X, mcl.Min.Y, mcl.Width, mcl.Height ) };
  			}
  		}
  
  		public override Point3D BaseBanLocation { get { return new Point3D( Components.Min.X, Components.Height - 1 - Components.Center.Y, 0 ); } }
  
  		public void CheckSignpost()
  		{
  			MultiComponentList mcl = this.Components;
  
  			int x = mcl.Min.X;
  			int y = mcl.Height - 2 - mcl.Center.Y;
  
  			if( CheckWall( mcl, x, y ) )
  			{
  				if( m_Signpost != null )
  					m_Signpost.Delete();
  
  				m_Signpost = null;
  			}
  			else if( m_Signpost == null )
  			{
  				m_Signpost = new Static( m_SignpostGraphic );
  				m_Signpost.MoveToWorld( new Point3D( X + x, Y + y, Z + 7 ), Map );
  			}
  			else
  			{
  				m_Signpost.ItemID = m_SignpostGraphic;
  				m_Signpost.MoveToWorld( new Point3D( X + x, Y + y, Z + 7 ), Map );
  			}
  		}
  
  		public bool CheckWall( MultiComponentList mcl, int x, int y )
  		{
  			x += mcl.Center.X;
  			y += mcl.Center.Y;
  
  			if( x >= 0 && x < mcl.Width && y >= 0 && y < mcl.Height )
  			{
  				StaticTile[] tiles = mcl.Tiles[x][y];
  
  				for( int i = 0; i < tiles.Length; ++i )
  				{
  					StaticTile tile = tiles[i];
  
  					if( tile.Z == 7 && tile.Height == 20 )
  						return true;
  				}
  			}
  
  			return false;
  		}
  
  		public HouseFoundation( Mobile owner, int multiID, int maxLockdowns, int maxSecures )
  			: base( multiID, owner, maxLockdowns, maxSecures )
  		{
  			m_SignpostGraphic = 9;
  
  			m_Fixtures = new List<Item>();
  
  			int x = Components.Min.X;
  			int y = Components.Height - 1 - Components.Center.Y;
  
  			m_SignHanger = new Static( 0xB98 );
  			m_SignHanger.MoveToWorld( new Point3D( X + x, Y + y, Z + 7 ), Map );
  
  			CheckSignpost();
  
  			SetSign( x, y, 7 );
  		}
  
  		public HouseFoundation( Serial serial )
  			: base( serial )
  		{
  		}
  
  		public void BeginCustomize( Mobile m )
  		{
  			if( !m.CheckAlive() ) {
  				return;
  			} else if ( SpellHelper.CheckCombat( m ) ) {
  				m.SendLocalizedMessage( 1005564, "", 0x22 ); // Wouldst thou flee during the heat of battle??
  				return;
  			}
  
  			RelocateEntities();
  
  			foreach( Item item in GetItems() )
  			{
  				item.Location = BanLocation;
  			}
  
  			foreach( Mobile mobile in GetMobiles() )
  			{
  				if( mobile != m )
  					mobile.Location = BanLocation;
  			}
  
  			DesignContext.Add( m, this );
  			m.Send( new BeginHouseCustomization( this ) );
  
  			NetState ns = m.NetState;
  			if( ns != null )
  				SendInfoTo( ns );
  
  			DesignState.SendDetailedInfoTo( ns );
  		}
  
  		public override void SendInfoTo( NetState state, bool sendOplPacket )
  		{
  			base.SendInfoTo( state, sendOplPacket );
  
  			DesignContext context = DesignContext.Find( state.Mobile );
  			DesignState stateToSend;
  
  			if( context != null && context.Foundation == this )
  				stateToSend = DesignState;
  			else
  				stateToSend = CurrentState;
  
  			stateToSend.SendGeneralInfoTo( state );
  		}
  
  		public override void Serialize( GenericWriter writer )
  		{
  			writer.Write( (int)5 ); // version
  
  			writer.Write( m_Signpost );
  			writer.Write( (int)m_SignpostGraphic );
  
  			writer.Write( (int)m_Type );
  
  			writer.Write( m_SignHanger );
  
  			writer.Write( (int)m_LastRevision );
  			writer.Write( m_Fixtures, true );
  
  			CurrentState.Serialize( writer );
  			DesignState.Serialize( writer );
  			BackupState.Serialize( writer );
  
  			base.Serialize( writer );
  		}
  
  		private int m_DefaultPrice;
  
  		public override int DefaultPrice { get { return m_DefaultPrice; } }
  
  		public override void Deserialize( GenericReader reader )
  		{
  			int version = reader.ReadInt();
  
  			switch( version )
  			{
  				case 5:
  				case 4:
  				{
  					m_Signpost = reader.ReadItem();
  					m_SignpostGraphic = reader.ReadInt();
  
  					goto case 3;
  				}
  				case 3:
  				{
  					m_Type = (FoundationType)reader.ReadInt();
  
  					goto case 2;
  				}
  				case 2:
  				{
  					m_SignHanger = reader.ReadItem();
  
  					goto case 1;
  				}
  				case 1:
  				{
  					if( version < 5 )
  						m_DefaultPrice = reader.ReadInt();
  
  					goto case 0;
  				}
  				case 0:
  				{
  					if( version < 3 )
  						m_Type = FoundationType.Stone;
  
  					if( version < 4 )
  						m_SignpostGraphic = 9;
  
  					m_LastRevision = reader.ReadInt();
  					m_Fixtures = reader.ReadStrongItemList();
  
  					m_Current = new DesignState( this, reader );
  					m_Design = new DesignState( this, reader );
  					m_Backup = new DesignState( this, reader );
  
  					break;
  				}
  			}
  
  			base.Deserialize( reader );
  		}
  
  		public bool IsHiddenToCustomizer( Item item )
  		{
  			return ( item == m_Signpost || item == m_SignHanger || item == Sign || IsFixture( item ) );
  		}
  
  		public static void Initialize()
  		{
  			PacketHandlers.RegisterExtended( 0x1E, true, new OnPacketReceive( QueryDesignDetails ) );
  
  			PacketHandlers.RegisterEncoded( 0x02, true, new OnEncodedPacketReceive( Designer_Backup ) );
  			PacketHandlers.RegisterEncoded( 0x03, true, new OnEncodedPacketReceive( Designer_Restore ) );
  			PacketHandlers.RegisterEncoded( 0x04, true, new OnEncodedPacketReceive( Designer_Commit ) );
  			PacketHandlers.RegisterEncoded( 0x05, true, new OnEncodedPacketReceive( Designer_Delete ) );
  			PacketHandlers.RegisterEncoded( 0x06, true, new OnEncodedPacketReceive( Designer_Build ) );
  			PacketHandlers.RegisterEncoded( 0x0C, true, new OnEncodedPacketReceive( Designer_Close ) );
  			PacketHandlers.RegisterEncoded( 0x0D, true, new OnEncodedPacketReceive( Designer_Stairs ) );
  			PacketHandlers.RegisterEncoded( 0x0E, true, new OnEncodedPacketReceive( Designer_Sync ) );
  			PacketHandlers.RegisterEncoded( 0x10, true, new OnEncodedPacketReceive( Designer_Clear ) );
  			PacketHandlers.RegisterEncoded( 0x12, true, new OnEncodedPacketReceive( Designer_Level ) );
  
  			PacketHandlers.RegisterEncoded( 0x13, true, new OnEncodedPacketReceive( Designer_Roof ) ); // Samurai Empire roof
  			PacketHandlers.RegisterEncoded( 0x14, true, new OnEncodedPacketReceive( Designer_RoofDelete ) ); // Samurai Empire roof
  
  			PacketHandlers.RegisterEncoded( 0x1A, true, new OnEncodedPacketReceive( Designer_Revert ) );
  
  			EventSink.Speech += new SpeechEventHandler( EventSink_Speech );
  		}
  
  		private static void EventSink_Speech( SpeechEventArgs e )
  		{
  			if( DesignContext.Find( e.Mobile ) != null )
  			{
  				e.Mobile.SendLocalizedMessage( 1061925 ); // You cannot speak while customizing your house.
  				e.Blocked = true;
  			}
  		}
  
  		public static void Designer_Sync( NetState state, IEntity e, EncodedReader pvSrc )
  		{
  			Mobile from = state.Mobile;
  			DesignContext context = DesignContext.Find( from );
  
  			if( context != null )
  			{
  				/* Client requested state synchronization
  				 *  - Resend full house state
  				 */
  
  				DesignState design = context.Foundation.DesignState;
  
  				// Resend full house state
  				design.SendDetailedInfoTo( state );
  			}
  		}
  
  		public static void Designer_Clear( NetState state, IEntity e, EncodedReader pvSrc )
  		{
  			Mobile from = state.Mobile;
  			DesignContext context = DesignContext.Find( from );
  
  			if( context != null )
  			{
  				/* Client chose to clear the design
  				 *  - Restore empty foundation
  				 *     - Construct new design state from empty foundation
  				 *     - Assign constructed state to foundation
  				 *  - Update revision
  				 *  - Update client with new state
  				 */
  
  				// Restore empty foundation : Construct new design state from empty foundation
  				DesignState newDesign = new DesignState( context.Foundation, context.Foundation.GetEmptyFoundation() );
  
  				// Restore empty foundation : Assign constructed state to foundation
  				context.Foundation.DesignState = newDesign;
  
  				// Update revision
  				newDesign.OnRevised();
  
  				// Update client with new state
  				context.Foundation.SendInfoTo( state );
  				newDesign.SendDetailedInfoTo( state );
  			}
  		}
  
  		public static void Designer_Restore( NetState state, IEntity e, EncodedReader pvSrc )
  		{
  			Mobile from = state.Mobile;
  			DesignContext context = DesignContext.Find( from );
  
  			if( context != null )
  			{
  				/* Client chose to restore design to the last backup state
  				 *  - Restore backup
  				 *     - Construct new design state from backup state
  				 *     - Assign constructed state to foundation
  				 *  - Update revision
  				 *  - Update client with new state
  				 */
  
  				// Restore backup : Construct new design state from backup state
  				DesignState backupDesign = new DesignState( context.Foundation.BackupState );
  
  				// Restore backup : Assign constructed state to foundation
  				context.Foundation.DesignState = backupDesign;
  
  				// Update revision;
  				backupDesign.OnRevised();
  
  				// Update client with new state
  				context.Foundation.SendInfoTo( state );
  				backupDesign.SendDetailedInfoTo( state );
  			}
  		}
  
  		public static void Designer_Backup( NetState state, IEntity e, EncodedReader pvSrc )
  		{
  			Mobile from = state.Mobile;
  			DesignContext context = DesignContext.Find( from );
  
  			if( context != null )
  			{
  				/* Client chose to backup design state
  				 *  - Construct a copy of the current design state
  				 *  - Assign constructed state to backup state field
  				 */
  
  				// Construct a copy of the current design state
  				DesignState copyState = new DesignState( context.Foundation.DesignState );
  
  				// Assign constructed state to backup state field
  				context.Foundation.BackupState = copyState;
  			}
  		}
  
  		public static void Designer_Revert( NetState state, IEntity e, EncodedReader pvSrc )
  		{
  			Mobile from = state.Mobile;
  			DesignContext context = DesignContext.Find( from );
  
  			if( context != null )
  			{
  				/* Client chose to revert design state to currently visible state
  				 *  - Revert design state
  				 *     - Construct a copy of the current visible state
  				 *     - Freeze fixtures in constructed state
  				 *     - Assign constructed state to foundation
  				 *     - If a signpost is needed, add it
  				 *  - Update revision
  				 *  - Update client with new state
  				 */
  
  				// Revert design state : Construct a copy of the current visible state
  				DesignState copyState = new DesignState( context.Foundation.CurrentState );
  
  				// Revert design state : Freeze fixtures in constructed state
  				copyState.FreezeFixtures();
  
  				// Revert design state : Assign constructed state to foundation
  				context.Foundation.DesignState = copyState;
  
  				// Revert design state : If a signpost is needed, add it
  				context.Foundation.CheckSignpost();
  
  				// Update revision
  				copyState.OnRevised();
  
  				// Update client with new state
  				context.Foundation.SendInfoTo( state );
  				copyState.SendDetailedInfoTo( state );
  			}
  		}
  
  		public void EndConfirmCommit( Mobile from )
  		{
  			int oldPrice = Price;
  			int newPrice = oldPrice + CustomizationCost + ((DesignState.Components.List.Length - ( CurrentState.Components.List.Length + CurrentState.Fixtures.Length )) * 500);
  			int cost = newPrice - oldPrice;
  
  			if ( !this.Deleted ) { // Temporary Fix. We should be booting a client out of customization mode in the delete handler.
  				if ( from.AccessLevel >= AccessLevel.GameMaster && cost != 0 )
  				{
  					from.SendMessage( "{0} gold would have been {1} your bank if you were not a GM.", cost.ToString(), ((cost > 0 )? "withdrawn from" : "deposited into" ) );
  				}
  				else
  				{
  					if ( cost > 0 )
  					{
  						if ( Banker.Withdraw( from, cost ) )
  						{
  							from.SendLocalizedMessage( 1060398, cost.ToString() ); // ~1_AMOUNT~ gold has been withdrawn from your bank box.
  						}
  						else
  						{
  							from.SendLocalizedMessage( 1061903 ); // You cannot commit this house design, because you do not have the necessary funds in your bank box to pay for the upgrade.  Please back up your design, obtain the required funds, and commit your design again.
  							return;
  						}
  					}
  					else if ( cost < 0 )
  					{
  						if ( Banker.Deposit( from, -cost ) )
  							from.SendLocalizedMessage( 1060397, ( -cost ).ToString() ); // ~1_AMOUNT~ gold has been deposited into your bank box.
  						else
  							return;
  					}
  				}
  			}
  
  			/* Client chose to commit current design state
  				 *  - Commit design state
  				 *     - Construct a copy of the current design state
  				 *     - Clear visible fixtures
  				 *     - Melt fixtures from constructed state
  				 *     - Add melted fixtures from constructed state
  				 *     - Assign constructed state to foundation
  				 *  - Update house price
  				 *  - Remove design context
  				 *  - Notify the client that customization has ended
  				 *  - Notify the core that the foundation has changed and should be resent to all clients
  				 *  - If a signpost is needed, add it
  				 *  - Eject all from house
  				 *  - Restore relocated entities
  				 */
  
  			// Commit design state : Construct a copy of the current design state
  			DesignState copyState = new DesignState( DesignState );
  
  			// Commit design state : Clear visible fixtures
  			ClearFixtures( from );
  
  			// Commit design state : Melt fixtures from constructed state
  			copyState.MeltFixtures();
  
  			// Commit design state : Add melted fixtures from constructed state
  			AddFixtures( from, copyState.Fixtures );
  
  			// Commit design state : Assign constructed state to foundation
  			CurrentState = copyState;
  
  			// Update house price
  			Price = newPrice - CustomizationCost;
  
  			// Remove design context
  			DesignContext.Remove( from );
  
  			// Notify the client that customization has ended
  			from.Send( new EndHouseCustomization( this ) );
  
  			// Notify the core that the foundation has changed and should be resent to all clients
  			Delta( ItemDelta.Update );
  			ProcessDelta();
  			CurrentState.SendDetailedInfoTo( from.NetState );
  
  			// If a signpost is needed, add it
  			CheckSignpost();
  
  			// Eject all from house
  			from.RevealingAction();
  
  			foreach( Item item in GetItems() )
  				item.Location = BanLocation;
  
  			foreach( Mobile mobile in GetMobiles() )
  				mobile.Location = BanLocation;
  
  			// Restore relocated entities
  			RestoreRelocatedEntities();
  		}
  
  		public static void Designer_Commit( NetState state, IEntity e, EncodedReader pvSrc )
  		{
  			Mobile from = state.Mobile;
  			DesignContext context = DesignContext.Find( from );
  
  			if( context != null )
  			{
  				int oldPrice = context.Foundation.Price;
  				int newPrice = oldPrice + context.Foundation.CustomizationCost + ((context.Foundation.DesignState.Components.List.Length - ( context.Foundation.CurrentState.Components.List.Length + context.Foundation.Fixtures.Count) ) * 500);
  				int bankBalance = Banker.GetBalance( from );
  
  				from.SendGump( new ConfirmCommitGump( from, context.Foundation, bankBalance, oldPrice, newPrice ) );
  			}
  		}
  
  		public int MaxLevels
  		{
  			get
  			{
  				MultiComponentList mcl = this.Components;
  
  				if( mcl.Width >= 14 || mcl.Height >= 14 )
  					return 4;
  				else
  					return 3;
  			}
  		}
  
  		public static int GetLevelZ( int level, HouseFoundation house )
  		{
  			if( level < 1 || level > house.MaxLevels )
  				level = 1;
  
  			return (level-1)*20 + 7;
  
  			/*
  			switch( level )
  			{
  				default:
  				case 1: return 07;
  				case 2: return 27;
  				case 3: return 47;
  				case 4: return 67;
  			}
  			 * */
  		}
  
  		public static int GetZLevel( int z, HouseFoundation house )
  		{
  			int level = (z - 7)/20 +1;
  
  			if( level < 1 || level > house.MaxLevels )
  				level = 1;
  
  			return level;
  		}
  
  		private static ComponentVerification m_Verification;
  
  		public static ComponentVerification Verification
  		{
  			get
  			{
  				if ( m_Verification == null )
  					m_Verification = new ComponentVerification();
  
  				return m_Verification;
  			}
  		}
  
  		public static bool ValidPiece( int itemID )
  		{
  			return ValidPiece( itemID, false );
  		}
  
  		public static bool ValidPiece( int itemID, bool roof )
  		{
  			itemID &= TileData.MaxItemValue;
  
  			if ( !roof && ( TileData.ItemTable[itemID].Flags & TileFlag.Roof ) != 0 )
  				return false;
  			else if ( roof && ( TileData.ItemTable[itemID].Flags & TileFlag.Roof ) == 0 )
  				return false;
  
  			return Verification.IsItemValid( itemID );
  		}
  
  		public static readonly bool AllowStairSectioning = true;
  
  		/* Stair block IDs
  		 * (sorted ascending)
  		 */
  		private static int[] m_BlockIDs = new int[]
  			{
  				0x3EE, 0x709, 0x71E, 0x721,
  				0x738, 0x750, 0x76C, 0x788,
  				0x7A3, 0x7BA, 0x35D2, 0x3609,
  				0x4317, 0x4318, 0x4B07, 0x7807
  			};
  
  		/* Stair sequence IDs
  		 * (sorted ascending)
  		 * Use this for stairs in the proper N,W,S,E sequence
  		 */
  		private static int[] m_StairSeqs = new int[]
  			{
  				0x3EF, 0x70A, 0x722, 0x739,
  				0x751, 0x76D, 0x789, 0x7A4
  			};
  
  		/* Other stair IDs
  		 * Listed in order: north, west, south, east
  		 * Use this for stairs not in the proper sequence
  		 */
  		private static int[] m_StairIDs = new int[]
  			{
  				0x71F, 0x736, 0x737, 0x749,
  				0x35D4, 0x35D3, 0x35D6, 0x35D5,
  				0x360B, 0x360A, 0x360D, 0x360C,
  				0x4360, 0x435E, 0x435F, 0x4361,
  				0x435C, 0x435A, 0x435B, 0x435D,
  				0x4364, 0x4362, 0x4363, 0x4365,
  				0x4B05, 0x4B04, 0x4B34, 0x4B33,
  				0x7809, 0x7808, 0x780A, 0x780B,
  				0x7BB, 0x7BC
  			};
  
  		public static bool IsStairBlock( int id )
  		{
  			int delta = -1;
  
  			for( int i = 0; delta < 0 && i < m_BlockIDs.Length; ++i )
  				delta = (m_BlockIDs[i] - id);
  
  			return (delta == 0);
  		}
  
  		public static bool IsStair( int id, ref int dir )
  		{
  			//dir n=0 w=1 s=2 e=3
  			int delta = -4;
  
  			for( int i = 0; delta < -3 && i < m_StairSeqs.Length; ++i )
  				delta = (m_StairSeqs[i] - id);
  
  			if( delta >= -3 && delta <= 0 )
  			{
  				dir = -delta;
  				return true;
  			}
  
  			for( int i = 0; i < m_StairIDs.Length; ++i )
  			{
  				if( m_StairIDs[i] == id )
  				{
  					dir = i % 4;
  					return true;
  				}
  			}
  
  			return false;
  		}
  
  		public static bool DeleteStairs( MultiComponentList mcl, int id, int x, int y, int z )
  		{
  			int ax = x + mcl.Center.X;
  			int ay = y + mcl.Center.Y;
  
  			if( ax < 0 || ay < 0 || ax >= mcl.Width || ay >= (mcl.Height - 1) || z < 7 || ((z - 7) % 5) != 0 )
  				return false;
  
  			if( IsStairBlock( id ) )
  			{
  				StaticTile[] tiles = mcl.Tiles[ax][ay];
  
  				for( int i = 0; i < tiles.Length; ++i )
  				{
  					StaticTile tile = tiles[i];
  
  					if( tile.Z == (z + 5) )
  					{
  						id = tile.ID;
  						z = tile.Z;
  
  						if( !IsStairBlock( id ) )
  							break;
  					}
  				}
  			}
  
  			int dir = 0;
  
  			if( !IsStair( id, ref dir ) )
  				return false;
  
  			if ( AllowStairSectioning )
  				return true; // skip deletion
  
  			int height = ((z - 7) % 20) / 5;
  
  			int xStart, yStart;
  			int xInc, yInc;
  
  			switch( dir )
  			{
  				default:
  				case 0: // North
  				{
  					xStart = x;
  					yStart = y + height;
  					xInc = 0;
  					yInc = -1;
  					break;
  				}
  				case 1: // West
  				{
  					xStart = x + height;
  					yStart = y;
  					xInc = -1;
  					yInc = 0;
  					break;
  				}
  				case 2: // South
  				{
  					xStart = x;
  					yStart = y - height;
  					xInc = 0;
  					yInc = 1;
  					break;
  				}
  				case 3: // East
  				{
  					xStart = x - height;
  					yStart = y;
  					xInc = 1;
  					yInc = 0;
  					break;
  				}
  			}
  
  			int zStart = z - (height * 5);
  
  			for( int i = 0; i < 4; ++i )
  			{
  				x = xStart + (i * xInc);
  				y = yStart + (i * yInc);
  
  				for( int j = 0; j <= i; ++j )
  					mcl.RemoveXYZH( x, y, zStart + (j * 5), 5 );
  
  				ax = x + mcl.Center.X;
  				ay = y + mcl.Center.Y;
  
  				if( ax >= 1 && ax < mcl.Width && ay >= 1 && ay < mcl.Height - 1 )
  				{
  					StaticTile[] tiles = mcl.Tiles[ax][ay];
  
  					bool hasBaseFloor = false;
  
  					for( int j = 0; !hasBaseFloor && j < tiles.Length; ++j )
  						hasBaseFloor = (tiles[j].Z == 7 && tiles[j].ID != 1);
  
  					if( !hasBaseFloor )
  						mcl.Add( 0x31F4, x, y, 7 );
  				}
  			}
  
  			return true;
  		}
  
  		public static void Designer_Delete( NetState state, IEntity e, EncodedReader pvSrc )
  		{
  			Mobile from = state.Mobile;
  			DesignContext context = DesignContext.Find( from );
  
  			if( context != null )
  			{
  				/* Client chose to delete a component
  				 *  - Read data detailing which component to delete
  				 *  - Verify component is deletable
  				 *  - Remove the component
  				 *  - If needed, replace removed component with a dirt tile
  				 *  - Update revision
  				 */
  
  				// Read data detailing which component to delete
  				int itemID = pvSrc.ReadInt32();
  				int x = pvSrc.ReadInt32();
  				int y = pvSrc.ReadInt32();
  				int z = pvSrc.ReadInt32();
  
  				// Verify component is deletable
  				DesignState design = context.Foundation.DesignState;
  				MultiComponentList mcl = design.Components;
  
  
gt: 				int ax = x + mcl.Center.X;