public static ItemClass GetItemClassBySpecifier(string specifier) { specifier = specifier.Trim().ToLower(); string commonspecifier = "common " + specifier; foreach (ItemClass cls in Classes) { string sname = cls.ServerName.ToLower(); if (sname == specifier || sname == commonspecifier) { return(cls); } } // try to guess based on specifier. this is used in world.res string[] specSplit = specifier.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); string searchFor; // this is reused // first off, if last word is "shield", then we're looking for something in Templates.Shields int gSlot = -1; Templates.TplArmor gOption = null; int gOptionId = -1; if (specSplit[specSplit.Length - 1] == "shield") { searchFor = ""; for (int j = 1; j >= 0; j--) { if (specSplit.Length - 1 - j < 0) { continue; } searchFor = string.Join(" ", specSplit.Skip(specSplit.Length - 1 - j).Take(j + 1).ToArray()); for (int i = 0; i < TemplateLoader.Templates.Shields.Count; i++) { Templates.TplArmor shieldKind = TemplateLoader.Templates.Shields[i]; if (shieldKind.Name.ToLower() == searchFor) { gOption = shieldKind; gOptionId = i; gSlot = shieldKind.Slot; break; } } if (gOption != null) { // remove the option specifier (leaving only class and material) specSplit = specSplit.Take(specSplit.Length - (j + 2)).ToArray(); break; } } } // otherwise search in weapons and armor. else { searchFor = ""; for (int j = 1; j >= 0; j--) { if (specSplit.Length - 1 - j < 0) { continue; } searchFor = string.Join(" ", specSplit.Skip(specSplit.Length - 1 - j).Take(j + 1).ToArray()); for (int i = 0; i < TemplateLoader.Templates.Weapons.Count; i++) { Templates.TplArmor weaponKind = TemplateLoader.Templates.Weapons[i]; if (weaponKind.Name.ToLower() == searchFor) { gOption = weaponKind; gOptionId = i; gSlot = weaponKind.Slot; break; } } if (gOption == null) { for (int i = 0; i < TemplateLoader.Templates.Armor.Count; i++) { Templates.TplArmor armorKind = TemplateLoader.Templates.Armor[i]; if (armorKind.Name.ToLower() == searchFor) { gOption = armorKind; gOptionId = i; gSlot = armorKind.Slot; break; } } } if (gOption != null) { specSplit = specSplit.Take(specSplit.Length - (j + 1)).ToArray(); break; } } } // here we either have gOption and gSlot or the item is not found. if (gOption == null || gSlot < 0) { return(null); } // now find material int gMaterial = -1; searchFor = ""; for (int j = 1; j >= 0; j--) { if (specSplit.Length - 1 - j < 0) { continue; } searchFor = string.Join(" ", specSplit.Skip(specSplit.Length - 1 - j).Take(j + 1).ToArray()); for (int i = 0; i < TemplateLoader.Templates.Materials.Count; i++) { Templates.TplMaterial fmat = TemplateLoader.Templates.Materials[i]; if (fmat.Name.ToLower() == searchFor) { gMaterial = i; break; } } if (gMaterial >= 0) { specSplit = specSplit.Take(specSplit.Length - (j + 1)).ToArray(); break; } } if (gMaterial < 0) { // check if option has "none" material allowed (aka 15) if (gOption.IsAllowed(-1, 15)) { gMaterial = 15; } else { // there are very special items like "Sonic Beam" that have ONLY option in them ItemClass newCls = new ItemClass(); newCls.ServerName = gOption.Name; newCls.VisualName = newCls.ServerName; newCls.Option = gOption; newCls.IsMagic = false; newCls.IsSpecial = true; newCls.ItemID = (ushort)gOptionId; newCls.UsableMage = false; newCls.UsableFighter = false; newCls.Price = 0; return(newCls); } } int gClass = -1; // common by default searchFor = string.Join(" ", specSplit); for (int i = 0; i < TemplateLoader.Templates.Classes.Count; i++) { Templates.TplClass fcls = TemplateLoader.Templates.Classes[i]; if (fcls.Name.ToLower() == searchFor) { gClass = i; break; } } if (gClass < 0) { gClass = 0; // default is common. the only default here. } // how try to find required item. Templates.TplClass reqClass = TemplateLoader.GetClassById(gClass); Templates.TplMaterial reqMaterial = TemplateLoader.GetMaterialById(gMaterial); Templates.TplArmor reqOption; if (reqClass == null || reqMaterial == null) // this shouldnt happen tbh { return(null); } // do option remap for shields. nival did NOT write "soft helm" or "soft large shield". instead they just wrote "helm" and "large shield". reqOption = gOption; int oldOptionId = gOptionId; if (reqMaterial.Name.ToLower().Contains("wood") && !reqOption.IsAllowed(gClass, gMaterial)) { gOptionId += 2; // small/large -> wooden small/wooden large } else if (reqMaterial.Name.ToLower().Contains("leather") && !reqOption.IsAllowed(gClass, gMaterial)) { gOptionId += 1; // small/large -> soft small/soft large } if (oldOptionId != gOptionId) { reqOption = TemplateLoader.GetOptionByIdAndSlot(gOptionId, gSlot); if (reqOption == null) { return(null); } } for (int i = 0; i < Classes.Count; i++) { if (Classes[i].Class == reqClass && Classes[i].Material == reqMaterial && Classes[i].Option == reqOption) { return(Classes[i]); } } return(null); }
public static void InitClasses() { if (ClassesLoaded) { return; } ClassesLoaded = true; MemoryStream ms = ResourceManager.OpenRead("world/data/itemname.pkt"); if (ms == null) { Core.Abort("Couldn't load \"world/data/itemname.pkt\"!"); return; } ms.Position = 3; BinaryReader br = new BinaryReader(ms); try { uint item_count = br.ReadUInt32(); ms.Position += 2; for (uint i = 0; i < item_count; i++) { ItemClass cls = new ItemClass(); cls.ItemID = br.ReadUInt16(); cls.ServerName = Locale.ItemServ[(int)i]; cls.VisualName = Locale.ItemName[(int)i]; ms.Position += 2; // some unknown info (usually 0x01 0x00) byte flags = br.ReadByte(); if (flags == 2 || flags == 6) // usablefighter { cls.UsableFighter = true; } if (flags == 4 || flags == 6) // usablemage { cls.UsableMage = true; } byte count_mods = br.ReadByte(); ms.Position += 2; // size of modifier array in bytes (i.e. 0x0B) + id of price (0x01) cls.Price = br.ReadUInt32(); count_mods--; for (byte j = 0; j < count_mods; j++) { ItemEffect effect = new ItemEffect(); byte r_effect = br.ReadByte(); sbyte r_value = br.ReadSByte(); effect.Type1 = (ItemEffect.Effects)r_effect; effect.Value1 = r_value; cls.Effects.Add(effect); } // done reading. init auxiliary fields. int materialId = (cls.ItemID & 0xF000) >> 12; int slotId = (cls.ItemID & 0x0F00) >> 8; int classId = (cls.ItemID & 0x0070) >> 5; int optionId = (cls.ItemID & 0x001F); if (materialId == 0 && slotId == 14) // 0x0E## { cls.IsMagic = true; cls.MagicID = (cls.ItemID & 0xFF) - 1; optionId = cls.MagicID + 1; classId = 0; materialId = 0; } else { cls.IsMagic = false; cls.MagicID = -1; } cls.IsSpecial = false; cls.Material = TemplateLoader.GetMaterialById(materialId); cls.Class = TemplateLoader.GetClassById(classId); cls.Option = TemplateLoader.GetOptionByIdAndSlot(optionId, slotId); string imageNameBase = string.Format("{0:D2}{1:D2}{2}{3:D2}", materialId, slotId, classId, optionId); cls.File_Pack = new ItemPackFile("graphics/inventory/" + imageNameBase + ".16a"); if (cls.UsableFighter) { cls.File_BodyMF1 = new ItemFile("graphics/equipment/mfighter/primary/" + imageNameBase + ".256"); cls.File_BodyFF1 = new ItemFile("graphics/equipment/ffighter/primary/" + imageNameBase + ".256"); cls.File_BodyMF2 = new ItemFile("graphics/equipment/mfighter/secondary/" + imageNameBase + ".256"); cls.File_BodyFF2 = new ItemFile("graphics/equipment/ffighter/secondary/" + imageNameBase + ".256"); } if (cls.UsableMage) { cls.File_BodyMM1 = new ItemFile("graphics/equipment/mmage/primary/" + imageNameBase + ".256"); cls.File_BodyFM1 = new ItemFile("graphics/equipment/fmage/primary/" + imageNameBase + ".256"); cls.File_BodyMM2 = new ItemFile("graphics/equipment/mmage/secondary/" + imageNameBase + ".256"); cls.File_BodyFM2 = new ItemFile("graphics/equipment/fmage/secondary/" + imageNameBase + ".256"); } Classes.Add(cls); } // create a fake item with ID=0xFFFF // "gold" ItemClass gcls = new ItemClass(); gcls.ServerName = "Gold"; gcls.VisualName = "Gold"; gcls.Price = 1; gcls.ItemID = 0xFFFF; gcls.IsMagic = false; gcls.MagicID = -1; gcls.IsSpecial = false; gcls.IsMoney = true; gcls.UsableFighter = true; gcls.UsableMage = true; gcls.File_Pack = new ItemPackFile("graphics/interface/money/money.16a"); Classes.Add(gcls); } finally { ms.Close(); } }
public Shelf(AllodsMap.AlmShop.AlmShopShelf rules) { // basic props PriceMin = rules.PriceMin; PriceMax = rules.PriceMax; MaxItems = rules.MaxItems; MaxSameType = rules.MaxSameItems; // get list of classes supported for the item. var Materials = new List <Templates.TplMaterial>(); var Classes = new List <Templates.TplClass>(); var Types = new List <Templates.TplArmor>(); // ItemClasses = new List <ItemClass>(); SpecialItemClasses = new List <ItemClass>(); // AllowMagic = rules.ItemExtras.HasFlag(AllodsMap.AlmShop.AlmShopItemExtra.Magic); AllowCommon = AllowMagic ? rules.ItemExtras.HasFlag(AllodsMap.AlmShop.AlmShopItemExtra.Common) : true; AllowSpecial = rules.ItemTypes.HasFlag(AllodsMap.AlmShop.AlmShopItemType.Other); // this currently abuses the fact that there is a hardcoded list of materials. // will need to be changed if we extend it. for (int i = 0; i < 15; i++) { uint flag = 1u << i; uint value = (uint)rules.ItemMaterials; if ((flag & value) != 0) { Materials.Add(TemplateLoader.GetMaterialById(i)); } } // same goes for classes. for (int i = 0; i < 7; i++) { uint flag = 1u << i; uint value = ((uint)rules.ItemClasses) >> 15; if ((flag & value) != 0) { Classes.Add(TemplateLoader.GetClassById(i)); } } // types are a bit more complicated, because there is no direct mapping for this. // we use "slot" // note that there is also separation between mage and warrior armor here. // add weapons if (rules.ItemTypes.HasFlag(AllodsMap.AlmShop.AlmShopItemType.Weapon)) { foreach (Templates.TplArmor weapon in TemplateLoader.Templates.Weapons) { if (!CheckArmorShapeAllowed(weapon, rules)) { continue; } if (weapon.SuitableFor == 1) { Types.Add(weapon); } } } if (rules.ItemTypes.HasFlag(AllodsMap.AlmShop.AlmShopItemType.Wands)) { foreach (Templates.TplArmor weapon in TemplateLoader.Templates.Weapons) { if (!CheckArmorShapeAllowed(weapon, rules)) { continue; } if (weapon.SuitableFor == 2) { Types.Add(weapon); } } } // add armor if (rules.ItemTypes.HasFlag(AllodsMap.AlmShop.AlmShopItemType.Armor)) { foreach (Templates.TplArmor armor in TemplateLoader.Templates.Armor) { if (!CheckArmorShapeAllowed(armor, rules)) { continue; } if (armor.SuitableFor == 1) { Types.Add(armor); } } } if (rules.ItemTypes.HasFlag(AllodsMap.AlmShop.AlmShopItemType.ArmorMage)) { foreach (Templates.TplArmor armor in TemplateLoader.Templates.Armor) { if (!CheckArmorShapeAllowed(armor, rules)) { continue; } if (armor.SuitableFor == 2) { Types.Add(armor); } } } // add armor suitable for everyone (e.g. rings) if (rules.ItemTypes.HasFlag(AllodsMap.AlmShop.AlmShopItemType.Armor) || rules.ItemTypes.HasFlag(AllodsMap.AlmShop.AlmShopItemType.ArmorMage)) { foreach (Templates.TplArmor armor in TemplateLoader.Templates.Armor) { if (!CheckArmorShapeAllowed(armor, rules)) { continue; } if (armor.SuitableFor == 3) { Types.Add(armor); } } } // add shields if (rules.ItemTypes.HasFlag(AllodsMap.AlmShop.AlmShopItemType.Shield)) { foreach (Templates.TplArmor shield in TemplateLoader.Templates.Shields) { if (!CheckArmorShapeAllowed(shield, rules)) { continue; } Types.Add(shield); } } // add special (other) // copy the list (we may want to skip some items later) if (rules.ItemTypes.HasFlag(AllodsMap.AlmShop.AlmShopItemType.Other)) { // generate id for scroll. for (ushort i = 6; i <= 0x3F; i++) { ushort itemId = (ushort)(0x0E00 | i); ItemClass cls = ItemClassLoader.GetItemClassById(itemId); if (cls == null) { continue; } if (cls.Price < PriceMin || cls.Price > PriceMax) { continue; } SpecialItemClasses.Add(cls); } } // now that we have all the allowed combinations, let's populate items! // note that not all possible IDs are valid -- we need to check this foreach (Templates.TplArmor armor in Types) { // class id for (int i = 0; i < 7; i++) { // material id for (int j = 0; j < 15; j++) { // check if allowed if ((armor.ClassesAllowed[i] & (1 << j)) == 0) { continue; } ushort itemId = (ushort)((i << 5) | (j << 12) | (armor.Slot << 8) | (armor.Index)); ItemClass cls = ItemClassLoader.GetItemClassById(itemId); if (cls == null) { continue; } if (cls.Price > PriceMax || (!AllowMagic && cls.Price < PriceMin)) { continue; } ItemClasses.Add(cls); } } } Items = new ItemPack(); GenerateItems(); }