コード例 #1
0
        public override void Handle(GameSession session, PacketReader packet)
        {
            int itemId = packet.ReadInt();

            packet.ReadShort(); // Unknown
            int     amount  = packet.ReadInt();
            BoxType boxType = (BoxType)packet.ReadShort();

            string functionName = ItemMetadataStorage.GetFunction(itemId).Name;

            if (functionName != "SelectItemBox" && functionName != "OpenItemBox")
            {
                return;
            }

            Dictionary <long, Item> items = new Dictionary <long, Item>(session.Player.Inventory.Items.Where(x => x.Value.Id == itemId)); // Make copy of items in-case new item is added

            if (items.Count == 0)
            {
                return;
            }

            int index = 0;

            if (boxType == BoxType.SELECT)
            {
                index = packet.ReadShort() - 0x30; // Starts at 0x30 for some reason
                if (index < 0)
                {
                    return;
                }
                SelectItemBox selectBox = ItemMetadataStorage.GetFunction(itemId).SelectItemBox;
                HandleSelectBox(session, items, selectBox, index, amount);
                return;
            }

            OpenItemBox openBox = ItemMetadataStorage.GetFunction(itemId).OpenItemBox;

            HandleOpenBox(session, items, /*openBox,*/ amount);
        }
コード例 #2
0
ファイル: ItemBoxHelper.cs プロジェクト: Zintixx/MapleServer2
    public static void GiveItemFromOpenBox(GameSession session, Item item)
    {
        OpenItemBox      box      = item.Function.OpenItemBox;
        ItemDropMetadata metadata = ItemDropMetadataStorage.GetItemDropMetadata(box.BoxId);

        if (metadata == null)
        {
            session.Send(NoticePacket.Notice("No items found", NoticeType.Chat));
            return;
        }

        if (box.AmountRequired > item.Amount)
        {
            return;
        }

        Inventory inventory = session.Player.Inventory;

        if (box.RequiredItemId > 0)
        {
            Item requiredItem = inventory.Items[box.RequiredItemId];
            if (requiredItem == null)
            {
                return;
            }

            inventory.ConsumeItem(session, requiredItem.Uid, 1);
        }

        inventory.ConsumeItem(session, item.Uid, box.AmountRequired);

        Random rng = RandomProvider.Get();

        // Receive one item from each drop group
        if (box.ReceiveOneItem)
        {
            foreach (DropGroup group in metadata.DropGroups)
            {
                //randomize the contents
                List <DropGroupContent> contentList = group.Contents.OrderBy(x => rng.Next()).ToList();
                foreach (DropGroupContent dropContent in contentList)
                {
                    List <Item> items = GetItemsFromDropGroup(dropContent, session.Player.Gender, session.Player.Job);
                    foreach (Item newItem in items)
                    {
                        inventory.AddItem(session, newItem, true);
                    }
                }
            }
            return;
        }

        // receive all items from each drop group
        foreach (DropGroup group in metadata.DropGroups)
        {
            foreach (DropGroupContent dropContent in group.Contents)
            {
                List <Item> items = GetItemsFromDropGroup(dropContent, session.Player.Gender, session.Player.Job);
                foreach (Item newItem in items)
                {
                    inventory.AddItem(session, newItem, true);
                }
            }
        }
    }
コード例 #3
0
    public static bool GiveItemFromOpenBox(GameSession session, Item item, out OpenBoxResult boxResult)
    {
        boxResult = OpenBoxResult.Success;

        OpenItemBox      box      = item.Function.OpenItemBox;
        ItemDropMetadata metadata = ItemDropMetadataStorage.GetItemDropMetadata(box.BoxId);

        if (metadata == null)
        {
            session.Send(NoticePacket.Notice("No items found", NoticeType.Chat));
            boxResult = OpenBoxResult.UnableToOpen;
            return(false);
        }

        if (box.AmountRequired > item.Amount)
        {
            boxResult = OpenBoxResult.UnableToOpen;
            return(false);
        }

        IInventory  inventory = session.Player.Inventory;
        List <Item> rewards   = new();

        // Receive one item from each drop group
        if (box.ReceiveOneItem)
        {
            foreach (DropGroup group in metadata.DropGroups)
            {
                bool receivedItem = false;

                // Randomize the contents
                IOrderedEnumerable <DropGroupContent> dropContent = group.Contents.OrderBy(_ => Random.Shared.Next());
                foreach (DropGroupContent content in dropContent)
                {
                    // If player has already received an item from this group, skip other contents
                    if (box.ReceiveOneItem && receivedItem)
                    {
                        continue;
                    }

                    List <Item> items = GetItemsFromDropGroup(content, session.Player, item);
                    foreach (Item newItem in items)
                    {
                        receivedItem = true;
                        rewards.Add(newItem);
                    }
                }
            }
        }
        else
        {
            // receive all items from each drop group
            foreach (DropGroup group in metadata.DropGroups)
            {
                foreach (DropGroupContent dropContent in group.Contents)
                {
                    List <Item> items = GetItemsFromDropGroup(dropContent, session.Player, item);
                    rewards.AddRange(items);
                }
            }
        }

        // Check if any inventory of the rewards is full
        if (rewards.Any(reward => inventory.GetFreeSlots(reward.InventoryTab) <= 0))
        {
            boxResult = OpenBoxResult.InventoryFull;
            return(false);
        }

        // Remove the box and required items
        if (box.RequiredItemId > 0)
        {
            Item requiredItem = inventory.GetByUid(box.RequiredItemId);
            if (requiredItem is null)
            {
                boxResult = OpenBoxResult.UnableToOpen;
                return(false);
            }

            inventory.ConsumeItem(session, requiredItem.Uid, 1);
        }

        inventory.ConsumeItem(session, item.Uid, box.AmountRequired);

        // give the rewards
        foreach (Item reward in rewards)
        {
            reward.Uid = DatabaseManager.Items.Insert(reward);

            if (inventory.CanHold(reward))
            {
                inventory.AddItem(session, reward, true);
                continue;
            }

            boxResult = OpenBoxResult.InventoryFull;
            MailHelper.InventoryWasFull(reward, session.Player.CharacterId);
        }

        return(true);
    }
コード例 #4
0
        protected override List <ItemMetadata> Parse()
        {
            // Item breaking ingredients
            Dictionary <int, List <ItemBreakReward> > rewards = new Dictionary <int, List <ItemBreakReward> >();

            foreach (PackFileEntry entry in Resources.XmlReader.Files)
            {
                if (!entry.Name.StartsWith("table/itembreakingredient"))
                {
                    continue;
                }

                XmlDocument innerDocument   = Resources.XmlReader.GetXmlDocument(entry);
                XmlNodeList individualItems = innerDocument.SelectNodes($"/ms2/item");
                foreach (XmlNode nodes in individualItems)
                {
                    string locale = nodes.Attributes["locale"]?.Value ?? "";
                    if (locale != "NA" && locale != "")
                    {
                        continue;
                    }
                    int itemID = int.Parse(nodes.Attributes["ItemID"].Value);
                    rewards[itemID] = new List <ItemBreakReward>();

                    int ingredientItemID1 = int.Parse(nodes.Attributes["IngredientItemID1"]?.Value ?? "0");
                    int ingredientCount1  = int.Parse(nodes.Attributes["IngredientCount1"]?.Value ?? "0");
                    rewards[itemID].Add(new ItemBreakReward(ingredientItemID1, ingredientCount1));

                    _ = int.TryParse(nodes.Attributes["IngredientItemID2"]?.Value ?? "0", out int ingredientItemID2);
                    _ = int.TryParse(nodes.Attributes["IngredientCount2"]?.Value ?? "0", out int ingredientCount2);
                    rewards[itemID].Add(new ItemBreakReward(ingredientItemID2, ingredientCount2));

                    _ = int.TryParse(nodes.Attributes["IngredientItemID3"]?.Value ?? "0", out int ingredientItemID3);
                    _ = int.TryParse(nodes.Attributes["IngredientCount3"]?.Value ?? "0", out int ingredientCount3);
                    rewards[itemID].Add(new ItemBreakReward(ingredientItemID3, ingredientCount3));
                }
            }

            // Item rarity
            Dictionary <int, int> rarities = new Dictionary <int, int>();

            foreach (PackFileEntry entry in Resources.XmlReader.Files)
            {
                if (!entry.Name.StartsWith("table/na/itemwebfinder"))
                {
                    continue;
                }
                XmlDocument innerDocument = Resources.XmlReader.GetXmlDocument(entry);
                XmlNodeList nodes         = innerDocument.SelectNodes($"/ms2/key");
                foreach (XmlNode node in nodes)
                {
                    int itemId = int.Parse(node.Attributes["id"].Value);
                    int rarity = int.Parse(node.Attributes["grade"].Value);
                    rarities[itemId] = rarity;
                }
            }

            // Items
            List <ItemMetadata> items = new List <ItemMetadata>();

            foreach (PackFileEntry entry in Resources.XmlReader.Files)
            {
                if (!entry.Name.StartsWith("item/"))
                {
                    continue;
                }

                ItemMetadata metadata = new ItemMetadata();
                string       filename = Path.GetFileNameWithoutExtension(entry.Name);
                int          itemId   = int.Parse(filename);

                if (items.Exists(item => item.Id == itemId))
                {
                    continue;
                }

                metadata.Id = itemId;
                Debug.Assert(metadata.Id > 0, $"Invalid Id {metadata.Id} from {itemId}");

                // Parse XML
                XmlDocument document = Resources.XmlReader.GetXmlDocument(entry);
                XmlNode     item     = document.SelectSingleNode("ms2/environment");

                // Tag
                XmlNode basic = item.SelectSingleNode("basic");
                metadata.Tag = basic.Attributes["stringTag"].Value;

                // Gear/Cosmetic slot
                XmlNode slots      = item.SelectSingleNode("slots");
                XmlNode slot       = slots.FirstChild;
                bool    slotResult = Enum.TryParse(slot.Attributes["name"].Value, out metadata.Slot);
                if (!slotResult && !string.IsNullOrEmpty(slot.Attributes["name"].Value))
                {
                    Console.WriteLine($"Failed to parse item slot for {itemId}: {slot.Attributes["name"].Value}");
                }

                int totalSlots = slots.SelectNodes("slot").Count;
                if (totalSlots > 1)
                {
                    if (metadata.Slot == ItemSlot.CL || metadata.Slot == ItemSlot.PA)
                    {
                        metadata.IsDress = true;
                    }
                    else if (metadata.Slot == ItemSlot.RH || metadata.Slot == ItemSlot.LH)
                    {
                        metadata.IsTwoHand = true;
                    }
                }

                // Hair data
                if (slot.Attributes["name"].Value == "HR")
                {
                    int     assetNodeCount = slot.SelectNodes("asset").Count;
                    XmlNode asset          = slot.FirstChild;

                    XmlNode scaleNode = slot.SelectSingleNode("scale");

                    if (assetNodeCount == 3)                      // This hair has a front and back positionable hair
                    {
                        XmlNode backHair  = asset.NextSibling;    // back hair info
                        XmlNode frontHair = backHair.NextSibling; // front hair info

                        int backHairNodes = backHair.SelectNodes("custom").Count;

                        CoordF[] bPosCord     = new CoordF[backHairNodes];
                        CoordF[] bPosRotation = new CoordF[backHairNodes];
                        CoordF[] fPosCord     = new CoordF[backHairNodes];
                        CoordF[] fPosRotation = new CoordF[backHairNodes];

                        for (int i = 0; i < backHairNodes; i++)
                        {
                            foreach (XmlNode backPresets in backHair)
                            {
                                if (backPresets.Name == "custom")
                                {
                                    bPosCord[i]     = CoordF.Parse(backPresets.Attributes["position"].Value);
                                    bPosRotation[i] = CoordF.Parse(backPresets.Attributes["rotation"].Value);
                                }
                            }
                            foreach (XmlNode frontPresets in frontHair)
                            {
                                if (frontPresets.Name == "custom")
                                {
                                    fPosCord[i]     = CoordF.Parse(frontPresets.Attributes["position"].Value);
                                    fPosRotation[i] = CoordF.Parse(frontPresets.Attributes["position"].Value);
                                }
                            }
                            HairPresets hairPresets = new HairPresets()
                            {
                            };

                            hairPresets.BackPositionCoord     = bPosCord[i];
                            hairPresets.BackPositionRotation  = bPosRotation[i];
                            hairPresets.FrontPositionCoord    = fPosCord[i];
                            hairPresets.FrontPositionRotation = fPosRotation[i];
                            hairPresets.MinScale = float.Parse(scaleNode?.Attributes["min"]?.Value ?? "0");
                            hairPresets.MaxScale = float.Parse(scaleNode?.Attributes["max"]?.Value ?? "0");

                            metadata.HairPresets.Add(hairPresets);
                        }
                    }
                    else if (assetNodeCount == 2)             // This hair only has back positionable hair
                    {
                        XmlNode backHair = asset.NextSibling; // back hair info

                        int backHairNodes = backHair.SelectNodes("custom").Count;

                        CoordF[] bPosCord     = new CoordF[backHairNodes];
                        CoordF[] bPosRotation = new CoordF[backHairNodes];

                        for (int i = 0; i < backHairNodes; i++)
                        {
                            foreach (XmlNode backPresets in backHair)
                            {
                                if (backPresets.Name == "custom")
                                {
                                    bPosCord[i]     = CoordF.Parse(backPresets.Attributes["position"].Value);
                                    bPosRotation[i] = CoordF.Parse(backPresets.Attributes["rotation"].Value);
                                }
                            }

                            HairPresets hairPresets = new HairPresets()
                            {
                            };

                            hairPresets.BackPositionCoord     = bPosCord[i];
                            hairPresets.BackPositionRotation  = bPosRotation[i];
                            hairPresets.FrontPositionCoord    = CoordF.Parse("0, 0, 0");
                            hairPresets.FrontPositionRotation = CoordF.Parse("0, 0, 0");
                            hairPresets.MinScale = float.Parse(scaleNode?.Attributes["min"]?.Value ?? "0");
                            hairPresets.MaxScale = float.Parse(scaleNode?.Attributes["max"]?.Value ?? "0");
                            metadata.HairPresets.Add(hairPresets);
                        }
                    }
                    else // hair does not have back or front positionable hair
                    {
                        HairPresets hairPresets = new HairPresets()
                        {
                        };
                        hairPresets.BackPositionCoord     = CoordF.Parse("0, 0, 0");
                        hairPresets.BackPositionRotation  = CoordF.Parse("0, 0, 0");
                        hairPresets.FrontPositionCoord    = CoordF.Parse("0, 0, 0");
                        hairPresets.FrontPositionRotation = CoordF.Parse("0, 0, 0");
                        hairPresets.MinScale = float.Parse(scaleNode?.Attributes["min"]?.Value ?? "0");
                        hairPresets.MaxScale = float.Parse(scaleNode?.Attributes["max"]?.Value ?? "0");
                        metadata.HairPresets.Add(hairPresets);
                    }
                }


                // Color data
                XmlNode customize = item.SelectSingleNode("customize");
                metadata.ColorIndex   = int.Parse(customize.Attributes["defaultColorIndex"].Value);
                metadata.ColorPalette = int.Parse(customize.Attributes["colorPalette"].Value);

                // Badge slot
                XmlNode gem       = item.SelectSingleNode("gem");
                bool    gemResult = Enum.TryParse(gem.Attributes["system"].Value, out metadata.Gem);
                if (!gemResult && !string.IsNullOrEmpty(gem.Attributes["system"].Value))
                {
                    Console.WriteLine($"Failed to parse badge slot for {itemId}: {gem.Attributes["system"].Value}");
                }

                // Inventory tab and max stack size
                XmlNode property = item.SelectSingleNode("property");
                try
                {
                    byte type    = byte.Parse(property.Attributes["type"].Value);
                    byte subType = byte.Parse(property.Attributes["subtype"].Value);
                    bool skin    = byte.Parse(property.Attributes["skin"].Value) != 0;
                    metadata.Tab                       = GetTab(type, subType, skin);
                    metadata.IsTemplate                = byte.Parse(property.Attributes["skinType"]?.Value ?? "0") == 99;
                    metadata.TradeableCount            = byte.Parse(property.Attributes["tradableCount"].Value);
                    metadata.RepackageCount            = byte.Parse(property.Attributes["rePackingLimitCount"].Value);
                    metadata.RepackageItemConsumeCount = byte.Parse(property.Attributes["rePackingItemConsumeCount"].Value);

                    // sales price
                    XmlNode sell = property.SelectSingleNode("sell");
                    metadata.SellPrice       = sell.Attributes["price"]?.Value.Split(',').Select(int.Parse).ToList() ?? null;
                    metadata.SellPriceCustom = sell.Attributes["priceCustom"]?.Value.Split(',').Select(int.Parse).ToList() ?? null;
                }
                catch (Exception e)
                {
                    Console.WriteLine($"Failed to parse tab slot for {itemId}: {e.Message}");
                }
                metadata.StackLimit = int.Parse(property.Attributes["slotMax"].Value);

                // Rarity
                XmlNode option = item.SelectSingleNode("option");
                metadata.OptionStatic      = int.Parse(option.Attributes["static"].Value);
                metadata.OptionRandom      = int.Parse(option.Attributes["random"].Value);
                metadata.OptionConstant    = int.Parse(option.Attributes["constant"].Value);
                metadata.OptionLevelFactor = int.Parse(option.Attributes["optionLevelFactor"].Value);

                XmlNode function    = item.SelectSingleNode("function");
                string  contentType = function.Attributes["name"].Value;
                metadata.FunctionData.Name = contentType;

                // Item boxes
                if (contentType == "OpenItemBox")
                {
                    // selection boxes are SelectItemBox and 1,boxid
                    // normal boxes are OpenItemBox and 0,1,0,boxid
                    // fragments are OpenItemBox and 0,1,0,boxid,required_amount
                    if (function.Attributes["parameter"].Value.Contains('l'))
                    {
                        continue; // TODO: Implement these CN items. Skipping for now
                    }

                    List <string> parameters = new List <string>(function.Attributes["parameter"].Value.Split(","));
                    OpenItemBox   box        = new OpenItemBox();
                    box.RequiredItemId = int.Parse(parameters[0]);
                    box.ReceiveOneItem = parameters[1] == "1";
                    box.BoxId          = int.Parse(parameters[3]);
                    box.AmountRequired = 1;
                    if (parameters.Count == 5)
                    {
                        box.AmountRequired = int.Parse(parameters[4]);
                    }
                    metadata.FunctionData.OpenItemBox = box;
                }
                else if (contentType == "SelectItemBox")
                {
                    if (function.Attributes["parameter"].Value.Contains('l'))
                    {
                        continue; // TODO: Implement these CN items. Skipping for now
                    }

                    List <string> parameters = new List <string>(function.Attributes["parameter"].Value.Split(","));
                    parameters.RemoveAll(param => param.Length == 0);
                    SelectItemBox box = new SelectItemBox();
                    box.GroupId = int.Parse(parameters[0]);
                    box.BoxId   = int.Parse(parameters[1]);
                    metadata.FunctionData.SelectItemBox = box;
                }
                else if (contentType == "ChatEmoticonAdd")
                {
                    ChatEmoticonAdd sticker          = new ChatEmoticonAdd();
                    string          rawParameter     = function.Attributes["parameter"].Value;
                    string          decodedParameter = HttpUtility.HtmlDecode(rawParameter);
                    XmlDocument     xmlParameter     = new XmlDocument();
                    xmlParameter.LoadXml(decodedParameter);
                    XmlNode functionParameters = xmlParameter.SelectSingleNode("v");
                    sticker.Id = byte.Parse(functionParameters.Attributes["id"].Value);

                    sticker.Duration = int.Parse(functionParameters.Attributes["durationSec"]?.Value ?? "0");
                    metadata.FunctionData.ChatEmoticonAdd = sticker;
                }
                else if (contentType == "OpenMassive")
                {
                    OpenMassiveEvent massiveEvent     = new OpenMassiveEvent();
                    string           rawParameter     = function.Attributes["parameter"].Value;
                    string           cleanParameter   = rawParameter.Remove(1, 1); // remove the unwanted space
                    string           decodedParameter = HttpUtility.HtmlDecode(cleanParameter);

                    XmlDocument xmlParameter = new XmlDocument();
                    xmlParameter.LoadXml(decodedParameter);
                    XmlNode functionParameters = xmlParameter.SelectSingleNode("v");
                    massiveEvent.FieldId  = int.Parse(functionParameters.Attributes["fieldID"].Value);
                    massiveEvent.Duration = int.Parse(functionParameters.Attributes["portalDurationTick"].Value);
                    massiveEvent.Capacity = byte.Parse(functionParameters.Attributes["maxCount"].Value);
                    metadata.FunctionData.OpenMassiveEvent = massiveEvent;
                }
                else if (contentType == "LevelPotion")
                {
                    LevelPotion levelPotion      = new LevelPotion();
                    string      rawParameter     = function.Attributes["parameter"].Value;
                    string      decodedParameter = HttpUtility.HtmlDecode(rawParameter);
                    XmlDocument xmlParameter     = new XmlDocument();
                    xmlParameter.LoadXml(decodedParameter);
                    XmlNode functionParameters = xmlParameter.SelectSingleNode("v");
                    levelPotion.TargetLevel           = byte.Parse(functionParameters.Attributes["targetLevel"].Value);
                    metadata.FunctionData.LevelPotion = levelPotion;
                }
                else if (contentType == "VIPCoupon")
                {
                    VIPCoupon   coupon           = new VIPCoupon();
                    string      rawParameter     = function.Attributes["parameter"].Value;
                    string      decodedParameter = HttpUtility.HtmlDecode(rawParameter);
                    XmlDocument xmlParameter     = new XmlDocument();
                    xmlParameter.LoadXml(decodedParameter);
                    XmlNode functionParameters = xmlParameter.SelectSingleNode("v");
                    coupon.Duration = int.Parse(functionParameters.Attributes["period"].Value);
                    metadata.FunctionData.VIPCoupon = coupon;
                }
                else if (contentType == "HongBao")
                {
                    HongBaoData hongBao          = new HongBaoData();
                    string      rawParameter     = function.Attributes["parameter"].Value;
                    string      decodedParameter = HttpUtility.HtmlDecode(rawParameter);
                    XmlDocument xmlParameter     = new XmlDocument();
                    xmlParameter.LoadXml(decodedParameter);
                    XmlNode functionParameters = xmlParameter.SelectSingleNode("v");
                    hongBao.Id                    = int.Parse(functionParameters.Attributes["itemId"].Value);
                    hongBao.Count                 = short.Parse(functionParameters.Attributes["totalCount"].Value);
                    hongBao.TotalUsers            = byte.Parse(functionParameters.Attributes["totalUser"].Value);
                    hongBao.Duration              = int.Parse(functionParameters.Attributes["durationSec"].Value);
                    metadata.FunctionData.HongBao = hongBao;
                }
                else if (contentType == "SuperWorldChat")
                {
                    string[] parameters = function.Attributes["parameter"].Value.Split(",");
                    metadata.FunctionData.Id = int.Parse(parameters[0]); // only storing the first parameter. Not sure if the server uses the other 2.
                }
                else if (contentType == "OpenGachaBox")
                {
                    string[] parameters = function.Attributes["parameter"].Value.Split(",");
                    metadata.FunctionData.Id = int.Parse(parameters[0]); // only storing the first parameter. Unknown what the second parameter is used for.
                }
                else if (contentType == "OpenCoupleEffectBox")
                {
                    OpenCoupleEffectBox box        = new OpenCoupleEffectBox();
                    string[]            parameters = function.Attributes["parameter"].Value.Split(",");
                    box.Id     = int.Parse(parameters[0]);
                    box.Rarity = byte.Parse(parameters[1]);
                    metadata.FunctionData.OpenCoupleEffectBox = box;
                }
                else if (contentType == "InstallBillBoard")
                {
                    InstallBillboard balloon          = new InstallBillboard();
                    string           rawParameter     = function.Attributes["parameter"].Value;
                    string           decodedParameter = HttpUtility.HtmlDecode(rawParameter);
                    XmlDocument      xmlParameter     = new XmlDocument();
                    xmlParameter.LoadXml(decodedParameter);
                    XmlNode functionParameters = xmlParameter.SelectSingleNode("v");
                    balloon.InteractId  = int.Parse(functionParameters.Attributes["interactID"].Value);
                    balloon.Duration    = int.Parse(functionParameters.Attributes["durationSec"].Value);
                    balloon.Model       = functionParameters.Attributes["model"].Value;
                    balloon.Asset       = functionParameters.Attributes["asset"]?.Value ?? "";
                    balloon.NormalState = functionParameters.Attributes["normal"].Value;
                    balloon.Reactable   = functionParameters.Attributes["reactable"].Value;
                    balloon.Scale       = float.Parse(functionParameters.Attributes["scale"]?.Value ?? "0");
                    metadata.FunctionData.InstallBillboard = balloon;
                }
                else if (contentType == "TitleScroll" || contentType == "ItemExchangeScroll" || contentType == "OpenInstrument" || contentType == "StoryBook" || contentType == "FishingRod" || contentType == "ItemChangeBeauty" ||
                         contentType == "ItemRePackingScroll")
                {
                    metadata.FunctionData.Id = int.Parse(function.Attributes["parameter"].Value);
                }

                // Music score charges
                XmlNode musicScore = item.SelectSingleNode("MusicScore");
                metadata.PlayCount     = int.Parse(musicScore.Attributes["playCount"].Value);
                metadata.FileName      = musicScore.Attributes["fileName"].Value;
                metadata.IsCustomScore = bool.Parse(musicScore.Attributes["isCustomNote"].Value);

                // Shop ID from currency items
                if (item["Shop"] != null)
                {
                    XmlNode shop = item.SelectSingleNode("Shop");
                    metadata.ShopID = int.Parse(shop.Attributes["systemShopID"].Value);
                }

                XmlNode skill = item.SelectSingleNode("skill");
                metadata.SkillID = int.Parse(skill.Attributes["skillID"].Value);

                XmlNode limit = item.SelectSingleNode("limit");
                metadata.EnableBreak   = byte.Parse(limit.Attributes["enableBreak"].Value) == 1;
                metadata.Level         = int.Parse(limit.Attributes["levelLimit"].Value);
                metadata.TransferType  = (TransferType)byte.Parse(limit.Attributes["transferType"].Value);
                metadata.Sellable      = byte.Parse(limit.Attributes["shopSell"].Value) == 1;
                metadata.RecommendJobs = limit.Attributes["recommendJobs"]?.Value.Split(",").Where(x => !string.IsNullOrEmpty(x)).Select(int.Parse).ToList();
                metadata.Gender        = byte.Parse(limit.Attributes["genderLimit"].Value);

                XmlNode installNode = item.SelectSingleNode("install");
                metadata.IsCubeSolid = byte.Parse(installNode.Attributes["cubeProp"].Value) == 1;
                metadata.ObjectId    = int.Parse(installNode.Attributes["objCode"].Value);

                XmlNode housingNode = item.SelectSingleNode("housing");
                string  value       = housingNode.Attributes["categoryTag"]?.Value;
                if (value is not null)
                {
                    List <string> categories = new List <string>(value.Split(","));
                    _ = short.TryParse(categories[0], out short category);

                    metadata.HousingCategory = (ItemHousingCategory)category;
                }

                // Item breaking ingredients
                if (rewards.ContainsKey(itemId))
                {
                    metadata.BreakRewards = rewards[itemId];
                }

                if (rarities.ContainsKey(itemId))
                {
                    metadata.Rarity = rarities[itemId];
                }

                items.Add(metadata);
            }
            return(items);
        }