353 lines
No EOL
11 KiB
C#
353 lines
No EOL
11 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using Server;
|
|
using Server.Items;
|
|
using Server.Regions;
|
|
|
|
namespace Server.Multis
|
|
{
|
|
public enum HousePlacementResult
|
|
{
|
|
Valid,
|
|
BadRegion,
|
|
BadLand,
|
|
BadStatic,
|
|
BadItem,
|
|
NoSurface,
|
|
BadRegionHidden
|
|
}
|
|
|
|
public class HousePlacement
|
|
{
|
|
private const int YardSize = 5;
|
|
|
|
// Any land tile which matches one of these ID numbers is considered a road and cannot be placed over.
|
|
private static int[] m_RoadIDs = new int[]
|
|
{
|
|
0x071, 0x08C,
|
|
0x0E8, 0x0EB,
|
|
0x14C, 0x14F,
|
|
0x161, 0x174,
|
|
0x1F0, 0x1F3,
|
|
0x26E, 0x279,
|
|
0x27E, 0x281,
|
|
0x324, 0x3AC,
|
|
0x547, 0x556,
|
|
0x597, 0x5A6,
|
|
0x637, 0x63A,
|
|
0x7AE, 0x7B1,
|
|
0x442, 0x479, // Sand stones
|
|
0x501, 0x510, // Sand stones
|
|
0x009, 0x015, // Furrows
|
|
0x150, 0x15C // Furrows
|
|
};
|
|
|
|
public static HousePlacementResult Check( Mobile from, int multiID, Point3D center, out ArrayList toMove )
|
|
{
|
|
// If this spot is considered valid, every item and mobile in this list will be moved under the house sign
|
|
toMove = new ArrayList();
|
|
|
|
Map map = from.Map;
|
|
|
|
if ( map == null || map == Map.Internal )
|
|
return HousePlacementResult.BadLand; // A house cannot go here
|
|
|
|
if ( from.AccessLevel >= AccessLevel.GameMaster )
|
|
return HousePlacementResult.Valid; // Staff can place anywhere
|
|
|
|
if ( map == Map.Ilshenar )
|
|
return HousePlacementResult.BadRegion; // No houses in Ilshenar
|
|
|
|
NoHousingRegion noHousingRegion = (NoHousingRegion) Region.Find( center, map ).GetRegion( typeof( NoHousingRegion ) );
|
|
|
|
if ( noHousingRegion != null )
|
|
return HousePlacementResult.BadRegion;
|
|
|
|
// This holds data describing the internal structure of the house
|
|
MultiComponentList mcl = MultiData.GetComponents( multiID );
|
|
|
|
if ( multiID >= 0x13EC && multiID < 0x1D00 )
|
|
HouseFoundation.AddStairsTo( ref mcl ); // this is a AOS house, add the stairs
|
|
|
|
// Location of the nortwest-most corner of the house
|
|
Point3D start = new Point3D( center.X + mcl.Min.X, center.Y + mcl.Min.Y, center.Z );
|
|
|
|
// These are storage lists. They hold items and mobiles found in the map for further processing
|
|
List<Item> items = new List<Item>();
|
|
List<Mobile> mobiles = new List<Mobile>();
|
|
|
|
// These are also storage lists. They hold location values indicating the yard and border locations.
|
|
List<Point2D> yard = new List<Point2D>(), borders = new List<Point2D>();
|
|
|
|
/* RULES:
|
|
*
|
|
* 1) All tiles which are around the -outside- of the foundation must not have anything impassable.
|
|
* 2) No impassable object or land tile may come in direct contact with any part of the house.
|
|
* 3) Five tiles from the front and back of the house must be completely clear of all house tiles.
|
|
* 4) The foundation must rest flatly on a surface. Any bumps around the foundation are not allowed.
|
|
* 5) No foundation tile may reside over terrain which is viewed as a road.
|
|
*/
|
|
|
|
for ( int x = 0; x < mcl.Width; ++x )
|
|
{
|
|
for ( int y = 0; y < mcl.Height; ++y )
|
|
{
|
|
int tileX = start.X + x;
|
|
int tileY = start.Y + y;
|
|
|
|
Tile[] addTiles = mcl.Tiles[x][y];
|
|
|
|
if ( addTiles.Length == 0 )
|
|
continue; // There are no tiles here, continue checking somewhere else
|
|
|
|
Point3D testPoint = new Point3D( tileX, tileY, center.Z );
|
|
|
|
Region reg = Region.Find( testPoint, map );
|
|
|
|
if ( !reg.AllowHousing( from, testPoint ) ) // Cannot place houses in dungeons, towns, treasure map areas etc
|
|
{
|
|
if ( reg.IsPartOf( typeof( TreasureRegion ) ) )
|
|
return HousePlacementResult.BadRegionHidden;
|
|
|
|
return HousePlacementResult.BadRegion;
|
|
}
|
|
|
|
Tile landTile = map.Tiles.GetLandTile( tileX, tileY );
|
|
int landID = landTile.ID & 0x3FFF;
|
|
|
|
Tile[] oldTiles = map.Tiles.GetStaticTiles( tileX, tileY, true );
|
|
|
|
Sector sector = map.GetSector( tileX, tileY );
|
|
|
|
items.Clear();
|
|
|
|
for ( int i = 0; i < sector.Items.Count; ++i )
|
|
{
|
|
Item item = sector.Items[i];
|
|
|
|
if ( item.Visible && item.X == tileX && item.Y == tileY )
|
|
items.Add( item );
|
|
}
|
|
|
|
mobiles.Clear();
|
|
|
|
for ( int i = 0; i < sector.Mobiles.Count; ++i )
|
|
{
|
|
Mobile m = sector.Mobiles[i];
|
|
|
|
if ( m.X == tileX && m.Y == tileY )
|
|
mobiles.Add( m );
|
|
}
|
|
|
|
int landStartZ = 0, landAvgZ = 0, landTopZ = 0;
|
|
|
|
map.GetAverageZ( tileX, tileY, ref landStartZ, ref landAvgZ, ref landTopZ );
|
|
|
|
bool hasFoundation = false;
|
|
|
|
for ( int i = 0; i < addTiles.Length; ++i )
|
|
{
|
|
Tile addTile = addTiles[i];
|
|
|
|
if ( addTile.ID == 0x4001 ) // Nodraw
|
|
continue;
|
|
|
|
TileFlag addTileFlags = TileData.ItemTable[addTile.ID & 0x3FFF].Flags;
|
|
|
|
bool isFoundation = ( addTile.Z == 0 && (addTileFlags & TileFlag.Wall) != 0 );
|
|
bool hasSurface = false;
|
|
|
|
if ( isFoundation )
|
|
hasFoundation = true;
|
|
|
|
int addTileZ = center.Z + addTile.Z;
|
|
int addTileTop = addTileZ + addTile.Height;
|
|
|
|
if ( (addTileFlags & TileFlag.Surface) != 0 )
|
|
addTileTop += 16;
|
|
|
|
if ( addTileTop > landStartZ && landAvgZ > addTileZ )
|
|
return HousePlacementResult.BadLand; // Broke rule #2
|
|
|
|
if ( isFoundation && ((TileData.LandTable[landTile.ID & 0x3FFF].Flags & TileFlag.Impassable) == 0) && landAvgZ == center.Z )
|
|
hasSurface = true;
|
|
|
|
for ( int j = 0; j < oldTiles.Length; ++j )
|
|
{
|
|
Tile oldTile = oldTiles[j];
|
|
ItemData id = TileData.ItemTable[oldTile.ID & 0x3FFF];
|
|
|
|
if ( (id.Impassable || (id.Surface && (id.Flags & TileFlag.Background) == 0)) && addTileTop > oldTile.Z && (oldTile.Z + id.CalcHeight) > addTileZ )
|
|
return HousePlacementResult.BadStatic; // Broke rule #2
|
|
/*else if ( isFoundation && !hasSurface && (id.Flags & TileFlag.Surface) != 0 && (oldTile.Z + id.CalcHeight) == center.Z )
|
|
hasSurface = true;*/
|
|
}
|
|
|
|
for ( int j = 0; j < items.Count; ++j )
|
|
{
|
|
Item item = items[j];
|
|
ItemData id = item.ItemData;
|
|
|
|
if ( addTileTop > item.Z && (item.Z + id.CalcHeight) > addTileZ )
|
|
{
|
|
if ( item.Movable )
|
|
toMove.Add( item );
|
|
else if ( (id.Impassable || (id.Surface && (id.Flags & TileFlag.Background) == 0)) )
|
|
return HousePlacementResult.BadItem; // Broke rule #2
|
|
}
|
|
/*else if ( isFoundation && !hasSurface && (id.Flags & TileFlag.Surface) != 0 && (item.Z + id.CalcHeight) == center.Z )
|
|
{
|
|
hasSurface = true;
|
|
}*/
|
|
}
|
|
|
|
if ( isFoundation && !hasSurface )
|
|
return HousePlacementResult.NoSurface; // Broke rule #4
|
|
|
|
for ( int j = 0; j < mobiles.Count; ++j )
|
|
{
|
|
Mobile m = mobiles[j];
|
|
|
|
if ( addTileTop > m.Z && (m.Z + 16) > addTileZ )
|
|
toMove.Add( m );
|
|
}
|
|
}
|
|
|
|
for ( int i = 0; i < m_RoadIDs.Length; i += 2 )
|
|
{
|
|
if ( landID >= m_RoadIDs[i] && landID <= m_RoadIDs[i + 1] )
|
|
return HousePlacementResult.BadLand; // Broke rule #5
|
|
}
|
|
|
|
if ( hasFoundation )
|
|
{
|
|
for ( int xOffset = -1; xOffset <= 1; ++xOffset )
|
|
{
|
|
for ( int yOffset = -YardSize; yOffset <= YardSize; ++yOffset )
|
|
{
|
|
Point2D yardPoint = new Point2D( tileX + xOffset, tileY + yOffset );
|
|
|
|
if ( !yard.Contains( yardPoint ) )
|
|
yard.Add( yardPoint );
|
|
}
|
|
}
|
|
|
|
for ( int xOffset = -1; xOffset <= 1; ++xOffset )
|
|
{
|
|
for ( int yOffset = -1; yOffset <= 1; ++yOffset )
|
|
{
|
|
if ( xOffset == 0 && yOffset == 0 )
|
|
continue;
|
|
|
|
// To ease this rule, we will not add to the border list if the tile here is under a base floor (z<=8)
|
|
|
|
int vx = x + xOffset;
|
|
int vy = y + yOffset;
|
|
|
|
if ( vx >= 0 && vx < mcl.Width && vy >= 0 && vy < mcl.Height )
|
|
{
|
|
Tile[] breakTiles = mcl.Tiles[vx][vy];
|
|
bool shouldBreak = false;
|
|
|
|
for ( int i = 0; !shouldBreak && i < breakTiles.Length; ++i )
|
|
{
|
|
Tile breakTile = breakTiles[i];
|
|
|
|
if ( breakTile.Height == 0 && breakTile.Z <= 8 && TileData.ItemTable[breakTile.ID & 0x3FFF].Surface )
|
|
shouldBreak = true;
|
|
}
|
|
|
|
if ( shouldBreak )
|
|
continue;
|
|
}
|
|
|
|
Point2D borderPoint = new Point2D( tileX + xOffset, tileY + yOffset );
|
|
|
|
if ( !borders.Contains( borderPoint ) )
|
|
borders.Add( borderPoint );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for ( int i = 0; i < borders.Count; ++i )
|
|
{
|
|
Point2D borderPoint = borders[i];
|
|
|
|
Tile landTile = map.Tiles.GetLandTile( borderPoint.X, borderPoint.Y );
|
|
int landID = landTile.ID & 0x3FFF;
|
|
|
|
if ( (TileData.LandTable[landID].Flags & TileFlag.Impassable) != 0 )
|
|
return HousePlacementResult.BadLand;
|
|
|
|
for ( int j = 0; j < m_RoadIDs.Length; j += 2 )
|
|
{
|
|
if ( landID >= m_RoadIDs[j] && landID <= m_RoadIDs[j + 1] )
|
|
return HousePlacementResult.BadLand; // Broke rule #5
|
|
}
|
|
|
|
Tile[] tiles = map.Tiles.GetStaticTiles( borderPoint.X, borderPoint.Y, true );
|
|
|
|
for ( int j = 0; j < tiles.Length; ++j )
|
|
{
|
|
Tile tile = tiles[j];
|
|
ItemData id = TileData.ItemTable[tile.ID & 0x3FFF];
|
|
|
|
if ( id.Impassable || (id.Surface && (id.Flags & TileFlag.Background) == 0 && (tile.Z + id.CalcHeight) > (center.Z + 2)) )
|
|
return HousePlacementResult.BadStatic; // Broke rule #1
|
|
}
|
|
|
|
Sector sector = map.GetSector( borderPoint.X, borderPoint.Y );
|
|
List<Item> sectorItems = sector.Items;
|
|
|
|
for ( int j = 0; j < sectorItems.Count; ++j )
|
|
{
|
|
Item item = sectorItems[j];
|
|
|
|
if ( item.X != borderPoint.X || item.Y != borderPoint.Y || item.Movable )
|
|
continue;
|
|
|
|
ItemData id = item.ItemData;
|
|
|
|
if ( id.Impassable || (id.Surface && (id.Flags & TileFlag.Background) == 0 && (item.Z + id.CalcHeight) > (center.Z + 2)) )
|
|
return HousePlacementResult.BadItem; // Broke rule #1
|
|
}
|
|
}
|
|
|
|
for ( int i = 0; i < yard.Count; ++i )
|
|
{
|
|
Point2D yardPoint = yard[i];
|
|
|
|
Sector sector = map.GetSector( yardPoint );
|
|
|
|
foreach ( BaseMulti multi in sector.Multis ) {
|
|
if ( multi is BaseBoat )
|
|
continue;
|
|
|
|
return HousePlacementResult.BadStatic; // Broke rule #3
|
|
}
|
|
|
|
/*IPooledEnumerable eable = map.GetMultiTilesAt( yardPoint.X, yardPoint.Y );
|
|
|
|
foreach ( Tile[] tile in eable )
|
|
{
|
|
for ( int j = 0; j < tile.Length; ++j )
|
|
{
|
|
if ( (TileData.ItemTable[tile[j].ID & 0x3FFF].Flags & (TileFlag.Impassable | TileFlag.Surface)) != 0 )
|
|
{
|
|
eable.Free();
|
|
return HousePlacementResult.BadStatic; // Broke rule #3
|
|
}
|
|
}
|
|
}
|
|
|
|
eable.Free();*/
|
|
}
|
|
|
|
return HousePlacementResult.Valid;
|
|
|
|
}
|
|
}
|
|
} |