mirror of
https://github.com/runuo/runuo
synced 2026-08-11 22:26:04 -04:00
532 lines
No EOL
14 KiB
C#
532 lines
No EOL
14 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using Server.Commands;
|
|
using Server.Mobiles;
|
|
|
|
namespace Server
|
|
{
|
|
public class VendorGenerator
|
|
{
|
|
public static void Initialize()
|
|
{
|
|
CommandSystem.Register( "VendorGen", AccessLevel.Administrator, new CommandEventHandler( VendorGenerator.VendorGen_OnCommand ) );
|
|
}
|
|
|
|
private static Rectangle2D[] m_BritRegions = new Rectangle2D[]
|
|
{
|
|
new Rectangle2D( new Point2D( 250, 750 ), new Point2D( 775, 1330 ) ),
|
|
new Rectangle2D( new Point2D( 525, 2095 ), new Point2D( 925, 2430 ) ),
|
|
new Rectangle2D( new Point2D( 1025, 2155 ), new Point2D( 1265, 2310 ) ),
|
|
new Rectangle2D( new Point2D( 1635, 2430 ), new Point2D( 1705, 2508 ) ),
|
|
new Rectangle2D( new Point2D( 1775, 2605 ), new Point2D( 2165, 2975 ) ),
|
|
new Rectangle2D( new Point2D( 1055, 3520 ), new Point2D( 1570, 4075 ) ),
|
|
new Rectangle2D( new Point2D( 2860, 3310 ), new Point2D( 3120, 3630 ) ),
|
|
new Rectangle2D( new Point2D( 2470, 1855 ), new Point2D( 3950, 3045 ) ),
|
|
new Rectangle2D( new Point2D( 3425, 990 ), new Point2D( 3900, 1455 ) ),
|
|
new Rectangle2D( new Point2D( 4175, 735 ), new Point2D( 4840, 1600 ) ),
|
|
new Rectangle2D( new Point2D( 2375, 330 ), new Point2D( 3100, 1045 ) ),
|
|
new Rectangle2D( new Point2D( 2100, 1090 ), new Point2D( 2310, 1450 ) ),
|
|
new Rectangle2D( new Point2D( 1495, 1400 ), new Point2D( 1550, 1475 ) ),
|
|
new Rectangle2D( new Point2D( 1085, 1520 ), new Point2D( 1415, 1910 ) ),
|
|
new Rectangle2D( new Point2D( 1410, 1500 ), new Point2D( 1745, 1795 ) ),
|
|
new Rectangle2D( new Point2D( 5120, 2300 ), new Point2D( 6143, 4095 ) )
|
|
};
|
|
|
|
private static Rectangle2D[] m_IlshRegions = new Rectangle2D[]
|
|
{
|
|
new Rectangle2D( new Point2D( 0, 0 ), new Point2D( 288*8, 200*8 ) )
|
|
};
|
|
|
|
[Usage( "VendorGen" )]
|
|
[Description( "Generates vendors based on display cases and floor plans. Analyzes the map files, slow." )]
|
|
private static void VendorGen_OnCommand( CommandEventArgs e )
|
|
{
|
|
Process( Map.Trammel, m_BritRegions );
|
|
Process( Map.Felucca, m_BritRegions );
|
|
Process( Map.Ilshenar, m_IlshRegions );
|
|
}
|
|
|
|
private static bool GetFloorZ( Map map, int x, int y, out int z )
|
|
{
|
|
LandTile lt = map.Tiles.GetLandTile( x, y );
|
|
|
|
if ( IsFloor( lt.ID ) && map.CanFit( x, y, lt.Z, 16, false, false ) )
|
|
{
|
|
z = lt.Z;
|
|
return true;
|
|
}
|
|
|
|
StaticTile[] tiles = map.Tiles.GetStaticTiles( x, y );
|
|
|
|
for ( int i = 0; i < tiles.Length; ++i )
|
|
{
|
|
StaticTile t = tiles[i];
|
|
ItemData id = TileData.ItemTable[t.ID & TileData.MaxItemValue];
|
|
|
|
if ( IsStaticFloor( t.ID ) && map.CanFit( x, y, t.Z + (id.Surface ? id.CalcHeight : 0), 16, false, false ) )
|
|
{
|
|
z = t.Z + (id.Surface ? id.CalcHeight : 0);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
z = 0;
|
|
return false;
|
|
}
|
|
|
|
private static bool IsFloor( Map map, int x, int y, bool canFit )
|
|
{
|
|
LandTile lt = map.Tiles.GetLandTile( x, y );
|
|
|
|
if ( IsFloor( lt.ID ) && (canFit||CanFit( map, x, y, lt.Z )) )
|
|
return true;
|
|
|
|
StaticTile[] tiles = map.Tiles.GetStaticTiles( x, y );
|
|
|
|
for ( int i = 0; i < tiles.Length; ++i )
|
|
{
|
|
StaticTile t = tiles[i];
|
|
ItemData id = TileData.ItemTable[t.ID & TileData.MaxItemValue];
|
|
|
|
if ( IsStaticFloor( t.ID ) && (canFit||CanFit( map, x, y, t.Z + (id.Surface ? id.CalcHeight : 0) )) )
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private static bool IsFloor( int itemID )
|
|
{
|
|
itemID &= TileData.MaxLandValue;
|
|
|
|
return ( itemID >= 0x406 && itemID <= 0x51A );
|
|
}
|
|
|
|
private static bool IsStaticFloor( int itemID )
|
|
{
|
|
return ( itemID >= 0x495 && itemID <= 0x514 )
|
|
|| ( itemID >= 0x519 && itemID <= 0x53A );
|
|
}
|
|
|
|
private static bool IsDisplayCase( int itemID )
|
|
{
|
|
return ( itemID >= 0xB00 && itemID <= 0xB02 )
|
|
|| ( itemID >= 0xB06 && itemID <= 0xB0A )
|
|
|| ( itemID >= 0xB0D && itemID <= 0xB17 );
|
|
}
|
|
|
|
private static void Process( Map map, Rectangle2D[] regions )
|
|
{
|
|
m_ShopTable = new Hashtable();
|
|
m_ShopList = new ArrayList();
|
|
|
|
World.Broadcast( 0x35, true, "Generating vendor spawns for {0}, please wait.", map );
|
|
|
|
for ( int i = 0; i < regions.Length; ++i )
|
|
for ( int x = 0; x < map.Width; ++x )
|
|
for ( int y = 0; y < map.Height; ++y )
|
|
CheckPoint( map, regions[i].X + x, regions[i].Y + y );
|
|
|
|
for ( int i = 0; i < m_ShopList.Count; ++i )
|
|
{
|
|
ShopInfo si = (ShopInfo)m_ShopList[i];
|
|
|
|
int xTotal = 0;
|
|
int yTotal = 0;
|
|
|
|
bool hasSpawner = false;
|
|
|
|
for ( int j = 0; !hasSpawner && j < si.m_Floor.Count; ++j )
|
|
{
|
|
Point2D fp = (Point2D)si.m_Floor[j];
|
|
|
|
xTotal += fp.X;
|
|
yTotal += fp.Y;
|
|
|
|
IPooledEnumerable eable = map.GetItemsInRange( new Point3D( fp.X, fp.Y, 0 ), 0 );
|
|
|
|
foreach ( Item item in eable )
|
|
{
|
|
if ( item is Spawner )
|
|
{
|
|
hasSpawner = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
eable.Free();
|
|
|
|
if ( hasSpawner )
|
|
break;
|
|
}
|
|
|
|
if ( hasSpawner )
|
|
continue;
|
|
|
|
int xAvg = xTotal / si.m_Floor.Count;
|
|
int yAvg = yTotal / si.m_Floor.Count;
|
|
|
|
ArrayList names = new ArrayList();
|
|
ShopFlags flags = si.m_Flags;
|
|
|
|
if ( (flags & ShopFlags.Armor) != 0 )
|
|
names.Add( "armorer" );
|
|
|
|
if ( (flags & ShopFlags.MetalWeapon) != 0 )
|
|
names.Add( "weaponsmith" );
|
|
|
|
if ( (flags & ShopFlags.ArcheryWeapon) != 0 )
|
|
names.Add( "bowyer" );
|
|
|
|
if ( (flags & ShopFlags.Scroll) != 0 )
|
|
names.Add( "mage" );
|
|
|
|
if ( (flags & ShopFlags.Spellbook) != 0 )
|
|
names.Add( "mage" );
|
|
|
|
if ( (flags & ShopFlags.Bread) != 0 )
|
|
names.Add( "baker" );
|
|
|
|
if ( (flags & ShopFlags.Jewel) != 0 )
|
|
names.Add( "jeweler" );
|
|
|
|
if ( (flags & ShopFlags.Potion) != 0 )
|
|
{
|
|
names.Add( "herbalist" );
|
|
names.Add( "alchemist" );
|
|
names.Add( "mage" );
|
|
}
|
|
|
|
if ( (flags & ShopFlags.Reagent) != 0 )
|
|
{
|
|
names.Add( "mage" );
|
|
names.Add( "herbalist" );
|
|
}
|
|
|
|
if ( (flags & ShopFlags.Clothes) != 0 )
|
|
{
|
|
names.Add( "tailor" );
|
|
names.Add( "weaver" );
|
|
}
|
|
|
|
for ( int j = 0; j < names.Count; ++j )
|
|
{
|
|
Point2D cp = Point2D.Zero;
|
|
int dist = 100000;
|
|
int tz;
|
|
|
|
for ( int k = 0; k < si.m_Floor.Count; ++k )
|
|
{
|
|
Point2D fp = (Point2D)si.m_Floor[k];
|
|
|
|
int rx = fp.X - xAvg;
|
|
int ry = fp.Y - yAvg;
|
|
int fd = (int)Math.Sqrt( rx*rx + ry*ry );
|
|
|
|
if ( fd > 0 && fd < 5 )
|
|
fd -= Utility.Random( 10 );
|
|
|
|
if ( fd < dist && GetFloorZ( map, fp.X, fp.Y, out tz ) )
|
|
{
|
|
dist = fd;
|
|
cp = fp;
|
|
}
|
|
}
|
|
|
|
if ( cp == Point2D.Zero )
|
|
continue;
|
|
|
|
int z;
|
|
|
|
if ( !GetFloorZ( map, cp.X, cp.Y, out z ) )
|
|
continue;
|
|
|
|
new Spawner( 1, 1, 1, 0, 4, (string)names[j] ).MoveToWorld( new Point3D( cp.X, cp.Y, z ), map );
|
|
}
|
|
}
|
|
|
|
World.Broadcast( 0x35, true, "Generation complete. {0} spawners generated.", m_ShopList.Count );
|
|
}
|
|
|
|
private static void CheckPoint( Map map, int x, int y )
|
|
{
|
|
if ( IsFloor( map, x, y, true ) )
|
|
CheckFloor( map, x, y );
|
|
}
|
|
|
|
private static void CheckFloor( Map map, int x, int y )
|
|
{
|
|
StaticTile[] tiles = map.Tiles.GetStaticTiles( x, y );
|
|
|
|
for ( int i = 0; i < tiles.Length; ++i )
|
|
{
|
|
if ( IsDisplayCase( tiles[i].ID ) )
|
|
{
|
|
ProcessDisplayCase( map, tiles, x, y );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
[Flags]
|
|
private enum ShopFlags
|
|
{
|
|
None = 0x000,
|
|
Armor = 0x001,
|
|
MetalWeapon = 0x002,
|
|
Jewel = 0x004,
|
|
Reagent = 0x008,
|
|
Potion = 0x010,
|
|
Bread = 0x020,
|
|
Clothes = 0x040,
|
|
ArcheryWeapon = 0x080,
|
|
Scroll = 0x100,
|
|
Spellbook = 0x200
|
|
}
|
|
|
|
private static bool IsClothes( int itemID )
|
|
{
|
|
if ( itemID >= 0x1515 && itemID <= 0x1518 )
|
|
return true;
|
|
|
|
if ( itemID >= 0x152E && itemID <= 0x1531 )
|
|
return true;
|
|
|
|
if ( itemID >= 0x1537 && itemID <= 0x154C )
|
|
return true;
|
|
|
|
if ( itemID >= 0x1EFD && itemID <= 0x1F04 )
|
|
return true;
|
|
|
|
if ( itemID >= 0x170B && itemID <= 0x171C )
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
private static bool IsArmor( int itemID )
|
|
{
|
|
if ( itemID >= 0x13BB && itemID <= 0x13E2 )
|
|
return true;
|
|
|
|
if ( itemID >= 0x13E5 && itemID <= 0x13F2 )
|
|
return true;
|
|
|
|
if ( itemID >= 0x1408 && itemID <= 0x141A )
|
|
return true;
|
|
|
|
if ( itemID >= 0x144E && itemID <= 0x1457 )
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
private static bool IsMetalWeapon( int itemID )
|
|
{
|
|
if ( itemID >= 0xF43 && itemID <= 0xF4E )
|
|
return true;
|
|
|
|
if ( itemID >= 0xF51 && itemID <= 0xF52 )
|
|
return true;
|
|
|
|
if ( itemID >= 0xF5C && itemID <= 0xF63 )
|
|
return true;
|
|
|
|
if ( itemID >= 0x13AF && itemID <= 0x13B0 )
|
|
return true;
|
|
|
|
if ( itemID >= 0x13B5 && itemID <= 0x13BA )
|
|
return true;
|
|
|
|
if ( itemID >= 0x13FA && itemID <= 0x13FB )
|
|
return true;
|
|
|
|
if ( itemID >= 0x13FE && itemID <= 0x1407 )
|
|
return true;
|
|
|
|
if ( itemID >= 0x1438 && itemID <= 0x1443 )
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
private static bool IsArcheryWeapon( int itemID )
|
|
{
|
|
if ( itemID >= 0xF4F && itemID <= 0xF50 )
|
|
return true;
|
|
|
|
if ( itemID >= 0x13B1 && itemID <= 0x13B2 )
|
|
return true;
|
|
|
|
if ( itemID >= 0x13FC && itemID <= 0x13FD )
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
private static ShopFlags ProcessDisplayedItem( int itemID )
|
|
{
|
|
itemID &= TileData.MaxItemValue;
|
|
|
|
ShopFlags res = ShopFlags.None;
|
|
|
|
ItemData id = TileData.ItemTable[itemID];
|
|
TileFlag flags = id.Flags;
|
|
|
|
if ( (flags & TileFlag.Wearable) != 0 )
|
|
{
|
|
if ( IsClothes( itemID ) )
|
|
res |= ShopFlags.Clothes;
|
|
else if ( IsArmor( itemID ) )
|
|
res |= ShopFlags.Armor;
|
|
else if ( IsMetalWeapon( itemID ) )
|
|
res |= ShopFlags.MetalWeapon;
|
|
else if ( IsArcheryWeapon( itemID ) )
|
|
res |= ShopFlags.ArcheryWeapon;
|
|
}
|
|
|
|
if ( itemID == 0x98C || itemID == 0x103B || itemID == 0x103C )
|
|
res |= ShopFlags.Bread;
|
|
|
|
if ( itemID >= 0xF0F && itemID <= 0xF30 )
|
|
res |= ShopFlags.Jewel;
|
|
|
|
if ( itemID >= 0xEFB && itemID <= 0xF0D )
|
|
res |= ShopFlags.Potion;
|
|
|
|
if ( itemID >= 0xF78 && itemID <= 0xF91 )
|
|
res |= ShopFlags.Reagent;
|
|
|
|
if ( (itemID >= 0xE35 && itemID <= 0xE3A) || (itemID >= 0xEF4 && itemID <= 0xEF9) || (itemID >= 0x1F2D && itemID <= 0x1F72) )
|
|
res |= ShopFlags.Scroll;
|
|
|
|
if ( itemID == 0xE38 || itemID == 0xEFA )
|
|
res |= ShopFlags.Spellbook;
|
|
|
|
return res;
|
|
}
|
|
|
|
private static void ProcessDisplayCase( Map map, StaticTile[] tiles, int x, int y )
|
|
{
|
|
ShopFlags flags = ShopFlags.None;
|
|
|
|
for ( int i = 0; i < tiles.Length; ++i )
|
|
flags |= ProcessDisplayedItem( tiles[i].ID );
|
|
|
|
if ( flags != ShopFlags.None )
|
|
{
|
|
Point2D p = new Point2D( x, y );
|
|
ShopInfo si = (ShopInfo)m_ShopTable[p];
|
|
|
|
if ( si == null )
|
|
{
|
|
ArrayList floor = new ArrayList();
|
|
|
|
RecurseFindFloor( map, x, y, floor );
|
|
|
|
if ( floor.Count == 0 )
|
|
return;
|
|
|
|
si = new ShopInfo();
|
|
si.m_Flags = flags;
|
|
si.m_Floor = floor;
|
|
m_ShopList.Add( si );
|
|
|
|
for ( int i = 0; i < floor.Count; ++i )
|
|
m_ShopTable[(Point2D)floor[i]] = si;
|
|
}
|
|
else
|
|
{
|
|
si.m_Flags |= flags;
|
|
}
|
|
}
|
|
}
|
|
|
|
private static Hashtable m_ShopTable;
|
|
private static ArrayList m_ShopList;
|
|
|
|
private class ShopInfo
|
|
{
|
|
public ShopFlags m_Flags;
|
|
public ArrayList m_Floor;
|
|
}
|
|
|
|
private static bool CanFit( Map map, int x, int y, int z )
|
|
{
|
|
bool hasSurface = false;
|
|
|
|
LandTile lt = map.Tiles.GetLandTile( x, y );
|
|
int lowZ = 0, avgZ = 0, topZ = 0;
|
|
|
|
map.GetAverageZ( x, y, ref lowZ, ref avgZ, ref topZ );
|
|
TileFlag landFlags = TileData.LandTable[lt.ID & TileData.MaxLandValue].Flags;
|
|
|
|
if ( (landFlags & TileFlag.Impassable) != 0 && topZ > z && (z + 16) > lowZ )
|
|
return false;
|
|
else if ( (landFlags & TileFlag.Impassable) == 0 && z == avgZ && !lt.Ignored )
|
|
hasSurface = true;
|
|
|
|
StaticTile[] staticTiles = map.Tiles.GetStaticTiles( x, y );
|
|
|
|
bool surface, impassable;
|
|
|
|
for ( int i = 0; i < staticTiles.Length; ++i )
|
|
{
|
|
if ( IsDisplayCase( staticTiles[i].ID ) )
|
|
continue;
|
|
|
|
ItemData id = TileData.ItemTable[staticTiles[i].ID & TileData.MaxItemValue];
|
|
|
|
surface = id.Surface;
|
|
impassable = id.Impassable;
|
|
|
|
if ( (surface || impassable) && (staticTiles[i].Z + id.CalcHeight) > z && (z + 16) > staticTiles[i].Z )
|
|
return false;
|
|
else if ( surface && !impassable && z == (staticTiles[i].Z + id.CalcHeight) )
|
|
hasSurface = true;
|
|
}
|
|
|
|
Sector sector = map.GetSector( x, y );
|
|
List<Item> items = sector.Items;
|
|
|
|
for ( int i = 0; i < items.Count; ++i )
|
|
{
|
|
Item item = items[i];
|
|
|
|
if ( item.AtWorldPoint( x, y ) )
|
|
{
|
|
ItemData id = item.ItemData;
|
|
surface = id.Surface;
|
|
impassable = id.Impassable;
|
|
|
|
if ( (surface || impassable) && (item.Z + id.CalcHeight) > z && (z + 16) > item.Z )
|
|
return false;
|
|
else if ( surface && !impassable && z == (item.Z + id.CalcHeight) )
|
|
hasSurface = true;
|
|
}
|
|
}
|
|
|
|
return hasSurface;
|
|
}
|
|
|
|
private static void RecurseFindFloor( Map map, int x, int y, ArrayList floor )
|
|
{
|
|
Point2D p = new Point2D( x, y );
|
|
|
|
if ( floor.Contains( p ) )
|
|
return;
|
|
|
|
floor.Add( p );
|
|
|
|
for ( int xo = -1; xo <= 1; ++xo )
|
|
{
|
|
for ( int yo = -1; yo <= 1; ++yo )
|
|
{
|
|
if ( (xo != 0 || yo != 0) && IsFloor( map, x + xo, y + yo, false ) )
|
|
RecurseFindFloor( map, x + xo, y + yo, floor );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} |