using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Reflection; using NHibernate.Linq; using DiIiS_NA.Core.Logging; using DiIiS_NA.Core.Storage; using DiIiS_NA.Core.Storage.AccountDataBase.Entities; using DiIiS_NA.Core.MPQ; using DiIiS_NA.Core.MPQ.FileFormats; using DiIiS_NA.GameServer.Core.Types.SNO; using Scene = DiIiS_NA.GameServer.GSSystem.MapSystem.Scene; using World = DiIiS_NA.GameServer.GSSystem.MapSystem.World; using static DiIiS_NA.Core.MPQ.FileFormats.GameBalance; using DiIiS_NA.Core.Helpers.Hash; using DiIiS_NA.GameServer.Core.Types.TagMap; using DiIiS_NA.GameServer.MessageSystem; using DiIiS_NA.LoginServer.Toons; using DiIiS_NA.Core.Helpers.Math; using DiIiS_NA.GameServer.GSSystem.PlayerSystem; namespace DiIiS_NA.GameServer.GSSystem.ItemsSystem { public static class ItemGenerator { private static readonly Logger Logger = LogManager.CreateLogger(nameof(ItemGenerator)); public static readonly ConcurrentDictionary Items = new(); public static readonly ConcurrentDictionary AllowedItems = new(); public static readonly ConcurrentDictionary AllowedUniqueItems = new(); private static readonly ConcurrentDictionary Recipes = new(); private static readonly ConcurrentDictionary Lore = new(); private static readonly List GemEffects = new(); private static readonly List ParagonBonuses = new(); private static readonly List ItemSetsEffects = new(); private static readonly ConcurrentDictionary GBIDHandlers = new(); private static readonly ConcurrentDictionary TypeHandlers = new(); private static readonly HashSet AllowedItemTypes = new(); private static readonly List CraftOnlyItems = new(); public static int TotalItems { get { return Items.Count; } } public static List Tutorials = new(); public static Dictionary Bounties = new(); static ItemGenerator() { Player.GeneratePLB(); Logger.Info("Loading Recipes..."); Logger.Info("Loading Items..."); LoadRecipes(); LoadItems(); Logger.Info("Loading Paragons..."); LoadParagonBonuses(); //LoadAffixes(); //just for checking values //LoadPowers(); //LoadQuests(); Logger.Info("Loading Tutorials..."); Tutorials = MPQStorage.Data.Assets[SNOGroup.Tutorial].Keys.OrderBy(i => i).ToList(); Logger.Info("Loading Bonuses..."); LoadItemSetBonuses(); LoadGemBonuses(); Logger.Info("Loading Handlers..."); LoadHandlers(); Logger.Info("Loading Lore..."); LoadLore(); Logger.Info("Loading Bounties..."); LoadBounties(); //LoadConversations(); //if (Net.GS.Config.Instance.Enabled) Logger.Info("Loading Worlds..."); Scene.PreCacheMarkers(); SetAllowedTypes(); System.Threading.Thread.CurrentThread.Name = "ItemGenerator"; } #region loading generator private static void LoadBounties() { foreach (var asset in MPQStorage.Data.Assets[SNOGroup.Quest].Values) { Quest data = asset.Data as Quest; if (data != null && data.QuestType == QuestType.Bounty) { if (data.BountyData0.ActData == BountyData.ActT.Invalid) { if (asset.Name.Contains("_A1_")) data.BountyData0.ActData = BountyData.ActT.A1; if (asset.Name.Contains("_A2_")) data.BountyData0.ActData = BountyData.ActT.A2; if (asset.Name.Contains("_A3_")) data.BountyData0.ActData = BountyData.ActT.A3; if (asset.Name.Contains("_A4_")) data.BountyData0.ActData = BountyData.ActT.A4; if (asset.Name.Contains("_A5_")) data.BountyData0.ActData = BountyData.ActT.A5; } //fixes for bugged bounties if (data.BountyData0.Type != BountyData.BountyType.CompleteEvent && data.QuestSteps .SelectMany(s => s.StepObjectiveSets).SelectMany(s => s.StepObjectives).Any(o => o.ObjectiveType == QuestStepObjectiveType.CompleteQuest)) data.BountyData0.Type = BountyData.BountyType.CompleteEvent; if (data.BountyData0.Type == BountyData.BountyType.KillUnique && data.QuestSteps .SelectMany(s => s.StepObjectiveSets).SelectMany(s => s.StepObjectives) .Any(o => o.ObjectiveType == QuestStepObjectiveType.KillAll)) data.BountyData0.Type = BountyData.BountyType.ClearDungeon; if (data.BountyData0.Type == BountyData.BountyType.KillUnique && !data.QuestSteps .SelectMany(s => s.StepObjectiveSets).SelectMany(s => s.StepObjectives) .Any(o => o.ObjectiveType == QuestStepObjectiveType.KillAny)) continue; if (data.BountyData0.Type == BountyData.BountyType.KillUnique && data.QuestSteps .SelectMany(s => s.StepObjectiveSets).SelectMany(s => s.StepObjectives) .Where(o => o.ObjectiveType == QuestStepObjectiveType.KillMonster).Count() > 1) continue; Bounties.Add(data.Header.SNOId, data); } } } private static void LoadHandlers() { foreach (var type in Assembly.GetExecutingAssembly().GetTypes()) { if (!type.IsSubclassOf(typeof(Item))) continue; var attributes = (HandledItemAttribute[])type.GetCustomAttributes(typeof(HandledItemAttribute), true); if (attributes.Length != 0) { foreach (var name in attributes.First().Names) { GBIDHandlers.TryAdd(StringHashHelper.HashItemName(name), type); } } var typeAttributes = (HandledTypeAttribute[])type.GetCustomAttributes(typeof(HandledTypeAttribute), true); if (typeAttributes.Length != 0) { foreach (var typeName in typeAttributes.First().Types) { TypeHandlers.TryAdd(StringHashHelper.HashItemName(typeName), type); } } } } private static void LoadItems() { //Logger.Info("LoadItems()"); foreach (var asset in MPQStorage.Data.Assets[SNOGroup.GameBalance].Values) { GameBalance data = asset.Data as GameBalance; if (data != null && data.Type == BalanceType.Items) { foreach (var itemDefinition in data.Item) { Items.TryAdd(itemDefinition.Hash, itemDefinition); if (itemDefinition.Name.EndsWith("_104") && itemDefinition.Name.Count(i => Char.IsDigit(i)) > 4) { ObsoleteItems.Add(itemDefinition.Hash); //Logger.Debug("flagged as obsolete _104: {0}, {1}", itemDefinition.Name, itemDefinition.Hash); } if (itemDefinition.Name.EndsWith("_1xx")) { ObsoleteItems.Add(itemDefinition.Hash); //Logger.Debug("flagged as obsolete _1xx: {0}, {1}", itemDefinition.Name, itemDefinition.Hash); } if (itemDefinition.Name.ToLower().Contains("unique") && !itemDefinition.Name.EndsWith("_x1") && !itemDefinition.Name.EndsWith("_104") && !itemDefinition.Name.EndsWith("_1xx")) { ObsoleteItems.Add(itemDefinition.Hash); //Logger.Debug("flagged as obsolete 1.0.3: {0}, {1}", itemDefinition.Name, itemDefinition.Hash); } if ( itemDefinition.SNOActor != 4812 //orb_norm_base_03 && !(itemDefinition.Name.ToLower().Contains("stone") && !itemDefinition.Name.ToLower().Contains("spiritstone")) //StoneOfRecall && !itemDefinition.Name.ToLower().StartsWith("ph_") //Kadala items && !itemDefinition.Name.ToLower().Contains("talisman") //TalismanUnlock && !itemDefinition.Name.ToLower().Contains("console") //bugged console items && !itemDefinition.Name.ToLower().Contains("scroll") //any kind of scrolls && !itemDefinition.Name.ToLower().Contains("powerpotion") //PowerPotion && !itemDefinition.Name.ToLower().Contains("cow") //StaffOfCow && !itemDefinition.Name.ToLower().Contains("wings") //AngelWings && !itemDefinition.Name.ToLower().Contains("wodflag") //WoDFlag && !itemDefinition.Name.ToLower().Contains("ptr") //WoDFlag && !itemDefinition.Name.ToLower().Contains("crafting") //recipes and reagents && !itemDefinition.Name.ToLower().Contains("nephalemcube") //NephalemCube && !itemDefinition.Name.ToLower().Contains("oftheancients") //BladeoftheAncients && !itemDefinition.Name.ToLower().Contains("giyuasfang") //Set_001_GiyuasFang && !itemDefinition.Name.ToLower().Contains("thortest") //ThorTest items && !itemDefinition.Name.ToLower().Contains("promo") //Promo items && !itemDefinition.Name.ToLower().Contains("shadowclone") //shadowClone OP weapon //&& !(itemDefinition.Name.Contains("_Set_") && itemDefinition.Name.EndsWith("_x1")) //RoS sets //&& !(itemDefinition.Name.ToLower().Contains("unique") && (itemDefinition.BonusAffixes + itemDefinition.BonusMajorAffixes + itemDefinition.BonusMinorAffixes == 0) && itemDefinition.LegendaryAffixFamily[0] == -1) //2.1+ items && !(CraftOnlyItems.Contains(itemDefinition.Hash) && itemDefinition.Name.ToLower().Contains("unique")) //crafted && !(CraftOnlyItems.Contains(itemDefinition.Hash) && itemDefinition.Name.ToLower().Contains("unique")) //crafted //&& !(itemDefinition.Name.ToLower().Contains("_10") && itemDefinition.Name.ToLower().Contains("_x1")) //"future" items && itemDefinition.ItemTypesGBID != StringHashHelper.HashItemName("Book") && itemDefinition.ItemTypesGBID != StringHashHelper.HashItemName("TreasureBag") && itemDefinition.ItemTypesGBID != StringHashHelper.HashItemName("Journal") && itemDefinition.ItemTypesGBID != StringHashHelper.HashItemName("Scroll") && itemDefinition.ItemTypesGBID != StringHashHelper.HashItemName("Utility") && itemDefinition.ItemTypesGBID != StringHashHelper.HashItemName("Unknown") && itemDefinition.ItemTypesGBID != StringHashHelper.HashItemName("Dye") && itemDefinition.ItemTypesGBID != StringHashHelper.HashItemName("Shard") && itemDefinition.ItemTypesGBID != StringHashHelper.HashItemName("GreaterShard") && itemDefinition.ItemTypesGBID != StringHashHelper.HashItemName("HealthPotion") ) { if (!IsBuggedItem(itemDefinition)) { if (itemDefinition.Name.ToLower().Contains("_set_") || itemDefinition.Name.ToLower().Contains("unique") || itemDefinition.Name.ToLower().StartsWith("p71_ethereal") ) { AllowedUniqueItems.TryAdd(itemDefinition.Hash, itemDefinition); AllowedItems.TryAdd(itemDefinition.Hash, itemDefinition); } else AllowedItems.TryAdd(itemDefinition.Hash, itemDefinition); //Logger.Debug("flagged as allowed item: {0}, {1}", itemDefinition.Name, itemDefinition.Hash); } } } } } } private static bool IsBuggedItem(ItemTable definition) { switch (definition.Name) { case "Unique_Axe_1H_101_x1": case "Unique_Axe_1H_102_x1": case "Unique_Axe_2H_101_x1": case "Unique_Axe_2H_102_x1": case "Unique_Axe_2H_103_x1": case "Unique_Axe_2H_104_x1": case "Unique_BarbBelt_102_x1": case "Unique_BarbBelt_103_x1": case "Unique_BarbBelt_104_x1": case "Unique_BarbBelt_105_x1": case "Unique_Belt_103_x1": case "Unique_Boots_102_x1": case "Unique_Boots_103_x1": case "Unique_Bow_102_x1": case "Unique_Bow_103_x1": case "Unique_Bow_104_x1": case "Unique_Bracer_105_x1": case "Unique_Bracer_108_x1": case "Unique_Cloak_102_x1": case "Unique_CruShield_104_x1": case "Unique_CruShield_108_x1": case "Unique_Dagger_101_x1": case "Unique_Dagger_102_x1": case "Unique_Dagger_103_x1": case "Unique_Fist_102_x1": case "Unique_Gloves_103_x1": case "Unique_HandXBow_102_x1": case "Unique_Helm_103_x1": case "Unique_Mace_1H_101_x1": case "Unique_Mace_2H_104_x1": case "Unique_Mighty_1H_101_x1": case "Unique_Mighty_1H_102_x1": case "Unique_Mighty_1H_103_x1": case "Unique_Mighty_1H_104_x1": case "Unique_Pants_102_x1": case "Unique_Polearm_102_x1": case "Unique_Shield_103_x1": case "Unique_Shield_104_x1": case "Unique_Shield_105_x1": case "Unique_Shield_106_x1": case "Unique_Shield_107_x1": case "Unique_Shoulder_103_x1": case "Unique_Spear_102_x1": case "Unique_Staff_104_x1": case "Unique_Sword_1H_105_x1": case "Unique_Sword_1H_106_x1": case "Unique_Sword_1H_107_x1": case "Unique_Sword_1H_108_x1": case "Unique_Sword_1H_110_x1": case "Unique_Sword_1H_112_x1": case "Unique_Sword_2H_103_x1": case "Unique_Amulet_105_x1": case "Unique_Amulet_106_x1": case "Unique_Ring_105_x1": case "Unique_Ring_107_x1": case "Unique_Ring_108_x1": case "Unique_Ring_109_x1": case "Unique_Mojo_101_x1": case "Unique_Mojo_104_x1": case "Unique_Quiver_104_x1": return true; } return false; } private static void LoadQuests() { //Logger.Info("LoadItems()"); foreach (var asset in MPQStorage.Data.Assets[SNOGroup.Quest].Values) { Quest data = asset.Data as Quest; if (data != null) { Logger.Info("-------------"); Logger.Info("Quest: [{0}] {1}", data.Header.SNOId, asset.Name); Logger.Info("Type: {0}", data.QuestType); Logger.Info("NumberOfSteps: {0}, NumberOfCompletionSteps: {1}", data.NumberOfSteps, data.NumberOfCompletionSteps); foreach (var step in data.QuestSteps) { int nextID = step.StepObjectiveSets.Count() > 0 ? step.StepObjectiveSets.First().FollowUpStepID : -1; Logger.Info("Step [{0}] {1} -> {2}", step.ID, step.Name, nextID); foreach (var objSet in step.StepObjectiveSets) foreach (var obj in objSet.StepObjectives) Logger.Info("objective type {0}, I0 {1}, I2 {2}, Target {3}, Sno1 {4}, Sno2 {5}", obj.ObjectiveType, obj.I0, obj.I2, obj.CounterTarget, obj.SNOName1, obj.SNOName2); } } } } private static void LoadRecipes() { //Logger.Info("LoadRecipes()"); foreach (var asset in MPQStorage.Data.Assets[SNOGroup.GameBalance].Values) { GameBalance data = asset.Data as GameBalance; if (data != null && data.Type == BalanceType.Recipes) { foreach (var recipeDefinition in data.Recipes) { Recipes.TryAdd(recipeDefinition.Hash, recipeDefinition); if (recipeDefinition.SNORecipe > 0 && MPQStorage.Data.Assets[SNOGroup.Recipe] .ContainsKey(recipeDefinition.SNORecipe)) { var reward = (MPQStorage.Data.Assets[SNOGroup.Recipe][recipeDefinition.SNORecipe].Data as Recipe) .ItemSpecifierData.ItemGBId; if (!CraftOnlyItems.Contains(reward)) CraftOnlyItems.Add(reward); } /*Logger.Info("-------------"); Logger.Info("recipe name: {0}, SNOId {1}", recipeDefinition.Name, recipeDefinition.SNORecipe); Logger.Info("recipe type: {0}", recipeDefinition.Type); Logger.Info("RecipeNeeded: {0}, I1: {1}, Gold: {2}, I3: {3}", recipeDefinition.RecipeNeeded, recipeDefinition.I1, recipeDefinition.Gold, recipeDefinition.I3); if (recipeDefinition.SNORecipe > 0) { var reward = (Mooege.Common.MPQ.FileFormats.Recipe)MPQStorage.Data.Assets[SNOGroup.Recipe][recipeDefinition.SNORecipe].Data; string affixes = ""; for (int i = 0; i<3; i++) if (reward.ItemSpecifierData.GBIdAffixes[i] > 0) affixes += string.Format("{0}, ", reward.ItemSpecifierData.GBIdAffixes[i]); Logger.Info("recipe reward- item {0}, I0 {1}, I1 {2}, I2 {3}, Affixes: {4}", reward.ItemSpecifierData.ItemGBId, reward.ItemSpecifierData.I0, reward.ItemSpecifierData.I1, reward.ItemSpecifierData.I2, affixes); } for (int i = 0; i<6; i++) if (recipeDefinition.Ingredients[i].ItemGBId > 0) Logger.Info("Ingredient {0}: {1}, amount of {2}", i+1, recipeDefinition.Ingredients[i].ItemGBId, recipeDefinition.Ingredients[i].Count);*/ } } } } private static void LoadGemBonuses() { foreach (var asset in MPQStorage.Data.Assets[SNOGroup.GameBalance].Values) { GameBalance data = asset.Data as GameBalance; if (data != null && data.Type == BalanceType.SocketedEffects) { foreach (var gemBonusDefinition in data.SocketedEffects) { GemEffects.Add(gemBonusDefinition); //Logger.Info("gemBonusDefinition: {0} {1} {2} {3}", gemBonusDefinition.Item, gemBonusDefinition.ItemType, gemBonusDefinition.Attribute[0].AttributeId, gemBonusDefinition.S0); } } } } private static void LoadParagonBonuses() { foreach (var asset in MPQStorage.Data.Assets[SNOGroup.GameBalance].Values) { GameBalance data = asset.Data as GameBalance; if (data != null && data.Type == BalanceType.ParagonBonuses) { foreach (var paragonBonusDefinition in data.ParagonBonusesTables) { ParagonBonuses.Add(paragonBonusDefinition); } } } } private static void LoadItemSetBonuses() { foreach (var asset in MPQStorage.Data.Assets[SNOGroup.GameBalance].Values) { GameBalance data = asset.Data as GameBalance; if (data != null && data.Type == BalanceType.SetItemBonuses) { foreach (var itemSetBonusDefinition in data.SetItemBonus) { ItemSetsEffects.Add(itemSetBonusDefinition); /*Logger.Info("ItemSetsEffectDefinition: {0}, set {1}, {2} bonuses:", itemSetBonusDefinition.Name, itemSetBonusDefinition.Set, itemSetBonusDefinition.Count); for (int i = 0; i < itemSetBonusDefinition.Count; i++) Logger.Info("{0}, {1}", itemSetBonusDefinition.Attribute[i].AttributeId, itemSetBonusDefinition.Attribute[i].SNOParam);*/ } } } } private static void LoadLore() { foreach (var asset in MPQStorage.Data.Assets[SNOGroup.Actor].Values) { Actor data = asset.Data as Actor; if (data != null && data.TagMap.ContainsKey(ActorKeys.Lore)) { if (Lore.ContainsKey(data.TagMap[ActorKeys.Lore].Id)) continue; var item = Items.Where(i => i.Value.SNOActor == data.Header.SNOId).ToList(); if (item.Count == 0) continue; Lore.TryAdd(data.TagMap[ActorKeys.Lore].Id, item.First().Value); //Logger.Info("LoreActor: {0}, Lore {1}, GbId {2}", data.Header.SNOId, data.TagMap[ActorKeys.Lore], item.First().Value.Hash); } } } private static void LoadPowers() { /*foreach (var asset in MPQStorage.Data.Assets[SNOGroup.Actor].Values) { Mooege.Common.MPQ.FileFormats.Actor data = asset.Data as Mooege.Common.MPQ.FileFormats.Actor; Logger.Info("---------------------------------------------------------"); Logger.Info("asset id: {0}", asset.SNOId); Logger.Info("asset name: {0}", asset.Name); Logger.Info("asset type: {0}", data.Type); if (data.TagMap.ContainsKey(ActorKeys.GizmoGroup)) Logger.Info("asset group: {0}", (GizmoGroup)data.TagMap[ActorKeys.GizmoGroup]); Logger.Info("MonsterSNO: {0}", data.MonsterSNO); Logger.Info("I0: {0}, I1: {1}, I2: {2}, I3: {3}, I4: {4}", data.Int0, data.Int1, data.Int2, data.Int3, data.Int4); Logger.Info("Type: {0}", data.Type); foreach (var tag in data.TagMap) Logger.Info("TagMap tag: {0}", tag.ToString()); }*/ /*foreach (var asset in MPQStorage.Data.Assets[SNOGroup.Worlds].Values) { Mooege.Common.MPQ.FileFormats.World data = asset.Data as Mooege.Common.MPQ.FileFormats.World; if (asset.Name.ToLower().StartsWith("x1_") && data.IsGenerated && !asset.Name.ToLower().Contains("_lr_")) { Logger.Info("World {0} - {1}", asset.SNOId, asset.Name); foreach (var scene_asset in MPQStorage.Data.Assets[SNOGroup.LevelArea].Values) if (scene_asset.Name.ToLower().Contains(asset.Name.ToLower())) Logger.Info("LevelArea {0} - {1}", scene_asset.SNOId, scene_asset.Name); foreach (var scene_asset in MPQStorage.Data.Assets[SNOGroup.Weather].Values) if (scene_asset.Name.ToLower().Contains(asset.Name.ToLower())) Logger.Info("Weather {0} - {1}", scene_asset.SNOId, scene_asset.Name); foreach (var scene_asset in MPQStorage.Data.Assets[SNOGroup.Scene].Values) if (scene_asset.Name.ToLower().StartsWith("x1_" + asset.Name.ToLower().Split("_")[1])) { if (LookupCommand.GetExitBits(scene_asset) == 0 && !scene_asset.Name.ToLower().Contains("filler")) continue; //Logger.Info("Scene {0} - {1}", scene_asset.SNOId, scene_asset.Name); int type = scene_asset.Name.Contains("Filler") ? 401 : (scene_asset.Name.Contains("Entrance") ? 200 : (scene_asset.Name.Contains("Exit") ? 300 : 100)); Logger.Info("INSERT INTO \"TileInfo\" VALUES ({0}, {1}, {2}, 100, 'True','True',0,0,0,'True',0,0,NULL); // {3}", LookupCommand.GetExitBits(scene_asset), type, scene_asset.SNOId, scene_asset.Name); } Logger.Info("----------------"); } //Logger.Info("INSERT OR IGNORE INTO \"TOC\" VALUES ('Worlds',{0},'{1}');", asset.SNOId, asset.Name); /*Logger.Info("---------------------------------------------------------"); Logger.Info("asset id: {0}", asset.SNOId); Logger.Info("asset name: {0}", asset.Name); Logger.Info("Int1: {0}, Int2: {1}", data.Int1, data.Int2); Logger.Info("Int4: {0}, Int5: {1}", data.Int4, data.Int5); Logger.Info("IsGenerated: {0}", data.IsGenerated);*/ //} /*foreach (var asset in MPQStorage.Data.Assets[SNOGroup.QuestRange].Values) { Mooege.Common.MPQ.FileFormats.QuestRange data = asset.Data as Mooege.Common.MPQ.FileFormats.QuestRange; Logger.Info("---------------------------------------------------------"); Logger.Info("asset id: {0}", asset.SNOId); Logger.Info("asset name: {0}", asset.Name); Logger.Info("I0: {0}", data.I0); foreach (var range in data.Entries) { Logger.Info("---Entry---"); Logger.Info("Start: quest {0}, step: {1}", range.Start.SNOQuest, range.Start.StepID); Logger.Info("End: quest {0}, step: {1}", range.End.SNOQuest, range.End.StepID); } }*/ /*foreach (var asset in MPQStorage.Data.Assets[SNOGroup.Act].Values) { Mooege.Common.MPQ.FileFormats.Act data = asset.Data as Mooege.Common.MPQ.FileFormats.Act; Logger.Info("---------------------------------------------------------"); Logger.Info("asset id: {0}", asset.SNOId); Logger.Info("asset name: {0}", asset.Name); foreach (var waypoint in data.WayPointInfo) { Logger.Info("---Waypoint---"); Logger.Info("GameModeFlags: {0}", waypoint.GameModeFlags); Logger.Info("World: {0}", waypoint.SNOWorld); Logger.Info("LevelArea: {0}", waypoint.SNOLevelArea); Logger.Info("I0: {0}", waypoint.I0); Logger.Info("I1: {0}", waypoint.I1); Logger.Info("I2: {0}", waypoint.I2); Logger.Info("QuestRange: {0}", waypoint.SNOQuestRange); Logger.Info("I3: {0}", waypoint.I3); Logger.Info("V0: X {0}, Y {1}", waypoint.V0.X, waypoint.V0.Y); } }*/ /*foreach (var asset in MPQStorage.Data.Assets[SNOGroup.Scene].Values) { Mooege.Common.MPQ.FileFormats.Scene data = asset.Data as Mooege.Common.MPQ.FileFormats.Scene; Logger.Info("---------------------------------------------------------"); Logger.Info("asset id: {0}", asset.SNOId); Logger.Info("asset name: {0}", asset.Name); Logger.Info("squaresCount: X {0}, Y {1}, NavMesh {2}", data.NavMesh.SquaresCountX, data.NavMesh.SquaresCountY, data.NavMesh.NavMeshSquareCount); Logger.Info("Int0: {0}, Int1: {1}", data.Int0, data.Int1); Logger.Info("HasGeodata: {0}", data.NavMesh.HasGeodata); Logger.Info("AABB Min: {0} {1} {2}", data.AABBBounds.Min.X, data.AABBBounds.Min.Y, data.AABBBounds.Min.Z); Logger.Info("AABB Max: {0} {1} {2}", data.AABBBounds.Max.X, data.AABBBounds.Max.Y, data.AABBBounds.Max.Z); }*/ /*foreach (var asset in MPQStorage.Data.Assets[SNOGroup.Monster].Values) { Mooege.Common.MPQ.FileFormats.Monster data = asset.Data as Mooege.Common.MPQ.FileFormats.Monster; Logger.Info("---------------------------------------------------------"); Logger.Info("asset name: {0}", asset.Name); Logger.Info("ActorSNO: {0}", data.ActorSNO); Logger.Info("I0: {0}, I1: {1}, I2: {2}, I3: {3}, I4: {4}, I5: {5}, I6: {6}, I7: {7}, I8: {8}, I9: {9}, I10: {10}, I11: {11}", data.I0, data.I1, data.I2, data.I3, data.I4, data.I5, data.I6, data.I7, data.I8, data.I9, data.I10, data.I11); Logger.Info("Type: {0}", data.Type); Logger.Info("MonsterDef: {0} - {1} - {2} - {3} - {4}", data.Monsterdef.F0, data.Monsterdef.F1, data.Monsterdef.F2, data.Monsterdef.F3, data.Monsterdef.I0); foreach (var skill in data.SkillDeclarations) Logger.Info("SkillDeclaration: {0} - {1}", skill.SNOPower, skill.I0); foreach (var mskill in data.MonsterSkillDeclarations) Logger.Info("MonsterSkillDeclaration: {0} - {1} - {2} - {3}", mskill.F0, mskill.F1, mskill.I0, mskill.F2); foreach (var summon in data.SNOSummonActor) Logger.Info("SNOSummonActor: {0}", summon); foreach (var tag in data.TagMap) Logger.Info("TagMap tag: {0}", tag.ToString()); for (int i = 0; i<138; i++) Logger.Info("Float{0}: {1}", i, data.Floats[i]); }*/ foreach (var asset in MPQStorage.Data.Assets[SNOGroup.Power].Values) { Power data = asset.Data as Power; Logger.Info("{0} => array(\"name\" => \"{1}\", \"desc\" => \"{2}\"),", data.Header.SNOId, asset.Name, data.LuaName); /*Logger.Info("---------------------------------------------------------"); Logger.Info("asset name: {0}", asset.Name); Logger.Info("SNOID: {0}", data.Header.SNOId); Logger.Info("LuaName: {0}", data.LuaName); Logger.Info("I0: {0}, I1: {1}, I3: {2}", data.I0, data.I1, data.i3); Logger.Info("String1: {0}", data.Chararray2); Logger.Info("SNOQuestMetaData: {0}", data.SNOQuestMetaData); Logger.Info("CompiledScript: {0}", data.CompiledScript); Logger.Info("Power Definition I0: {0}", data.Powerdef.I0); foreach (var anim in data.Powerdef.PVPGeneralTagMap) Logger.Info("PVPGeneralTagMap tag: {0}", anim.ToString()); foreach (var anim in data.Powerdef.GeneralTagMap) Logger.Info("GeneralTagMap tag: {0}", anim.ToString()); foreach (var anim in data.Powerdef.ContactTagMap0) Logger.Info("ContactTagMap0 tag: {0}", anim.ToString()); foreach (var anim in data.Powerdef.ContactTagMap1) Logger.Info("ContactTagMap1 tag: {0}", anim.ToString()); foreach (var anim in data.Powerdef.ContactTagMap2) Logger.Info("ContactTagMap2 tag: {0}", anim.ToString()); foreach (var anim in data.Powerdef.ContactTagMap3) Logger.Info("ContactTagMap3 tag: {0}", anim.ToString()); Logger.Info("Power Scripts:"); foreach (var script in data.ScriptFormulaDetails) { Logger.Info("---------"); Logger.Info("Script CharArray1: {0}", script.CharArray1); Logger.Info("Script CharArray2: {0}", script.CharArray2); Logger.Info("Script I0: {0}", script.I0); Logger.Info("Script I1: {0}", script.I1); }*/ } /*foreach (var asset in MPQStorage.Data.Assets[SNOGroup.Tutorial].Values) { Mooege.Common.MPQ.FileFormats.Tutorial data = asset.Data as Mooege.Common.MPQ.FileFormats.Tutorial; Logger.Info("---------------------------------------------------------"); Logger.Info("asset id: {0}", asset.SNOId); Logger.Info("asset name: {0}", asset.Name); Logger.Info("Int0: {0}, Int1: {1}", data.I0, data.I1); Logger.Info("Int2: {0}, Int3: {1}", data.I2, data.I3); }*/ } private static void LoadAffixes() { foreach (var asset in MPQStorage.Data.Assets[SNOGroup.GameBalance].Values) { GameBalance data = asset.Data as GameBalance; /*if (data != null && data.Type == BalanceType.AffixList) { foreach (var affixDefinition in data.Affixes) { Logger.Info("---------------------------------------------------------"); Logger.Info("affix GBid: {0}", affixDefinition.Hash); Logger.Info("affix name: {0}", affixDefinition.Name); Logger.Info("affix Level: {0}, item Level: {1}, level min: {2}, level max: {3}", affixDefinition.AffixLevel, affixDefinition.ItemLevel, affixDefinition.MinLevel, affixDefinition.MaxLevel); Logger.Info("I201: {0}", affixDefinition.I201); Logger.Info("I202: {0}", affixDefinition.I202); Logger.Info("I203: {0}", affixDefinition.I203); Logger.Info("I204: {0}", affixDefinition.I204); Logger.Info("I205: {0}", affixDefinition.I205); Logger.Info("I206: {0}", affixDefinition.I206); Logger.Info("I207: {0}", affixDefinition.I207); Logger.Info("I208: {0}", affixDefinition.I208); Logger.Info("I209: {0}", affixDefinition.I209); Logger.Info("I210: {0}", affixDefinition.I210); Logger.Info("I211: {0}", affixDefinition.I211); Logger.Info("I212: {0}", affixDefinition.I212); Logger.Info("Type-1: {0}, Type-2: {1}, Class: {2}, QualityMask: {3}, SupMask: {4}", affixDefinition.AffixType1, affixDefinition.AffixType2, affixDefinition.Class, affixDefinition.QualityMask, affixDefinition.SupMask); Logger.Info("I0: {0}, I3: {1}, I6: {2}", affixDefinition.I0, affixDefinition.I3, affixDefinition.I6); Logger.Info("ItemGroups: {0}, {1}, {2}, {3}, {4}, {5}, {6}, {7}, {8}, {9}, {10}, {11}, {12}, {13}, {14}, {15}", affixDefinition.ItemGroup[0], affixDefinition.ItemGroup[1], affixDefinition.ItemGroup[2], affixDefinition.ItemGroup[3], affixDefinition.ItemGroup[4], affixDefinition.ItemGroup[5], affixDefinition.ItemGroup[6], affixDefinition.ItemGroup[7], affixDefinition.ItemGroup[8], affixDefinition.ItemGroup[9], affixDefinition.ItemGroup[10], affixDefinition.ItemGroup[11], affixDefinition.ItemGroup[12], affixDefinition.ItemGroup[13], affixDefinition.ItemGroup[14], affixDefinition.ItemGroup[15]); Logger.Info("I10: {0}, {1}, {2}, {3}, {4}, {5}, {6}, {7}, {8}, {9}, {10}, {11}, {12}, {13}, {14}, {15}", affixDefinition.I10[0], affixDefinition.I10[1], affixDefinition.I10[2], affixDefinition.I10[3], affixDefinition.I10[4], affixDefinition.I10[5], affixDefinition.I10[6], affixDefinition.I10[7], affixDefinition.I10[8], affixDefinition.I10[9], affixDefinition.I10[10], affixDefinition.I10[11], affixDefinition.I10[12], affixDefinition.I10[13], affixDefinition.I10[14], affixDefinition.I10[15]); Logger.Info("AffixFamily0: {0}", affixDefinition.AffixFamily0); Logger.Info("AffixFamily1: {0}", affixDefinition.AffixFamily1); Logger.Info("AssociatedAffix: {0}", affixDefinition.AssociatedAffix); Logger.Info("ExclusionCategory: {0}", affixDefinition.ExclusionCategory); Logger.Info("Price: {0}", affixDefinition.Price); foreach (var attribute in affixDefinition.AttributeSpecifier) { float result; float minValue; float maxValue; if (FormulaScript.Evaluate(attribute.Formula.ToArray(), new ItemRandomHelper(35674658), out result, out minValue, out maxValue)) Logger.Info("AttributeSpecifier - attribute {0}, param {1}, value {2}-{3}", GameAttribute.Attributes[attribute.AttributeId].Name, attribute.SNOParam, minValue, maxValue); } } }*/ /*if (data != null && data.Type == BalanceType.SocketedEffects) { foreach (var affixDefinition in data.SocketedEffects) { Logger.Info("affix: {1}, {2} {3}, AffixName: {0}", affixDefinition.Name, affixDefinition.S0, affixDefinition.Item, affixDefinition.ItemType); foreach(var attr in affixDefinition.Attribute) { float result; if (attr.AttributeId > 0) if (FormulaScript.Evaluate(attr.Formula.ToArray(), new ItemRandomHelper(35674658), out result)) Logger.Info("attr: {0}, {1} {2}", attr.AttributeId, attr.SNOParam, result); } foreach(var attr in affixDefinition.ReqAttribute) { float result; if (attr.AttributeId > 0) if (FormulaScript.Evaluate(attr.Formula.ToArray(), new ItemRandomHelper(35674658), out result)) Logger.Info("reqAttr: {0}, {1} {2}", attr.AttributeId, attr.SNOParam, result); } } }*/ /*if (data != null && data.Type == BalanceType.ItemTypes) { foreach (var itemType in data.ItemType) { Logger.Info("item type: {1}, parent {2}, I0: {3}, Flags {4}, Type0 {5}, Type1{6}, Type2 {7}, Type3 {8}, array: {9} {10} {11} {12}, TypeName: {0}", itemType.Name, itemType.Hash, itemType.ParentType, itemType.I0, itemType.Flags, itemType.Type0, itemType.Type1, itemType.Type2, itemType.Type3, itemType.Array[0], itemType.Array[1], itemType.Array[2], itemType.Array[3]); } }*/ /*if (data != null && data.Type == BalanceType.MonsterAffixes) { foreach (var affixDefinition in data.MonsterAffixes) { Logger.Info("monster affix: {1}, {2} {3} {4} {5} {6} A1: {7} A2:{8} Type: {9}, I4: {10}, I5: {11}, SNOPower: {12}, AffixName: {0}", affixDefinition.Name, affixDefinition.Hash, affixDefinition.I0, affixDefinition.I1, affixDefinition.I2, affixDefinition.I3, affixDefinition.Attributes[0].AttributeId, affixDefinition.Attributes[1].AttributeId, affixDefinition.MonsterAffix, affixDefinition.AffixType, affixDefinition.I4, affixDefinition.I5, affixDefinition.SNOOnSpawnPowerChampion); } }*/ /*if (data != null && data.Type == BalanceType.ExperienceTable) { Logger.Info("---------------------------------------------------------"); Logger.Info("asset name: {0}", asset.Name); Logger.Info("SNOID: {0}", data.Header.SNOId); foreach (var row in data.Experience) { Logger.Info("Experience: {0}", row.Exp); Logger.Info("I201: {0}", row.I201); Logger.Info("Multiplier: {0}", row.Multiplier); } }*/ /*if (data != null && data.Type == BalanceType.ParagonBonuses) { Logger.Info("---------------------------------------------------------"); Logger.Info("asset name: {0}", asset.Name); Logger.Info("SNOID: {0}", data.Header.SNOId); foreach (var row in data.ParagonBonuses) { Logger.Info("Name: {0}", row.Name); Logger.Info("Hash: {0}", row.Hash); Logger.Info("IconName: {0}", row.IconName); Logger.Info("I1: {0}", row.I1); Logger.Info("I2: {0}", row.I2); Logger.Info("Limit: {0}", row.Limit); Logger.Info("Category: {0}", row.Category); Logger.Info("Index: {0}", row.Index); Logger.Info("Class: {0}", row.Class); foreach(var attr in row.Attribute) { if (attr.AttributeId > 0) Logger.Info("Attr: {0}, {1} - {2}", attr.AttributeId, attr.SNOParam, FormulaScript.ToString(attr.Formula.ToArray())); } } }*/ /*if (data != null && data.Type == BalanceType.MonsterLevels) { Logger.Info("---------------------------------------------------------"); Logger.Info("asset name: {0}", asset.Name); Logger.Info("SNOID: {0}", data.Header.SNOId); foreach (var row in data.MonsterLevel) { Logger.Info("I0: {0}", row.I0); Logger.Info("F0: {0}", row.F0); Logger.Info("F1: {0}", row.F1); Logger.Info("F2: {0}", row.F2); Logger.Info("F3: {0}", row.F3); Logger.Info("F4: {0}", row.F4); Logger.Info("F5: {0}", row.F5); Logger.Info("F6: {0}", row.F6); Logger.Info("F7: {0}", row.F7); Logger.Info("F8: {0}", row.F8); Logger.Info("F9: {0}", row.F9); Logger.Info("F10: {0}", row.F10); Logger.Info("F11: {0}", row.F11); Logger.Info("F12: {0}", row.F12); Logger.Info("F13: {0}", row.F13); Logger.Info("F14: {0}", row.F14); Logger.Info("F15: {0}", row.F15); Logger.Info("F16: {0}", row.F16); Logger.Info("F17: {0}", row.F17); Logger.Info("F18: {0}", row.F18); Logger.Info("F19: {0}", row.F19); Logger.Info("F20: {0}", row.F20); Logger.Info("F21: {0}", row.F21); Logger.Info("F22: {0}", row.F22); Logger.Info("F23: {0}", row.F23); Logger.Info("F24: {0}", row.F24); Logger.Info("F25: {0}", row.F25); Logger.Info("F26: {0}", row.F26); Logger.Info("F27: {0}", row.F27); Logger.Info("F28: {0}", row.F28); Logger.Info("F29: {0}", row.F29); Logger.Info("F30: {0}", row.F30); Logger.Info("F31: {0}", row.F31); Logger.Info("F32: {0}", row.F32); Logger.Info("F33: {0}", row.F33); Logger.Info("F34: {0}", row.F34); Logger.Info("F35: {0}", row.F35); Logger.Info("F36: {0}", row.F36); Logger.Info("F37: {0}", row.F37); Logger.Info("F38: {0}", row.F38); Logger.Info("F39: {0}", row.F39); Logger.Info("F40: {0}", row.F40); Logger.Info("F41: {0}", row.F41); Logger.Info("F42: {0}", row.F42); Logger.Info("F43: {0}", row.F43); Logger.Info("F44: {0}", row.F44); Logger.Info("F45: {0}", row.F45); Logger.Info("F46: {0}", row.F46); Logger.Info("F47: {0}", row.F47); Logger.Info("F48: {0}", row.F48); Logger.Info("F49: {0}", row.F49); Logger.Info("F50: {0}", row.F50); Logger.Info("F51: {0}", row.F51); } }*/ if (data != null && data.Type == BalanceType.Heros) { Logger.Info("---------------------------------------------------------"); Logger.Info("asset name: {0}", asset.Name); Logger.Info("SNOID: {0}", data.Header.SNOId); /* foreach (var row in data.Heros) { Logger.Info("Name: {0}", row.Name); Logger.Info("Hash: {0}", row.Hash); Logger.Info("I20: {0}", row.I20); Logger.Info("I21: {0}", row.I21); Logger.Info("SNOMaleActor: {0}", row.SNOMaleActor); Logger.Info("SNOFemaleActor: {0}", row.SNOFemaleActor); Logger.Info("SNOInventory: {0}", row.SNOInventory); Logger.Info("I0: {0}", row.I0); Logger.Info("SNOStartingLMBSkill: {0}", row.SNOStartingLMBSkill); Logger.Info("SNOStartingRMBSkill: {0}", row.SNOStartingRMBSkill); Logger.Info("SNOSKillKit0: {0}", row.SNOSKillKit0); Logger.Info("SNOSKillKit1: {0}", row.SNOSKillKit1); Logger.Info("SNOSKillKit2: {0}", row.SNOSKillKit2); Logger.Info("SNOSKillKit3: {0}", row.SNOSKillKit3); Logger.Info("PrimaryResource: {0}", row.PrimaryResource); Logger.Info("SecondaryResource: {0}", row.SecondaryResource); Logger.Info("CoreAttribute: {0}", row.CoreAttribute); Logger.Info("F0: {0}", row.F0); Logger.Info("I1: {0}", row.I1); Logger.Info("HitpointsMax: {0}", row.HitpointsMax); Logger.Info("HitpointsFactorLevel: {0}", row.HitpointsFactorLevel); Logger.Info("F3: {0}", row.F3); Logger.Info("PrimaryResourceMax: {0}", row.PrimaryResourceMax); Logger.Info("PrimaryResourceFactorLevel: {0}", row.PrimaryResourceFactorLevel); Logger.Info("PrimaryResourceRegenPerSecond: {0}", row.PrimaryResourceRegenPerSecond); Logger.Info("SecondaryResourceMax: {0}", row.SecondaryResourceMax); Logger.Info("SecondaryResourceFactorLevel: {0}", row.SecondaryResourceFactorLevel); Logger.Info("SecondaryResourceRegenPerSecond: {0}", row.SecondaryResourceRegenPerSecond); Logger.Info("F10: {0}", row.F10); Logger.Info("F201: {0}", row.F201); Logger.Info("F11: {0}", row.F11); Logger.Info("CritPercentCap: {0}", row.CritPercentCap); Logger.Info("F13: {0}", row.F13); Logger.Info("F14: {0}", row.F14); Logger.Info("WalkingRate: {0}", row.WalkingRate); Logger.Info("RunningRate: {0}", row.RunningRate); Logger.Info("F17: {0}", row.F17); Logger.Info("F18: {0}", row.F18); Logger.Info("F19: {0}", row.F19); Logger.Info("F20: {0}", row.F20); Logger.Info("F21: {0}", row.F21); Logger.Info("F22: {0}", row.F22); Logger.Info("F23: {0}", row.F23); Logger.Info("F24: {0}", row.F24); Logger.Info("F25: {0}", row.F25); Logger.Info("F26: {0}", row.F26); Logger.Info("F27: {0}", row.F27); Logger.Info("F28: {0}", row.F28); Logger.Info("F29: {0}", row.F29); Logger.Info("F30: {0}", row.F30); Logger.Info("F31: {0}", row.F31); Logger.Info("F32: {0}", row.F32); Logger.Info("F33: {0}", row.F33); Logger.Info("F34: {0}", row.F34); Logger.Info("Strength: {0}", row.Strength); Logger.Info("Dexterity: {0}", row.Dexterity); Logger.Info("Vitality: {0}", row.Vitality); Logger.Info("Intelligence: {0}", row.Intelligence); Logger.Info("GetHitMaxBase: {0}", row.GetHitMaxBase); Logger.Info("GetHitMaxPerLevel: {0}", row.GetHitMaxPerLevel); Logger.Info("GetHitRecoveryBase: {0}", row.GetHitRecoveryBase); Logger.Info("GetHitRecoveryPerLevel: {0}", row.GetHitRecoveryPerLevel); Logger.Info("F35: {0}", row.F35); Logger.Info("F201: {0}", row.F201); Logger.Info("I203: {0}", row.I203); } //*/ } /*if (data != null && data.Type == BalanceType.MonsterNames) { foreach (var affixDefinition in data.RareMonsterNames) { Logger.Info("affix: {1}, {2} {3}, AffixName: {0}", affixDefinition.Name, affixDefinition.Hash, affixDefinition.AffixType, affixDefinition.S0); } }*/ /*if (data != null && data.Type == BalanceType.HandicapLevels) { foreach (var diffDefinition in data.HandicapLevels) { Logger.Info("Difficulty: {1}, {2}, {3}, {4}, {5}, {6}, {7}, {8}, Name: {0}", diffDefinition.Name, diffDefinition.I0, diffDefinition.I1, diffDefinition.HPMod, diffDefinition.DmgMod, diffDefinition.F2, diffDefinition.XPMod, diffDefinition.GoldMod, diffDefinition.F5 ); } }*/ /*if (data != null && data.Type == BalanceType.RareItemNames) { foreach (var affixDefinition in data.RareItemNames) { Logger.Info("RareItemName: {1}, {2} {3}, AffixName: {0}", affixDefinition.Name, affixDefinition.Hash, affixDefinition.Type, affixDefinition.AffixType); } }*/ /*if (data != null && data.Type == BalanceType.SetItemBonuses) { foreach (var affixDefinition in data.SetItemBonus) { Logger.Info("SetItemBonus: {1}, {2} {3}, SetItemBonusName: {0}", affixDefinition.Name, affixDefinition.Count, affixDefinition.Set, affixDefinition.Attribute[0].AttributeId); } }*/ /*if (data != null && data.Type == BalanceType.Heros) { foreach (var affixDefinition in data.Heros) { Logger.Info("Heros: {1}, {2} {3}, HeroName: {0}", affixDefinition.Name, affixDefinition.SNOMaleActor, affixDefinition.I0, affixDefinition.F0); } }*/ /*if (data != null && data.Type == BalanceType.ItemEnhancements) { foreach (var enchantDefinition in data.ItemEnhancement) { Logger.Info("Enchant: {1}, {2} {3} {4} {5} {6} ItemEnhancement: {0}", enchantDefinition.Name, enchantDefinition.Hash, enchantDefinition.I0, enchantDefinition.I1, enchantDefinition.I2, enchantDefinition.I3, enchantDefinition.Attribute[0].AttributeId); } }*/ } /*foreach (var asset in MPQStorage.Data.Assets[SNOGroup.Lore].Values) { Mooege.Common.MPQ.FileFormats.Lore data = asset.Data as Mooege.Common.MPQ.FileFormats.Lore; Logger.Info("Lore: snoid {1}, Type: {2}, I0 {3}, I1 {4} I2 {5} I3 {6}, ConvId {7}, SNOName: {0}", asset.Name, data.Header.SNOId, data.Category, data.I0, data.I1, data.I2, data.I3, data.SNOConversation); }*/ /*foreach (var asset in MPQStorage.Data.Assets[SNOGroup.Condition].Values) { Mooege.Common.MPQ.FileFormats.Condition data = asset.Data as Mooege.Common.MPQ.FileFormats.Condition; Logger.Info("Condition: snoid {1}, I0 {2}, I1 {3}, LoreCond1 {4}-{5}, LoreCond2 {6}-{7}, LoreCond3 {8}-{9}, SNOName: {0}", asset.Name, data.Header.SNOId, data.I0, data.I1, data.LoreCondition[0].SNOLore, data.LoreCondition[0].I0, data.LoreCondition[1].SNOLore, data.LoreCondition[1].I0, data.LoreCondition[2].SNOLore, data.LoreCondition[2].I0); }*/ } private static void LoadConversations() { foreach (var asset in MPQStorage.Data.Assets[SNOGroup.Conversation].Values) { DiIiS_NA.Core.MPQ.FileFormats.Conversation data = asset.Data as DiIiS_NA.Core.MPQ.FileFormats.Conversation; /*Logger.Info("Conversation: [{8}]{0} I4: {1}, Type {9}, NPC {10} Act {2}, SNOConvUnlocks {3}, {4}, {5}, I5: {6}, I6: {7}", asset.Name, data.I4, data.I201, data.SNOConvUnlocks[0], data.SNOConvUnlocks[1], data.SNOConvUnlocks[2], data.I5, data.I6, data.Header.SNOId, data.ConversationType, data.SNOPrimaryNpc);*/ } } private static void SetAllowedTypes() { foreach (int hash in ItemGroup.SubTypesToHashList("Weapon")) AllowedItemTypes.Add(hash); foreach (int hash in ItemGroup.SubTypesToHashList("Armor")) AllowedItemTypes.Add(hash); foreach (int hash in ItemGroup.SubTypesToHashList("Offhand")) AllowedItemTypes.Add(hash); foreach (int hash in ItemGroup.SubTypesToHashList("Jewelry")) AllowedItemTypes.Add(hash); foreach (int hash in ItemGroup.SubTypesToHashList("Utility")) AllowedItemTypes.Add(hash); foreach (int hash in ItemGroup.SubTypesToHashList("CraftingPlan")) AllowedItemTypes.Add(hash); foreach (int hash in ItemGroup.SubTypesToHashList("Jewel")) AllowedItemTypes.Add(hash); foreach (int hash in TypeHandlers.Keys) { if (AllowedItemTypes.Contains(hash)) { // already added structure continue; } foreach (int subhash in ItemGroup.SubTypesToHashList(ItemGroup.FromHash(hash).Name)) { if (AllowedItemTypes.Contains(subhash)) { // already added structure continue; } AllowedItemTypes.Add(subhash); } } } #endregion #region generating items // generates a random item. public static Item GenerateRandom(ActorSystem.Actor owner) { var itemDefinition = GetRandom(Items.Values .Where(def => def.ItemLevel == owner.Attributes[GameAttribute.Level]).ToList()); return CreateItem(owner, itemDefinition); } public static Item GenerateLegOrSetRandom(ActorSystem.Actor owner) { var itemDefinition = GetLegOrSetRandom(AllowedUniqueItems.Values .Where(def => def.ItemLevel == owner.Attributes[GameAttribute.Level]).ToList()); return CreateItem(owner, itemDefinition); } public static List ObsoleteItems = new(); // generates a random equip item (for vendors) public static Item GenerateRandomEquip(ActorSystem.Actor owner, int level, int minQuality = 1, int maxQuality = -1, ItemTypeTable type = null, ToonClass ownerClass = ToonClass.Unknown, bool crafted = false, bool canBeUnidentified = true) { if (level < 0) level = owner.Attributes[GameAttribute.Level]; int quality = minQuality; //if (quality > 7) // quality -= 5; if (maxQuality > -1) quality = RandomHelper.Next(minQuality, maxQuality); if (quality > 8) //Unique items level scaling { var legaDefinition = GetRandom(AllowedItems.Values .Where(def => def.ItemLevel <= Math.Min(level + 2, 73) && !ObsoleteItems.Contains(def.Hash) && UniqueItems.UniqueItemStats.ContainsKey(def.Hash) && def.Quality != ItemTable.ItemQuality.Special && (type == null || ItemGroup.HierarchyToHashList(ItemGroup.FromHash(def.ItemTypesGBID)).Contains(type.Hash)) && (quality > 2 || !ItemGroup.HierarchyToHashList(ItemGroup.FromHash(def.ItemTypesGBID)).Contains(-740765630)) //not jewelry && (ownerClass == ToonClass.Unknown || (ownerClass switch { ToonClass.Barbarian => ItemGroup.FromHash(def.ItemTypesGBID).Usable.HasFlag(ItemFlags.Barbarian), ToonClass.Crusader => ItemGroup.FromHash(def.ItemTypesGBID).Usable.HasFlag(ItemFlags.Crusader), ToonClass.Monk => ItemGroup.FromHash(def.ItemTypesGBID).Usable.HasFlag(ItemFlags.Monk), ToonClass.Necromancer => ItemGroup.FromHash(def.ItemTypesGBID).Usable.HasFlag(ItemFlags.Necromancer), ToonClass.Wizard => ItemGroup.FromHash(def.ItemTypesGBID).Usable.HasFlag(ItemFlags.Wizard), ToonClass.DemonHunter => ItemGroup.FromHash(def.ItemTypesGBID).Usable.HasFlag(ItemFlags.DemonHunter), _ => ownerClass != ToonClass.WitchDoctor || ItemGroup.FromHash(def.ItemTypesGBID).Usable.HasFlag(ItemFlags.WitchDoctor) }) ) ).ToList() , (quality > 8)); legaDefinition.ItemLevel = level; legaDefinition.RequiredLevel = level; legaDefinition.CrafterRequiredLevel = level; for (int i = 0; i < 6; i++) legaDefinition.MaxAffixLevel[i] = level; return CreateItem(owner, legaDefinition, quality, crafted, canBeUnidentified: canBeUnidentified); } var itemDefinition = GetRandom(AllowedItems.Values .Where(def => //def.ItemLevel == owner.World.Game.InitialLevel //owner.Attributes[GameAttribute.Level] def.ItemLevel >= Math.Max(Math.Min(level - 3, 60), 1) && def.ItemLevel <= Math.Min(level + 3, 73) && !ObsoleteItems.Contains(def.Hash) //obsolete 1.0.3 items && (type == null || ItemGroup.HierarchyToHashList(ItemGroup.FromHash(def.ItemTypesGBID)).Contains(type.Hash)) && (quality > 2 || !ItemGroup.HierarchyToHashList(ItemGroup.FromHash(def.ItemTypesGBID)).Contains(-740765630)) //not jewelry && (ownerClass == ToonClass.Unknown || (ownerClass switch { ToonClass.Barbarian => ItemGroup.FromHash(def.ItemTypesGBID).Usable.HasFlag(ItemFlags.Barbarian), ToonClass.Crusader => ItemGroup.FromHash(def.ItemTypesGBID).Usable.HasFlag(ItemFlags.Crusader), ToonClass.Monk => ItemGroup.FromHash(def.ItemTypesGBID).Usable.HasFlag(ItemFlags.Monk), ToonClass.Wizard => ItemGroup.FromHash(def.ItemTypesGBID).Usable.HasFlag(ItemFlags.Wizard), ToonClass.DemonHunter => ItemGroup.FromHash(def.ItemTypesGBID).Usable .HasFlag(ItemFlags.DemonHunter), ToonClass.Necromancer => ItemGroup.FromHash(def.ItemTypesGBID).Usable .HasFlag(ItemFlags.Necromancer), ToonClass.WitchDoctor => ItemGroup.FromHash(def.ItemTypesGBID).Usable .HasFlag(ItemFlags.WitchDoctor), _ => true })) ).ToList() , false //(quality > 8) ); return CreateItem(owner, itemDefinition, quality, crafted, canBeUnidentified: canBeUnidentified); } // generates a random dye (for vendors) public static Item GenerateRandomDye(ActorSystem.Actor owner) { var itemDefinition = GetRandom(Items.Values .Where(def => def.ItemTypesGBID == StringHashHelper.HashItemName("Dye") && !def.Name.Contains("CE") ).ToList() ); return CreateItem(owner, itemDefinition); } // generates a random potion (for innkeepers) public static Item GenerateRandomPotion(ActorSystem.Actor owner) { return Cook((owner as Player), "HealthPotionBottomless"); } public static Item GenerateRandomCraftItem(ActorSystem.Actor player, int level, bool dropRecipe = false) { if (level < 0) level = player.Attributes[GameAttribute.Level]; ItemTable itemDefinition = null; if (dropRecipe && FastRandom.Instance.Next(100) < 2) itemDefinition = GetRandom(Items.Values .Where(def => def.ItemLevel <= (level + 3) && !def.Name.Contains("StaffofCow") && def.Name.Contains("CraftingPlan") && !def.Name.Contains("CraftingPlan_Jeweler") ).ToList() ); if (itemDefinition == null) return null; return CreateItem(player, itemDefinition); } public static List GemNames = new() { "x1_Topaz", "x1_Ruby", "x1_Amethyst", "x1_Diamond", "x1_Emerald" }; public static Item GenerateRandomUniqueGem(ActorSystem.Actor player) { string baseN = "Unique_"; string gemName = "Gem"; int gem_grade = RandomHelper.Next(1, 23); gemName += string.Format("_0{0:00}", gem_grade) + "_x1"; return Cook((player as Player), baseN + gemName); } public static Item GenerateRandomGem(ActorSystem.Actor player, int level, bool is_goblin) { string gemName = GemNames[FastRandom.Instance.Next(GemNames.Count)]; int lvl = Math.Max(player.Attributes[GameAttribute.Level], 20); int gem_grade = ((lvl - 10) / 8) + 1; if (is_goblin) gem_grade += 2; gemName += string.Format("_{0:00}", gem_grade); return Cook((player as Player), gemName); } // generates a random item from given type category. // we can also set a difficulty mode parameter here, but it seems current db doesnt have nightmare or hell-mode items with valid snoId's /raist. public static Item GenerateRandom(ActorSystem.Actor player, ItemTypeTable type) { var itemDefinition = GetRandom(Items.Values .Where(def => ItemGroup.HierarchyToHashList(ItemGroup.FromHash(def.ItemTypesGBID)).Contains(type.Hash)) .ToList()); return CreateItem(player, itemDefinition); } private static ItemTable GetRandom(List pool, bool isUnique = false) { //var found = false; //ItemTable itemDefinition = null; if (pool.Count() == 0) return null; List pool_filtered = pool.Where(it => it.SNOActor != -1 && it.WeaponDamageMin != 100.0f && !it.Name.ToLower().Contains("lootrun") && //TieredLootrunKey !it.Name.ToLower().Contains("gold") && !it.Name.ToLower().Contains("Retro") && !it.Name.ToLower().Contains("healthglobe") && !it.Name.ToLower().Contains("consumable") && !it.Name.ToLower().Contains("pvp") && !it.Name.ToLower().Contains("test") && !it.Name.ToLower().Contains("cosmetic") && !it.Name.ToLower().Contains("transmog") && !it.Name.ToLower().Contains("reagent") && !it.Name.ToLower().ToLower().Contains("retro") && !it.Name.ToLower().Contains("pet") && !it.Name.ToLower().Contains("set") && !it.Name.ToLower().Contains("pvp") && (isUnique ? it.Name.ToLower().Contains("unique") : !it.Name.ToLower().Contains("unique")) && !it.Name.ToLower().Contains("crafted") && !it.Name.ToLower().StartsWith("p71_ethereal") && !it.Name.ToLower().Contains("debug") && !it.Name.ToLower().Contains("promo") && !it.Name.ToLower().Contains("powerpotion") && !((it.ItemTypesGBID == StringHashHelper.HashItemName("Book")) && (it.Cost == 0)) && // i hope it catches all lore with npc spawned /xsochor !(!GBIDHandlers.ContainsKey(it.Hash) && !AllowedItemTypes.Contains(it.ItemTypesGBID)) ).ToList(); /* List pool_filtered = pool.Where(it => it.SNOActor != -1 && it.WeaponDamageMin != 100.0f && !it.Name.ToLower().Contains("lootrun") && //TieredLootrunKey !it.Name.ToLower().Contains("gold") && !it.Name.ToLower().Contains("healthglobe") && !it.Name.ToLower().Contains("consumable") && !it.Name.ToLower().Contains("pvp") && !it.Name.ToLower().Contains("test") && !it.Name.ToLower().Contains("cosmetic") && !it.Name.ToLower().Contains("pet") && !it.Name.ToLower().Contains("set") && !it.Name.ToLower().Contains("norm") && !it.Name.ToLower().Contains("unique") && //(!isUnique) && !it.Name.ToLower().Contains("crafted") && !it.Name.ToLower().Contains("debug") && !it.Name.ToLower().Contains("promo") && !it.Name.ToLower().Contains("powerpotion") && !((it.ItemTypesGBID == StringHashHelper.HashItemName("Book")) && (it.Cost == 0)) && // i hope it catches all lore with npc spawned /xsochor !(//!GBIDHandlers.ContainsKey(it.Hash) && !AllowedItemTypes.Contains(it.ItemTypesGBID)) ).ToList(); //*/ if (pool_filtered.Count() == 0) return null; ItemTable selected = pool_filtered[FastRandom.Instance.Next(0, pool_filtered.Count() - 1)]; return selected; } private static ItemTable GetLegOrSetRandom(List pool, bool isUnique = false) { //var found = false; //ItemTable itemDefinition = null; if (pool.Count() == 0) return null; List pool_filtered = pool.Where(it => it.SNOActor != -1 && it.WeaponDamageMin != 100.0f && !it.Name.ToLower().Contains("lootrun") && //TieredLootrunKey !it.Name.ToLower().Contains("gold") && !it.Name.ToLower().Contains("Retro") && !it.Name.ToLower().Contains("healthglobe") && !it.Name.ToLower().Contains("consumable") && !it.Name.ToLower().Contains("pvp") && !it.Name.ToLower().Contains("test") && !it.Name.ToLower().Contains("cosmetic") && !it.Name.ToLower().Contains("transmog") && !it.Name.ToLower().Contains("reagent") && !it.Name.ToLower().ToLower().Contains("retro") && !it.Name.ToLower().Contains("pet") && !it.Name.ToLower().Contains("set") && !it.Name.ToLower().Contains("pvp") && (it.Name.ToLower().Contains("unique") || it.Name.ToLower().Contains("set")) && !it.Name.ToLower().Contains("crafted") && !it.Name.ToLower().StartsWith("p71_ethereal") && !it.Name.ToLower().Contains("debug") && !it.Name.ToLower().Contains("promo") && !it.Name.ToLower().Contains("powerpotion") && !((it.ItemTypesGBID == StringHashHelper.HashItemName("Book")) && (it.Cost == 0)) && // i hope it catches all lore with npc spawned /xsochor !(!GBIDHandlers.ContainsKey(it.Hash) && !AllowedItemTypes.Contains(it.ItemTypesGBID)) ).ToList(); if (pool_filtered.Count() == 0) return null; ItemTable selected = pool_filtered[FastRandom.Instance.Next(0, pool_filtered.Count() - 1)]; return selected; } #endregion #region misc public static Type GetItemClass(ItemTable definition) { Type type = typeof(Item); if (GBIDHandlers.ContainsKey(definition.Hash)) { type = GBIDHandlers[definition.Hash]; } else { foreach (var hash in ItemGroup.HierarchyToHashList(ItemGroup.FromHash(definition.ItemTypesGBID))) { if (TypeHandlers.ContainsKey(hash)) { type = TypeHandlers[hash]; break; } } } return type; } public static int GetItemHash(string name) { var item = Items.FirstOrDefault(i => i.Value.Name == name); return (item.Value == null ? -1 : item.Key); } public static Item CloneItem(Item originalItem) { Item clonedItem = CreateItem(originalItem.Owner, originalItem.ItemDefinition, originalItem.Attributes[GameAttribute.Item_Quality_Level], originalItem.Attributes[GameAttribute.IsCrafted], originalItem.Attributes[GameAttribute.Seed]); //clonedItem.AffixList = originalItem.AffixList; //clonedItem.Attributes = originalItem.Attributes; AffixGenerator.CloneIntoItem(originalItem, clonedItem); clonedItem.Attributes[GameAttribute.ItemStackQuantityLo] = originalItem.Attributes[GameAttribute.ItemStackQuantityLo]; clonedItem.RareItemName = originalItem.RareItemName; clonedItem.Unidentified = originalItem.Unidentified; return clonedItem; } public static Item GetRandomItemOfType(Player player, ItemTypeTable itemType, bool canBeUnidentified = true) { int minQuality = 1; if (ItemGroup.HierarchyToHashList(itemType).Contains(-740765630)) //jewelry minQuality = 3; Item item = GenerateRandomEquip(player, player.Level, minQuality, 10, itemType); if (canBeUnidentified) RandomSetUnidentified(item); return item; } // Creates an item based on supplied definition. public static Item CreateItem(ActorSystem.Actor owner, ItemTable definition, int forceQuality = -1, bool crafted = false, int seed = -1, bool canBeUnidentified = true) { Logger.Debug("Creating item: $[underline blue]${0}$[/]$ [sno:$[underline blue]${1}$[/]$, gbid $[underline blue]${2}$[/]$]", definition.Name, definition.SNOActor, StringHashHelper.HashItemName(definition.Name)); Type type = GetItemClass(definition); var item = (Item)Activator.CreateInstance(type, owner.World, definition, forceQuality, crafted, seed); if (item == null) { Logger.Warn($"Could not create item $[red]${definition.Name}$[/]$ [sno:$[red]${definition.SNOActor}$[/]$, gbid $[red]${StringHashHelper.HashItemName(definition.Name)}]$[/]$"); return null; } if (forceQuality == 9) item.Attributes[GameAttribute.Item_Quality_Level] = 9; if (canBeUnidentified) RandomSetUnidentified(item); return item; } /// /// Randomly sets unidentified flag on item. /// If the item is unique, legendary, special or set, it will have % chance to be unidentified. /// Otherwise, it will have % chance to be unidentified. /// /// The item to set the flag private static void RandomSetUnidentified(Item item) => item.Unidentified = FastRandom.Instance.Chance(item.Name.Contains("unique", StringComparison.InvariantCultureIgnoreCase) || item.ItemDefinition.Quality is ItemTable.ItemQuality.Legendary or ItemTable.ItemQuality.Special or ItemTable.ItemQuality.Set ? Config.Instance.ChanceHighQualityUnidentified : Config.Instance.ChanceNormalUnidentified); // Allows cooking a custom item. public static Item Cook(Player player, string name) { int hash = StringHashHelper.HashItemName(name); ItemTable definition = Items[hash]; //Unique items level scaling if (definition.Name.ToLower().Contains("unique") || definition.Quality is ItemTable.ItemQuality.Legendary or ItemTable.ItemQuality.Special or ItemTable.ItemQuality.Set) { definition.ItemLevel = player.Attributes[GameAttribute.Level]; definition.RequiredLevel = player.Attributes[GameAttribute.Level]; definition.CrafterRequiredLevel = player.Attributes[GameAttribute.Level]; for (int i = 0; i < 6; i++) definition.MaxAffixLevel[i] = player.Attributes[GameAttribute.Level]; } return CookFromDefinition(player.World, definition); } // Allows cooking a custom item. public static Item CookFromDefinition(World world, ItemTable definition, int forceQuality = -1, bool binded = false, bool crafted = false, int seed = -1) { Type type = GetItemClass(definition); var item = (Item)Activator.CreateInstance(type, new object[] { world, definition, forceQuality, crafted, seed }); item.Attributes[GameAttribute.Item_Quality_Level] = Math.Min(item.Attributes[GameAttribute.Item_Quality_Level], 9); if (binded) item.Attributes[GameAttribute.Item_Binding_Level_Override] = 1; return item; } public static ItemTable GetItemDefinition(int gbid) { return (Items.ContainsKey(gbid)) ? Items[gbid] : null; } public static List GetParagonBonusTable(ToonClass toonClass) { Class @class = Class.None; if (toonClass == ToonClass.Barbarian) @class = Class.Barbarian; if (toonClass == ToonClass.Crusader) @class = Class.Crusader; if (toonClass == ToonClass.DemonHunter) @class = Class.DemonHunter; if (toonClass == ToonClass.Monk) @class = Class.Monk; if (toonClass == ToonClass.WitchDoctor) @class = Class.Witchdoctor; if (toonClass == ToonClass.Wizard) @class = Class.Wizard; if (toonClass == ToonClass.Necromancer) @class = Class.Necromancer; return ParagonBonuses.Where(b => b.HeroClass == @class || b.HeroClass == Class.None).ToList(); } public static RecipeTable GetRecipeDefinition(int gbid) { return (Recipes.ContainsKey(gbid)) ? Recipes[gbid] : null; } public static RecipeTable GetRecipeDefinition(string name) { var recipe = Recipes.FirstOrDefault(r => r.Value.Name == name); return recipe.Value; } public static SocketedEffectTable GetGemEffectDefinition(int gem_gbid, int item_type) { var effects = GemEffects.Where(ge => ge.Item == gem_gbid && ge.ItemType == item_type).ToList(); return (effects.Count == 1) ? effects.First() : null; } public static SetItemBonusTable GetItemSetEffectDefinition(int set_gbid, int count) { var effects = ItemSetsEffects.Where(ge => ge.Set == set_gbid && ge.Count == count).ToList(); return (effects.Count == 1) ? effects.First() : null; } public static Item CreateGold(Player player, int amount) { string item_name = "Gold1"; if (amount > 10) item_name = "Gold2"; if (amount > 100) item_name = "Gold3"; if (amount > 500) item_name = "Gold4"; if (amount > 1000) item_name = "Gold5"; var item = Cook(player, item_name); item.Attributes[GameAttribute.Gold] = amount; return item; } public static Item CreateBloodShards(Player player, int amount) { var item = Cook(player, "HoradricRelic"); item.Attributes[GameAttribute.ItemStackQuantityLo] = amount; return item; } public static Item CreateHealthGlobe(Player player, int amount) { if (amount > 10) amount = 10 + ((amount - 10) * 5); var item = Cook(player, "HealthGlobe" + amount); item.Attributes[GameAttribute.Health_Globe_Bonus_Health] = amount; return item; } public static Item CreateArcaneGlobe(Player player) { var item = Cook(player, "ArcaneGlobe"); return item; } public static Item CreatePowerGlobe(Player player) { var item = Cook(player, "PowerGlobe_v2_x1_NoFlippy"); return item; } public static Item CreateLore(Player player, int loreId) { var loreDefinition = Lore[loreId]; if (loreDefinition == null) return null; return CookFromDefinition(player.World, loreDefinition); } public static bool IsValidItem(string name) { return Items.ContainsKey(StringHashHelper.HashItemName(name)); } #endregion #region Database handling public static void SaveToDB(Item item) { //var timestart = DateTime.Now; var affixSer = SerializeAffixList(item.AffixList); var attributesSer = item.Attributes.Serialize(); item.DBInventory.Affixes = affixSer; item.DBInventory.DyeType = item.Attributes[GameAttribute.DyeType]; item.DBInventory.Count = item.Attributes[GameAttribute.ItemStackQuantityLo]; item.DBInventory.Durability = item.Attributes[GameAttribute.Durability_Cur]; item.DBInventory.Binding = item.Attributes[GameAttribute.Item_Binding_Level_Override]; item.DBInventory.Rating = item.Rating; item.DBInventory.RareItemName = (item.RareItemName == null ? null : item.RareItemName.ToByteArray()); item.DBInventory.Quality = item.Attributes[GameAttribute.Item_Quality_Level]; item.DBInventory.Attributes = attributesSer; item.DBInventory.GbId = item.GBHandle.GBID; item.DBInventory.Version = 2; item.DBInventory.TransmogGBID = item.Attributes[GameAttribute.TransmogGBID]; //Logger.Info("ItemFlags: {0}", (int)item.ItemType.Flags); //var timeTaken = DateTime.Now - timestart; //Logger.Debug("Save item instance #{0}, took {1} msec", item.DBInventory.Id, timeTaken.TotalMilliseconds); } public static Item LoadFromDB(Player owner, DBInventory instance) // int dbID, int gbid, string attributesSer, string affixesSer) { var table = Items[instance.GbId]; var itm = new Item(owner.World, table, DeSerializeAffixList(instance.Affixes), instance.Attributes, instance.Count); if (itm.Attributes[GameAttribute.Durability_Max] > 0) itm.Attributes[GameAttribute.Durability_Cur] = instance.Durability; itm.Attributes[GameAttribute.DyeType] = instance.DyeType; itm.Attributes[GameAttribute.TransmogGBID] = instance.TransmogGBID; itm.DBInventory = instance; itm.Unidentified = instance.Unidentified; if (instance.Version == 1) itm.Attributes[GameAttribute.IsCrafted] = true; if (!owner.World.DbItems.ContainsKey(owner.World)) owner.World.DbItems.Add(owner.World, new List()); if (!owner.World.DbItems[owner.World].Contains(itm)) owner.World.DbItems[owner.World].Add(itm); owner.World.CachedItems[instance.Id] = itm; if (instance.FirstGem != -1) itm.Gems.Add(CookFromDefinition(owner.World, GetItemDefinition(instance.FirstGem), 1)); if (instance.SecondGem != -1) itm.Gems.Add(CookFromDefinition(owner.World, GetItemDefinition(instance.SecondGem), 1)); if (instance.ThirdGem != -1) itm.Gems.Add(CookFromDefinition(owner.World, GetItemDefinition(instance.ThirdGem), 1)); for (int i = 0; i < itm.Gems.Count; i++) { itm.Gems[i].Owner = itm; itm.Gems[i].SetInventoryLocation(20, 0, i); } itm.Attributes[GameAttribute.Sockets_Filled] = itm.Gems.Count; if (instance.RareItemName != null) itm.RareItemName = D3.Items.RareItemName.ParseFrom(instance.RareItemName); return itm; } public static string SerializeAffixList(List affixList) { var affixgbIdList = affixList.Select(af => af.AffixGbid); var affixSer = affixgbIdList.Aggregate(",", (current, affixId) => current + (affixId + ",")) .Trim(new[] { ',' }); return affixSer; } public static List DeSerializeAffixList(string serializedAffixList) { var affixListStr = serializedAffixList.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); var affixList = affixListStr.Select(int.Parse).Select(affixId => new Affix(affixId)).ToList(); return affixList; } public static int GetSeed(string attributesList) { if (attributesList == "") { return 0; } var pairs = attributesList.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries); foreach (var pair in pairs) { try { var pairParts = pair.Split(new[] { ':' }, StringSplitOptions.RemoveEmptyEntries); if (pairParts.Length != 2) { continue; } var keyData = pairParts[0].Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); var attributeId = int.Parse(keyData[0].Trim()); if (attributeId != 394) { continue; } var values = pairParts[1].Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries); var valueI = int.Parse(values[0].Trim()); return valueI; } catch { return 0; } } return 0; } public static bool IsCrafted(string attributesList) { if (attributesList == "") { return false; } var pairs = attributesList.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries); foreach (var pair in pairs) { try { var pairParts = pair.Split(new[] { ':' }, StringSplitOptions.RemoveEmptyEntries); if (pairParts.Length != 2) { continue; } var keyData = pairParts[0].Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); var attributeId = int.Parse(keyData[0].Trim()); if (attributeId != 395) { continue; } var values = pairParts[1].Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries); var valueI = int.Parse(values[0].Trim()); return (valueI == 1); } catch { return false; } } return false; } #endregion } }