using System; using System.Collections.Generic; using System.IO; using System.Xml; namespace NecromancerTome { /// /// Группы предметов для фильтра урны-логиста (16.10.2026): «пиктограммы, которые по умолчанию выбраны и активны... /// изображения предмета, символизирующего группу предметов... Дух-логист будет переносить только те предметы, которые /// активированы в его урне. Это же прямой путь к автоматизации сортировки». Деление игры (свойство Group) грубое - /// «Ресурсы» смешивают руду, слитки, детали и кур, - поэтому группы свои, а предмет относится к группе по классу, тегам и /// имени. Предметы других модов разойдутся по тем же правилам; что не подошло никуда - «Прочее». /// Номер группы = номер бита в TEFeatureCinerarium.GroupMask - порядок НЕ менять (сейвы), новые - только в конец. /// public static class CinerariumGroups { public const int Cooked = 0, Canned = 1, Raw = 2, Harvest = 3, Flowers = 4, Seeds = 5, Drinks = 6, Medicine = 7, Ore = 8, Ingots = 9, Parts = 10, Materials = 11, Chemicals = 12, Tools = 13, Weapons = 14, Ammo = 15, Armor = 16, Mods = 17, Books = 18, Blocks = 19, Necro = 20, Other = 21, // 17.10: «Корм для кур вынеси в отдельную группу» (был в «Сыром»). ChickenFeed = 22, // 23.10 (проверка всех предметов: куры и сложенный транспорт падали в «Прочее»; «Можно и добавить группы»). Livestock = 23, Vehicles = 24; public const int Count = 25; public const uint All = (1u << Count) - 1; /// Значок группы - иконка типичного предмета (ItemIconAtlas). public static readonly string[] Icons = { "foodMeatStew", "foodCanChili", "foodRawMeat", "foodCropCorn", "plantedChrysanthemum3Harvest", "plantedCorn1", "drinkJarBoiledWater", "medicalFirstAidKit", "resourceRockSmall", "resourceForgedSteel", "resourceMechanicalParts", "resourceWood", "resourceGunPowder", "meleeToolPickT1IronPickaxe", "gunHandgunT1Pistol", "ammo9mmBulletBall", "armorMinerHelmet", "modGunLaserSight", "bookArtOfMiningLuckyStrike", "cobblestoneMaster", "necroSkull", "oldCash", "resourceChickenFeed", "domesticatedChicken", "vehicleMinibikePlaceable" }; /// Ключ названия группы в Localization.csv: necroGroup0..necroGroup24. public static string NameKey(int _g) => "necroGroup" + _g; static readonly HashSet OreNames = new HashSet { "resourceRockSmall", "resourceRockSmallBundle", "resourceScrapIron", "resourceScrapIronBundle", "resourceScrapLead", "resourceLeadBundle", "resourceCoal", "resourceCoalBundle", "resourcePotassiumNitratePowder", "resourcePotassiumNitratePowderBundle", "resourceOilShale", "resourceOilShaleBundle", "resourceClayLump", "resourceCrushedSand", "resourceCobblestones", "resourceGoldNugget", "resourceSilverNugget", "resourceRawDiamond", "resourceSnowBall" }; static readonly HashSet IngotNames = new HashSet { "resourceForgedIron", "resourceForgedSteel" }; static readonly HashSet ChemNames = new HashSet { "resourceGunPowder", "resourceGunPowderBundle", "resourceOil", "ammoGasCan", "ammoGasCanBundle", "resourceAcid", "resourceGlue", "resourcePaint", "resourceTestosteroneExtract" }; static readonly HashSet MaterialNames = new HashSet { "resourceWood", "resourceWoodBundle", "resourceCloth", "resourceLeather", "resourceBone", "resourceFeather", "resourceYuccaFibers", "resourcePaper", "resourceCement", "resourceConcreteMix", "resourceBeeswax", "resourceCandleStick", "resourceAnimalFat" }; /// Детали, которые теги отнесли бы не туда (гвозди - патроны гвоздомёта). static readonly HashSet PartNames = new HashSet { "resourceNail" }; static readonly HashSet AmmoParts = new HashSet { "resourceArrowHeadIron", "resourceArrowHeadSteelAP", "resourceBuckshot", "resourceBulletCasing", "resourceBulletTip", "resourceRocketCasing", "resourceRocketTip" }; static readonly HashSet RawNames = new HashSet { "foodRawMeat", "foodRawMeatBundle", "foodEgg", "foodHoney", "foodRottingFlesh" }; /// Предметы и блоки самого мода - по его Config/items.xml, blocks.xml и item_modifiers.xml (у многих нет «necro» /// в имени: книги призыва, браслет, камни портала, прах зомби, кожа жертвы; 23.10 - кровавая сфера и кровавый камень, /// они модификаторы, и без item_modifiers.xml уходили в «Модификаторы»). Читается один раз. static HashSet _modNames; static HashSet ModNames() { if (_modNames != null) { return _modNames; } _modNames = new HashSet(); foreach (string f in new[] { "items.xml", "blocks.xml", "item_modifiers.xml" }) { try { XmlDocument doc = new XmlDocument(); doc.Load(Path.Combine(ModEntry.Instance.Path, "Config", f)); foreach (XmlNode n in doc.SelectNodes("//item[@name] | //block[@name] | //item_modifier[@name]")) { _modNames.Add(n.Attributes["name"].Value); } } catch (Exception e) { UnityEngine.Debug.LogWarning("[NecromancerTome] CinerariumGroups: " + f + " мода не прочитан (" + e.Message + ")"); } } return _modNames; } static readonly Dictionary _cache = new Dictionary(); public static int Of(ItemValue _iv) { ItemClass ic = _iv?.ItemClass; if (ic == null) { return Other; } if (!_cache.TryGetValue(ic.Id, out int g)) { g = Classify(ic, _iv); _cache[ic.Id] = g; } return g; } static bool Tag(ItemClass _ic, string _t) { return _ic.ItemTags.Test_AnySet(FastTags.Parse(_t)); } static int Classify(ItemClass _ic, ItemValue _iv) { string n = _ic.GetItemName() ?? ""; // 23.10: семена - раньше «Некромантии»: кровавые семена (plantedNecroBloodRose1) уходили с грядок к логисту // «только некромантия», и сборщику нечего было сеять. Всё, что сажается на грядку, - «Семена». if (_ic.IsBlock() && new ItemValue(_ic.Id).ToBlockValue().Block is BlockPlantGrowing) { return Seeds; } // 23.10: живность (куры для курятника, кролик в мешке) и сложенный транспорт - свои группы. if (n.StartsWith("domesticated") || n == RabbitGrab.SackItem) { return Livestock; } if (n.StartsWith("vehicle") && n.EndsWith("Placeable")) { return Vehicles; } // 23.10: жестяные банки - предметы мода, но по смыслу это стеклянные банки игры (кипятить воду) - к ним, не в «Некромантию». if (n.StartsWith("tinCan")) { return Drinks; } if (n.IndexOf("necro", StringComparison.OrdinalIgnoreCase) >= 0 || ModNames().Contains(n)) { return Necro; } if (n == "resourceChickenFeed") { return ChickenFeed; } if (_ic is ItemClassModifier) { return Mods; } if (_ic.IsBlock()) { return new ItemValue(_ic.Id).ToBlockValue().Block is BlockPlantGrowing ? Seeds : Blocks; } if (_ic is ItemClassArmor || n.StartsWith("armor") || n.StartsWith("clothing")) { return Armor; } if (n.StartsWith("book") || n.Contains("Schematic") || n.Contains("Magazine") || Tag(_ic, "books")) { return Books; } if (OreNames.Contains(n)) { return Ore; } if (IngotNames.Contains(n) || n.StartsWith("unit_")) { return Ingots; } if (ChemNames.Contains(n)) { return Chemicals; } if (PartNames.Contains(n)) { return Parts; } if (MaterialNames.Contains(n)) { return Materials; } if (AmmoParts.Contains(n) || n.StartsWith("ammo") || n.StartsWith("thrown") || Tag(_ic, "ammo")) { return Ammo; } if (n.EndsWith("Parts") || n == "partsMaster" || n.StartsWith("vehicle") && !n.EndsWith("Placeable")) { return Parts; } if (n.StartsWith("meleeTool")) { return Tools; // раньше оружия: у факела тег weapon } if (n.StartsWith("gun") || n.StartsWith("meleeWpn") || Tag(_ic, "weapon")) { return Weapons; } if (n.StartsWith("meleeTool") || n.StartsWith("tool") || n.StartsWith("bucket") || Tag(_ic, "tool")) { return Tools; } if (n.StartsWith("foodCan")) { return Canned; } if (n.StartsWith("foodCrop")) { return Harvest; } if (n.StartsWith("resourceCrop")) { return Flowers; } if (RawNames.Contains(n) || n.StartsWith("foodRaw")) { return Raw; } if (n.StartsWith("drink") || n.StartsWith("tinCan")) { return Drinks; } if (n.StartsWith("medical") || n.StartsWith("drug") || Tag(_ic, "medical")) { return Medicine; } if (n.StartsWith("food") || Tag(_ic, "food")) { return Cooked; } if (n.StartsWith("resource") || n == "carBattery" || n == "smallEngine" || n == "solarCell") { return Parts; } return Other; } } }