Example #1
0
        private static void HandleRandomHair(GameSession session, PacketReader packet)
        {
            int  shopId     = packet.ReadInt();
            bool useVoucher = packet.ReadBool();

            BeautyMetadata    beautyShop  = BeautyMetadataStorage.GetShopById(shopId);
            List <BeautyItem> beautyItems = BeautyMetadataStorage.GetGenderItems(beautyShop.ShopId, session.Player.Gender);

            if (!HandleShopPay(session, beautyShop, useVoucher))
            {
                return;
            }

            // Grab random hair
            Random     random     = new Random();
            int        indexHair  = random.Next(beautyItems.Count);
            BeautyItem chosenHair = beautyItems[indexHair];

            //Grab a preset hair and length of hair
            ItemMetadata beautyItemData = ItemMetadataStorage.GetMetadata(chosenHair.ItemId);
            int          indexPreset    = random.Next(beautyItemData.HairPresets.Count);
            HairPresets  chosenPreset   = beautyItemData.HairPresets[indexPreset];

            //Grab random front hair length
            double chosenFrontLength = random.NextDouble() *
                                       (beautyItemData.HairPresets[indexPreset].MaxScale - beautyItemData.HairPresets[indexPreset].MinScale) + beautyItemData.HairPresets[indexPreset].MinScale;

            //Grab random back hair length
            double chosenBackLength = random.NextDouble() *
                                      (beautyItemData.HairPresets[indexPreset].MaxScale - beautyItemData.HairPresets[indexPreset].MinScale) + beautyItemData.HairPresets[indexPreset].MinScale;

            // Grab random preset color
            ColorPaletteMetadata palette = ColorPaletteMetadataStorage.GetMetadata(2); // pick from palette 2. Seems like it's the correct palette for basic hair colors

            int        indexColor = random.Next(palette.DefaultColors.Count);
            MixedColor color      = palette.DefaultColors[indexColor];

            Dictionary <ItemSlot, Item> equippedInventory = session.Player.GetEquippedInventory(InventoryTab.Gear);

            Item newHair = new Item(chosenHair.ItemId)
            {
                Color      = EquipColor.Argb(color, indexColor, palette.PaletteId),
                HairD      = new HairData((float)chosenBackLength, (float)chosenFrontLength, chosenPreset.BackPositionCoord, chosenPreset.BackPositionRotation, chosenPreset.FrontPositionCoord, chosenPreset.FrontPositionRotation),
                IsTemplate = false
            };

            //Remove old hair
            if (session.Player.Equips.Remove(ItemSlot.HR, out Item previousHair))
            {
                previousHair.Slot = -1;
                session.Player.HairInventory.RandomHair = previousHair; // store the previous hair
                session.FieldManager.BroadcastPacket(EquipmentPacket.UnequipItem(session.FieldPlayer, previousHair));
            }

            equippedInventory[ItemSlot.HR] = newHair;

            session.FieldManager.BroadcastPacket(EquipmentPacket.EquipItem(session.FieldPlayer, newHair, ItemSlot.HR));
            session.Send(BeautyPacket.RandomHairOption(previousHair, newHair));
        }
Example #2
0
    public static void ChangeHair(GameSession session, int hairId, out Item previousHair, out Item newHair)
    {
        Random random = Random.Shared;

        //Grab a preset hair and length of hair
        ItemCustomizeMetadata customize = ItemMetadataStorage.GetMetadata(hairId).Customize;
        int         indexPreset         = random.Next(customize.HairPresets.Count);
        HairPresets chosenPreset        = customize.HairPresets[indexPreset];

        //Grab random front hair length
        double chosenFrontLength = random.NextDouble() *
                                   (customize.HairPresets[indexPreset].MaxScale - customize.HairPresets[indexPreset].MinScale) + customize.HairPresets[indexPreset].MinScale;

        //Grab random back hair length
        double chosenBackLength = random.NextDouble() *
                                  (customize.HairPresets[indexPreset].MaxScale - customize.HairPresets[indexPreset].MinScale) + customize.HairPresets[indexPreset].MinScale;

        // Grab random preset color
        ColorPaletteMetadata palette = ColorPaletteMetadataStorage.GetMetadata(2); // pick from palette 2. Seems like it's the correct palette for basic hair colors

        int        indexColor = random.Next(palette.DefaultColors.Count);
        MixedColor color      = palette.DefaultColors[indexColor];

        newHair = new(hairId)
        {
            Color              = EquipColor.Argb(color, indexColor, palette.PaletteId),
            HairData           = new((float)chosenBackLength, (float)chosenFrontLength, chosenPreset.BackPositionCoord, chosenPreset.BackPositionRotation, chosenPreset.FrontPositionCoord, chosenPreset.FrontPositionRotation),
            IsEquipped         = true,
            OwnerCharacterId   = session.Player.CharacterId,
            OwnerCharacterName = session.Player.Name
        };
        Dictionary <ItemSlot, Item> cosmetics = session.Player.Inventory.Cosmetics;

        //Remove old hair
        if (cosmetics.Remove(ItemSlot.HR, out previousHair))
        {
            previousHair.Slot = -1;
            session.Player.HairInventory.RandomHair = previousHair; // store the previous hair
            DatabaseManager.Items.Delete(previousHair.Uid);
            session.FieldManager.BroadcastPacket(EquipmentPacket.UnequipItem(session.Player.FieldPlayer, previousHair));
        }

        cosmetics[ItemSlot.HR] = newHair;
    }
Example #3
0
        protected override List <ItemMetadata> Parse()
        {
            // Item boxes
            Dictionary <string, List <ItemContent> > itemDrops = new Dictionary <string, List <ItemContent> >();

            foreach (PackFileEntry entry in Resources.XmlFiles)
            {
                if (!entry.Name.StartsWith("table/individualitemdrop") && !entry.Name.StartsWith("table/na/individualitemdrop"))
                {
                    continue;
                }

                XmlDocument innerDocument      = Resources.XmlMemFile.GetDocument(entry.FileHeader);
                XmlNodeList individualBoxItems = innerDocument.SelectNodes($"/ms2/individualDropBox");
                foreach (XmlNode individualBoxItem in individualBoxItems)
                {
                    // Skip locales other than NA and null
                    string locale = string.IsNullOrEmpty(individualBoxItem.Attributes["locale"]?.Value) ? "" : individualBoxItem.Attributes["locale"].Value;

                    if (locale != "NA" && locale != "")
                    {
                        continue;
                    }

                    if (individualBoxItem.Attributes["minCount"].Value.Contains("."))
                    {
                        continue;
                    }

                    string box           = individualBoxItem.Attributes["individualDropBoxID"].Value;
                    int    id            = int.Parse(individualBoxItem.Attributes["item"].Value);
                    int    minAmount     = int.Parse(individualBoxItem.Attributes["minCount"].Value);
                    int    maxAmount     = int.Parse(individualBoxItem.Attributes["maxCount"].Value);
                    int    dropGroup     = int.Parse(individualBoxItem.Attributes["dropGroup"].Value);
                    int    smartDropRate = string.IsNullOrEmpty(individualBoxItem.Attributes["smartDropRate"]?.Value) ? 0 : int.Parse(individualBoxItem.Attributes["smartDropRate"].Value);
                    int    contentRarity = string.IsNullOrEmpty(individualBoxItem.Attributes["PackageUIShowGrade"]?.Value) ? 0 : int.Parse(individualBoxItem.Attributes["PackageUIShowGrade"].Value);
                    int    enchant       = string.IsNullOrEmpty(individualBoxItem.Attributes["enchantLevel"]?.Value) ? 0 : int.Parse(individualBoxItem.Attributes["enchantLevel"].Value);
                    int    id2           = string.IsNullOrEmpty(individualBoxItem.Attributes["item2"]?.Value) ? 0 : int.Parse(individualBoxItem.Attributes["item2"].Value);

                    ItemContent content = new ItemContent(id, minAmount, maxAmount, dropGroup, smartDropRate, contentRarity, enchant, id2);
                    if (itemDrops.ContainsKey(box))
                    {
                        itemDrops[box].Add(content);
                    }
                    else
                    {
                        itemDrops[box] = new List <ItemContent>()
                        {
                            content
                        };
                    }
                }
            }

            // Item breaking ingredients
            Dictionary <int, List <ItemBreakReward> > rewards = new Dictionary <int, List <ItemBreakReward> >();

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

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

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

                    int ingredientItemID2 = string.IsNullOrEmpty(nodes.Attributes["IngredientItemID2"]?.Value) ? 0 : int.Parse(nodes.Attributes["IngredientItemID2"].Value);
                    int ingredientCount2  = string.IsNullOrEmpty(nodes.Attributes["IngredientCount2"]?.Value) ? 0 : int.Parse(nodes.Attributes["IngredientCount2"].Value);
                    rewards[itemID].Add(new ItemBreakReward(ingredientItemID2, ingredientCount2));

                    int ingredientItemID3 = string.IsNullOrEmpty(nodes.Attributes["IngredientItemID3"]?.Value) ? 0 : int.Parse(nodes.Attributes["IngredientItemID3"].Value);
                    int ingredientCount3  = string.IsNullOrEmpty(nodes.Attributes["IngredientCount3"]?.Value) ? 0 : int.Parse(nodes.Attributes["IngredientCount3"].Value);
                    rewards[itemID].Add(new ItemBreakReward(ingredientItemID3, ingredientCount3));
                }
            }

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

            foreach (PackFileEntry entry in Resources.XmlFiles)
            {
                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))
                {
                    //Console.WriteLine($"Duplicate {entry.Name} was already added.");
                    continue;
                }

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

                // Parse XML
                XmlDocument document = Resources.XmlMemFile.GetDocument(entry.FileHeader);
                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 <ItemSlot>(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];
                            if (scaleNode != null)
                            {
                                hairPresets.MinScale = float.Parse(scaleNode.Attributes["min"].Value ?? "0");
                                hairPresets.MaxScale = float.Parse(scaleNode.Attributes["max"].Value ?? "0");
                            }
                            else
                            {
                                hairPresets.MinScale = 0;
                                hairPresets.MaxScale = 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");
                            if (scaleNode != null)
                            {
                                hairPresets.MinScale = float.Parse(scaleNode.Attributes["min"].Value ?? "0");
                                hairPresets.MaxScale = float.Parse(scaleNode.Attributes["max"].Value ?? "0");
                            }
                            else
                            {
                                hairPresets.MinScale = 0;
                                hairPresets.MaxScale = 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");
                        if (scaleNode != null)
                        {
                            hairPresets.MinScale = float.Parse(scaleNode.Attributes["min"].Value ?? "0");
                            hairPresets.MaxScale = float.Parse(scaleNode.Attributes["max"].Value ?? "0");
                        }
                        else
                        {
                            hairPresets.MinScale = 0;
                            hairPresets.MaxScale = 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 <GemSlot>(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;

                    // sales price
                    XmlNode sell = property.SelectSingleNode("sell");
                    metadata.SellPrice       = string.IsNullOrEmpty(sell.Attributes["price"]?.Value) ? null : sell.Attributes["price"].Value.Split(',').Select(int.Parse).ToList();
                    metadata.SellPriceCustom = string.IsNullOrEmpty(sell.Attributes["priceCustom"]?.Value) ? null : sell.Attributes["priceCustom"].Value.Split(',').Select(int.Parse).ToList();
                }
                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");
                int     rarity = 1;
                if (option.Attributes["constant"].Value.Length == 1)
                {
                    rarity = int.Parse(option.Attributes["constant"].Value);
                }
                metadata.Rarity = rarity;

                // Item boxes
                XmlNode function    = item.SelectSingleNode("function");
                string  contentType = function.Attributes["name"].Value;
                metadata.FunctionData.Name = contentType;
                if (contentType == "OpenItemBox" || contentType == "SelectItemBox")
                {
                    // 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
                    List <string> parameters = new List <string>(function.Attributes["parameter"].Value.Split(","));
                    // Remove empty params
                    parameters.RemoveAll(param => param.Length == 0);

                    if (parameters.Count >= 2)
                    {
                        string boxId = contentType == "OpenItemBox" ? parameters[3] : parameters[1];

                        foreach (KeyValuePair <string, List <ItemContent> > box in itemDrops) // Search for box id and set the rewards previously parsed
                        {
                            if (box.Key == boxId)
                            {
                                metadata.Content = box.Value;
                                break;
                            }
                        }
                    }
                }
                else if (contentType == "ChatEmoticonAdd")
                {
                    string      rawParameter     = function.Attributes["parameter"].Value;
                    string      decodedParameter = HttpUtility.HtmlDecode(rawParameter);
                    XmlDocument xmlParameter     = new XmlDocument();
                    xmlParameter.LoadXml(decodedParameter);
                    XmlNode functionParameters = xmlParameter.SelectSingleNode("v");
                    metadata.FunctionData.Id = byte.Parse(functionParameters.Attributes["id"].Value);

                    int durationSec = 0;

                    if (functionParameters.Attributes["durationSec"] != null)
                    {
                        durationSec = int.Parse(functionParameters.Attributes["durationSec"].Value);
                    }
                    metadata.FunctionData.Duration = durationSec;
                }
                else if (contentType == "OpenMassive")
                {
                    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");
                    metadata.FunctionData.FieldId  = int.Parse(functionParameters.Attributes["fieldID"].Value);
                    metadata.FunctionData.Duration = int.Parse(functionParameters.Attributes["portalDurationTick"].Value);
                    metadata.FunctionData.Capacity = byte.Parse(functionParameters.Attributes["maxCount"].Value);
                }
                else if (contentType == "LevelPotion")
                {
                    string      rawParameter     = function.Attributes["parameter"].Value;
                    string      decodedParameter = HttpUtility.HtmlDecode(rawParameter);
                    XmlDocument xmlParameter     = new XmlDocument();
                    xmlParameter.LoadXml(decodedParameter);
                    XmlNode functionParameters = xmlParameter.SelectSingleNode("v");
                    metadata.FunctionData.TargetLevel = byte.Parse(functionParameters.Attributes["targetLevel"].Value);
                }
                else if (contentType == "VIPCoupon")
                {
                    string      rawParameter     = function.Attributes["parameter"].Value;
                    string      decodedParameter = HttpUtility.HtmlDecode(rawParameter);
                    XmlDocument xmlParameter     = new XmlDocument();
                    xmlParameter.LoadXml(decodedParameter);
                    XmlNode functionParameters = xmlParameter.SelectSingleNode("v");
                    metadata.FunctionData.Duration = int.Parse(functionParameters.Attributes["period"].Value);
                }
                else if (contentType == "HongBao")
                {
                    string      rawParameter     = function.Attributes["parameter"].Value;
                    string      decodedParameter = HttpUtility.HtmlDecode(rawParameter);
                    XmlDocument xmlParameter     = new XmlDocument();
                    xmlParameter.LoadXml(decodedParameter);
                    XmlNode functionParameters = xmlParameter.SelectSingleNode("v");
                    metadata.FunctionData.Id        = int.Parse(functionParameters.Attributes["itemId"].Value);
                    metadata.FunctionData.Count     = short.Parse(functionParameters.Attributes["totalCount"].Value);
                    metadata.FunctionData.TotalUser = byte.Parse(functionParameters.Attributes["totalUser"].Value);
                    metadata.FunctionData.Duration  = int.Parse(functionParameters.Attributes["durationSec"].Value);
                }
                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")
                {
                    string[] parameters = function.Attributes["parameter"].Value.Split(",");
                    metadata.FunctionData.Id     = int.Parse(parameters[0]);
                    metadata.FunctionData.Rarity = byte.Parse(parameters[1]);
                }
                else if (contentType == "InstallBillBoard")
                {
                    AdBalloonData balloon          = new AdBalloonData();
                    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;
                    if (functionParameters.Attributes["asset"] != null)
                    {
                        balloon.Asset = functionParameters.Attributes["asset"].Value;
                    }
                    balloon.NormalState = functionParameters.Attributes["normal"].Value;
                    balloon.Reactable   = functionParameters.Attributes["reactable"].Value;
                    if (functionParameters.Attributes["scale"] != null)
                    {
                        balloon.Scale = float.Parse(functionParameters.Attributes["scale"].Value);
                    }
                    metadata.AdBalloonData = balloon;
                }
                else if (contentType == "TitleScroll" || contentType == "ItemExchangeScroll" || contentType == "OpenInstrument" || contentType == "StoryBook" || contentType == "FishingRod")
                {
                    metadata.FunctionData.Id = int.Parse(function.Attributes["parameter"].Value);
                }

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

                // 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");
                int     skillID = int.Parse(skill.Attributes["skillID"].Value);
                metadata.SkillID = skillID;

                XmlNode limit       = item.SelectSingleNode("limit");
                bool    enableBreak = byte.Parse(limit.Attributes["enableBreak"].Value) == 1;
                metadata.EnableBreak = enableBreak;

                int level = int.Parse(limit.Attributes["levelLimit"].Value);
                metadata.Level = level;

                if (!string.IsNullOrEmpty(limit.Attributes["recommendJobs"].Value))
                {
                    List <string> recommendJobs = new List <string>(limit.Attributes["recommendJobs"].Value.Split(","));
                    foreach (string recommendJob in recommendJobs)
                    {
                        metadata.RecommendJobs.Add(int.Parse(recommendJob));
                    }
                }

                metadata.Gender = byte.Parse(limit.Attributes["genderLimit"].Value);

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

                items.Add(metadata);
            }
            return(items);
        }
Example #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);
        }