1274 lines
43 KiB
C#
1274 lines
43 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.IO;
|
|
|
|
using Server;
|
|
using Server.Items;
|
|
using Server.Mobiles;
|
|
using Server.ContextMenus;
|
|
using Server.Commands;
|
|
using Server.Targeting;
|
|
using Server.Regions;
|
|
using Server.Gumps;
|
|
using Server.Engines.Auction;
|
|
|
|
namespace Server.Engines.VendorSearching
|
|
{
|
|
public class VendorSearch
|
|
{
|
|
public static string FilePath = Path.Combine("Saves/Misc", "VendorSearch.bin");
|
|
public static Ultima.StringList StringList { get; private set; }
|
|
|
|
public static List<SearchItem> DoSearchAuction(Mobile m, SearchCriteria criteria)
|
|
{
|
|
if (criteria == null || Auction.Auction.Auctions == null || Auction.Auction.Auctions.Count == 0)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
List<SearchItem> list = new List<SearchItem>();
|
|
bool excludefel = criteria.Details.FirstOrDefault(d => d.Attribute is Misc && (Misc)d.Attribute == Misc.ExcludeFel) != null;
|
|
|
|
foreach (Auction.Auction pv in Auction.Auction.Auctions.Where(pv => pv.AuctionItem != null &&
|
|
pv.AuctionItem.Map != Map.Internal &&
|
|
pv.AuctionItem.Map != null &&
|
|
pv.OnGoing &&
|
|
(!excludefel || pv.AuctionItem.Map != Map.Felucca)))
|
|
{
|
|
list.Add(new SearchItem(pv.Safe, pv.AuctionItem, (int)pv.Buyout, false));
|
|
}
|
|
|
|
switch (criteria.SortBy)
|
|
{
|
|
case SortBy.LowToHigh: list = list.OrderBy(vi => vi.Price).ToList(); break;
|
|
case SortBy.HighToLow: list = list.OrderBy(vi => -vi.Price).ToList(); break;
|
|
}
|
|
|
|
return list;
|
|
}
|
|
|
|
public static List<SearchItem> DoSearch(Mobile m, SearchCriteria criteria)
|
|
{
|
|
if (criteria == null || PlayerVendor.PlayerVendors == null || PlayerVendor.PlayerVendors.Count == 0)
|
|
return null;
|
|
|
|
List<SearchItem> list = new List<SearchItem>();
|
|
bool excludefel = criteria.Details.FirstOrDefault(d => d.Attribute is Misc && (Misc)d.Attribute == Misc.ExcludeFel) != null;
|
|
|
|
foreach (PlayerVendor pv in PlayerVendor.PlayerVendors.Where(pv => pv.Map != Map.Internal &&
|
|
pv.Map != null &&
|
|
pv.Backpack != null &&
|
|
pv.VendorSearch &&
|
|
pv.Backpack.Items.Count > 0 &&
|
|
(!excludefel || pv.Map != Map.Felucca)))
|
|
{
|
|
List<Item> items = GetItems(pv);
|
|
|
|
foreach (Item item in items)
|
|
{
|
|
VendorItem vendorItem = pv.GetVendorItem(item);
|
|
int price = 0;
|
|
bool isChild = false;
|
|
|
|
if (vendorItem != null)
|
|
{
|
|
price = vendorItem.Price;
|
|
}
|
|
else if (item.Parent is Container)
|
|
{
|
|
vendorItem = GetParentVendorItem(pv, (Container)item.Parent);
|
|
|
|
if (vendorItem != null)
|
|
{
|
|
isChild = true;
|
|
price = vendorItem.Price;
|
|
}
|
|
}
|
|
|
|
if (price > 0 && CheckMatch(item, price, criteria))
|
|
{
|
|
list.Add(new SearchItem(pv, item, price, isChild));
|
|
}
|
|
}
|
|
|
|
ColUtility.Free(items);
|
|
}
|
|
|
|
switch (criteria.SortBy)
|
|
{
|
|
case SortBy.LowToHigh: list = list.OrderBy(vi => vi.Price).ToList(); break;
|
|
case SortBy.HighToLow: list = list.OrderBy(vi => -vi.Price).ToList(); break;
|
|
}
|
|
|
|
return list;
|
|
}
|
|
|
|
private static VendorItem GetParentVendorItem(PlayerVendor pv, Container parent)
|
|
{
|
|
VendorItem vendorItem = pv.GetVendorItem(parent);
|
|
|
|
if (vendorItem == null)
|
|
{
|
|
if (parent.Parent is Container)
|
|
{
|
|
return GetParentVendorItem(pv, (Container)parent.Parent);
|
|
}
|
|
}
|
|
|
|
return vendorItem;
|
|
}
|
|
|
|
public static bool CheckMatch(Item item, int price, SearchCriteria searchCriteria)
|
|
{
|
|
if (item is CommodityDeed && ((CommodityDeed)item).Commodity != null)
|
|
{
|
|
item = ((CommodityDeed)item).Commodity;
|
|
}
|
|
|
|
if (searchCriteria.MinPrice > -1 && price < searchCriteria.MinPrice)
|
|
return false;
|
|
|
|
if (searchCriteria.MaxPrice > -1 && price > searchCriteria.MaxPrice)
|
|
return false;
|
|
|
|
if (!string.IsNullOrEmpty(searchCriteria.SearchName))
|
|
{
|
|
string name;
|
|
|
|
if (item is CommodityDeed && ((CommodityDeed)item).Commodity is ICommodity)
|
|
{
|
|
var commodity = (ICommodity)((CommodityDeed)item).Commodity;
|
|
|
|
if (!string.IsNullOrEmpty(commodity.Description.String))
|
|
{
|
|
name = commodity.Description.String;
|
|
}
|
|
else
|
|
{
|
|
name = StringList.GetString(commodity.Description.Number);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
name = GetItemName(item);
|
|
}
|
|
|
|
if(name == null)
|
|
{
|
|
return false; // TODO? REturn null names?
|
|
}
|
|
|
|
if (!CheckKeyword(searchCriteria.SearchName, item) && name.ToLower().IndexOf(searchCriteria.SearchName.ToLower()) < 0)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (searchCriteria.SearchType != Layer.Invalid && searchCriteria.SearchType != item.Layer)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if(searchCriteria.Details.Count == 0)
|
|
return true;
|
|
|
|
foreach(SearchDetail detail in searchCriteria.Details)
|
|
{
|
|
object o = detail.Attribute;
|
|
int value = detail.Value;
|
|
|
|
if (value == 0)
|
|
value = 1;
|
|
|
|
if (o is AosAttribute)
|
|
{
|
|
AosAttributes attrs = RunicReforging.GetAosAttributes(item);
|
|
|
|
if (attrs == null || attrs[(AosAttribute)o] < value)
|
|
return false;
|
|
}
|
|
else if (o is AosWeaponAttribute)
|
|
{
|
|
AosWeaponAttributes attrs = RunicReforging.GetAosWeaponAttributes(item);
|
|
|
|
if ((AosWeaponAttribute)o == AosWeaponAttribute.MageWeapon)
|
|
{
|
|
if (attrs == null || attrs[(AosWeaponAttribute)o] == 0 || attrs[(AosWeaponAttribute)o] > Math.Max(0, 30 - value))
|
|
return false;
|
|
}
|
|
else if (attrs == null || attrs[(AosWeaponAttribute)o] < value)
|
|
return false;
|
|
}
|
|
else if (o is SAAbsorptionAttribute)
|
|
{
|
|
SAAbsorptionAttributes attrs = RunicReforging.GetSAAbsorptionAttributes(item);
|
|
|
|
if (attrs == null || attrs[(SAAbsorptionAttribute)o] < value)
|
|
return false;
|
|
}
|
|
else if (o is AosArmorAttribute)
|
|
{
|
|
AosArmorAttributes attrs = RunicReforging.GetAosArmorAttributes(item);
|
|
|
|
if (attrs == null || attrs[(AosArmorAttribute)o] < value)
|
|
return false;
|
|
}
|
|
else if (o is SkillName)
|
|
{
|
|
if (detail.Category != Category.RequiredSkill)
|
|
{
|
|
AosSkillBonuses skillbonuses = RunicReforging.GetAosSkillBonuses(item);
|
|
|
|
if (skillbonuses != null)
|
|
{
|
|
bool hasSkill = false;
|
|
|
|
for (int i = 0; i < 5; i++)
|
|
{
|
|
SkillName check;
|
|
double bonus;
|
|
|
|
if (skillbonuses.GetValues(i, out check, out bonus) && check == (SkillName)o && bonus >= value)
|
|
{
|
|
hasSkill = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!hasSkill)
|
|
return false;
|
|
}
|
|
else if (item is SpecialScroll && value >= 105)
|
|
{
|
|
if (((SpecialScroll)item).Skill != (SkillName)o || ((SpecialScroll)item).Value < value)
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
else if (!(item is BaseWeapon) || ((BaseWeapon)item).DefSkill != (SkillName)o)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
else if (!CheckSlayer(item, o))
|
|
{
|
|
return false;
|
|
}
|
|
else if (o is AosElementAttribute)
|
|
{
|
|
if (item is BaseWeapon)
|
|
{
|
|
BaseWeapon wep = item as BaseWeapon;
|
|
|
|
if (detail.Category == Category.DamageType)
|
|
{
|
|
int phys, fire, cold, pois, nrgy, chaos, direct;
|
|
wep.GetDamageTypes(null, out phys, out fire, out cold, out pois, out nrgy, out chaos, out direct);
|
|
|
|
switch ((AosElementAttribute)o)
|
|
{
|
|
case AosElementAttribute.Physical: if (phys < value) return false; break;
|
|
case AosElementAttribute.Fire: if (fire < value) return false; break;
|
|
case AosElementAttribute.Cold: if (cold < value) return false; break;
|
|
case AosElementAttribute.Poison: if (pois < value) return false; break;
|
|
case AosElementAttribute.Energy: if (nrgy < value) return false; break;
|
|
case AosElementAttribute.Chaos: if (chaos < value) return false; break;
|
|
case AosElementAttribute.Direct: if (direct < value) return false; break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch ((AosElementAttribute)o)
|
|
{
|
|
case AosElementAttribute.Physical:
|
|
if (wep.WeaponAttributes.ResistPhysicalBonus < value) return false;
|
|
break;
|
|
case AosElementAttribute.Fire:
|
|
if (wep.WeaponAttributes.ResistFireBonus < value) return false;
|
|
break;
|
|
case AosElementAttribute.Cold:
|
|
if (wep.WeaponAttributes.ResistColdBonus < value) return false;
|
|
break;
|
|
case AosElementAttribute.Poison:
|
|
if (wep.WeaponAttributes.ResistPoisonBonus < value) return false;
|
|
break;
|
|
case AosElementAttribute.Energy:
|
|
if (wep.WeaponAttributes.ResistEnergyBonus < value) return false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else if (item is BaseArmor && detail.Category == Category.Resists)
|
|
{
|
|
BaseArmor armor = item as BaseArmor;
|
|
|
|
switch ((AosElementAttribute)o)
|
|
{
|
|
case AosElementAttribute.Physical:
|
|
if (armor.PhysicalResistance < value) return false;
|
|
break;
|
|
case AosElementAttribute.Fire:
|
|
if (armor.FireResistance < value) return false;
|
|
break;
|
|
case AosElementAttribute.Cold:
|
|
if (armor.ColdResistance < value) return false;
|
|
break;
|
|
case AosElementAttribute.Poison:
|
|
if (armor.PoisonResistance < value) return false;
|
|
break;
|
|
case AosElementAttribute.Energy:
|
|
if (armor.EnergyResistance < value) return false;
|
|
break;
|
|
}
|
|
}
|
|
else if (detail.Category != Category.DamageType)
|
|
{
|
|
AosElementAttributes attrs = RunicReforging.GetElementalAttributes(item);
|
|
|
|
if (attrs == null || attrs[(AosElementAttribute)o] < value)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
else if (o is Misc)
|
|
{
|
|
switch ((Misc)o)
|
|
{
|
|
case Misc.ExcludeFel: break;
|
|
case Misc.GargoyleOnly:
|
|
if (!IsGargoyle(item))
|
|
return false;
|
|
break;
|
|
case Misc.NotGargoyleOnly:
|
|
if (IsGargoyle(item))
|
|
return false;
|
|
break;
|
|
case Misc.ElvesOnly:
|
|
if (!IsElf(item))
|
|
return false;
|
|
break;
|
|
case Misc.NotElvesOnly:
|
|
if (IsElf(item))
|
|
return false;
|
|
break;
|
|
case Misc.FactionItem:
|
|
if (!(item is Factions.IFactionItem))
|
|
return false;
|
|
break;
|
|
case Misc.PromotionalToken:
|
|
if (!(item is PromotionalToken))
|
|
return false;
|
|
break;
|
|
case Misc.Cursed:
|
|
if (item.LootType != LootType.Cursed)
|
|
return false;
|
|
break;
|
|
case Misc.NotCursed:
|
|
if (item.LootType == LootType.Cursed)
|
|
return false;
|
|
break;
|
|
case Misc.CannotRepair:
|
|
if (CheckCanRepair(item))
|
|
return false;
|
|
break;
|
|
case Misc.NotCannotBeRepaired:
|
|
if (!CheckCanRepair(item))
|
|
return false;
|
|
break;
|
|
case Misc.Brittle:
|
|
NegativeAttributes neg2 = RunicReforging.GetNegativeAttributes(item);
|
|
if (neg2 == null || neg2.Brittle == 0)
|
|
return false;
|
|
break;
|
|
case Misc.NotBrittle:
|
|
NegativeAttributes neg3 = RunicReforging.GetNegativeAttributes(item);
|
|
if (neg3 != null && neg3.Brittle > 0)
|
|
return false;
|
|
break;
|
|
case Misc.Antique:
|
|
NegativeAttributes neg4 = RunicReforging.GetNegativeAttributes(item);
|
|
if (neg4 == null || neg4.Antique == 0)
|
|
return false;
|
|
break;
|
|
case Misc.NotAntique:
|
|
NegativeAttributes neg5 = RunicReforging.GetNegativeAttributes(item);
|
|
if (neg5 != null && neg5.Antique > 0)
|
|
return false;
|
|
break;
|
|
}
|
|
}
|
|
else if (o is string)
|
|
{
|
|
string str = o as string;
|
|
|
|
if (str == "WeaponVelocity" && (!(item is BaseRanged) || ((BaseRanged)item).Velocity < value))
|
|
return false;
|
|
|
|
if (str == "SearingWeapon" && (!(item is BaseWeapon) || !((BaseWeapon)item).SearingWeapon))
|
|
return false;
|
|
|
|
if (str == "ArtifactRarity" && (!(item is IArtifact) || ((IArtifact)item).ArtifactRarity < value))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private static bool CheckSlayer(Item item, object o)
|
|
{
|
|
if (o is TalismanSlayerName && (TalismanSlayerName)o == TalismanSlayerName.Undead)
|
|
{
|
|
if (!(item is ISlayer) || ((((ISlayer)item).Slayer != SlayerName.Silver && ((ISlayer)item).Slayer2 != SlayerName.Silver)))
|
|
{
|
|
if (!(item is BaseTalisman) || ((BaseTalisman)item).Slayer != TalismanSlayerName.Undead)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (o is SlayerName && (!(item is ISlayer) || (((ISlayer)item).Slayer != (SlayerName)o && ((ISlayer)item).Slayer2 != (SlayerName)o)))
|
|
{
|
|
return false;
|
|
}
|
|
else if (o is TalismanSlayerName && (!(item is BaseTalisman) || ((BaseTalisman)item).Slayer != (TalismanSlayerName)o))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private static bool CheckCanRepair(Item item)
|
|
{
|
|
NegativeAttributes neg = RunicReforging.GetNegativeAttributes(item);
|
|
|
|
return neg != null && neg.NoRepair != 0;
|
|
}
|
|
|
|
private static bool CheckKeyword(string searchstring, Item item)
|
|
{
|
|
if (item is CommodityDeed && ((CommodityDeed)item).Commodity != null)
|
|
{
|
|
item = ((CommodityDeed)item).Commodity;
|
|
}
|
|
|
|
if (item is IResource)
|
|
{
|
|
var resName = CraftResources.GetName(((IResource)item).Resource);
|
|
|
|
if (resName.ToLower().IndexOf(searchstring.ToLower()) >= 0)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if (item is ICommodity)
|
|
{
|
|
var commodity = (ICommodity)item;
|
|
|
|
string name = commodity.Description.String;
|
|
|
|
if (string.IsNullOrEmpty(name) && commodity.Description.Number > 0)
|
|
{
|
|
name = StringList.GetString(commodity.Description.Number);
|
|
}
|
|
|
|
if (!string.IsNullOrEmpty(name) && name.ToLower().IndexOf(searchstring.ToLower()) >= 0)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return Keywords.ContainsKey(searchstring.ToLower()) && Keywords[searchstring.ToLower()] == item.GetType();
|
|
}
|
|
|
|
public static bool IsGargoyle(Item item)
|
|
{
|
|
if (item is BaseArmor)
|
|
{
|
|
return ((BaseArmor)item).RequiredRace == Race.Gargoyle;
|
|
}
|
|
else if (item is BaseWeapon)
|
|
{
|
|
return ((BaseWeapon)item).RequiredRace == Race.Gargoyle;
|
|
}
|
|
else if (item is BaseClothing)
|
|
{
|
|
return ((BaseClothing)item).RequiredRace == Race.Gargoyle;
|
|
}
|
|
else if (item is BaseJewel)
|
|
{
|
|
return ((BaseJewel)item).RequiredRace == Race.Gargoyle;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public static bool IsElf(Item item)
|
|
{
|
|
if (item is BaseArmor)
|
|
{
|
|
return ((BaseArmor)item).RequiredRace == Race.Elf;
|
|
}
|
|
else if (item is BaseWeapon)
|
|
{
|
|
return ((BaseWeapon)item).RequiredRace == Race.Elf;
|
|
}
|
|
else if (item is BaseClothing)
|
|
{
|
|
return ((BaseClothing)item).RequiredRace == Race.Elf;
|
|
}
|
|
else if (item is BaseJewel)
|
|
{
|
|
return ((BaseJewel)item).RequiredRace == Race.Elf;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public static SearchCriteria AddNewContext(PlayerMobile pm)
|
|
{
|
|
SearchCriteria criteria = new SearchCriteria();
|
|
|
|
Contexts[pm] = criteria;
|
|
|
|
return criteria;
|
|
}
|
|
|
|
public static SearchCriteria GetContext(PlayerMobile pm)
|
|
{
|
|
if (Contexts.ContainsKey(pm))
|
|
return Contexts[pm];
|
|
|
|
return null;
|
|
}
|
|
|
|
public static Dictionary<PlayerMobile, SearchCriteria> Contexts { get; set; }
|
|
public static List<SearchCategory> Categories { get; set; }
|
|
|
|
public static Dictionary<string, Type> Keywords { get; set; }
|
|
|
|
public static void Configure()
|
|
{
|
|
EventSink.WorldSave += OnSave;
|
|
EventSink.WorldLoad += OnLoad;
|
|
}
|
|
|
|
public static void OnSave(WorldSaveEventArgs e)
|
|
{
|
|
Persistence.Serialize(
|
|
FilePath,
|
|
writer =>
|
|
{
|
|
writer.Write(0);
|
|
|
|
writer.Write(Contexts == null ? 0 : Contexts.Where(kvp => !kvp.Value.IsEmpty).Count());
|
|
|
|
if (Contexts != null)
|
|
{
|
|
foreach (var kvp in Contexts.Where(kvp => !kvp.Value.IsEmpty))
|
|
{
|
|
writer.Write(kvp.Key);
|
|
kvp.Value.Serialize(writer);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
public static void OnLoad()
|
|
{
|
|
Persistence.Deserialize(
|
|
FilePath,
|
|
reader =>
|
|
{
|
|
int version = reader.ReadInt();
|
|
int count = reader.ReadInt();
|
|
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
PlayerMobile pm = reader.ReadMobile() as PlayerMobile;
|
|
var criteria = new SearchCriteria(reader);
|
|
|
|
if (pm != null)
|
|
{
|
|
if (Contexts == null)
|
|
Contexts = new Dictionary<PlayerMobile, SearchCriteria>();
|
|
|
|
Contexts[pm] = criteria;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
public static void Initialize()
|
|
{
|
|
try
|
|
{
|
|
StringList = new Ultima.StringList("enu");
|
|
}
|
|
catch { }
|
|
|
|
CommandSystem.Register("GetOPLString", AccessLevel.Administrator, e =>
|
|
{
|
|
e.Mobile.BeginTarget(-1, false, TargetFlags.None, (m, targeted) =>
|
|
{
|
|
if (targeted is Item)
|
|
{
|
|
Console.WriteLine(GetItemName((Item)targeted));
|
|
e.Mobile.SendMessage(GetItemName((Item)targeted));
|
|
}
|
|
});
|
|
});
|
|
|
|
Categories = new List<SearchCategory>();
|
|
|
|
if (Contexts == null)
|
|
{
|
|
Contexts = new Dictionary<PlayerMobile, SearchCriteria>();
|
|
}
|
|
|
|
Timer.DelayCall(TimeSpan.FromSeconds(1), () =>
|
|
{
|
|
SearchCategory price = new SearchCategory(Category.PriceRange);
|
|
Categories.Add(price);
|
|
|
|
SearchCriteriaCategory.AllCategories.ToList().ForEach(x =>
|
|
{
|
|
SearchCategory cat = new SearchCategory(x.Category);
|
|
|
|
x.Criteria.ToList().ForEach(y =>
|
|
{
|
|
if (y.PropCliloc != 0)
|
|
{
|
|
cat.Register(y.Object, y.Cliloc, y.PropCliloc);
|
|
}
|
|
else
|
|
{
|
|
cat.Register(y.Object, y.Cliloc);
|
|
}
|
|
|
|
});
|
|
|
|
Categories.Add(cat);
|
|
});
|
|
|
|
SearchCategory sort = new SearchCategory(Category.Sort);
|
|
Categories.Add(sort);
|
|
});
|
|
|
|
Keywords = new Dictionary<string, Type>();
|
|
|
|
Keywords["power scroll"] = typeof(PowerScroll);
|
|
Keywords["stat scroll"] = typeof(StatCapScroll);
|
|
}
|
|
|
|
public static string GetItemName(Item item)
|
|
{
|
|
if (StringList == null || item.Name != null)
|
|
return item.Name;
|
|
|
|
ObjectPropertyList opl = new ObjectPropertyList(item);
|
|
item.GetProperties(opl);
|
|
|
|
if (opl == null)
|
|
{
|
|
//if there was a problem with this process, just return null
|
|
return null;
|
|
}
|
|
|
|
//since the object property list is based on a packet object, the property info is packed away in a packet format
|
|
byte[] data = opl.UnderlyingStream.UnderlyingStream.ToArray();
|
|
|
|
int index = 15; // First localization number index
|
|
string basestring = null;
|
|
|
|
//reset the number property
|
|
uint number = 0;
|
|
|
|
//if there's not enough room for another record, quit
|
|
if (index + 4 >= data.Length)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
//read number property from the packet data
|
|
number = (uint)(data[index++] << 24 | data[index++] << 16 | data[index++] << 8 | data[index++]);
|
|
|
|
//reset the length property
|
|
ushort length = 0;
|
|
|
|
//if there's not enough room for another record, quit
|
|
if (index + 2 > data.Length)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
//read length property from the packet data
|
|
length = (ushort)(data[index++] << 8 | data[index++]);
|
|
|
|
//determine the location of the end of the string
|
|
int end = index + length;
|
|
|
|
//truncate if necessary
|
|
if (end >= data.Length)
|
|
{
|
|
end = data.Length - 1;
|
|
}
|
|
|
|
//read the string into a StringBuilder object
|
|
|
|
StringBuilder s = new StringBuilder();
|
|
while (index + 2 <= end + 1)
|
|
{
|
|
short next = (short)(data[index++] | data[index++] << 8);
|
|
|
|
if (next == 0)
|
|
break;
|
|
|
|
s.Append(Encoding.Unicode.GetString(BitConverter.GetBytes(next)));
|
|
}
|
|
|
|
basestring = StringList.GetString((int)number);
|
|
string args = s.ToString();
|
|
|
|
if (args == null || args == string.Empty)
|
|
{
|
|
return basestring;
|
|
}
|
|
|
|
string[] parms = args.Split('\t');
|
|
|
|
try
|
|
{
|
|
if (parms.Length > 1)
|
|
{
|
|
for (int i = 0; i < parms.Length; i++)
|
|
{
|
|
parms[i] = parms[i].Trim(' ');
|
|
|
|
if (parms[i].IndexOf("#") == 0)
|
|
{
|
|
parms[i] = StringList.GetString(Convert.ToInt32(parms[i].Substring(1, parms[i].Length - 1)));
|
|
}
|
|
}
|
|
}
|
|
else if (parms.Length == 1 && parms[0].IndexOf("#") == 0)
|
|
{
|
|
parms[0] = StringList.GetString(Convert.ToInt32(args.Substring(1, parms[0].Length - 1)));
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
return null;
|
|
}
|
|
|
|
Ultima.StringEntry entry = StringList.GetEntry((int)number);
|
|
|
|
if (entry != null)
|
|
{
|
|
return entry.Format(parms);
|
|
}
|
|
|
|
return basestring;
|
|
}
|
|
|
|
private static List<Item> GetItems(PlayerVendor pv)
|
|
{
|
|
List<Item> list = new List<Item>();
|
|
|
|
foreach (Item item in pv.Items)
|
|
if (item.Movable && item != pv.Backpack && item.Layer != Layer.Hair && item.Layer != Layer.FacialHair)
|
|
list.Add(item);
|
|
|
|
if (pv.Backpack != null)
|
|
{
|
|
GetItems(pv.Backpack, list);
|
|
}
|
|
|
|
return list;
|
|
}
|
|
|
|
public static void GetItems(Container c, List<Item> list)
|
|
{
|
|
if (c == null || c.Items.Count == 0)
|
|
return;
|
|
|
|
foreach (Item item in c.Items)
|
|
{
|
|
if (item is Container && !IsSearchableContainer(item.GetType()))
|
|
GetItems((Container)item, list);
|
|
else
|
|
list.Add(item);
|
|
}
|
|
}
|
|
|
|
public static bool CanSearch(Mobile m)
|
|
{
|
|
Region r = m.Region;
|
|
|
|
if (r.GetLogoutDelay(m) == TimeSpan.Zero)
|
|
return true;
|
|
|
|
return r is GuardedRegion && !((GuardedRegion)r).Disabled || r is HouseRegion && ((HouseRegion)r).House.IsFriend(m);
|
|
}
|
|
|
|
private static bool IsSearchableContainer(Type type)
|
|
{
|
|
return _SearchableContainers.Any(t => t == type || type.IsSubclassOf(t));
|
|
}
|
|
|
|
private static Type[] _SearchableContainers =
|
|
{
|
|
typeof(BaseQuiver), typeof(BaseResourceSatchel),
|
|
typeof(FishBowl), typeof(FirstAidBelt),
|
|
typeof(Plants.SeedBox), typeof(BaseSpecialScrollBook),
|
|
typeof(GardenShedBarrel), typeof(JewelryBox),
|
|
};
|
|
}
|
|
|
|
public enum SortBy
|
|
{
|
|
LowToHigh,
|
|
HighToLow
|
|
}
|
|
|
|
public enum Category
|
|
{
|
|
PriceRange,
|
|
Misc,
|
|
Equipment,
|
|
Combat,
|
|
Casting,
|
|
DamageType,
|
|
HitSpell,
|
|
HitArea,
|
|
Resists,
|
|
Stats,
|
|
Slayer1,
|
|
Slayer2,
|
|
Slayer3,
|
|
RequiredSkill,
|
|
Skill1,
|
|
Skill2,
|
|
Skill3,
|
|
Skill4,
|
|
Skill5,
|
|
Skill6,
|
|
Sort,
|
|
Auction
|
|
}
|
|
|
|
public enum Misc
|
|
{
|
|
ExcludeFel,
|
|
GargoyleOnly,
|
|
NotGargoyleOnly,
|
|
ElvesOnly,
|
|
NotElvesOnly,
|
|
FactionItem,
|
|
PromotionalToken,
|
|
Cursed,
|
|
NotCursed,
|
|
CannotRepair,
|
|
NotCannotBeRepaired,
|
|
Brittle,
|
|
NotBrittle,
|
|
Antique,
|
|
NotAntique
|
|
}
|
|
|
|
public enum SpecialSearch
|
|
{
|
|
PowerScroll,
|
|
StatScroll,
|
|
Transcendence,
|
|
Alacrity,
|
|
UsesRemaining
|
|
}
|
|
|
|
public class SearchCategory
|
|
{
|
|
public Category Category { get; private set; }
|
|
public int Label { get { return (int)Category; } }
|
|
|
|
public List<Tuple<object, int, int>> Objects { get; private set; }
|
|
|
|
public SearchCategory(Category category)
|
|
{
|
|
Category = category;
|
|
|
|
Objects = new List<Tuple<object, int, int>>();
|
|
}
|
|
|
|
public void Register(object o, int label)
|
|
{
|
|
if (Objects.FirstOrDefault(t => t.Item1 == o) == null)
|
|
{
|
|
Objects.Add(new Tuple<object, int, int>(o, label, 0));
|
|
}
|
|
}
|
|
|
|
public void Register(object o, int label, int pcliloc)
|
|
{
|
|
if (Objects.FirstOrDefault(t => t.Item1 == o) == null)
|
|
{
|
|
Objects.Add(new Tuple<object, int, int>(o, label, pcliloc));
|
|
}
|
|
}
|
|
}
|
|
|
|
public class SearchCriteria
|
|
{
|
|
public Layer SearchType { get; set; }
|
|
public string SearchName { get; set; }
|
|
public SortBy SortBy { get; set; }
|
|
public bool Auction { get; set; }
|
|
public long MinPrice { get; set; }
|
|
public long MaxPrice { get; set; }
|
|
|
|
public bool EntryPrice { get; set; }
|
|
|
|
public List<SearchDetail> Details { get; set; }
|
|
|
|
public SearchCriteria()
|
|
{
|
|
Details = new List<SearchDetail>();
|
|
|
|
MinPrice = 0;
|
|
MaxPrice = 175000000;
|
|
SearchType = Layer.Invalid;
|
|
}
|
|
|
|
public void Reset()
|
|
{
|
|
Details.Clear();
|
|
Details.TrimExcess();
|
|
Details = new List<SearchDetail>();
|
|
|
|
MinPrice = 0;
|
|
MaxPrice = 175000000;
|
|
SortBy = SortBy.LowToHigh;
|
|
Auction = false;
|
|
SearchName = null;
|
|
SearchType = Layer.Invalid;
|
|
EntryPrice = false;
|
|
}
|
|
|
|
public int GetValueForDetails(object o)
|
|
{
|
|
SearchDetail detail = Details.FirstOrDefault(d => d.Attribute == o);
|
|
|
|
return detail != null ? detail.Value : 0;
|
|
}
|
|
|
|
public void TryAddDetails(object o, int name, int propname, int value, Category cat)
|
|
{
|
|
SearchDetail d = Details.FirstOrDefault(det => det.Attribute == o);
|
|
|
|
if (o is Layer)
|
|
{
|
|
SearchDetail layer = Details.FirstOrDefault(det => det.Attribute is Layer && (Layer)det.Attribute != (Layer)o);
|
|
|
|
if (layer != null)
|
|
{
|
|
Details.Remove(layer);
|
|
}
|
|
|
|
Details.Add(new SearchDetail(o, name, propname, value, cat));
|
|
SearchType = (Layer)o;
|
|
}
|
|
else if (d == null)
|
|
{
|
|
d = new SearchDetail(o, name, propname, value, cat);
|
|
|
|
Details.Add(d);
|
|
}
|
|
else if (d.Value != value)
|
|
{
|
|
d.Value = value;
|
|
}
|
|
|
|
/*if (d.Attribute is TalismanSlayerName && (TalismanSlayerName)d.Attribute == TalismanSlayerName.Undead)
|
|
{
|
|
TryAddDetails(SlayerName.Silver, name, value, cat);
|
|
}*/
|
|
}
|
|
|
|
public bool IsEmpty
|
|
{
|
|
get { return Details.Count == 0 && !EntryPrice && string.IsNullOrEmpty(SearchName) && SearchType == Layer.Invalid; }
|
|
}
|
|
|
|
public SearchCriteria(GenericReader reader)
|
|
{
|
|
int version = reader.ReadInt();
|
|
|
|
Details = new List<SearchDetail>();
|
|
|
|
if (version > 1)
|
|
Auction = reader.ReadBool();
|
|
|
|
if (version != 0)
|
|
EntryPrice = reader.ReadBool();
|
|
|
|
SearchType = (Layer)reader.ReadInt();
|
|
SearchName = reader.ReadString();
|
|
SortBy = (SortBy)reader.ReadInt();
|
|
MinPrice = reader.ReadLong();
|
|
MaxPrice = reader.ReadLong();
|
|
|
|
int count = reader.ReadInt();
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
Details.Add(new SearchDetail(reader));
|
|
}
|
|
}
|
|
|
|
public void Serialize(GenericWriter writer)
|
|
{
|
|
writer.Write(2);
|
|
|
|
writer.Write((bool)Auction);
|
|
writer.Write((bool)EntryPrice);
|
|
writer.Write((int)SearchType);
|
|
writer.Write(SearchName);
|
|
writer.Write((int)SortBy);
|
|
writer.Write(MinPrice);
|
|
writer.Write(MaxPrice);
|
|
|
|
writer.Write(Details.Count);
|
|
|
|
for (int i = 0; i < Details.Count; i++)
|
|
{
|
|
Details[i].Serialize(writer);
|
|
}
|
|
}
|
|
}
|
|
|
|
public class SearchDetail
|
|
{
|
|
public enum AttributeID
|
|
{
|
|
None = 0,
|
|
AosAttribute,
|
|
AosArmorAttribute,
|
|
AosWeaponAttribute,
|
|
AosElementAttribute,
|
|
SkillName,
|
|
SAAbosorptionAttribute,
|
|
ExtendedWeaponAttribute,
|
|
NegativeAttribute,
|
|
SlayerName,
|
|
String,
|
|
TalismanSlayerName,
|
|
TalismanSkill,
|
|
TalismanRemoval,
|
|
Int,
|
|
}
|
|
|
|
public object Attribute { get; set; }
|
|
public int Label { get; set; }
|
|
public int PropLabel { get; set; }
|
|
public int Value { get; set; }
|
|
public Category Category { get; set; }
|
|
|
|
public SearchDetail(object o, int label, int proplabel, int value, Category category)
|
|
{
|
|
Attribute = o;
|
|
Label = label;
|
|
PropLabel = proplabel;
|
|
Value = value;
|
|
Category = category;
|
|
}
|
|
|
|
public SearchDetail(GenericReader reader)
|
|
{
|
|
int version = reader.ReadInt(); // version
|
|
|
|
if (version > 0)
|
|
{
|
|
PropLabel = reader.ReadInt();
|
|
}
|
|
|
|
ReadAttribute(reader);
|
|
|
|
Label = reader.ReadInt();
|
|
Value = reader.ReadInt();
|
|
Category = (Category)reader.ReadInt();
|
|
}
|
|
|
|
public void Serialize(GenericWriter writer)
|
|
{
|
|
writer.Write(1);
|
|
|
|
writer.Write(PropLabel);
|
|
|
|
WriteAttribute(writer);
|
|
|
|
writer.Write(Label);
|
|
writer.Write(Value);
|
|
writer.Write((int)Category);
|
|
}
|
|
|
|
private void WriteAttribute(GenericWriter writer)
|
|
{
|
|
int attrID = GetAttributeID(Attribute);
|
|
writer.Write(attrID);
|
|
|
|
switch (attrID)
|
|
{
|
|
case 0: break;
|
|
case 1: writer.Write((int)(AosAttribute)Attribute); break;
|
|
case 2: writer.Write((int)(AosArmorAttribute)Attribute); break;
|
|
case 3: writer.Write((int)(AosWeaponAttribute)Attribute); break;
|
|
case 4: writer.Write((int)(AosElementAttribute)Attribute); break;
|
|
case 5: writer.Write((int)(SkillName)Attribute); break;
|
|
case 6: writer.Write((int)(SAAbsorptionAttribute)Attribute); break;
|
|
case 7: writer.Write((int)(ExtendedWeaponAttribute)Attribute); break;
|
|
case 8: writer.Write((int)(NegativeAttribute)Attribute); break;
|
|
case 9: writer.Write((int)(SlayerName)Attribute); break;
|
|
case 10: writer.Write((string)Attribute); break;
|
|
case 11: writer.Write((int)(TalismanSlayerName)Attribute); break;
|
|
case 12: writer.Write((int)(TalismanSkill)Attribute); break;
|
|
case 13: writer.Write((int)(TalismanRemoval)Attribute); break;
|
|
case 14: writer.Write((int)Attribute); break;
|
|
}
|
|
}
|
|
|
|
private void ReadAttribute(GenericReader reader)
|
|
{
|
|
switch (reader.ReadInt())
|
|
{
|
|
case 0: break;
|
|
case 1: Attribute = (AosAttribute)reader.ReadInt(); break;
|
|
case 2: Attribute = (AosArmorAttribute)reader.ReadInt(); break;
|
|
case 3: Attribute = (AosWeaponAttribute)reader.ReadInt(); break;
|
|
case 4: Attribute = (AosElementAttribute)reader.ReadInt(); break;
|
|
case 5: Attribute = (SkillName)reader.ReadInt(); break;
|
|
case 6: Attribute = (SAAbsorptionAttribute)reader.ReadInt(); break;
|
|
case 7: Attribute = (ExtendedWeaponAttribute)reader.ReadInt(); break;
|
|
case 8: Attribute = (NegativeAttribute)reader.ReadInt(); break;
|
|
case 9: Attribute = (SlayerName)reader.ReadInt(); break;
|
|
case 10: Attribute = reader.ReadString(); break;
|
|
case 11: Attribute = (TalismanSlayerName)reader.ReadInt(); break;
|
|
case 12: Attribute = (TalismanSkill)reader.ReadInt(); break;
|
|
case 13: Attribute = (TalismanRemoval)reader.ReadInt(); break;
|
|
case 14: Attribute = reader.ReadInt(); break;
|
|
}
|
|
}
|
|
|
|
public static int GetAttributeID(object o)
|
|
{
|
|
if (o is AosAttribute)
|
|
return (int)AttributeID.AosAttribute;
|
|
|
|
if (o is AosArmorAttribute)
|
|
return (int)AttributeID.AosArmorAttribute;
|
|
|
|
if (o is AosWeaponAttribute)
|
|
return (int)AttributeID.AosWeaponAttribute;
|
|
|
|
if (o is AosElementAttribute)
|
|
return (int)AttributeID.AosElementAttribute;
|
|
|
|
if (o is SkillName)
|
|
return (int)AttributeID.SkillName;
|
|
|
|
if (o is SAAbsorptionAttribute)
|
|
return (int)AttributeID.SAAbosorptionAttribute;
|
|
|
|
if (o is ExtendedWeaponAttribute)
|
|
return (int)AttributeID.ExtendedWeaponAttribute;
|
|
|
|
if (o is NegativeAttribute)
|
|
return (int)AttributeID.NegativeAttribute;
|
|
|
|
if (o is SlayerName)
|
|
return (int)AttributeID.SlayerName;
|
|
|
|
if (o is TalismanSlayerName)
|
|
return (int)AttributeID.TalismanSlayerName;
|
|
|
|
if (o is string)
|
|
return (int)AttributeID.String;
|
|
|
|
if (o is TalismanSkill)
|
|
return (int)AttributeID.TalismanSkill;
|
|
|
|
if (o is TalismanRemoval)
|
|
return (int)AttributeID.TalismanRemoval;
|
|
|
|
if (o is int)
|
|
return (int)AttributeID.Int;
|
|
|
|
return (int)AttributeID.None;
|
|
}
|
|
|
|
}
|
|
|
|
public class SearchVendors : ContextMenuEntry
|
|
{
|
|
public PlayerMobile Player { get; set; }
|
|
|
|
public SearchVendors(PlayerMobile pm)
|
|
: base(1154679, -1)
|
|
{
|
|
Player = pm;
|
|
|
|
Enabled = VendorSearch.CanSearch(pm);
|
|
}
|
|
|
|
public override void OnClick()
|
|
{
|
|
if (VendorSearch.CanSearch(Player))
|
|
{
|
|
BaseGump.SendGump(new VendorSearchGump(Player));
|
|
}
|
|
}
|
|
}
|
|
|
|
public class SearchItem
|
|
{
|
|
public PlayerVendor Vendor { get; set; }
|
|
public AuctionSafe AuctionSafe { get; set; }
|
|
public Item Item { get; set; }
|
|
public int Price { get; set; }
|
|
public bool IsChild { get; set; }
|
|
public bool IsAuction { get; set; }
|
|
|
|
public SearchItem(PlayerVendor vendor, Item item, int price, bool isChild)
|
|
{
|
|
Vendor = vendor;
|
|
Item = item;
|
|
Price = price;
|
|
IsChild = isChild;
|
|
IsAuction = false;
|
|
}
|
|
|
|
public SearchItem(AuctionSafe auctionsafe, Item item, int price, bool isChild)
|
|
{
|
|
AuctionSafe = auctionsafe;
|
|
Item = item;
|
|
Price = price;
|
|
IsChild = isChild;
|
|
IsAuction = true;
|
|
}
|
|
}
|
|
}
|