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!

NewWorldItem

Peoharen

Sorceror
NewWorldItem Support

I edited WorldItem to look like the following
Code:
		public WorldItem( Item item ) : base( 0xF3, 24 ) // SA only
		{
			int unknown = 0x01;
			int datatype = 0x00; // force use tile data for testing
			uint serial = (uint)item.Serial.Value;
			int itemID = item.ItemID;
			int direction = (int)item.Direction;

			// Amount is sent twice? o.O
			int amountone = item.Amount;
			int amounttwo = item.Amount;

			Point3D loc = item.Location;
			int x = loc.m_X;
			int y = loc.m_Y;
			int z = loc.m_Z;

			int light = (int)item.Light;
			int hue = item.Hue;
			int flags = item.GetPacketFlags();

			if ( amountone != 0 )
			{
				serial |= 0x80000000;
			}
			else
			{
				serial &= 0x7FFFFFFF;
			}

			x &= 0x7FFF;

			if ( direction != 0 )
			{
				x |= 0x8000;
			}

			y &= 0x3FFF;

			if ( hue != 0 )
			{
				y |= 0x8000;
			}

			if ( flags != 0 )
			{
				y |= 0x4000;
			}

			m_Stream.Write( (short) unknown );
			m_Stream.Write( (byte) datatype );
			m_Stream.Write( (uint) serial );
			m_Stream.Write( (short) (itemID & 0x7FFF) );
			m_Stream.Write( (byte) direction );
			m_Stream.Write( (short) amountone );
			m_Stream.Write( (short) amounttwo );
			m_Stream.Write( (short) x );
			m_Stream.Write( (short) y );
			m_Stream.Write( (sbyte) loc.m_Z );
			m_Stream.Write( (byte) light );
			m_Stream.Write( (ushort) hue );
			m_Stream.Write( (byte) flags );
		}
using this as my guide.

However, the outcome is less than desirable.
1. All movable items cannot be seen.
2. All nonmovable items that produce light cannot be seen.
3. All nonmovable items, while they can be seen cannot be targeted (serial part is off?)
4. Even forcing the light type to send 0x0, altering the light type of an item can make it changes it's itemid to the client.
5. Finally, the new items are visible, assuming they are not a light source and marked unmovable...

I've played the lottery with thing thing enough to finally call it quits. Removing the 0x01 part creates crashes, as does sending amount once or not doing that fancy X/Y editing that I don't understand.
So any help and/or insights would be nice.
 

Peoharen

Sorceror
Mark helped me out in chat.

The amount and direction data needed to be reversed, and as an "off the top of his head" thing the only thing I had to edit was itemID & 0x3FFF (on his) to itemID & 0x7FFF

Code:
		public WorldItem( Item item ) : base( 0xF3, 24 )
		{
			m_Stream.Write( (short) 0x1 );
			int itemID = item.ItemID;

			//if ( itemID >= 0x4000 )
				//m_Stream.Write( (byte) 0x02 );
			//else
				m_Stream.Write( (byte) 0x00 );

			m_Stream.Write( (int) item.Serial );
			m_Stream.Write( (short) ( itemID & 0x7FFF ) );

			int amount = item.Amount;

			m_Stream.Write( (short) amount );
			m_Stream.Write( (short) amount );
			m_Stream.Write( (byte) item.Direction );

			Point3D loc = item.Location;
			int x = loc.m_X & 0x7FFF;
			int y = loc.m_Y & 0x3FFF;

			m_Stream.Write( (short) x );
			m_Stream.Write( (short) y );
			m_Stream.Write( (sbyte) loc.m_Z );
			m_Stream.Write( (byte) item.Light );
			m_Stream.Write( (short) item.Hue );
			m_Stream.Write( (byte) item.GetPacketFlags() );
		}
So with that edit 7.0+ clients can see and use the new items. However, Multis as you can guess are no longer viewable, tiledata is pulling the wrong names, the item in you're pack packet needs updated as well.

Supporting multi items is easy, and I'll probably have those edits up by tomarrow. Tiledata, dunno. backpack. Oooh I hope it's not as bad as this one...
 

Garret

Sorceror
Peoharen;815451 said:
using this as my only guide
New World Item Packet.
24 bytes
byte ID (F3)
word 0x01
byte Data Type (0x00 = use TileData, 0x02 = use MultiData)
dword Serial
word Item ID
byte Item Direction (0x00 = North, 0x01 = Right, 0x02 = East, 0x03 = Down, 0x04 = South, 0x05 = Left, 0x06 = West, 0x07 = Up, 0x80 = Running)
word Amount
word Amount
word X
word Y
sbyte Z
byte Light Type
word Hue
byte Flag (0x20 = Show Properties; 0x80 = Hidden)

Perhaps you accidentally forgotten a link to the source packet guide where you got this information from.

http://ruosi.org/packetguide/index.xml#serverF3

As I see it is one to one until the last character.

And note the top of the page please =)
 

Peoharen

Sorceror
Garret;815548 said:
Perhaps you accidentally forgotten a link to the source packet guide where you got this information from.

http://ruosi.org/packetguide/index.xml#serverF3

As I see it is one to one until the last character.

And note the top of the page please =)

No, I didn't forget. I simply choose to copy/pasta RUOSI's format that I found on google after a bit of searching for the flight packet over Semerkhet's format (which btw, is the first hit for uo new world packet). Capitalization of text isn't worth crediting, and if it was, I removed spaces and replaced them with tabs, So yeah.

Actually, ignore the above. I only paid attention to RUOSI's information. Thanks for correcting my memory on things I've read since you know all about that. RUOSI's information was so helpful I had to post at RunUO for support and find out my information is faulty and doesn't work at all. It's ok, a deva at RunUO helped me fix it, thanks again Mark. And now RunUO has a public copy of the packet for everyone to use.

However, I did find the ToogleFlying packet information there, but that's off topic now isn't it?
 

Peoharen

Sorceror
Here is what I got so far.

Server\Network\Packets.cs
Find the WorldItem class, then above or below it add
Code:
	#region SA
	public sealed class NewWorldItem : Packet
	{
		public NewWorldItem( Item item ) : base( 0xF3, 24 )
		{
			m_Stream.Write( (short) 0x1 );
			int itemID = item.ItemID;

			if ( item is BaseMulti )
			{
				m_Stream.Write( (byte) 0x02 );
				itemID &= 0x3FFF;
			}
			else
				m_Stream.Write( (byte) 0x00 );

			m_Stream.Write( (int) item.Serial );
			m_Stream.Write( (short) ( itemID & 0x7FFF ) );

			int amount = item.Amount;

			m_Stream.Write( (short) amount );
			m_Stream.Write( (short) amount );
			m_Stream.Write( (byte) item.Direction );

			Point3D loc = item.Location;
			int x = loc.m_X & 0x7FFF;
			int y = loc.m_Y & 0x3FFF;

			m_Stream.Write( (short) x );
			m_Stream.Write( (short) y );
			m_Stream.Write( (sbyte) loc.m_Z );
			m_Stream.Write( (byte) item.Light );
			m_Stream.Write( (short) item.Hue );
			m_Stream.Write( (byte) item.GetPacketFlags() );
		}
	}
	#endregion

Next, find the ContainerContentUpdate class.
Code:
			#region SA
			if ( cid > 0x7FFF ) // Was 0x3FFF )
				cid = 0x9D7;
			#endregion

Next, find the ContainerContentUpdate6017 class.
Code:
			#region SA
			if ( cid > 0x7FFF ) // Was 0x3FFF
				cid = 0x9D7;
			#endregion

***

Server\Item.cs
Code:
		public virtual double DefaultWeight
		{
			get
			{
				#region SA
				if ( m_ItemID < 0 || m_ItemID >= 0x8000 ) // Was 0x4000
					return 0;
				#endregion

				int weight = TileData.ItemTable[m_ItemID].Weight;

				if ( weight == 255 || weight == 0 )
					weight = 1;

				return weight;
			}
		}
And
Code:
		#region SA
		public virtual int LabelNumber
		{
			get
			{
				if ( m_ItemID <= 0x4000 )
					return 1020000 + (m_ItemID & 0x3FFF);
				else
					return 1095256 + ( (m_ItemID - 0x4000) & 0x3FFF );
			}
		}
		#endregion
And finally
Code:
		public Packet WorldPacket
		{
			get
			{
				// This needs to be invalidated when any of the following changes:
				//  - ItemID
				//  - Amount
				//  - Location
				//  - Hue
				//  - Packet Flags
				//  - Direction

				if ( m_WorldPacket == null )
				{
					#region SA
					m_WorldPacket = new NewWorldItem( this );
					#endregion
					m_WorldPacket.SetStatic();
				}

				return m_WorldPacket;
			}
		}
***

Serve\TileData.cs
Find the TileData() method.
Code:
					#region SA
					m_ItemData = new ItemData[0x8000]; // Was 0x4000

					for ( int i = 0; i < 0x8000; ++i ) // Was 0x4000
					{
					#endregion

***

Server\TileMatrix.cs
ReadStaticBlock method.
Code:
						while ( pCur < pEnd )
						{
							#region SA
							lists[pCur->m_X & 0x7][pCur->m_Y & 0x7].Add( (short)((pCur->m_ID & 0x7FFF) + 0x4000), pCur->m_Z );
							#endregion
							pCur = pCur + 1;
						}
and
Code:
		public int Height
		{
			get
			{
				#region SA
				if ( m_ID < 0x4000 )
				{
				#endregion
					return 0;
				}
				else
				{
					#region SA
					return TileData.ItemTable[m_ID & 0x7FFF].Height;
					#endregion
				}
			}
		}

***

Server\TileMatrixPatch.cs
PatchStatics method
Code:
								while ( pCur < pEnd )
								{
									#region SA
									lists[pCur->m_X & 0x7][pCur->m_Y & 0x7].Add( (short)((pCur->m_ID & 0x7FFF) + 0x4000), pCur->m_Z );
									#endregion
									pCur = pCur + 1;
								}

***

Server\Map.cs
Theres a few in this one, honestly it would be easier to say to search it and replace 0x4000 with 0x8000 in every case BUT this one
Code:
				foreach ( Item item in sector.Items )
					if ( item.AtWorldPoint( p.m_X, p.m_Y ) )
					{
						#region SA
						list.Add( new Tile( (short) ( ( item.ItemID & 0x7FFF ) + 0x4000 ), (sbyte) item.Z ) );
						#endregion
					}
Leave that one 0x4000.

Search it for 0x3FFF and make sure it's something like
TileData.ItemTable[___ & 0x3FFF];
before changing it. If you accidentally bump the 0x3FFFs that deal with the land tile you'll have problems.

***

Scripts\Engines\Pathing\Movement.cs
IsOk method
Code:
				#region SA
				ItemData itemData = TileData.ItemTable[(check.ID-0x4000) & 0x7FFF];
				#endregion
then below that in the for loop
Code:
				#region SA
				int itemID = (item.ItemID-0x4000) & 0x7FFF;
				#endregion
Check method, inside the tiles region
Code:
				#region SA
				ItemData itemData = TileData.ItemTable[tile.ID / TileData.ItemTable.Length];
				#endregion
CheckMovement method
Code:
						#region SA
						if ( sector == sectorStart && item.AtWorldPoint( xStart, yStart ) && item.ItemID < 0x8000 )
							itemsStart.Add( item );
						else if ( sector == sectorForward && item.AtWorldPoint( xForward, yForward ) && item.ItemID < 0x8000 )
							itemsForward.Add( item );
						else if ( sector == sectorLeft && item.AtWorldPoint( xLeft, yLeft ) && item.ItemID < 0x8000 )
							itemsLeft.Add( item );
						else if ( sector == sectorRight && item.AtWorldPoint( xRight, yRight ) && item.ItemID < 0x8000 )
							itemsRight.Add( item );
						#endregion
and below that
Code:
						#region SA
						if ( item.AtWorldPoint( xStart, yStart ) && item.ItemID < 0x8000 )
							itemsStart.Add( item );
						else if ( item.AtWorldPoint( xForward, yForward ) && item.ItemID < 0x8000 )
							itemsForward.Add( item );
						#endregion
and below that one
Code:
						#region SA
						if ( item.AtWorldPoint( xForward, yForward ) && item.ItemID < 0x8000 )
							itemsForward.Add( item );
						#endregion
one more in this method
Code:
						#region SA
						if ( item.AtWorldPoint( xStart, yStart ) && item.ItemID < 0x8000 )
							itemsStart.Add( item );
						#endregion
Next method to edit is GetStartZ.
Code:
				#region SA
				ItemData id = TileData.ItemTable[(tile.ID-0x4000) & 0x7FFF];
				#endregion

***

And bam, you're done. With that you will be able to see and use SA items. However, you cannot fly on gargoyle flight paths, and it's not so much as a server sided problem as the flight paths are not marked as a surface so it looks like you can fly off some towers, but when you hit a tree because the server things you've been running on the ground the whole time you'll rubberband back to the ground.

Also, I've have yet to cover housing so theres no support for the new housing tiles posted in here (yet).

Thanks goes to Mark yet again for helping me with viewing Multis as I didn't know about needing &= 3FFF; at all.
 

kegmeister

Sorceror
Why 0x5749 instead of 0x7FFF?

You may want to change the <= in LabelNumber to a <.

I think the number of changes necessary for minimal support of the higher range of item ids is a bit more extensive than you'd want to describe in a forum post. It's more than just core changes.
 

Garret

Sorceror
Peoharen;815564 said:
No, I didn't forget. I simply choose to copy/pasta RUOSI's format that I found on google after a bit of searching for the flight packet over Semerkhet's format (which btw, is the first hit for uo new world packet). Capitalization of text isn't worth crediting, and if it was, I removed spaces and replaced them with tabs, So yeah.

Actually, ignore the above. I only paid attention to RUOSI's information. Thanks for correcting my memory on things I've read since you know all about that. RUOSI's information was so helpful I had to post at RunUO for support and find out my information is faulty and doesn't work at all. It's ok, a deva at RunUO helped me fix it, thanks again Mark. And now RunUO has a public copy of the packet for everyone to use.

However, I did find the ToogleFlying packet information there, but that's off topic now isn't it?

That's why text on top of page was added, people like you simply can't add a link to the source,
source who spent time to find, decode, describe and published it.
 

Peoharen

Sorceror
kegmeister;815595 said:
Why 0x5749 instead of 0x7FFF?

You may want to change the <= in LabelNumber to a <.

I think the number of changes necessary for minimal support of the higher range of item ids is a bit more extensive than you'd want to describe in a forum post. It's more than just core changes.
Lack of sleep mostly. Between this (and other stuff) eating up my time and not having a day off I'm lucky to have a compiler checking my mistakes.

And no, there isn't much more to change than that.Scripts\ Movement is the last major part, which also requires edits to the server's Map and I think TileMatrix (could just be comments sitting in that file though). But what I've got done so far allows you can see and use walls, deco, equip ables, all floor tiles provided they are layered over a supported floor. Also, while the new housing tiles can be built, their not using the proper IDs (I believe MultiData needs edited, plus component listing in the data section), but I'll move to that once I get Movement figured out since it is sort of related and currently irritating me.

Rest of so called support is stuff like coding the weapons, making addons out of statics, updating the defs to make said items, defining new loot packs, etc. You know, the stuff you you should expect to supporting an entire expansion, I've just got tunnel vision on providing a way to get support on SA up to where everyone started out when AOS/SE was first released.

@Garret, we don't seem to be talking about the same subject at all. You are convinced some person that pasted the results of SpyUO on a site some how massively contributed to me getting this far and took them a lot of effort to do so. You know what? RUOSI is actually the problem I had with the packet. After I gave up thinking the post here wasn't sufficient I found RUOSI's packet section, where they supposedly have full SA support but choose to list the incorrect packet information. Given that I had two things saying the same thing I moved back to believing I made a mistake and it took someone here to figure it out. My first post is fixed, problem solved. Take your drama else where, I'm providing the best support I can offer to everyone on my own time and do not care for the rants and whining of someone unwilling to be helpful to me at the very least.
 
Garret;815548 said:
Perhaps you accidentally forgotten a link to the source packet guide where you got this information from.

http://ruosi.org/packetguide/index.xml#serverF3

As I see it is one to one until the last character.

And note the top of the page please =)

or maybe it was this one?

PenUltima Online ( POL ) <-- link

See, your packet list is not only the one across all of the internet.

I'm curious to know how can you say where something got from yours, where there is a big caption "Not for RunUO users". The source is always OSI in the end. 2+2 is always 4, no matter who did it first or if you discover it from yourself.
 

Peoharen

Sorceror
I lol'ed at you too

According to a PM I got, Wyatt just shot a load at making fun of how well I've gotten things done here and saying I have no clue what I'm doing. Thank you captain obvious, in case you missed the multiple posts asking for help and references of it. I'm struggling and pulling this out of my ass as I go. And I'm also f'ing proud of it.

See am I forcing my self to learn something new even if most of it is nothing more than a 0x3FFF/0x4000 to 0x7FFF/0x8000 swap, which is always good. I'm providing free help to others who maybe having problems, it's a rewarding thing assholes lack the appreciation for. And, as I just learned today, I am technically waving the biggest middle finger possible at RUOSI right now. I'm not perfect and like to be an ass on forums like everyone else, so insulting someone while holding the higher moral ground is entertaining.

Wyatt seems to get off on hording code only a few people have. It's so bad he goes out of his way to read someone else's forums and post about how awesome he is to his sucks ups, all of which are of course acting to be his lackey so they can buy his hand outs. Just last week or so he shot a load over after posting some bragging pictures, most of which can be done with a SVN 380ish server with only script sided edits. A few hours of effort and questions asked I (who's only project outside of RunUO/Ultima.dll was a text reading number swapper) can take the same pictures that he thought were so brag worthy. So his response is to point out that, under the fallacy that I am RunUO and represent all303,824,640 people living in the USA, American's have a problem with their brain and we're all idiots for not linking commonly found information to a site that still won't credit their work is based off illegally decompiling someone else's project and then claim his superiority over Americans becuase he uses a fancy ^. After this post, I no longer deem him even worthy of the button wear on my key board my fingers do to the finished plastic coating it takes to comment about him.

Anyway, I got movement working. Mostly. I screwed up reading the map for some stupid reason so I have to track down what I messed up, I probably bumped the land tiles around in my haste. Would anyone like to volunteer listing the new housing tiles? It's a simple job, log into a shard and start to edit a custom house, compare the items seen to UO Fiddler's list to figure out the ID then note it. Simple, but very time consuming. Many hands make light work.

@osd_daedalus, Yeah, I was in chat and was told about POLs listing the other day. I didn't find a link in plain sight at sphereserver.com, though their cool enough to host a wiki.
 

Thilgon

Sorceror
Dunno what the numbers exactly are for (still have to learn this part about the client-side work), but this are the new housing things taken from the client itself...

from doors.txt
Code:
 26	0	0	16539	16541	16543	16545	0	0	65536	Gargish Carved Green Door
27	0	0	16652	16654	16656	16658	0	0	65536	Gargish Brown Door
28	0	0	16834	16836	16838	16840	0	0	65536	Sun Door
29	0	0	16847	16849	16851	16853	0	0	65536	Gargish Grey Door
30	17262	17264	17266	17268	17270	17272	17274	17276	65536	Gargish Set Door
31	0	0	18141	18143	18145	18147	0	0	65536	Ruined Door
32	0	0	19746	19748	19750	19752	0	0	65536	Gargish Blue Door
33	20680	20682	20688	20690	20684	20686	20692	20694	65536	Gargish Red Doors
34	0	0	20802	20804	20806	20808	0	0	65536	Gargish Prison Door

from stairs.txt
Code:
12	17175	17248	17249	17247	17246	17261	17260	17257	17256	7648	7649	7650	7651	65536	Gargish 1
13	17175	17244	17253	17243	17250	0	0	0	0	7652	7653	7654	7655	65536	Gargish 2
14	17176	17252	17245	17251	17242	0	0	0	0	7656	7657	7658	7659	65536	Gargish 3


from floors.txt
Code:
42	17196	17197	17192	17193	17194	17195	17206	17207	17208	17209	17210	17211	0	0	0	0	65536	Gargish Floor 1
43	17198	17199	17200	17201	17202	17203	17204	17205	0	0	0	0	0	0	0	0	65536	Gargish Floor 2
44	19946	19947	19948	19949	19950	19951	19952	19953	19954	19942	19943	19945	19755	19756	19757	19758	65536	Gargish Striped
45	20345	20340	20341	20342	20343	20336	20337	20338	20339	20332	20333	20334	20335	20329	20330	20331	65536	Gargish Blue Stripe
46	20706	20707	20708	20702	20703	20704	20705	20711	20712	20713	20714	20715	20716	20717	20718	20709	65536	Gargish Red
47	20723	20719	20720	20721	20722	20724	20725	20726	20727	0	0	0	0	0	0	0	65536	Gargish Dark
48	16815	16816	16817	16818	16821	16822	16823	16824	16825	16826	16827	16828	0	0	0	0	65536	Gargish Green
49	16884	16872	16873	16874	16875	16876	16877	16878	16879	16880	16881	16882	16883	16885	16886	16887	65536	Gargish Stone
50	16864	16860	16861	16862	16863	16865	16866	16867	16868	0	0	0	0	0	0	0	65536	Gargish Motif
51	16778	16774	16775	16776	16777	16779	16780	16781	16782	0	0	0	0	0	0	0	65536	Gargish Sun
52	16523	16524	16525	16526	16527	16528	16529	16530	16531	16532	16533	16534	16535	16536	16537	16538	65536	Gargish Green Stone
53	16514	16510	16511	16512	16513	16515	16516	16517	16518	16519	16520	16521	16522	0	0	0	65536	Gargish Green Stone 2
54	18124	18125	18126	18127	18326	18327	18328	18329	0	0	0	0	0	0	0	0	65536	Gargish Ruins

from walls.txt
Code:
32	0	1112080	17229	17228	17226	17190	17227	17224	17225	17191	0	0	0	0	0	0	65536	Gargish Wall Set 1
32	1	0	17354	17355	17358	17356	17357	0	0	0	0	0	0	0	0	0	65536	Gargish Wall Set 1
33	0	1112080	17230	17231	17178	17180	17232	17233	17183	17181	0	0	0	0	0	0	65536	Gargish Wall Set 2
33	1	0	17182	17179	0	0	0	0	0	0	0	0	0	0	0	0	65536	Gargish Wall Set 2
34	0	1112080	17240	17237	17239	17238	17241	17236	0	0	0	0	0	0	0	0	65536	Gargish Windows 3
35	0	1112082	16790	16791	16805	16785	16784	16786	16787	16796	16788	16789	0	0	0	0	65536	Gargish Green Marble 1
35	1	0	16797	16799	16806	16807	16808	16792	16793	16794	0	0	0	0	0	0	65536	Gargish Green Marble 2
35	2	0	16795	16798	0	0	0	0	0	0	0	0	0	0	0	0	65536	Gargish Green Marble 3
36	0	1112083	16720	16721	16724	16725	16730	16722	16723	16733	16736	16737	0	0	0	0	65536	Gargish Two-Tone Stone Walls 
36	1	0	16731	16738	16739	16734	16718	16719	16732	16735	0	0	0	0	0	0	65536	Gargish Two-tone Stone Half Walls
36	2	0	16742	16743	16744	16745	0	0	0	0	0	0	0	0	0	0	65536	Gargish Two-tone Stone Half Walls
37	0	1112084	16664	16665	16666	16675	16660	16661	16662	16663	0	0	0	0	0	0	65536	Gargish Gold Stone Walls 1
37	1	0	16676	16677	16678	16679	0	0	0	0	0	0	0	0	0	0	65536	Gargish Gold Stone Walls 2
38	0	1112086	20787	20786	20800	20781	20795	20796	20782	20783	20799	20801	20754	20735	0	0	65536	Gargish Red Marble Walls 1
38	1	0	20734	20738	20753	20757	20736	20737	20755	20756	0	0	0	0	0	0	65536	Gargish Red Marble Walls 2
38	2	0	20741	20742	20743	20744	20745	20746	20739	20758	0	0	0	0	0	0	65536	Gargish Red Marble Walls 3
38	3	0	0	20733	20747	20748	20749	20750	20752	0	0	0	0	0	0	0	65536	Gargish Red Marble Walls 4
38	4	0	20760	20761	20762	20763	20764	20765	20766	20767	0	0	0	0	0	0	65536	Gargish Red Marble Walls 5
38	5	0	20768	20769	0	0	0	0	0	0	0	0	0	0	0	0	65536	Gargish Red Marble Walls 6
39	0	1112085	20751	20759	20778	20775	20794	20797	20798	20784	0	0	0	0	0	0	65536	Gargish Marble Walls 1
39	1	0	20785	20698	20699	20700	20701	20791	20792	20793	0	0	0	0	0	0	65536	Gargish Marble Walls 2
39	2	0	20788	20789	20790	20780	20779	0	0	0	0	0	0	0	0	0	65536	Gargish Marble Tiles 2
40	0	1112087	18139	18138	18136	18140	18304	18305	18306	18307	0	0	0	0	0	0	65536	Gargish Ruined Walls 1
40	1	0	18308	18310	18313	18314	18315	18316	18317	18318	0	0	0	0	0	0	65536	Gargish Ruined Walls 2
40	2	0	18309	18311	18312	18302	18303	18201	18202	18204	0	0	0	0	0	0	65536	Gargish Ruined Walls 3
40	3	0	18205	18206	18207	18208	0	18129	18130	18131	0	0	0	0	0	0	65536	Gargish Ruined Walls 4
40	4	0	18132	18133	18134	18135	18110	18111	18112	18113	0	0	0	0	0	0	65536	Gargish Ruined Walls 4
40	5	0	18114	18115	18116	18117	18118	18122	18123	18120	0	0	0	0	0	0	65536	Gargish Ruined Walls 4
41	0	1112088	16761	16760	16759	16756	16763	16762	16765	16766	16757	16758	0	0	0	0	65536	Gargish Blue Marble Walls
41	1	0	16764	16767	16768	16769	16770	16771	16772	16773	0	0	0	0	0	0	65536	Gargish Blue Marble Half Walls
41	2	0	16751	16750	16748	16749	0	0	0	0	0	0	0	0	0	0	65536	Gargish Blue Marble Half Walls 2
42	0	1112080	17184	17349	17350	17347	17348	17351	17352	17353	0	0	0	0	0	0	65536	Gargish Wall Set 3

from misc.txt
Code:
23	0	1112089	17166	17167	17363	17364	17170	17171	17367	17368	65536	Gargish Battlement 1
23	1	0	17168	17169	17365	17366	17162	17163	17345	17346	65536	Gargish Battlement 2
23	2	0	17160	17161	17359	17360	17164	17165	17218	17219	65536	Gargish Battlement 3
23	3	0	17232	17233	17371	17372	17234	17235	0	0	65536	Gargish Battlement 4
24	0	1112089	17188	17185	17186	17187	17189	17172	17173	17174	65536	Gargish Battlement 5
25	0	1112090	17212	17213	17214	17215	17216	17217	17177	0	65536	Dark Red Stone Arches
26	0	1060074	17319	17318	17369	17370	17321	17322	17325	17326	65536	Miscellaneous Roof Pieces 1
26	1	0	17340	17342	17343	17341	17338	17339	17337	17336	65536	Miscellaneous Roof Pieces 2
26	2	0	17320	17323	17324	17327	17221	17220	17222	17223	65536	Miscellaneous Roof Pieces 3
27	0	1112091	17278	17282	17286	17290	17294	17298	17302	17306	65536	Gargish Fountain 1
27	1	0	17310	17314	0	0	0	0	0	0	65536	Gargish Fountain 2
28	0	1112092	16800	16801	16803	16804	16809	16810	16811	16812	65536	Green Marble Arches
29	0	1112093	16754	16755	16752	16753	16756	16750	16751	0	65536	Blue Marble Arches
30	0	1112094	16714	16715	16716	16717	16727	16726	16728	16729	65536	Two-Tone Stone Archways
30	1	0	0	0	0	16746	16747	0	0	0	65536	Two-Tone Stone Archways 2
31	0	1112095	16672	16671	16673	16674	16667	16668	16669	16670	65536	Gold Stone Battlement
32	0	1112096	20771	20772	20773	20774	20728	20729	20731	20732	65536	Gargish Marble Arches
33	0	1112097	18401	18402	18403	18407	18408	18409	0	0	65536	Gargish Ruined Arches
34	0	1112098	18600	18601	18602	18603	18604	18605	0	0	65536	Gargish Stone Heads


roof.txt did not contain any new info, or so it seems to me... teleporters.txt too...
those are what i got from the client itself, since from what i've understood custom housing is based on those files, and those are the things right after the crystal and shadow sets, that should be the latest building sets added, if i got it right...
 

Garret

Sorceror
@osd_daedalus
Check date when packets was added and date of this thread.

@Alex21
In some case, becouse only on runuo.com most users can't link source, can't keep copyrights and even sometimes pass it off as his own.
(like doom lamp room puzzle?)
 
Garret;815816 said:
@osd_daedalus
Check date when packets was added and date of this thread.

Yes I have checked, and so?
Like you are the only man that can do a packet logger.

@Alex21
In some case, becouse only on runuo.com most users can't link source, can't keep copyrights and even sometimes pass it off as his own.
(like doom lamp room puzzle?)

you neither have a source the lever puzzle is yours, so the discussion can be inverted. Plus I don't have seen GPL headers. I don't believe you are in condition to make lessons of copyrights, right?

I love your policy... "free for all but not if you use RunUO". Isn't RuOSI a RunUO fork after all?

If you don't want people to use your work, don't publish it: make it private, close the source (even if you couldn't but we can pass through), but please don't just rage and shit talk on people.
You can think to be cool because you have SA support, but RunUO will reach his goal, even without your help, so stop raging and go coding if you want your shiny forked emulator to survive longer.

P.S. Just to make you happy, we are going to replace that lever puzzle with a new one, builded from scratch. The reason is both of your cryings and because there are bugs in it. And you CAN use it, no need to add credits or sorta. Just don't come back anymore saying you created it 313174 years ago ;)
 

Peoharen

Sorceror
Thilgon;815791 said:
Dunno what the numbers exactly are for (still have to learn this part about the client-side work), but this are the new housing things taken from the client itself...
<snip>
those are what i got from the client itself, since from what i've understood custom housing is based on those files, and those are the things right after the crystal and shadow sets, that should be the latest building sets added, if i got it right...
You know, until now I never compared the two. +1 to RunUO for just supporting the client's list.

Well, I'm going to have to revert to Monday's changes or so. Whatever the hell I did, without editing get average z mind you, I've screwed my map up. I figured I accidentally increased one of the land tile checks but all the land tables are still using 0x3FFF. *shurgs*
What annoys me most is I had movement working, just had to figure out why the flight tiles were not being recognized as a surface. >.>

Anyway, in case I take too long on my end and you'd like to have things rolling now I can tell you the only hang up I had for the tiles. When checking the ID of the item on the script side of Movement.cs, all the IDs are currently increased by 0x4000 before being listed so you'll have to take that into account.

In other news, I dropped in the tile listings and must have missed a range to increase somewhere, as editing BaseHouse and ComponentVariation don't seem to be enough.
 

Peoharen

Sorceror
Alex21;816017 said:
What is this thread about?
Well, post #1 looks like a guy asking for help, he lists what he has, what he is trying to do, and all problems related to it.

#2 Looks the poster got the answer in chat and shared it with everyone.

#3 Looks like someone inserting a plug for a very specific site that hosts information found on multiple websites then ranted about it.

#4 Seems to point out said information when googled searched points you to RunUO.com as the first hit where it's been posted in Razor support for awhile, and while a different site was copypasta due to including flag notes it's not a reason to post flashing banners linking it back. It's like demanding a link to wikipedia for saying "a fallacy is a misconception resulting from incorrect reasoning in argumentation." simply becuase I copied it from there instead of any of the other sites that say the same but use different punctuation.

#5 Looks like a post telling you how to support SA items.

And you know what, why in the hell am I explaining this? If you can't read the damn thread to begin with how can I expect you to read this post?

Anyway, I updated the 5th post to include the edits for movement (outside of flight paths).
 

un4g1v3n

Wanderer
Sorry for the bad question, i made the edits in post 5 10 times but i cant get it to work.
After the edits im unable to walk stairs, sure the are some more which doesn´t work but i think i misunderstood this part:

Code:
Server\Map.cs
Theres a few in this one, honestly it would be easier to say to search it and replace 0x4000 with 0x8000 in every case BUT this one
Code:

				foreach ( Item item in sector.Items )
					if ( item.AtWorldPoint( p.m_X, p.m_Y ) )
					{
						#region SA
						list.Add( new Tile( (short) ( ( item.ItemID & 0x7FFF ) + 0x4000 ), (sbyte) item.Z ) );
						#endregion
					}

Leave that one 0x4000.

Search it for 0x3FFF and make sure it's something like
TileData.ItemTable[___ & 0x3FFF];
before changing it. If you accidentally bump the 0x3FFFs that deal with the land tile you'll have problems.

Could you please describe the edits in Server/map.cs another time, or look what i did wrong?

My map.cs with the edits:

Code:
/***************************************************************************
 *                                  Map.cs
 *                            -------------------
 *   begin                : May 1, 2002
 *   copyright            : (C) The RunUO Software Team
 *   email                : [email protected]
 *
 *   $Id: Map.cs 304 2009-01-30 23:12:13Z mark $
 *
 ***************************************************************************/

/***************************************************************************
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 ***************************************************************************/

using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.Serialization;
using Server.Items;
using Server.Network;
using Server.Targeting;

namespace Server
{
	[Flags]
	public enum MapRules
	{
		None					= 0x0000,
		Internal				= 0x0001, // Internal map (used for dragging, commodity deeds, etc)
		FreeMovement			= 0x0002, // Anyone can move over anyone else without taking stamina loss
		BeneficialRestrictions	= 0x0004, // Disallow performing beneficial actions on criminals/murderers
		HarmfulRestrictions		= 0x0008, // Disallow performing harmful actions on innocents
		TrammelRules			= FreeMovement | BeneficialRestrictions | HarmfulRestrictions,
		FeluccaRules			= None
	}

	public interface IPooledEnumerable : IEnumerable
	{
		void Free();
	}

	public interface IPooledEnumerator : IEnumerator
	{
		IPooledEnumerable Enumerable{ get; set; }
		void Free();
	}

	[Parsable]
	//[CustomEnum( new string[]{ "Felucca", "Trammel", "Ilshenar", "Malas", "Internal" } )]
	public sealed class Map : IComparable, IComparable<Map>
	{
		public const int SectorSize = 16;
		public const int SectorShift = 4;
		public static int SectorActiveRange = 2;

		private static Map[] m_Maps = new Map[0x100];

		public static Map[] Maps { get { return m_Maps; } }

		public static Map Felucca { get { return m_Maps[0]; } }
		public static Map Trammel { get { return m_Maps[1]; } }
		public static Map Ilshenar { get { return m_Maps[2]; } }
		public static Map Malas { get { return m_Maps[3]; } }
		public static Map Tokuno { get { return m_Maps[4]; } }
		public static Map Internal { get { return m_Maps[0x7F]; } }

		private static List<Map> m_AllMaps = new List<Map>();

		public static List<Map> AllMaps { get { return m_AllMaps; } }

		private int m_MapID, m_MapIndex, m_FileIndex;

		private int m_Width, m_Height;
		private int m_SectorsWidth, m_SectorsHeight;
		private int m_Season;
		private Dictionary<string, Region> m_Regions;
		private Region m_DefaultRegion;

		public int Season { get { return m_Season; } set { m_Season = value; } }

		private string m_Name;
		private MapRules m_Rules;
		private Sector[][] m_Sectors;
		private Sector m_InvalidSector;

		private TileMatrix m_Tiles;

		private static string[] m_MapNames;
		private static Map[] m_MapValues;

		public static string[] GetMapNames()
		{
			CheckNamesAndValues();
			return m_MapNames;
		}

		public static Map[] GetMapValues()
		{
			CheckNamesAndValues();
			return m_MapValues;
		}

		public static Map Parse( string value )
		{
			CheckNamesAndValues();

			for ( int i = 0; i < m_MapNames.Length; ++i )
			{
				if ( Insensitive.Equals( m_MapNames[i], value ) )
					return m_MapValues[i];
			}

			int index;

			if( int.TryParse( value, out index ) )
			{
				if( index >= 0 && index < m_Maps.Length && m_Maps[index] != null )
					return m_Maps[index];
			}

			throw new Exception( "Invalid map name" );
		}

		private static void CheckNamesAndValues()
		{
			if ( m_MapNames != null && m_MapNames.Length == m_AllMaps.Count )
				return;

			m_MapNames = new string[m_AllMaps.Count];
			m_MapValues = new Map[m_AllMaps.Count];

			for ( int i = 0; i < m_AllMaps.Count; ++i )
			{
				Map map = m_AllMaps[i];

				m_MapNames[i] = map.Name;
				m_MapValues[i] = map;
			}
		}

		public override string ToString()
		{
			return m_Name;
		}

		public int GetAverageZ( int x, int y )
		{
			int z = 0, avg = 0, top = 0;

			GetAverageZ( x, y, ref z, ref avg, ref top );

			return avg;
		}

		public void GetAverageZ( int x, int y, ref int z, ref int avg, ref int top )
		{
			int zTop = Tiles.GetLandTile( x, y ).Z;
			int zLeft = Tiles.GetLandTile( x, y + 1 ).Z;
			int zRight = Tiles.GetLandTile( x + 1, y ).Z;
			int zBottom = Tiles.GetLandTile( x + 1, y + 1 ).Z;

			z = zTop;
			if ( zLeft < z )
				z = zLeft;
			if ( zRight < z )
				z = zRight;
			if ( zBottom < z )
				z = zBottom;

			top = zTop;
			if ( zLeft > top )
				top = zLeft;
			if ( zRight > top )
				top = zRight;
			if ( zBottom > top )
				top = zBottom;

			if ( Math.Abs( zTop - zBottom ) > Math.Abs( zLeft - zRight ) )
				avg = FloorAverage( zLeft, zRight );
			else
				avg = FloorAverage( zTop, zBottom );
		}

		private static int FloorAverage( int a, int b )
		{
			int v = a + b;

			if ( v < 0 )
				--v;

			return ( v / 2 );
		}

		#region Get*InRange/Bounds
		public IPooledEnumerable GetObjectsInRange( Point3D p )
		{
			if ( this == Map.Internal )
				return NullEnumerable.Instance;

			return PooledEnumerable.Instantiate( ObjectEnumerator.Instantiate( this, new Rectangle2D( p.m_X - 18, p.m_Y - 18, 37, 37 ) ) );
		}

		public IPooledEnumerable GetObjectsInRange( Point3D p, int range )
		{
			if ( this == Map.Internal )
				return NullEnumerable.Instance;

			return PooledEnumerable.Instantiate( ObjectEnumerator.Instantiate( this, new Rectangle2D( p.m_X - range, p.m_Y - range, range * 2 + 1, range * 2 + 1 ) ) );
		}

		public IPooledEnumerable GetObjectsInBounds( Rectangle2D bounds )
		{
			if ( this == Map.Internal )
				return NullEnumerable.Instance;

			return PooledEnumerable.Instantiate( ObjectEnumerator.Instantiate( this, bounds ) );
		}

		public IPooledEnumerable GetClientsInRange( Point3D p )
		{
			if ( this == Map.Internal )
				return NullEnumerable.Instance;

			return PooledEnumerable.Instantiate( TypedEnumerator.Instantiate( this, new Rectangle2D( p.m_X - 18, p.m_Y - 18, 37, 37 ), SectorEnumeratorType.Clients ) );
		}

		public IPooledEnumerable GetClientsInRange( Point3D p, int range )
		{
			if ( this == Map.Internal )
				return NullEnumerable.Instance;

			return PooledEnumerable.Instantiate( TypedEnumerator.Instantiate( this, new Rectangle2D( p.m_X - range, p.m_Y - range, range * 2 + 1, range * 2 + 1 ), SectorEnumeratorType.Clients ) );
		}

		public IPooledEnumerable GetClientsInBounds( Rectangle2D bounds )
		{
			if ( this == Map.Internal )
				return NullEnumerable.Instance;

			return PooledEnumerable.Instantiate( TypedEnumerator.Instantiate( this, bounds, SectorEnumeratorType.Clients ) );
		}

		public IPooledEnumerable GetItemsInRange( Point3D p )
		{
			if ( this == Map.Internal )
				return NullEnumerable.Instance;

			return PooledEnumerable.Instantiate( TypedEnumerator.Instantiate( this, new Rectangle2D( p.m_X - 18, p.m_Y - 18, 37, 37 ), SectorEnumeratorType.Items ) );
		}

		public IPooledEnumerable GetItemsInRange( Point3D p, int range )
		{
			if ( this == Map.Internal )
				return NullEnumerable.Instance;

			return PooledEnumerable.Instantiate( TypedEnumerator.Instantiate( this, new Rectangle2D( p.m_X - range, p.m_Y - range, range * 2 + 1, range * 2 + 1 ), SectorEnumeratorType.Items ) );
		}

		public IPooledEnumerable GetItemsInBounds( Rectangle2D bounds )
		{
			if ( this == Map.Internal )
				return NullEnumerable.Instance;

			return PooledEnumerable.Instantiate( TypedEnumerator.Instantiate( this, bounds, SectorEnumeratorType.Items ) );
		}

		public IPooledEnumerable GetMobilesInRange( Point3D p )
		{
			if ( this == Map.Internal )
				return NullEnumerable.Instance;

			return PooledEnumerable.Instantiate( TypedEnumerator.Instantiate( this, new Rectangle2D( p.m_X - 18, p.m_Y - 18, 37, 37 ), SectorEnumeratorType.Mobiles ) );
		}

		public IPooledEnumerable GetMobilesInRange( Point3D p, int range )
		{
			if ( this == Map.Internal )
				return NullEnumerable.Instance;

			return PooledEnumerable.Instantiate( TypedEnumerator.Instantiate( this, new Rectangle2D( p.m_X - range, p.m_Y - range, range * 2 + 1, range * 2 + 1 ), SectorEnumeratorType.Mobiles ) );
		}

		public IPooledEnumerable GetMobilesInBounds( Rectangle2D bounds )
		{
			if ( this == Map.Internal )
				return NullEnumerable.Instance;

			return PooledEnumerable.Instantiate( TypedEnumerator.Instantiate( this, bounds, SectorEnumeratorType.Mobiles ) );
		}
		#endregion

		public IPooledEnumerable GetMultiTilesAt( int x, int y )
		{
			if ( this == Map.Internal )
				return NullEnumerable.Instance;

			Sector sector = GetSector( x, y );

			if ( sector.Multis.Count == 0 )
				return NullEnumerable.Instance;

			return PooledEnumerable.Instantiate( MultiTileEnumerator.Instantiate( sector, new Point2D( x, y ) ) );
		}

		#region CanFit
		public bool CanFit( Point3D p, int height, bool checkBlocksFit )
		{
			return CanFit( p.m_X, p.m_Y, p.m_Z, height, checkBlocksFit, true, true );
		}

		public bool CanFit( Point3D p, int height, bool checkBlocksFit, bool checkMobiles )
		{
			return CanFit( p.m_X, p.m_Y, p.m_Z, height, checkBlocksFit, checkMobiles, true );
		}

		public bool CanFit( Point2D p, int z, int height, bool checkBlocksFit )
		{
			return CanFit( p.m_X, p.m_Y, z, height, checkBlocksFit, true, true );
		}

		public bool CanFit( Point3D p, int height )
		{
			return CanFit( p.m_X, p.m_Y, p.m_Z, height, false, true, true );
		}

		public bool CanFit( Point2D p, int z, int height )
		{
			return CanFit( p.m_X, p.m_Y, z, height, false, true, true );
		}

		public bool CanFit( int x, int y, int z, int height )
		{
			return CanFit( x, y, z, height, false, true, true );
		}

		public bool CanFit( int x, int y, int z, int height, bool checksBlocksFit )
		{
			return CanFit( x, y, z, height, checksBlocksFit, true, true );
		}

		public bool CanFit( int x, int y, int z, int height, bool checkBlocksFit, bool checkMobiles )
		{
			return CanFit( x, y, z, height, checkBlocksFit, checkMobiles, true );
		}

		public bool CanFit( int x, int y, int z, int height, bool checkBlocksFit, bool checkMobiles, bool requireSurface )
		{
			if ( this == Map.Internal )
				return false;

			if ( x < 0 || y < 0 || x >= m_Width || y >= m_Height )
				return false;

			bool hasSurface = false;

			Tile lt = Tiles.GetLandTile( x, y );
			int lowZ = 0, avgZ = 0, topZ = 0;

			GetAverageZ( x, y, ref lowZ, ref avgZ, ref topZ );
			TileFlag landFlags = TileData.LandTable[lt.ID & 0x3FFF].Flags;

			if ( ( landFlags & TileFlag.Impassable ) != 0 && avgZ > z && ( z + height ) > lowZ )
				return false;
			else if ( ( landFlags & TileFlag.Impassable ) == 0 && z == avgZ && !lt.Ignored )
				hasSurface = true;

			Tile[] staticTiles = Tiles.GetStaticTiles( x, y, true );

			bool surface, impassable;

			for ( int i = 0; i < staticTiles.Length; ++i )
			{
				ItemData id = TileData.ItemTable[staticTiles[i].ID & 0x3FFF];
				surface = id.Surface;
				impassable = id.Impassable;

				if ( ( surface || impassable ) && ( staticTiles[i].Z + id.CalcHeight ) > z && ( z + height ) > staticTiles[i].Z )
					return false;
				else if ( surface && !impassable && z == ( staticTiles[i].Z + id.CalcHeight ) )
					hasSurface = true;
			}

			Sector sector = GetSector( x, y );
			List<Item> items  = sector.Items;
			List<Mobile> mobs = sector.Mobiles;

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

				if ( item.ItemID < 0x8000 && item.AtWorldPoint( x, y ) )
				{
					ItemData id = item.ItemData;
					surface = id.Surface;
					impassable = id.Impassable;

					if ( ( surface || impassable || ( checkBlocksFit && item.BlocksFit ) ) && ( item.Z + id.CalcHeight ) > z && ( z + height ) > item.Z )
						return false;
					else if ( surface && !impassable && !item.Movable && z == ( item.Z + id.CalcHeight ) )
						hasSurface = true;
				}
			}

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

					if ( m.Location.m_X == x && m.Location.m_Y == y && ( m.AccessLevel == AccessLevel.Player || !m.Hidden ) )
						if ( ( m.Z + 16 ) > z && ( z + height ) > m.Z )
							return false;
				}
			}

			return !requireSurface || hasSurface;
		}

		#endregion

		#region CanSpawnMobile
		public bool CanSpawnMobile( Point3D p )
		{
			return CanSpawnMobile( p.m_X, p.m_Y, p.m_Z );
		}

		public bool CanSpawnMobile( Point2D p, int z )
		{
			return CanSpawnMobile( p.m_X, p.m_Y, z );
		}

		public bool CanSpawnMobile( int x, int y, int z )
		{
			if ( !Region.Find( new Point3D( x, y, z ), this ).AllowSpawn() )
				return false;

			return CanFit( x, y, z, 16 );
		}
		#endregion

		private class ZComparer : IComparer<Item>
		{
			public static readonly ZComparer Default = new ZComparer();

			public int Compare( Item x, Item y )
			{
				return x.Z.CompareTo( y.Z );
			}
		}

		public void FixColumn( int x, int y )
		{
			Tile landTile = Tiles.GetLandTile( x, y );

			int landZ = 0, landAvg = 0, landTop = 0;
			GetAverageZ( x, y, ref landZ, ref landAvg, ref landTop );

			Tile[] tiles = Tiles.GetStaticTiles( x, y, true );

			List<Item> items = new List<Item>();

			IPooledEnumerable eable = GetItemsInRange( new Point3D( x, y, 0 ), 0 );

			foreach ( Item item in eable )
			{
				if ( item.ItemID < 0x8000 )
				{
					items.Add( item );

					if ( items.Count > 100 )
						break;
				}
			}

			eable.Free();

			if ( items.Count > 100 )
				return;

			items.Sort( ZComparer.Default );

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

				if ( !toFix.Movable )
					continue;

				int z = int.MinValue;
				int currentZ = toFix.Z;

				if ( !landTile.Ignored && landAvg <= currentZ )
					z = landAvg;

				for ( int j = 0; j < tiles.Length; ++j )
				{
					Tile tile = tiles[j];
					ItemData id = TileData.ItemTable[tile.ID & 0x3FFF];

					int checkZ = tile.Z;
					int checkTop = checkZ + id.CalcHeight;

					if ( checkTop == checkZ && !id.Surface )
						++checkTop;

					if ( checkTop > z && checkTop <= currentZ )
						z = checkTop;
				}

				for ( int j = 0; j < items.Count; ++j )
				{
					if ( j == i )
						continue;

					Item item = items[j];
					ItemData id = item.ItemData;

					int checkZ = item.Z;
					int checkTop = checkZ + id.CalcHeight;

					if ( checkTop == checkZ && !id.Surface )
						++checkTop;

					if ( checkTop > z && checkTop <= currentZ )
						z = checkTop;
				}

				if ( z != int.MinValue )
					toFix.Location = new Point3D( toFix.X, toFix.Y, z );
			}
		}

		public List<Tile> GetTilesAt( Point2D p, bool items, bool land, bool statics )
		{
			List<Tile> list = new List<Tile>();

			if ( this == Map.Internal )
				return list;

			if ( land )
				list.Add( Tiles.GetLandTile( p.m_X, p.m_Y ) );

			if ( statics )
				list.AddRange( Tiles.GetStaticTiles( p.m_X, p.m_Y, true ) );

			if ( items )
			{
				Sector sector = GetSector( p );

				foreach ( Item item in sector.Items )
					if ( item.AtWorldPoint( p.m_X, p.m_Y ) )
						list.Add( new Tile( (short) ( ( item.ItemID & 0x7FFF ) + 0x4000 ), (sbyte) item.Z ) );
			}

			return list;
		}

		/// <summary>
		/// Gets the highest surface that is lower than <paramref name="p"/>.
		/// </summary>
		/// <param name="p">The reference point.</param>
		/// <returns>A surface <typeparamref name="Tile"/> or <typeparamref name="Item"/>.</returns>
		public object GetTopSurface( Point3D p )
		{
			if ( this == Map.Internal )
				return null;

			object surface = null;
			int surfaceZ = int.MinValue;


			Tile lt = Tiles.GetLandTile( p.X, p.Y );

			if ( !lt.Ignored )
			{
				int avgZ = GetAverageZ( p.X, p.Y );

				if ( avgZ <= p.Z )
				{
					surface = lt;
					surfaceZ = avgZ;

					if ( surfaceZ == p.Z )
						return surface;
				}
			}


			Tile[] staticTiles = Tiles.GetStaticTiles( p.X, p.Y, true );

			for ( int i = 0; i < staticTiles.Length; i++ )
			{
				Tile tile = staticTiles[i];
				ItemData id = TileData.ItemTable[tile.ID & 0x3FFF];

				if ( id.Surface || ( id.Flags & TileFlag.Wet ) != 0 )
				{
					int tileZ = tile.Z + id.CalcHeight;

					if ( tileZ > surfaceZ && tileZ <= p.Z )
					{
						surface = tile;
						surfaceZ = tileZ;

						if ( surfaceZ == p.Z )
							return surface;
					}
				}
			}


			Sector sector = GetSector( p.X, p.Y );

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

				if ( item.ItemID < 0x8000 && item.AtWorldPoint( p.X, p.Y ) && !item.Movable )
				{
					ItemData id = item.ItemData;

					if ( id.Surface || ( id.Flags & TileFlag.Wet ) != 0 )
					{
						int itemZ = item.Z + id.CalcHeight;

						if ( itemZ > surfaceZ && itemZ <= p.Z )
						{
							surface = item;
							surfaceZ = itemZ;

							if ( surfaceZ == p.Z )
								return surface;
						}
					}
				}
			}


			return surface;
		}

		public void Bound( int x, int y, out int newX, out int newY )
		{
			if ( x < 0 )
				newX = 0;
			else if ( x >= m_Width )
				newX = m_Width - 1;
			else
				newX = x;

			if ( y < 0 )
				newY = 0;
			else if ( y >= m_Height )
				newY = m_Height - 1;
			else
				newY = y;
		}

		public Point2D Bound( Point2D p )
		{
			int x = p.m_X, y = p.m_Y;

			if ( x < 0 )
				x = 0;
			else if ( x >= m_Width )
				x = m_Width - 1;

			if ( y < 0 )
				y = 0;
			else if ( y >= m_Height )
				y = m_Height - 1;

			return new Point2D( x, y );
		}

		public Map( int mapID, int mapIndex, int fileIndex, int width, int height, int season, string name, MapRules rules )
		{
			m_MapID = mapID;
			m_MapIndex = mapIndex;
			m_FileIndex = fileIndex;
			m_Width = width;
			m_Height = height;
			m_Season = season;
			m_Name = name;
			m_Rules = rules;
			m_Regions = new Dictionary<string, Region>( StringComparer.OrdinalIgnoreCase );
			m_InvalidSector = new Sector( 0, 0, this );
			m_SectorsWidth = width >> SectorShift;
			m_SectorsHeight = height >> SectorShift;
			m_Sectors = new Sector[m_SectorsWidth][];
		}

		#region GetSector
		public Sector GetSector( Point3D p )
		{
			return InternalGetSector( p.m_X >> SectorShift, p.m_Y >> SectorShift );
		}

		public Sector GetSector( Point2D p )
		{
			return InternalGetSector( p.m_X >> SectorShift, p.m_Y >> SectorShift );
		}

		public Sector GetSector( IPoint2D p )
		{
			return InternalGetSector( p.X >> SectorShift, p.Y >> SectorShift );
		}

		public Sector GetSector( int x, int y )
		{
			return InternalGetSector( x >> SectorShift, y >> SectorShift );
		}

		public Sector GetRealSector( int x, int y )
		{
			return InternalGetSector( x, y );
		}

		private Sector InternalGetSector( int x, int y )
		{
			if ( x >= 0 && x < m_SectorsWidth && y >= 0 && y < m_SectorsHeight )
			{
				Sector[] xSectors = m_Sectors[x];

				if ( xSectors == null )
					m_Sectors[x] = xSectors = new Sector[m_SectorsHeight];

				Sector sec = xSectors[y];

				if ( sec == null )
					xSectors[y] = sec = new Sector( x, y, this );

				return sec;
			}
			else
			{
				return m_InvalidSector;
			}
		}
		#endregion

		public void ActivateSectors( int cx, int cy )
		{
			for ( int x = cx - SectorActiveRange; x <= cx + SectorActiveRange; ++x )
			{
				for ( int y = cy - SectorActiveRange; y <= cy + SectorActiveRange; ++y )
				{
					Sector sect = GetRealSector( x, y );
					if ( sect != m_InvalidSector )
						sect.Activate();
				}
			}
		}

		public void DeactivateSectors( int cx, int cy )
		{
			for ( int x = cx - SectorActiveRange; x <= cx + SectorActiveRange; ++x )
			{
				for ( int y = cy - SectorActiveRange; y <= cy + SectorActiveRange; ++y )
				{
					Sector sect = GetRealSector( x, y );
					if ( sect != m_InvalidSector && !PlayersInRange( sect, SectorActiveRange ) )
						sect.Deactivate();
				}
			}
		}

		private bool PlayersInRange( Sector sect, int range )
		{
			for ( int x = sect.X - range; x <= sect.X + range; ++x )
			{
				for ( int y = sect.Y - range; y <= sect.Y + range; ++y )
				{
					Sector check = GetRealSector( x, y );
					if ( check != m_InvalidSector && check.Players.Count > 0 )
						return true;
				}
			}

			return false;
		}

		public void OnClientChange( NetState oldState, NetState newState, Mobile m )
		{
			if ( this == Map.Internal )
				return;

			GetSector( m ).OnClientChange( oldState, newState );
		}

		public void OnEnter( Mobile m )
		{
			if ( this == Map.Internal )
				return;

			Sector sector = GetSector( m );

			sector.OnEnter( m );
		}

		public void OnEnter( Item item )
		{
			if ( this == Map.Internal )
				return;

			GetSector( item ).OnEnter( item );

			if ( item is BaseMulti )
			{
				BaseMulti m = (BaseMulti)item;
				MultiComponentList mcl = m.Components;

				Sector start = GetMultiMinSector( item.Location, mcl );
				Sector end = GetMultiMaxSector( item.Location, mcl );

				AddMulti( m, start, end );
			}
		}

		public void OnLeave( Mobile m )
		{
			if ( this == Map.Internal )
				return;

			Sector sector = GetSector( m );

			sector.OnLeave( m );
		}

		public void OnLeave( Item item )
		{
			if ( this == Map.Internal )
				return;

			GetSector( item ).OnLeave( item );

			if ( item is BaseMulti )
			{
				BaseMulti m = (BaseMulti)item;
				MultiComponentList mcl = m.Components;

				Sector start = GetMultiMinSector( item.Location, mcl );
				Sector end = GetMultiMaxSector( item.Location, mcl );

				RemoveMulti( m, start, end );
			}
		}

		public void RemoveMulti( BaseMulti m, Sector start, Sector end )
		{
			if ( this == Map.Internal )
				return;

			for ( int x = start.X; x <= end.X; ++x )
				for ( int y = start.Y; y <= end.Y; ++y )
					InternalGetSector( x, y ).OnMultiLeave( m );
		}

		public void AddMulti( BaseMulti m, Sector start, Sector end )
		{
			if ( this == Map.Internal )
				return;

			for ( int x = start.X; x <= end.X; ++x )
				for ( int y = start.Y; y <= end.Y; ++y )
					InternalGetSector( x, y ).OnMultiEnter( m );
		}

		public Sector GetMultiMinSector( Point3D loc, MultiComponentList mcl )
		{
			return GetSector( Bound( new Point2D( loc.m_X + mcl.Min.m_X, loc.m_Y + mcl.Min.m_Y ) ) );
		}

		public Sector GetMultiMaxSector( Point3D loc, MultiComponentList mcl )
		{
			return GetSector( Bound( new Point2D( loc.m_X + mcl.Max.m_X, loc.m_Y + mcl.Max.m_Y ) ) );
		}

		public void OnMove( Point3D oldLocation, Mobile m )
		{
			if ( this == Map.Internal )
				return;

			Sector oldSector = GetSector( oldLocation );
			Sector newSector = GetSector( m.Location );

			if ( oldSector != newSector )
			{
				oldSector.OnLeave( m );
				newSector.OnEnter( m );
			}
		}

		public void OnMove( Point3D oldLocation, Item item )
		{
			if ( this == Map.Internal )
				return;

			Sector oldSector = GetSector( oldLocation );
			Sector newSector = GetSector( item.Location );

			if ( oldSector != newSector )
			{
				oldSector.OnLeave( item );
				newSector.OnEnter( item );
			}

			if ( item is BaseMulti )
			{
				BaseMulti m = (BaseMulti)item;
				MultiComponentList mcl = m.Components;

				Sector start = GetMultiMinSector( item.Location, mcl );
				Sector end = GetMultiMaxSector( item.Location, mcl );

				Sector oldStart = GetMultiMinSector( oldLocation, mcl );
				Sector oldEnd = GetMultiMaxSector( oldLocation, mcl );

				if ( oldStart != start || oldEnd != end )
				{
					RemoveMulti( m, oldStart, oldEnd );
					AddMulti( m, start, end );
				}
			}
		}

		public TileMatrix Tiles
		{
			get
			{
				if ( m_Tiles == null )
					m_Tiles = new TileMatrix( this, m_FileIndex, m_MapID, m_Width, m_Height );

				return m_Tiles;
			}
		}

		public int MapID
		{
			get
			{
				return m_MapID;
			}
		}

		public int MapIndex
		{
			get
			{
				return m_MapIndex;
			}
		}

		public int Width
		{
			get
			{
				return m_Width;
			}
		}

		public int Height
		{
			get
			{
				return m_Height;
			}
		}

		public Dictionary<string, Region> Regions
		{
			get
			{
				return m_Regions;
			}
		}

		public void RegisterRegion( Region reg )
		{
			string regName = reg.Name;

			if ( regName != null )
			{
				if ( m_Regions.ContainsKey( regName ) )
					Console.WriteLine( "Warning: Duplicate region name '{0}' for map '{1}'", regName, this.Name );
				else
					m_Regions[regName] = reg;
			}
		}

		public void UnregisterRegion( Region reg )
		{
			string regName = reg.Name;

			if ( regName != null )
				m_Regions.Remove( regName );
		}

		public Region DefaultRegion
		{
			get
			{
				if ( m_DefaultRegion == null )
					m_DefaultRegion = new Region( null, this, 0, new Rectangle3D[0] );

				return m_DefaultRegion;
			}
			set
			{
				m_DefaultRegion = value;
			}
		}

		public MapRules Rules
		{
			get
			{
				return m_Rules;
			}
			set
			{
				m_Rules = value;
			}
		}

		public Sector InvalidSector
		{
			get
			{
				return m_InvalidSector;
			}
		}

		public string Name
		{
			get
			{
				return m_Name;
			}
			set
			{
				m_Name = value;
			}
		}

		#region Enumerables
		public class NullEnumerable : IPooledEnumerable
		{
			private InternalEnumerator m_Enumerator;

			public static readonly NullEnumerable Instance = new NullEnumerable();

			private NullEnumerable()
			{
				m_Enumerator = new InternalEnumerator();
			}

			public IEnumerator GetEnumerator()
			{
				return m_Enumerator;
			}

			public void Free()
			{
			}

			private class InternalEnumerator : IEnumerator
			{
				public void Reset()
				{
				}

				public object Current
				{
					get
					{
						return null;
					}
				}

				public bool MoveNext()
				{
					return false;
				}
			}
		}

		private class PooledEnumerable : IPooledEnumerable, IDisposable
		{
			private IPooledEnumerator m_Enumerator;

			private static Queue<PooledEnumerable> m_InstancePool = new Queue<PooledEnumerable>();
			private static int m_Depth = 0;

			public static PooledEnumerable Instantiate( IPooledEnumerator etor )
			{
				++m_Depth;

				if ( m_Depth >= 5 )
					Console.WriteLine( "Warning: Make sure to call .Free() on pooled enumerables." );

				PooledEnumerable e;

				if ( m_InstancePool.Count > 0 )
				{
					e = m_InstancePool.Dequeue();
					e.m_Enumerator = etor;
				}
				else
				{
					e = new PooledEnumerable( etor );
				}

				etor.Enumerable = e;

				return e;
			}

			private PooledEnumerable( IPooledEnumerator etor )
			{
				m_Enumerator = etor;
			}

			public IEnumerator GetEnumerator()
			{
				if ( m_Enumerator == null )
					throw new ObjectDisposedException( "PooledEnumerable", "GetEnumerator() called after Free()" );

				return m_Enumerator;
			}

			public void Free()
			{
				if ( m_Enumerator != null )
				{
					m_InstancePool.Enqueue( this );

					m_Enumerator.Free();
					m_Enumerator = null;

					--m_Depth;
				}
			}

			public void Dispose()
			{
				Free();
			}
		}
		#endregion

		#region Enumerators
		private enum SectorEnumeratorType
		{
			Mobiles,
			Items,
			Clients
		}

		private class TypedEnumerator : IPooledEnumerator, IDisposable
		{
			private IPooledEnumerable m_Enumerable;

			public IPooledEnumerable Enumerable
			{
				get { return m_Enumerable; }
				set { m_Enumerable = value; }
			}

			private Map m_Map;
			private Rectangle2D m_Bounds;
			private SectorEnumerator m_Enumerator;
			private SectorEnumeratorType m_Type;
			private object m_Current;

			private static Queue<TypedEnumerator> m_InstancePool = new Queue<TypedEnumerator>();

			public static TypedEnumerator Instantiate( Map map, Rectangle2D bounds, SectorEnumeratorType type )
			{
				TypedEnumerator e;

				if ( m_InstancePool.Count > 0 )
				{
					e = m_InstancePool.Dequeue();

					e.m_Map = map;
					e.m_Bounds = bounds;
					e.m_Type = type;

					e.Reset();
				}
				else
				{
					e = new TypedEnumerator( map, bounds, type );
				}

				return e;
			}

			public void Free()
			{
				if ( m_Map == null )
					return;

				m_InstancePool.Enqueue( this );

				m_Map = null;

				if ( m_Enumerator != null )
				{
					m_Enumerator.Free();
					m_Enumerator = null;
				}

				if ( m_Enumerable != null )
					m_Enumerable.Free();
			}

			public TypedEnumerator( Map map, Rectangle2D bounds, SectorEnumeratorType type )
			{
				m_Map = map;
				m_Bounds = bounds;
				m_Type = type;

				Reset();
			}

			public object Current
			{
				get
				{
					return m_Current;
				}
			}

			public bool MoveNext()
			{
				while ( true )
				{
					if ( m_Enumerator.MoveNext() )
					{
						object o;

						try
						{
							o = m_Enumerator.Current;
						}
						catch
						{
							continue;
						}

						if ( o is Mobile )
						{
							Mobile m = (Mobile) o;

							if ( !m.Deleted && m_Bounds.Contains( m.Location ) )
							{
								m_Current = o;
								return true;
							}
						}
						else if ( o is Item )
						{
							Item item = (Item) o;

							if ( !item.Deleted && item.Parent == null && m_Bounds.Contains( item.Location ) )
							{
								m_Current = o;
								return true;
							}
						}
						else if ( o is NetState )
						{
							Mobile m = ( (NetState) o ).Mobile;

							if ( m != null && !m.Deleted && m_Bounds.Contains( m.Location ) )
							{
								m_Current = o;
								return true;
							}
						}
					}
					else
					{
						m_Current = null;

						m_Enumerator.Free();
						m_Enumerator = null;

						return false;
					}
				}
			}

			public void Reset()
			{
				m_Current = null;

				if ( m_Enumerator != null )
					m_Enumerator.Free();

				m_Enumerator = SectorEnumerator.Instantiate( m_Map, m_Bounds, m_Type );//new SectorEnumerator( m_Map, m_Origin, m_Type, m_Range );
			}

			public void Dispose()
			{
				Free();
			}
		}

		private class MultiTileEnumerator : IPooledEnumerator, IDisposable
		{
			private IPooledEnumerable m_Enumerable;

			public IPooledEnumerable Enumerable
			{
				get { return m_Enumerable; }
				set { m_Enumerable = value; }
			}

			private List<BaseMulti> m_List;
			private Point2D m_Location;
			private object m_Current;
			private int m_Index;

			private static Queue<MultiTileEnumerator> m_InstancePool = new Queue<MultiTileEnumerator>();

			public static MultiTileEnumerator Instantiate( Sector sector, Point2D loc )
			{
				MultiTileEnumerator e;

				if ( m_InstancePool.Count > 0 )
				{
					e = m_InstancePool.Dequeue();

					e.m_List = sector.Multis;
					e.m_Location = loc;

					e.Reset();
				}
				else
				{
					e = new MultiTileEnumerator( sector, loc );
				}

				return e;
			}

			private MultiTileEnumerator( Sector sector, Point2D loc )
			{
				m_List = sector.Multis;
				m_Location = loc;

				Reset();
			}

			public object Current
			{
				get
				{
					return m_Current;
				}
			}

			public bool MoveNext()
			{
				while ( ++m_Index < m_List.Count )
				{
					BaseMulti m = m_List[m_Index];

					if ( m != null && !m.Deleted )
					{
						MultiComponentList list = m.Components;

						int xOffset = m_Location.m_X - ( m.Location.m_X + list.Min.m_X );
						int yOffset = m_Location.m_Y - ( m.Location.m_Y + list.Min.m_Y );

						if ( xOffset >= 0 && xOffset < list.Width && yOffset >= 0 && yOffset < list.Height )
						{
							Tile[] tiles = list.Tiles[xOffset][yOffset];

							if ( tiles.Length > 0 )
							{
								// TODO: How to avoid this copy?
								Tile[] copy = new Tile[tiles.Length];

								for ( int i = 0; i < copy.Length; ++i )
								{
									copy[i] = tiles[i];
									copy[i].Z += m.Z;
								}

								m_Current = copy;
								return true;
							}
						}
					}
				}

				return false;
			}

			public void Free()
			{
				if ( m_List == null )
					return;

				m_InstancePool.Enqueue( this );

				m_List = null;

				if ( m_Enumerable != null )
					m_Enumerable.Free();
			}

			public void Reset()
			{
				m_Current = null;
				m_Index = -1;
			}

			public void Dispose()
			{
				Free();
			}
		}

		private class ObjectEnumerator : IPooledEnumerator, IDisposable
		{
			private IPooledEnumerable m_Enumerable;

			public IPooledEnumerable Enumerable
			{
				get { return m_Enumerable; }
				set { m_Enumerable = value; }
			}

			private Map m_Map;
			private Rectangle2D m_Bounds;
			private SectorEnumerator m_Enumerator;
			private int m_Stage; // 0 = items, 1 = mobiles
			private object m_Current;

			private static Queue<ObjectEnumerator> m_InstancePool = new Queue<ObjectEnumerator>();

			public static ObjectEnumerator Instantiate( Map map, Rectangle2D bounds )
			{
				ObjectEnumerator e;

				if ( m_InstancePool.Count > 0 )
				{
					e = m_InstancePool.Dequeue();

					e.m_Map = map;
					e.m_Bounds = bounds;

					e.Reset();
				}
				else
				{
					e = new ObjectEnumerator( map, bounds );
				}

				return e;
			}

			public void Free()
			{
				if ( m_Map == null )
					return;

				m_InstancePool.Enqueue( this );

				m_Map = null;

				if ( m_Enumerator != null )
				{
					m_Enumerator.Free();
					m_Enumerator = null;
				}

				if ( m_Enumerable != null )
					m_Enumerable.Free();
			}

			private ObjectEnumerator( Map map, Rectangle2D bounds )
			{
				m_Map = map;
				m_Bounds = bounds;

				Reset();
			}

			public object Current
			{
				get
				{
					return m_Current;
				}
			}

			public bool MoveNext()
			{
				while ( true )
				{
					if ( m_Enumerator.MoveNext() )
					{
						object o;

						try
						{
							o = m_Enumerator.Current;
						}
						catch
						{
							continue;
						}

						if ( o is Mobile )
						{
							Mobile m = (Mobile) o;

							if ( m_Bounds.Contains( m.Location ) )
							{
								m_Current = o;
								return true;
							}
						}
						else if ( o is Item )
						{
							Item item = (Item) o;

							if ( item.Parent == null && m_Bounds.Contains( item.Location ) )
							{
								m_Current = o;
								return true;
							}
						}
					}
					else if ( m_Stage == 0 )
					{
						m_Enumerator.Free();
						m_Enumerator = SectorEnumerator.Instantiate( m_Map, m_Bounds, SectorEnumeratorType.Mobiles );

						m_Current = null;
						m_Stage = 1;
					}
					else
					{
						m_Enumerator.Free();
						m_Enumerator = null;

						m_Current = null;
						m_Stage = -1;

						return false;
					}
				}
			}

			public void Reset()
			{
				m_Stage = 0;

				m_Current = null;

				if ( m_Enumerator != null )
					m_Enumerator.Free();

				m_Enumerator = SectorEnumerator.Instantiate( m_Map, m_Bounds, SectorEnumeratorType.Items );
			}

			public void Dispose()
			{
				Free();
			}
		}

		private class SectorEnumerator : IPooledEnumerator, IDisposable
		{
			private IPooledEnumerable m_Enumerable;

			public IPooledEnumerable Enumerable
			{
				get { return m_Enumerable; }
				set { m_Enumerable = value; }
			}

			private Map m_Map;
			private Rectangle2D m_Bounds;

			private int m_xSector, m_ySector;
			private int m_xSectorStart, m_ySectorStart;
			private int m_xSectorEnd, m_ySectorEnd;
			private IList m_CurrentList;
			private int m_CurrentIndex;
			private SectorEnumeratorType m_Type;

			private static Queue<SectorEnumerator> m_InstancePool = new Queue<SectorEnumerator>();

			public static SectorEnumerator Instantiate( Map map, Rectangle2D bounds, SectorEnumeratorType type )
			{
				SectorEnumerator e;

				if ( m_InstancePool.Count > 0 )
				{
					e = m_InstancePool.Dequeue();

					e.m_Map = map;
					e.m_Bounds = bounds;
					e.m_Type = type;

					e.Reset();
				}
				else
				{
					e = new SectorEnumerator( map, bounds, type );
				}

				return e;
			}

			public void Free()
			{
				if ( m_Map == null )
					return;

				m_InstancePool.Enqueue( this );

				m_Map = null;

				if ( m_Enumerable != null )
					m_Enumerable.Free();
			}

			private SectorEnumerator( Map map, Rectangle2D bounds, SectorEnumeratorType type )
			{
				m_Map = map;
				m_Bounds = bounds;
				m_Type = type;

				Reset();
			}

			private IList GetListForSector( Sector sector )
			{
				switch ( m_Type )
				{
					case SectorEnumeratorType.Clients:
						return sector.Clients;
					case SectorEnumeratorType.Mobiles:
						return sector.Mobiles;
					case SectorEnumeratorType.Items:
						return sector.Items;
					default:
						throw new Exception( "Invalid SectorEnumeratorType" );
				}
			}

			public object Current
			{
				get
				{
					return m_CurrentList[m_CurrentIndex];
					/*try
					{
						return m_CurrentList[m_CurrentIndex];
					}
					catch
					{
						Console.WriteLine( "Warning: Object removed during enumeration. May not be recoverable" );

						m_CurrentIndex = -1;
						m_CurrentList = GetListForSector( m_Map.InternalGetSector( m_xSector, m_ySector ) );

						if ( MoveNext() )
						{
							return Current;
						}
						else
						{
							throw new Exception( "Object disposed during enumeration. Was not recoverable." );
						}
					}*/
				}
			}

			public bool MoveNext()
			{
				while ( true )
				{
					++m_CurrentIndex;

					if ( m_CurrentIndex == m_CurrentList.Count )
					{
						++m_ySector;

						if ( m_ySector > m_ySectorEnd )
						{
							m_ySector = m_ySectorStart;
							++m_xSector;

							if ( m_xSector > m_xSectorEnd )
							{
								m_CurrentIndex = -1;
								m_CurrentList = null;

								return false;
							}
						}

						m_CurrentIndex = -1;
						m_CurrentList = GetListForSector( m_Map.InternalGetSector( m_xSector, m_ySector ) );//m_Map.m_Sectors[m_xSector][m_ySector] );
					}
					else
					{
						return true;
					}
				}
			}

			public void Reset()
			{
				m_Map.Bound( m_Bounds.Start.m_X, m_Bounds.Start.m_Y, out m_xSectorStart, out m_ySectorStart );
				m_Map.Bound( m_Bounds.End.m_X - 1, m_Bounds.End.m_Y - 1, out m_xSectorEnd, out m_ySectorEnd );

				m_xSector = m_xSectorStart >>= Map.SectorShift;
				m_ySector = m_ySectorStart >>= Map.SectorShift;

				m_xSectorEnd >>= Map.SectorShift;
				m_ySectorEnd >>= Map.SectorShift;

				m_CurrentIndex = -1;
				m_CurrentList = GetListForSector( m_Map.InternalGetSector( m_xSector, m_ySector ) );
			}

			public void Dispose()
			{
				Free();
			}
		}
		#endregion

		public Point3D GetPoint( object o, bool eye )
		{
			Point3D p;

			if( o is Mobile )
			{
				p = ((Mobile)o).Location;
				p.Z += 14;//eye ? 15 : 10;
			}
			else if( o is Item )
			{
				p = ((Item)o).GetWorldLocation();
				p.Z += (((Item)o).ItemData.Height / 2) + 1;
			}
			else if( o is Point3D )
			{
				p = (Point3D)o;
			}
			else if( o is LandTarget )
			{
				p = ((LandTarget)o).Location;

				int low = 0, avg = 0, top = 0;
				GetAverageZ( p.X, p.Y, ref low, ref avg, ref top );

				p.Z = top + 1;
			}
			else if( o is StaticTarget )
			{
				StaticTarget st = (StaticTarget)o;
				ItemData id = TileData.ItemTable[st.ItemID & 0x3FFF];

				p = new Point3D( st.X, st.Y, st.Z - id.CalcHeight + (id.Height / 2) + 1 );
			}
			else if( o is IPoint3D )
			{
				p = new Point3D( (IPoint3D)o );
			}
			else
			{
				Console.WriteLine( "Warning: Invalid object ({0}) in line of sight", o );
				p = Point3D.Zero;
			}

			return p;
		}

		#region Line Of Sight
		private static int m_MaxLOSDistance = 25;

		public static int MaxLOSDistance
		{
			get { return m_MaxLOSDistance; }
			set { m_MaxLOSDistance = value; }
		}

		public bool LineOfSight( Point3D org, Point3D dest )
		{
			if( this == Map.Internal )
				return false;

			if( !Utility.InRange( org, dest, m_MaxLOSDistance ) )
				return false;

			Point3D start = org;
			Point3D end = dest;

			if( org.X > dest.X || (org.X == dest.X && org.Y > dest.Y) || (org.X == dest.X && org.Y == dest.Y && org.Z > dest.Z) )
			{
				Point3D swap = org;
				org = dest;
				dest = swap;
			}

			double rise, run, zslp;
			double sq3d;
			double x, y, z;
			int xd, yd, zd;
			int ix, iy, iz;
			int height;
			bool found;
			Point3D p;
			Point3DList path = m_PathList;
			TileFlag flags;

			if( org == dest )
				return true;

			if( path.Count > 0 )
				path.Clear();

			xd = dest.m_X - org.m_X;
			yd = dest.m_Y - org.m_Y;
			zd = dest.m_Z - org.m_Z;
			zslp = Math.Sqrt( xd * xd + yd * yd );
			if( zd != 0 )
				sq3d = Math.Sqrt( zslp * zslp + zd * zd );
			else
				sq3d = zslp;

			rise = ((float)yd) / sq3d;
			run = ((float)xd) / sq3d;
			zslp = ((float)zd) / sq3d;

			y = org.m_Y;
			z = org.m_Z;
			x = org.m_X;
			while( Utility.NumberBetween( x, dest.m_X, org.m_X, 0.5 ) && Utility.NumberBetween( y, dest.m_Y, org.m_Y, 0.5 ) && Utility.NumberBetween( z, dest.m_Z, org.m_Z, 0.5 ) )
			{
				ix = (int)Math.Round( x );
				iy = (int)Math.Round( y );
				iz = (int)Math.Round( z );
				if( path.Count > 0 )
				{
					p = path.Last;

					if( p.m_X != ix || p.m_Y != iy || p.m_Z != iz )
						path.Add( ix, iy, iz );
				}
				else
				{
					path.Add( ix, iy, iz );
				}
				x += run;
				y += rise;
				z += zslp;
			}

			if( path.Count == 0 )
				return true;//<--should never happen, but to be safe.

			p = path.Last;

			if( p != dest )
				path.Add( dest );

			Point3D pTop = org, pBottom = dest;
			Utility.FixPoints( ref pTop, ref pBottom );

			int pathCount = path.Count;

			for( int i = 0; i < pathCount; ++i )
			{
				Point3D point = path[i];

				Tile landTile = Tiles.GetLandTile( point.X, point.Y );
				int landZ = 0, landAvg = 0, landTop = 0;
				GetAverageZ( point.m_X, point.m_Y, ref landZ, ref landAvg, ref landTop );

				if( landZ <= point.m_Z && landTop >= point.m_Z && (point.m_X != end.m_X || point.m_Y != end.m_Y || landZ > end.m_Z || landTop < end.m_Z) && !landTile.Ignored )
					return false;

				/* --Do land tiles need to be checked?  There is never land between two people, always statics.--
				Tile landTile = Tiles.GetLandTile( point.X, point.Y );
				if ( landTile.Z-1 >= point.Z && landTile.Z+1 <= point.Z && (TileData.LandTable[landTile.ID & 0x3FFF].Flags & TileFlag.Impassable) != 0 )
					return false;
				*/

				Tile[] statics = Tiles.GetStaticTiles( point.m_X, point.m_Y, true );

				bool contains = false;
				int ltID = landTile.ID;

				for( int j = 0; !contains && j < m_InvalidLandTiles.Length; ++j )
					contains = (ltID == m_InvalidLandTiles[j]);

				if( contains && statics.Length == 0 )
				{
					IPooledEnumerable eable = GetItemsInRange( point, 0 );

					foreach( Item item in eable )
					{
						if( item.Visible )
							contains = false;

						if( !contains )
							break;
					}

					eable.Free();

					if( contains )
						return false;
				}

				for( int j = 0; j < statics.Length; ++j )
				{
					Tile t = statics[j];

					ItemData id = TileData.ItemTable[t.ID & 0x3FFF];

					flags = id.Flags;
					height = id.CalcHeight;

					if( t.Z <= point.Z && t.Z + height >= point.Z && (flags & (TileFlag.Window | TileFlag.NoShoot)) != 0 )
					{
						if( point.m_X == end.m_X && point.m_Y == end.m_Y && t.Z <= end.m_Z && t.Z + height >= end.m_Z )
							continue;

						return false;
					}

					/*if ( t.Z <= point.Z && t.Z+height >= point.Z && (flags&TileFlag.Window)==0 && (flags&TileFlag.NoShoot)!=0
						&& ( (flags&TileFlag.Wall)!=0 || (flags&TileFlag.Roof)!=0 || (((flags&TileFlag.Surface)!=0 && zd != 0)) ) )*/
					/*{
						//Console.WriteLine( "LoS: Blocked by Static \"{0}\" Z:{1} T:{3} P:{2} F:x{4:X}", TileData.ItemTable[t.ID&0x3FFF].Name, t.Z, point, t.Z+height, flags );
						//Console.WriteLine( "if ( {0} && {1} && {2} && ( {3} || {4} || {5} || ({6} && {7} && {8}) ) )", t.Z <= point.Z, t.Z+height >= point.Z, (flags&TileFlag.Window)==0, (flags&TileFlag.Impassable)!=0, (flags&TileFlag.Wall)!=0, (flags&TileFlag.Roof)!=0, (flags&TileFlag.Surface)!=0, t.Z != dest.Z, zd != 0 ) ;
						return false;
					}*/
				}
			}

			Rectangle2D rect = new Rectangle2D( pTop.m_X, pTop.m_Y, (pBottom.m_X - pTop.m_X) + 1, (pBottom.m_Y - pTop.m_Y) + 1 );

			IPooledEnumerable area = GetItemsInBounds( rect );

			foreach( Item i in area )
			{
				if( !i.Visible )
					continue;

				if( i.ItemID >= 0x8000 )
					continue;

				ItemData id = i.ItemData;
				flags = id.Flags;

				if( (flags & (TileFlag.Window | TileFlag.NoShoot)) == 0 )
					continue;

				height = id.CalcHeight;

				found = false;

				int count = path.Count;

				for( int j = 0; j < count; ++j )
				{
					Point3D point = path[j];
					Point3D loc = i.Location;

					//if ( t.Z <= point.Z && t.Z+height >= point.Z && ( height != 0 || ( t.Z == dest.Z && zd != 0 ) ) )
					if( loc.m_X == point.m_X && loc.m_Y == point.m_Y &&
						loc.m_Z <= point.m_Z && loc.m_Z + height >= point.m_Z )
					{
						if( loc.m_X == end.m_X && loc.m_Y == end.m_Y && loc.m_Z <= end.m_Z && loc.m_Z + height >= end.m_Z )
							continue;

						found = true;
						break;
					}
				}

				if( !found )
					continue;

				area.Free();
				return false;

				/*if ( (flags & (TileFlag.Impassable | TileFlag.Surface | TileFlag.Roof)) != 0 )

				//flags = TileData.ItemTable[i.ItemID&0x3FFF].Flags;
				//if ( (flags&TileFlag.Window)==0 && (flags&TileFlag.NoShoot)!=0 && ( (flags&TileFlag.Wall)!=0 || (flags&TileFlag.Roof)!=0 || (((flags&TileFlag.Surface)!=0 && zd != 0)) ) )
				{
					//height = TileData.ItemTable[i.ItemID&0x3FFF].Height;
					//Console.WriteLine( "LoS: Blocked by ITEM \"{0}\" P:{1} T:{2} F:x{3:X}", TileData.ItemTable[i.ItemID&0x3FFF].Name, i.Location, i.Location.Z+height, flags );
					area.Free();
					return false;
				}*/
			}

			area.Free();

			return true;
		}

		public bool LineOfSight( object from, object dest )
		{
			if ( from == dest || ( from is Mobile && ( (Mobile) from ).AccessLevel > AccessLevel.Player ) )
				return true;
			else if ( dest is Item && from is Mobile && ( (Item) dest ).RootParent == from )
				return true;

			return LineOfSight( GetPoint( from, true ), GetPoint( dest, false ) );
		}

		public bool LineOfSight( Mobile from, Point3D target )
		{
			if ( from.AccessLevel > AccessLevel.Player )
				return true;

			Point3D eye = from.Location;

			eye.Z += 14;

			return LineOfSight( eye, target );
		}

		public bool LineOfSight( Mobile from, Mobile to )
		{
			if ( from == to || from.AccessLevel > AccessLevel.Player )
				return true;

			Point3D eye = from.Location;
			Point3D target = to.Location;

			eye.Z += 14;
			target.Z += 14;//10;

			return LineOfSight( eye, target );
		}
		#endregion

		private static int[] m_InvalidLandTiles = new int[] { 0x244 };

		public static int[] InvalidLandTiles
		{
			get { return m_InvalidLandTiles; }
			set { m_InvalidLandTiles = value; }
		}

		private static Point3DList m_PathList = new Point3DList();
		public int CompareTo( Map other )
		{
			if ( other == null )
				return -1;

			return m_MapID.CompareTo( other.m_MapID );
		}

		public int CompareTo( object other )
		{
			if ( other == null || other is Map )
				return this.CompareTo( other );

			throw new ArgumentException();
		}
	}
}

thank you
 

Peoharen

Sorceror
You still have a lot of 0x3FFF's to change to 0x7FFF which may be doing it. I suppose I wasn't clear enough on what I meant. For the most part supporting the new items is a simple edit, you'll look for 0x4000 and change it to 0x8000 and look for 0x3FFF and replace with with 0x7FFF. This is the "default" editing strategy you're supposed to do.

However, there are cases where you will not be replacing 0x4000 or 0x3FFF.
Example, Scripts\Movement, Check method.
Code:
			bool landBlocks = (TileData.LandTable[landTile.ID & 0x3FFF].Flags & TileFlag.Impassable) != 0;
			bool considerLand = !landTile.Ignored;

			if ( landBlocks && canSwim && (TileData.LandTable[landTile.ID & 0x3FFF].Flags & TileFlag.Wet) != 0 )	//Impassable, Can Swim, and Is water.  Don't block it.
				landBlocks = false;
			else if ( cantWalk && (TileData.LandTable[landTile.ID & 0x3FFF].Flags & TileFlag.Wet) == 0 )	//Can't walk and it's not water
				landBlocks = true;
Land tiles have not been altered. Any instance of 3FFF dealing with land blocks, land tiles, or of the LandTable are to be left alone.

And another example would be the item shift in TileMatrix or TileMatrixPatch (and yes, Map has something like it too)
Code:
lists[pCur->m_X & 0x7][pCur->m_Y & 0x7].Add( (short)((pCur->m_ID & 0x7FFF) + 0x4000), pCur->m_Z );
As you do not alter the 0x4000. For some reason RunUO shifts the itemids by 0x4000, I tried not shifting them and could no longer walk on any statics so either I missed an edit, or more likely there is a important reason why everything is shifted and it has to be done.
 
Top