Decompiled source of GK2 Codex v0.10.14
GK2Codex.dll
Decompiled 2 hours ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Text; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using GK2.Framework; using HarmonyLib; using LazyBearTechnology; using Rewired; using TMPro; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.Events; using UnityEngine.SceneManagement; using UnityEngine.UI; [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: AssemblyVersion("0.0.0.0")] namespace GK2Codex; internal sealed class AlchemyMix { public string MixId; public readonly List<string> Ingredients = new List<string>(); } internal sealed class AlchemyFormulaInfo { public string Id; public string Name; public string IconId; public int RunesRed; public int RunesGreen; public int RunesBlue; public readonly List<string> Stations = new List<string>(); public readonly List<AlchemyMix> Mixes = new List<AlchemyMix>(); public bool Known; } internal static class Alchemy { public static List<AlchemyFormulaInfo> Collect(bool showAll) { List<AlchemyFormulaInfo> list = new List<AlchemyFormulaInfo>(); try { GameBalance me = GameBalance.Me; if ((Object)(object)me == (Object)null || me.alchemyFormulaDefs == null) { return list; } HashSet<string> hashSet = KnownMixes(); Dictionary<string, List<AlchemyMixSourceDef>> dictionary = new Dictionary<string, List<AlchemyMixSourceDef>>(); if (me.alchemyMixSourceDefs != null) { foreach (AlchemyMixSourceDef alchemyMixSourceDef in me.alchemyMixSourceDefs) { if (alchemyMixSourceDef != null && !string.IsNullOrEmpty(alchemyMixSourceDef.formulaId)) { if (!dictionary.TryGetValue(alchemyMixSourceDef.formulaId, out var value)) { value = (dictionary[alchemyMixSourceDef.formulaId] = new List<AlchemyMixSourceDef>()); } value.Add(alchemyMixSourceDef); } } } foreach (AlchemyFormulaDef alchemyFormulaDef in me.alchemyFormulaDefs) { if (alchemyFormulaDef == null || string.IsNullOrEmpty(((BalanceBaseObject)alchemyFormulaDef).id)) { continue; } bool flag = FormulaKnown(alchemyFormulaDef); if (!showAll && !flag) { continue; } string text = GameApi.ItemName(((BalanceBaseObject)alchemyFormulaDef).id, isGroup: false); if (string.IsNullOrEmpty(text) || text == ((BalanceBaseObject)alchemyFormulaDef).id) { continue; } AlchemyFormulaInfo alchemyFormulaInfo = new AlchemyFormulaInfo(); alchemyFormulaInfo.Id = ((BalanceBaseObject)alchemyFormulaDef).id; alchemyFormulaInfo.Name = text; alchemyFormulaInfo.IconId = GameApi.ItemIcon(((BalanceBaseObject)alchemyFormulaDef).id, isGroup: false); alchemyFormulaInfo.RunesRed = alchemyFormulaDef.runesRed; alchemyFormulaInfo.RunesGreen = alchemyFormulaDef.runesGreen; alchemyFormulaInfo.RunesBlue = alchemyFormulaDef.runesBlue; alchemyFormulaInfo.Known = flag; AlchemyFormulaInfo alchemyFormulaInfo2 = alchemyFormulaInfo; if (alchemyFormulaDef.craftsIn != null) { foreach (string item in alchemyFormulaDef.craftsIn.Where(GameApi.HasWgoName).Distinct()) { alchemyFormulaInfo2.Stations.Add(item); } } if (dictionary.TryGetValue(((BalanceBaseObject)alchemyFormulaDef).id, out var value2)) { foreach (AlchemyMixSourceDef item2 in value2) { if (item2 == null || string.IsNullOrEmpty(item2.mixId) || (!showAll && !hashSet.Contains(item2.mixId))) { continue; } AlchemyMix alchemyMix = new AlchemyMix(); alchemyMix.MixId = item2.mixId; AlchemyMix alchemyMix2 = alchemyMix; string[] array = new string[3] { item2.ingredient1, item2.ingredient2, item2.ingredient3 }; foreach (string text2 in array) { if (!string.IsNullOrEmpty(text2)) { alchemyMix2.Ingredients.Add(text2); } } if (alchemyMix2.Ingredients.Count > 0) { alchemyFormulaInfo2.Mixes.Add(alchemyMix2); } } } list.Add(alchemyFormulaInfo2); } } catch (Exception ex) { Plugin.Log.LogError((object)("Alchemy: could not read the formulas: " + ex)); } return list.OrderBy((AlchemyFormulaInfo f) => f.Name ?? "").ToList(); } public static bool FormulaKnown(AlchemyFormulaDef f) { if (f == null) { return false; } try { KnowledgeSystem knowledgeSystem = MainGame.Instance.GameSave.knowledgeSystem; if (knowledgeSystem == null) { return false; } return knowledgeSystem.IsAlchemyFormulaKnown(f); } catch (Exception ex) { Plugin.LogThrottled("Alchemy: knowledge check failed, hiding the formula: " + ex.Message); return false; } } private static HashSet<string> KnownMixes() { HashSet<string> hashSet = new HashSet<string>(); try { KnowledgeSystem knowledgeSystem = MainGame.Instance.GameSave.knowledgeSystem; if (knowledgeSystem == null) { return hashSet; } List<string> value = Traverse.Create((object)knowledgeSystem).Field("knownMixCrafts").GetValue<List<string>>(); if (value != null) { foreach (string item in value) { if (!string.IsNullOrEmpty(item)) { hashSet.Add(item); } } } } catch (Exception ex) { Plugin.LogThrottled("Alchemy: known mixes unreadable: " + ex.Message); } return hashSet; } } internal static class Cm { public static ConfigDescription D(string description, string name, int order, bool advanced = false, AcceptableValueBase range = null, bool browsable = true) { //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Expected O, but got Unknown ConfigurationManagerAttributes configurationManagerAttributes = new ConfigurationManagerAttributes(); configurationManagerAttributes.DispName = name; configurationManagerAttributes.Order = order; ConfigurationManagerAttributes configurationManagerAttributes2 = configurationManagerAttributes; if (advanced) { configurationManagerAttributes2.IsAdvanced = true; } if (!browsable) { configurationManagerAttributes2.Browsable = false; } return new ConfigDescription(description, range, new object[1] { configurationManagerAttributes2 }); } } internal sealed class ConfigurationManagerAttributes { public delegate void CustomHotkeyDrawerFunc(ConfigEntryBase setting, ref bool isCurrentlyAcceptingInput); public bool? ShowRangeAsPercent; public Action<ConfigEntryBase> CustomDrawer; public CustomHotkeyDrawerFunc CustomHotkeyDrawer; public bool? Browsable; public string Category; public object DefaultValue; public bool? HideDefaultButton; public bool? HideSettingName; public string Description; public string DispName; public int? Order; public bool? ReadOnly; public bool? IsAdvanced; public Func<object, string> ObjToStr; public Func<string, object> StrToObj; } internal sealed class Ingredient { public string Id; public int Count; public bool IsGroup; } internal sealed class Recipe { public string Key; public bool IsBuilding; public CraftDef Craft; public BuildingDef Building; public List<string> Stations = new List<string>(); public List<Ingredient> Inputs = new List<Ingredient>(); public Dictionary<string, int> Outputs = new Dictionary<string, int>(); } internal sealed class VendorOffer { public string VendorId; public string Name; public string IconId; public int Tier; public int Count; } internal sealed class Source { public string WgoId; } internal enum EntryKind { Item, Building, Zombie, Alchemy, Zone, Vendor } internal sealed class Entry { public EntryKind Kind; public string Id; public string Name; public string IconId; public ItemDef Item; public BuildingDef Building; public ZombieInfo Zombie; public AlchemyFormulaInfo Formula; public GraveyardInfo Graveyard; public GraveInfo Grave; public VendorInfo VendorInfo; public string SortKey; } internal sealed class CodexIndex { public readonly List<Entry> Items = new List<Entry>(); public readonly List<Entry> Buildings = new List<Entry>(); public readonly Dictionary<string, Entry> ItemById = new Dictionary<string, Entry>(); public readonly Dictionary<string, Entry> BuildingById = new Dictionary<string, Entry>(); public readonly Dictionary<string, List<Recipe>> MadeBy = new Dictionary<string, List<Recipe>>(); public readonly Dictionary<string, List<Recipe>> UsedIn = new Dictionary<string, List<Recipe>>(); public readonly Dictionary<string, List<string>> DroppedBy = new Dictionary<string, List<string>>(); public readonly Dictionary<string, Recipe> BuildingRecipe = new Dictionary<string, Recipe>(); public readonly Dictionary<string, List<Recipe>> CraftedAt = new Dictionary<string, List<Recipe>>(); public readonly Dictionary<string, List<VendorOffer>> SoldBy = new Dictionary<string, List<VendorOffer>>(); public readonly Dictionary<string, List<VendorOffer>> BoughtBy = new Dictionary<string, List<VendorOffer>>(); public readonly List<VendorInfo> Vendors = new List<VendorInfo>(); public int HiddenCrafts; public int KnownCrafts; public bool ShowAll; public string Lang; private KnowledgeSystem _k; private readonly HashSet<string> _worldWgos = new HashSet<string>(); private readonly HashSet<string> _builtTownBuildings = new HashSet<string>(); private readonly HashSet<string> _knownStations = new HashSet<string>(); private static readonly Dictionary<string, string> MeetQuest = new Dictionary<string, string> { { "npc_head_of_the_village", "7_village_head_meet" }, { "npc_herm", "8_village_trader_meet" }, { "npc_astrologer", "27_port_astrologer_meet" }, { "npc_warehouse_manager", "30_port_warehouse_inside" }, { "npc_bishop", "106_foundry_meeting_portal" }, { "npc_woodcarver", "146_stilt_master_meet" } }; public static CodexIndex Build(bool showAll) { CodexIndex codexIndex = new CodexIndex(); codexIndex.ShowAll = showAll; codexIndex.Lang = GameApi.Lang(); CodexIndex codexIndex2 = codexIndex; try { codexIndex2.BuildInternal(); } catch (Exception ex) { Plugin.Log.LogError((object)("Index build failed: " + ex)); } return codexIndex2; } private void BuildInternal() { //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Invalid comparison between Unknown and I4 GameBalance me = GameBalance.Me; try { _k = MainGame.Instance.GameSave.knowledgeSystem; } catch { _k = null; } CollectWorld(); List<BuildingDef> list = new List<BuildingDef>(); foreach (BuildingDef buildingDef in me.buildingDefs) { if (buildingDef != null && !string.IsNullOrEmpty(buildingDef.wgoId) && (int)buildingDef.buildingMode != 0 && (int)buildingDef.buildingMode != 2 && BuildingKnown(buildingDef)) { list.Add(buildingDef); _knownStations.Add(buildingDef.wgoId); } } foreach (string worldWgo in _worldWgos) { _knownStations.Add(worldWgo); } HashSet<string> hashSet = new HashSet<string>(); foreach (BuildingDef item2 in list) { Recipe recipe = new Recipe(); recipe.Key = "build:" + ((BalanceBaseObject)item2).id; recipe.IsBuilding = true; recipe.Building = item2; Recipe recipe2 = recipe; if (item2.buildsIn != null) { recipe2.Stations.AddRange(item2.buildsIn.Where((string s) => (ShowAll || _knownStations.Contains(s)) && GameApi.HasWgoName(s)).Distinct()); } if (recipe2.Stations.Count == 0) { continue; } recipe2.Inputs = ToIngredients(item2.needItems); string text = SafeHeader(item2); if (text == null || recipe2.Inputs.Any((Ingredient i) => !GameApi.IsLocalizedNeed(i.Id, i.IsGroup))) { continue; } string item = text + "|" + string.Join(",", recipe2.Inputs.Select((Ingredient i) => i.Id + "x" + i.Count).ToArray()); if (!hashSet.Add(item)) { continue; } Entry entry = new Entry(); entry.Kind = EntryKind.Building; entry.Id = ((BalanceBaseObject)item2).id; entry.Building = item2; entry.Name = text; entry.IconId = SafeIcon(item2); Entry e = entry; AddEntry(Buildings, BuildingById, e); BuildingRecipe[((BalanceBaseObject)item2).id] = recipe2; foreach (Ingredient input in recipe2.Inputs) { Add(UsedIn, input.Id, recipe2); } Dictionary<string, int> dictionary = new Dictionary<string, int>(); CollectOutput(item2.outputItems, dictionary); if (dictionary.Count == 0 && GameApi.ItemDef(item2.wgoId) != null) { dictionary[item2.wgoId] = 1; } foreach (string key in dictionary.Keys) { recipe2.Outputs[key] = dictionary[key]; Add(MadeBy, key, recipe2); } } foreach (CraftDef craftDef in me.craftDefs) { if (craftDef == null || string.IsNullOrEmpty(((BalanceBaseObject)craftDef).id) || ((BalanceBaseObject)craftDef).id.StartsWith("test")) { continue; } if (!CraftKnown(craftDef)) { HiddenCrafts++; continue; } KnownCrafts++; Recipe recipe = new Recipe(); recipe.Key = "craft:" + ((BalanceBaseObject)craftDef).id; recipe.Craft = craftDef; Recipe recipe3 = recipe; if (((CraftDefBase)craftDef).craftsIn != null) { recipe3.Stations.AddRange(((CraftDefBase)craftDef).craftsIn.Where((string s) => (ShowAll || _knownStations.Contains(s)) && GameApi.HasWgoName(s))); } if (recipe3.Stations.Count == 0) { continue; } recipe3.Inputs = ToIngredients(((CraftDefBase)craftDef).needItems); CollectOutputs(craftDef, recipe3.Outputs); foreach (Ingredient input2 in recipe3.Inputs) { recipe3.Outputs.Remove(input2.Id); } if (recipe3.Outputs.Count == 0 || recipe3.Outputs.Keys.Any((string o) => !GameApi.IsLocalizedItem(o)) || recipe3.Inputs.Any((Ingredient i) => !GameApi.IsLocalizedNeed(i.Id, i.IsGroup))) { continue; } foreach (string key2 in recipe3.Outputs.Keys) { Add(MadeBy, key2, recipe3); } foreach (Ingredient input3 in recipe3.Inputs) { Add(UsedIn, input3.Id, recipe3); } foreach (string item3 in recipe3.Stations.Distinct()) { Add(CraftedAt, item3, recipe3); } } foreach (WGODef wgoDef in me.wgoDefs) { if (wgoDef == null || string.IsNullOrEmpty(((BalanceBaseObject)wgoDef).id) || (!ShowAll && !_worldWgos.Contains(((BalanceBaseObject)wgoDef).id)) || !GameApi.HasWgoName(((BalanceBaseObject)wgoDef).id)) { continue; } Dictionary<string, int> dictionary2 = new Dictionary<string, int>(); CollectOutput(wgoDef.deathChanceItems, dictionary2); foreach (string key3 in dictionary2.Keys) { if (!DroppedBy.TryGetValue(key3, out var value)) { value = (DroppedBy[key3] = new List<string>()); } if (!value.Contains(((BalanceBaseObject)wgoDef).id)) { value.Add(((BalanceBaseObject)wgoDef).id); } } } CollectVendors(me); HashSet<string> hashSet2 = new HashSet<string>(); foreach (string key4 in MadeBy.Keys) { hashSet2.Add(key4); } foreach (string key5 in UsedIn.Keys) { hashSet2.Add(key5); } foreach (string key6 in DroppedBy.Keys) { hashSet2.Add(key6); } foreach (string key7 in SoldBy.Keys) { hashSet2.Add(key7); } foreach (string key8 in BoughtBy.Keys) { hashSet2.Add(key8); } foreach (string item4 in hashSet2) { ItemDef val = GameApi.ItemDef(item4); if (val != null) { Entry entry = new Entry(); entry.Kind = EntryKind.Item; entry.Id = item4; entry.Item = val; entry.Name = GameApi.ItemName(item4, isGroup: false); entry.IconId = val.iconId; Entry e2 = entry; if (GameApi.IsLocalizedItem(item4)) { AddEntry(Items, ItemById, e2); } } } Items.Sort((Entry a, Entry b) => string.Compare(a.SortKey, b.SortKey, StringComparison.Ordinal)); Buildings.Sort((Entry a, Entry b) => string.Compare(a.SortKey, b.SortKey, StringComparison.Ordinal)); Plugin.Log.LogInfo((object)("Codex index: " + Items.Count + " items, " + Buildings.Count + " buildings, crafts known " + KnownCrafts + " / hidden " + HiddenCrafts + ", world objects " + _worldWgos.Count + ((!ShowAll) ? "" : " [show all]"))); } private static void AddEntry(List<Entry> list, Dictionary<string, Entry> map, Entry e) { if (!map.ContainsKey(e.Id)) { e.SortKey = (e.Name ?? e.Id).ToLowerInvariant(); map[e.Id] = e; list.Add(e); } } private static void Add(Dictionary<string, List<Recipe>> map, string key, Recipe r) { if (!map.TryGetValue(key, out var value)) { value = (map[key] = new List<Recipe>()); } if (!value.Contains(r)) { value.Add(r); } } private static Dictionary<string, List<string>> TownVendors(GameBalance gb) { Dictionary<string, List<string>> dictionary = new Dictionary<string, List<string>>(); try { if (gb.townBuildingDefs == null) { return dictionary; } foreach (TownBuildingDef townBuildingDef in gb.townBuildingDefs) { if (townBuildingDef != null && !string.IsNullOrEmpty(((BalanceBaseObject)townBuildingDef).id) && !string.IsNullOrEmpty(townBuildingDef.vendorId)) { if (!dictionary.TryGetValue(townBuildingDef.vendorId, out var value)) { value = (dictionary[townBuildingDef.vendorId] = new List<string>()); } value.Add(((BalanceBaseObject)townBuildingDef).id); } } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Town vendors: " + ex.Message)); } return dictionary; } private bool VendorReachable(string vendorId, Dictionary<string, List<string>> townVendors) { if (!townVendors.TryGetValue(vendorId, out var value)) { return true; } foreach (string item in value) { if (_builtTownBuildings.Contains(item)) { return true; } } return false; } private static bool NpcMet(GameBalance gb, string npcId) { if (npcId == "npc_workshop_foreman") { return true; } try { QuestSystemData questSystemData = MainGame.Instance.GameSave.questSystemData; if (questSystemData == null) { return true; } if (MeetQuest.TryGetValue(npcId, out var value) && questSystemData.IsQuestInStatus(value, (QuestStatus)3)) { return true; } if (gb.questDefs != null) { foreach (QuestDef questDef in gb.questDefs) { if (questDef != null && questDef.wgoNpcId == npcId && questSystemData.IsQuestInStatus(((BalanceBaseObject)questDef).id, (QuestStatus)3)) { return true; } } } return false; } catch (Exception ex) { Plugin.LogThrottled("NpcMet " + npcId + ": " + ex.Message); return true; } } private void CollectVendors(GameBalance gb) { List<VendorDef> list = null; try { list = gb.vendorDefs; } catch { } if (list == null) { return; } Dictionary<string, Vendor> dictionary = new Dictionary<string, Vendor>(); try { VendorSystem vendorSystem = MainGame.Instance.GameSave.vendorSystem; if (vendorSystem != null && vendorSystem.vendors != null) { foreach (Vendor vendor in vendorSystem.vendors) { if (vendor != null) { VendorDef val = null; try { val = ((ObjectLinkedToDefinition<VendorDef>)(object)vendor).Definition; } catch { } if (val != null && !string.IsNullOrEmpty(((BalanceBaseObject)val).id)) { dictionary[((BalanceBaseObject)val).id] = vendor; } } } } } catch { } HashSet<string> hashSet = new HashSet<string>(); try { if (_k != null && _k.unlockedVendorsForOrders != null) { foreach (string unlockedVendorsForOrder in _k.unlockedVendorsForOrders) { hashSet.Add(unlockedVendorsForOrder); } } } catch { } Dictionary<string, List<string>> dictionary2 = TownVendors(gb); foreach (VendorDef item in list) { if (item == null || string.IsNullOrEmpty(((BalanceBaseObject)item).id) || ((BalanceBaseObject)item).id.StartsWith("test") || item.tierDataList == null || item.tierDataList.Count == 0) { continue; } dictionary.TryGetValue(((BalanceBaseObject)item).id, out var value); int num; if (ShowAll) { num = item.tierDataList.Count; } else { if (value == null || !VendorReachable(((BalanceBaseObject)item).id, dictionary2) || (!dictionary2.ContainsKey(((BalanceBaseObject)item).id) && !NpcMet(gb, ((BalanceBaseObject)item).id))) { continue; } num = Mathf.Clamp(value.CurTier, 1, item.tierDataList.Count); } string text = VendorName(gb, item); if (text == null) { continue; } string iconId = VendorIcon(gb, item); VendorTierData val2 = item.tierDataList[num - 1]; if (val2 == null) { continue; } VendorInfo vendorInfo = new VendorInfo(); vendorInfo.Id = ((BalanceBaseObject)item).id; vendorInfo.Name = text; vendorInfo.IconId = iconId; vendorInfo.Tier = num; vendorInfo.MaxTier = item.tierDataList.Count; vendorInfo.Town = item.townVendor; vendorInfo.Shop = dictionary2.ContainsKey(((BalanceBaseObject)item).id); vendorInfo.LiveTier = ((value != null) ? SafeTier(value) : 0); vendorInfo.OrdersUnlocked = hashSet.Contains(((BalanceBaseObject)item).id); VendorInfo vendorInfo2 = vendorInfo; if (value != null) { try { vendorInfo2.Money = value.CurMoney; } catch { } } try { vendorInfo2.DailyIncome = val2.dailyMoneyIncome; } catch { } Dictionary<string, int> dictionary3 = new Dictionary<string, int>(); for (int i = 0; i < num; i++) { VendorTierData val3 = item.tierDataList[i]; if (val3 == null || val3.vendorProducts == null) { continue; } foreach (VendorProductData vendorProduct in val3.vendorProducts) { if (vendorProduct != null && !string.IsNullOrEmpty(vendorProduct.itemId) && !dictionary3.ContainsKey(vendorProduct.itemId)) { dictionary3[vendorProduct.itemId] = i + 1; } } } List<VendorTierData> list2 = new List<VendorTierData>(); if (ShowAll) { for (int j = 0; j < num; j++) { if (item.tierDataList[j] != null) { list2.Add(item.tierDataList[j]); } } } else { list2.Add(val2); } HashSet<string> hashSet2 = new HashSet<string>(); foreach (VendorTierData item2 in list2) { if (item2.vendorProducts == null) { continue; } foreach (VendorProductData vendorProduct2 in item2.vendorProducts) { if (vendorProduct2 == null || string.IsNullOrEmpty(vendorProduct2.itemId) || !hashSet2.Add(vendorProduct2.itemId) || !GameApi.IsLocalizedItem(vendorProduct2.itemId)) { continue; } bool flag = true; bool flag2 = true; try { flag = item2.IsSellingProduct(vendorProduct2.itemId); } catch { } try { flag2 = item2.IsBuyingProduct(vendorProduct2.itemId); } catch { } if (!flag && !flag2) { continue; } if (!dictionary3.TryGetValue(vendorProduct2.itemId, out var value2)) { value2 = num; } VendorTradeLine vendorTradeLine = new VendorTradeLine(); vendorTradeLine.ItemId = vendorProduct2.itemId; vendorTradeLine.Name = GameApi.ItemName(vendorProduct2.itemId, isGroup: false); vendorTradeLine.IconId = GameApi.ItemIcon(vendorProduct2.itemId, isGroup: false); vendorTradeLine.Tier = value2; vendorTradeLine.Stock = vendorProduct2.baseCount; vendorTradeLine.Sells = flag; vendorTradeLine.Buys = flag2; VendorTradeLine vendorTradeLine2 = vendorTradeLine; if (value != null) { if (flag) { try { vendorTradeLine2.BuyPrice = value.CurPrice(vendorProduct2.itemId, true, 1); } catch { } } if (flag2) { try { vendorTradeLine2.SellPrice = value.CurPrice(vendorProduct2.itemId, false, 1); } catch { } } } vendorInfo2.Lines.Add(vendorTradeLine2); VendorOffer vendorOffer = new VendorOffer(); vendorOffer.VendorId = ((BalanceBaseObject)item).id; vendorOffer.Name = text; vendorOffer.IconId = iconId; vendorOffer.Tier = value2; vendorOffer.Count = vendorProduct2.baseCount; VendorOffer offer = vendorOffer; if (flag) { Register(SoldBy, vendorProduct2.itemId, offer); } if (flag2) { Register(BoughtBy, vendorProduct2.itemId, offer); } } } vendorInfo2.Lines.Sort((VendorTradeLine a, VendorTradeLine b) => string.Compare(a.Name, b.Name, StringComparison.CurrentCultureIgnoreCase)); Vendors.Add(vendorInfo2); } Vendors.Sort((VendorInfo a, VendorInfo b) => string.Compare(a.Name, b.Name, StringComparison.CurrentCultureIgnoreCase)); } private static void Register(Dictionary<string, List<VendorOffer>> map, string itemId, VendorOffer offer) { if (!map.TryGetValue(itemId, out var value)) { value = (map[itemId] = new List<VendorOffer>()); } if (!value.Any((VendorOffer o) => o.VendorId == offer.VendorId)) { value.Add(offer); } } private static int SafeTier(Vendor v) { try { return v.CurTier; } catch { return 0; } } private static string VendorName(GameBalance gb, VendorDef def) { string text = GameApi.WgoNameOrNull(((BalanceBaseObject)def).id); if (text != null) { return text; } try { foreach (TownBuildingDef townBuildingDef in gb.townBuildingDefs) { if (townBuildingDef != null && !(townBuildingDef.vendorId != ((BalanceBaseObject)def).id)) { string text2 = null; try { text2 = townBuildingDef.GetHeader(); } catch { } if (!string.IsNullOrEmpty(text2) && text2 != ((BalanceBaseObject)townBuildingDef).id && !text2.StartsWith("[")) { return text2; } } } } catch { } return null; } private static string VendorIcon(GameBalance gb, VendorDef def) { string text = null; try { text = Traverse.Create((object)def).Field("icon").GetValue<string>(); } catch { } string[] array = new string[2] { text, ((BalanceBaseObject)def).id }; foreach (string text2 in array) { if (string.IsNullOrEmpty(text2)) { continue; } WGODef val = GameApi.WgoDef(text2); if (val != null) { string text3 = null; try { text3 = Traverse.Create((object)val).Field("portrait").GetValue<string>(); } catch { } if (!string.IsNullOrEmpty(text3) && (Object)(object)GameApi.Sprite(text3) != (Object)null) { return text3; } } } if (!string.IsNullOrEmpty(text) && (Object)(object)GameApi.Sprite(text) != (Object)null) { return text; } try { foreach (TownBuildingDef townBuildingDef in gb.townBuildingDefs) { if (townBuildingDef != null && townBuildingDef.vendorId == ((BalanceBaseObject)def).id && !string.IsNullOrEmpty(townBuildingDef.iconId)) { return townBuildingDef.iconId; } } } catch { } return null; } public List<Recipe> CraftsAt(Entry building) { if (building == null) { return new List<Recipe>(); } List<string> list = new List<string>(); string text = ((building.Building == null) ? null : building.Building.wgoId); if (!string.IsNullOrEmpty(text)) { list.Add(text); string[] array = new string[3] { "_place", "_p", "_pre" }; foreach (string text2 in array) { if (text.EndsWith(text2)) { list.Add(text.Substring(0, text.Length - text2.Length)); } } } foreach (string item in list) { if (CraftedAt.TryGetValue(item, out var value)) { return value; } } foreach (KeyValuePair<string, List<Recipe>> item2 in CraftedAt) { if (GameApi.WgoNameOrNull(item2.Key) == building.Name) { return item2.Value; } } return new List<Recipe>(); } public List<VendorOffer> VendorsFor(string itemId) { List<VendorOffer> value; return (!SoldBy.TryGetValue(itemId, out value)) ? new List<VendorOffer>() : value; } public List<VendorOffer> BuyersFor(string itemId) { List<VendorOffer> value; return (!BoughtBy.TryGetValue(itemId, out value)) ? new List<VendorOffer>() : value; } public VendorInfo VendorById(string id) { foreach (VendorInfo vendor in Vendors) { if (vendor.Id == id) { return vendor; } } return null; } private void CollectWorld() { try { WorldData worldData = MainGame.Instance.GameSave.worldData; foreach (GameSceneData gameSceneData in worldData.gameSceneDataList) { if (gameSceneData == null || gameSceneData.wgoDataList == null) { continue; } foreach (WgoData wgoData in gameSceneData.wgoDataList) { if (wgoData == null || string.IsNullOrEmpty(((ObjectLinkedToDefinition<WGODef>)(object)wgoData).id)) { continue; } _worldWgos.Add(((ObjectLinkedToDefinition<WGODef>)(object)wgoData).id); try { TownBuildingWgoComponent townBuildingWgoComponent = wgoData.TownBuildingWgoComponent; if (townBuildingWgoComponent != null && !string.IsNullOrEmpty(townBuildingWgoComponent.TownBuildingId)) { _builtTownBuildings.Add(townBuildingWgoComponent.TownBuildingId); } } catch { } } } } catch (Exception ex) { Plugin.Log.LogWarning((object)("World scan failed: " + ex.Message)); } } private bool BuildingKnown(BuildingDef b) { if (ShowAll) { return true; } if (_k == null) { return false; } if (_k.lockedBuildings != null && _k.lockedBuildings.Contains(((BalanceBaseObject)b).id)) { return false; } return !b.isNeedsUnlock || (_k.unlockedBuildings != null && _k.unlockedBuildings.Contains(((BalanceBaseObject)b).id)); } private bool CraftKnown(CraftDef c) { if (((CraftDefBase)c).isHidden) { return false; } if (ShowAll) { return true; } if (_k == null) { return false; } if (_k.blackListCrafts != null && _k.blackListCrafts.Contains(((BalanceBaseObject)c).id)) { return false; } if (c.isNeedsUnlock && (_k.unlockedCrafts == null || !_k.unlockedCrafts.Contains(((BalanceBaseObject)c).id))) { return false; } if (((CraftDefBase)c).craftsIn == null || ((CraftDefBase)c).craftsIn.Count == 0) { return false; } foreach (string item in ((CraftDefBase)c).craftsIn) { if (_knownStations.Contains(item)) { return true; } } return false; } public static List<Ingredient> ToIngredients(List<NeedItemData> needs) { List<Ingredient> list = new List<Ingredient>(); if (needs == null) { return list; } foreach (NeedItemData n in needs) { if (n == null || string.IsNullOrEmpty(n.id)) { continue; } bool flag = false; try { flag = n.IsEmpty; } catch { } if (flag) { continue; } int num = 0; try { num = n.GetCount((WgoData)null); } catch { try { num = ((n.count != null) ? n.count.EvaluateInt() : 0); } catch { } } if (num > 0) { bool isGroup = false; try { isGroup = n.IsGroup; } catch { } Ingredient ingredient = list.FirstOrDefault((Ingredient x) => x.Id == n.id); if (ingredient != null) { ingredient.Count += num; continue; } list.Add(new Ingredient { Id = n.id, Count = num, IsGroup = isGroup }); } } return list; } private static void CollectOutputs(CraftDef c, Dictionary<string, int> outs) { CollectOutput(((CraftDefBase)c).outputItems, outs); if (outs.Count != 0) { return; } try { ItemDef val = c.TryGetResultingItemDef(false); if (val != null) { outs[((BalanceBaseObject)val).id] = 1; } } catch { } } private static void CollectOutput(OutputItems o, Dictionary<string, int> outs) { if (o == null) { return; } if (o.chanceOutputItems != null) { foreach (ChanceOutputItem chanceOutputItem in o.chanceOutputItems) { AddOut(chanceOutputItem, outs); } } if (o.groupChanceOutputItems == null) { return; } foreach (GroupChanceOutputItem groupChanceOutputItem in o.groupChanceOutputItems) { if (groupChanceOutputItem == null || groupChanceOutputItem.chanceItems == null) { continue; } foreach (ChanceOutputItem chanceItem in groupChanceOutputItem.chanceItems) { AddOut(chanceItem, outs); } } } private static void AddOut(ChanceOutputItem ci, Dictionary<string, int> outs) { if (ci == null || string.IsNullOrEmpty(ci.id) || GameApi.ItemDef(ci.id) == null) { return; } int num = 1; try { if (ci.count != null) { num = Math.Max(1, ci.count.EvaluateInt()); } } catch { num = 1; } outs[ci.id] = ((!outs.TryGetValue(ci.id, out var value)) ? num : Math.Max(value, num)); } private static string SafeHeader(BuildingDef b) { string text = GameApi.L(((BalanceBaseObject)b).id); if (!string.IsNullOrEmpty(text) && text != ((BalanceBaseObject)b).id) { return text; } text = GameApi.WgoName(b.wgoId); return (!GameApi.HasWgoName(b.wgoId)) ? null : text; } private static string SafeIcon(BuildingDef b) { string text = null; try { text = b.BuildResultIcon; } catch { } if (string.IsNullOrEmpty(text) || (Object)(object)GameApi.Sprite(text) == (Object)null) { text = GameApi.WgoIcon(b.wgoId); } return text; } public void Diagnose(string itemId) { try { StringBuilder stringBuilder = new StringBuilder("Codex diagnose '" + itemId + "':"); foreach (CraftDef craftDef in GameBalance.Me.craftDefs) { if (craftDef == null) { continue; } Dictionary<string, int> dictionary = new Dictionary<string, int>(); CollectOutputs(craftDef, dictionary); if (dictionary.ContainsKey(itemId)) { string text = (((CraftDefBase)craftDef).isHidden ? "hidden" : (CraftKnown(craftDef) ? "shown" : ("not known (needsUnlock=" + craftDef.isNeedsUnlock + ", unlocked=" + (_k != null && _k.unlockedCrafts != null && _k.unlockedCrafts.Contains(((BalanceBaseObject)craftDef).id)) + ")"))); string text2 = ((((CraftDefBase)craftDef).craftsIn != null) ? string.Join(",", ((CraftDefBase)craftDef).craftsIn.Select((string s) => s + ((!_knownStations.Contains(s)) ? "-" : "+") + ((!GameApi.HasWgoName(s)) ? "(noname)" : "")).ToArray()) : ""); string text3 = string.Join(",", (from i in ToIngredients(((CraftDefBase)craftDef).needItems) select i.Id + ((!i.IsGroup) ? "" : "[g]") + ((!GameApi.IsLocalizedNeed(i.Id, i.IsGroup)) ? "(noname)" : "")).ToArray()); stringBuilder.Append("\n craft " + ((BalanceBaseObject)craftDef).id + " -> " + text + " | stations " + text2 + " | in " + text3); } } Plugin.Log.LogInfo((object)stringBuilder.ToString()); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Diagnose failed: " + ex.Message)); } } public string StationIcon(string wgoId) { string text = GameApi.WgoIcon(wgoId); if (text != null && (Object)(object)GameApi.Sprite(text) != (Object)null) { return text; } string text2 = GameApi.WgoNameOrNull(wgoId); if (text2 == null) { return null; } foreach (Entry building in Buildings) { if (building.Name == text2 && !string.IsNullOrEmpty(building.IconId) && (Object)(object)GameApi.Sprite(building.IconId) != (Object)null) { return building.IconId; } } foreach (BuildingDef buildingDef in GameBalance.Me.buildingDefs) { if (buildingDef == null) { continue; } string text3 = GameApi.L(((BalanceBaseObject)buildingDef).id); if (!(text3 != text2)) { string text4 = null; try { text4 = buildingDef.BuildResultIcon; } catch { } if (!string.IsNullOrEmpty(text4) && (Object)(object)GameApi.Sprite(text4) != (Object)null) { return text4; } } } return null; } public List<Recipe> RecipesFor(string itemId) { List<Recipe> value; return (!MadeBy.TryGetValue(itemId, out value)) ? new List<Recipe>() : value; } public List<Recipe> UsesOf(string itemId) { List<Recipe> value; return (!UsedIn.TryGetValue(itemId, out value)) ? new List<Recipe>() : value; } public List<string> DropsOf(string itemId) { List<string> value; return (!DroppedBy.TryGetValue(itemId, out value)) ? new List<string>() : value; } public List<KeyValuePair<string, int>> RawFor(Recipe root, int batches) { Dictionary<string, int> raw = new Dictionary<string, int>(); List<string> list = new List<string>(); foreach (Ingredient input in root.Inputs) { Expand(input.Id, input.IsGroup, input.Count * batches, raw, list, new HashSet<string>(root.Outputs.Keys), 0); } return list.Select((string k) => new KeyValuePair<string, int>(k, raw[k])).ToList(); } private void Expand(string id, bool isGroup, int qty, Dictionary<string, int> raw, List<string> order, HashSet<string> path, int depth) { Recipe recipe = null; if (!isGroup && depth < 10 && !path.Contains(id)) { foreach (Recipe item in RecipesFor(id)) { if (item.IsBuilding || item.Inputs.Any((Ingredient x) => path.Contains(x.Id) || x.Id == id)) { continue; } recipe = item; break; } } if (recipe == null) { if (!raw.TryGetValue(id, out var value)) { order.Add(id); value = 0; } raw[id] = value + qty; return; } if (!recipe.Outputs.TryGetValue(id, out var value2) || value2 <= 0) { value2 = 1; } int num = (qty + value2 - 1) / value2; path.Add(id); foreach (Ingredient input in recipe.Inputs) { Expand(input.Id, input.IsGroup, input.Count * num, raw, order, path, depth + 1); } path.Remove(id); } public bool HasIntermediate(Recipe r) { foreach (Ingredient input in r.Inputs) { if (!input.IsGroup && RecipesFor(input.Id).Any((Recipe x) => !x.IsBuilding)) { return true; } } return false; } } internal static class Framework { private sealed class Entry : Gk2ModBase { private readonly Gk2ModMetadata _meta = new Gk2ModMetadata("gk2.codex", "GK2 Codex", "SaintArchI", "0.10.14", FwText.L("In-game reference book: recipes, uses, raw materials, vendors and what each station can make."), false, false); private readonly IReadOnlyList<Gk2ModDependency> _deps = (IReadOnlyList<Gk2ModDependency>)(object)new Gk2ModDependency[1] { new Gk2ModDependency("ru.superman4eg.gk2.framework", "0.1.0", "0.2.0", false) }; private static readonly string[] PadChoices = new string[29] { "None", "RightStick", "LeftStick", "RightBumper", "RightTrigger", "LeftTrigger", "RightTrigger+RightStick", "RightTrigger+LeftStick", "LeftTrigger+RightStick", "LeftTrigger+LeftStick", "Fold", "Inventory", "ChangeWeapon", "Map", "QuestTree", "TechTree", "Inspirations", "Plant", "StartResurrection", "AlchemyBoost", "SaveImport", "AcceptVendorDeal", "MoveAllItemsFromPlayer", "MoveAllItemsToPlayer", "EndPrefight", "AttackFocus", "FoldAdditionalInfo", "PrevSubTab", "NextSubTab" }; public override Gk2ModMetadata Metadata => _meta; public override IReadOnlyList<Gk2ModDependency> Dependencies => _deps; public override void OnRegister(Gk2ModContext context) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) Gk2Settings settings = context.Settings; settings.AddKeybind("Hotkeys", "ToggleCodex", new KeyboardShortcut((KeyCode)285, Array.Empty<KeyCode>()), FwText.L("Open the codex"), FwText.L("Opens and closes the codex window."), 0); settings.AddKeybind("Hotkeys", "ShowHoveredItem", new KeyboardShortcut((KeyCode)103, Array.Empty<KeyCode>()), FwText.L("Codex for the item under the cursor"), FwText.L("Opens the codex on the item under the mouse (or the gamepad focus) in any inventory, chest, craft or shop window."), 0); settings.AddToggle("General", "InventoryMenu", true, FwText.L("\"Codex\" in the inventory menu"), FwText.L("Adds \"Codex\" to the right-click menu of items in your inventory; it opens the codex on that item."), 1); settings.AddToggle("General", "ShowAll", false, FwText.L("Show all"), FwText.L("Show everything, not only what the loaded save has unlocked."), 1); settings.AddToggle("General", "PauseGame", true, FwText.L("Pause the game"), FwText.L("The game stands still while the codex is open, as it does for its own windows."), 1); settings.AddFloatSlider("General", "TextSize", 0.82f, 0.6f, 1.2f, FwText.L("Text size"), FwText.L("How big the codex draws its contents. 1 is the size of the game's own text; bigger shows less at a time."), 0.02f, 1); settings.AddFloatSlider("General", "WindowSize", 1f, 0.6f, 1f, FwText.L("Window size"), FwText.L("Size of the codex window. 1 fills the screen; Text size changes what's inside."), 0.05f, 1); settings.AddToggle("Tabs", "Items", true, FwText.L("Items tab"), FwText.L("Show the Items tab."), 2); settings.AddToggle("Tabs", "Buildings", true, FwText.L("Buildings tab"), FwText.L("Show the Buildings tab."), 3); settings.AddToggle("Tabs", "Zombies", true, FwText.L("Zombies tab"), FwText.L("Show the Zombies tab: your zombies, their talents and which station fits them."), 4); settings.AddToggle("Tabs", "Alchemy", true, FwText.L("Alchemy tab"), FwText.L("Show the Alchemy tab: only what this save has already discovered."), 5); settings.AddToggle("Tabs", "Graveyard", true, FwText.L("Graveyard tab"), FwText.L("Show the Graveyard tab: your graveyard, church and morgue, and what their quality is made of."), 6); settings.AddToggle("Tabs", "Vendors", true, FwText.L("Vendors tab"), FwText.L("Show the Vendors tab: what each vendor you have met buys and sells."), 7); settings.AddText("Gamepad", "OpenKey", "RightTrigger+RightStick", FwText.L("Open with a gamepad"), FwText.L("Gamepad action that opens and closes the codex, by the game's own name for it (RightStick, Fold, RightTrigger+RightStick for a chord...). The default is R2+R3. None turns it off. Turn Log pad input on to see which actions each button of your pad sends."), 21); settings.AddText("Gamepad", "ShowAllKey", "Fold", FwText.L("Unlocked only / show all"), FwText.L("Gamepad action that switches the codex between what the save has unlocked and everything. One physical button sends several actions at once, so any name from its group works."), 22); settings.AddToggle("Gamepad", "LogInput", false, FwText.L("Log pad input"), FwText.L("Writes every gamepad action to BepInEx/LogOutput.log, so a free button can be found."), 23); } } public const string FrameworkGuid = "ru.superman4eg.gk2.framework"; public static void TryRegister(BaseUnityPlugin plugin) { try { if (AppDomain.CurrentDomain.GetAssemblies().Any((Assembly a) => a.GetName().Name == "GK2.Framework")) { Register(plugin); } } catch (Exception ex) { Plugin.Log.LogInfo((object)("Mod Framework not used: " + ex.Message)); } } [MethodImpl(MethodImplOptions.NoInlining)] private static void Register(BaseUnityPlugin plugin) { FrameworkApi.RegisterMod((IGk2Mod)(object)new Entry(), plugin.Config); Plugin.Log.LogInfo((object)"Registered in GK2 Mod Framework"); } } internal static class FwText { private static readonly string[] Langs = new string[10] { "ru", "de", "fr", "es", "pt", "pl", "tr", "ja", "zh", "ko" }; private static int _idx = -2; private static readonly Dictionary<string, string[]> Table = new Dictionary<string, string[]> { { "Log pad input", new string[10] { "Писать кнопки геймпада в лог", "Gamepad-Eingaben protokollieren", "Journaliser la manette", "Registrar el mando", "Registrar o controle", "Zapisuj pad do logu", "Gamepad girdilerini kaydet", "パッド入力をログに記録", "记录手柄输入", "패드 입력 기록" } }, { "Writes every gamepad action to BepInEx/LogOutput.log, so a free button can be found.", new string[10] { "Пишет каждое действие геймпада в BepInEx/LogOutput.log, чтобы найти свободную кнопку.", "Schreibt jede Gamepad-Aktion in BepInEx/LogOutput.log, damit sich eine freie Taste finden lässt.", "Écrit chaque action de la manette dans BepInEx/LogOutput.log, pour trouver un bouton libre.", "Escribe cada acción del mando en BepInEx/LogOutput.log para encontrar un botón libre.", "Grava cada ação do controle em BepInEx/LogOutput.log, para achar um botão livre.", "Zapisuje każdą akcję pada w BepInEx/LogOutput.log, żeby znaleźć wolny przycisk.", "Boş bir tuş bulmak için her gamepad eylemini BepInEx/LogOutput.log dosyasına yazar.", "空いているボタンを探せるよう、パッドの操作をすべて BepInEx/LogOutput.log に書き出します。", "将手柄的每个动作写入 BepInEx/LogOutput.log,便于找到空闲按键。", "빈 버튼을 찾을 수 있도록 패드의 모든 동작을 BepInEx/LogOutput.log에 기록합니다." } }, { "Unlocked only / show all", new string[10] { "Только открытое / всё", "Nur Freigeschaltetes / alles", "Débloqué seulement / tout", "Solo desbloqueado / todo", "Só desbloqueado / tudo", "Tylko odblokowane / wszystko", "Yalnızca açılanlar / tümü", "解放済みのみ / すべて", "仅已解锁 / 全部", "해금된 것만 / 전부" } }, { "Gamepad action that switches the codex between what the save has unlocked and everything. One physical button sends several actions at once, so any name from its group works.", new string[10] { "Действие геймпада, которое переключает кодекс между открытым в сохранении и всем подряд. Одна кнопка шлёт сразу несколько действий, поэтому подходит любое название из её группы.", "Gamepad-Aktion, die den Codex zwischen dem Freigeschalteten und allem umschaltet. Eine Taste sendet mehrere Aktionen zugleich, daher funktioniert jeder Name aus ihrer Gruppe.", "Action de la manette qui bascule le codex entre ce que la sauvegarde a débloqué et tout. Un bouton envoie plusieurs actions à la fois, donc n'importe quel nom de son groupe convient.", "Acción del mando que alterna el códice entre lo desbloqueado en la partida y todo. Un botón envía varias acciones a la vez, así que sirve cualquier nombre de su grupo.", "Ação do controle que alterna o códice entre o que o save desbloqueou e tudo. Um botão envia várias ações de uma vez, então qualquer nome do grupo serve.", "Akcja pada przełączająca kodeks między tym, co odblokowano w zapisie, a wszystkim. Jeden przycisk wysyła kilka akcji naraz, więc działa każda nazwa z jego grupy.", "Kodeksi kayıtta açılanlar ile her şey arasında değiştiren gamepad eylemi. Bir tuş aynı anda birkaç eylem gönderir; grubundaki herhangi bir ad işe yarar.", "図鑑の表示を「セーブで解放済みのもの」と「すべて」で切り替えるパッドの操作。1つのボタンが複数の操作を送るので、そのグループのどの名前でも動きます。", "在“存档已解锁内容”和“全部内容”之间切换图鉴的手柄动作。一个按键会同时发送多个动作,因此其组内任一名称都可用。", "도감을 세이브에서 해금된 것과 전부 사이에서 전환하는 패드 동작입니다. 버튼 하나가 여러 동작을 동시에 보내므로 그 그룹의 어떤 이름이든 됩니다." } }, { "Open with a gamepad", new string[10] { "Открыть с геймпада", "Mit Gamepad öffnen", "Ouvrir à la manette", "Abrir con el mando", "Abrir pelo controle", "Otwórz padem", "Gamepad ile aç", "パッドで開く", "用手柄打开", "패드로 열기" } }, { "Gamepad action that opens and closes the codex, by the game's own name for it (RightStick, Fold, RightTrigger+RightStick for a chord...). The default is R2+R3. None turns it off. Turn Log pad input on to see which actions each button of your pad sends.", new string[10] { "Действие геймпада, которое открывает и закрывает кодекс, по названию из самой игры (RightStick, Fold, RightTrigger+RightStick для сочетания…). По умолчанию R2+R3, None отключает. Включите «Писать кнопки геймпада в лог», чтобы увидеть, какие действия шлёт каждая кнопка.", "Gamepad-Aktion, die den Codex öffnet und schließt, mit dem spieleigenen Namen (RightStick, Fold, RightTrigger+RightStick für eine Kombination …). Standard ist R2+R3, None schaltet es ab. Schalte „Gamepad-Eingaben protokollieren“ ein, um zu sehen, welche Aktionen jede Taste sendet.", "Action de la manette qui ouvre et ferme le codex, sous le nom que lui donne le jeu (RightStick, Fold, RightTrigger+RightStick pour une combinaison…). Par défaut R2+R3, None la désactive. Activez « Journaliser la manette » pour voir ce qu'envoie chaque bouton.", "Acción del mando que abre y cierra el códice, con el nombre que le da el juego (RightStick, Fold, RightTrigger+RightStick para una combinación…). Por defecto R2+R3; None la desactiva. Activa «Registrar el mando» para ver qué envía cada botón.", "Ação do controle que abre e fecha o códice, pelo nome que o jogo lhe dá (RightStick, Fold, RightTrigger+RightStick para uma combinação…). O padrão é R2+R3; None desativa. Ative «Registrar o controle» para ver o que cada botão envia.", "Akcja pada, która otwiera i zamyka kodeks, pod nazwą z gry (RightStick, Fold, RightTrigger+RightStick dla kombinacji…). Domyślnie R2+R3, None wyłącza. Włącz „Zapisuj pad do logu”, by zobaczyć, co wysyła każdy przycisk.", "Kodeksi açıp kapatan gamepad eylemi, oyunun verdiği adla (RightStick, Fold, kombinasyon için RightTrigger+RightStick…). Varsayılan R2+R3; None kapatır. Her tuşun ne gönderdiğini görmek için «Gamepad girdilerini kaydet»i açın.", "図鑑を開閉するパッドの操作(ゲーム内の名前で指定:RightStick、Fold、組み合わせは RightTrigger+RightStick など)。初期値は R2+R3、None で無効。各ボタンが何を送るかは「パッド入力をログに記録」をオンにすると分かります。", "打开和关闭图鉴的手柄动作,使用游戏自己的名称(RightStick、Fold,组合键如 RightTrigger+RightStick…)。默认为 R2+R3,None 为关闭。打开“记录手柄输入”可查看每个按键发送的动作。", "도감을 열고 닫는 패드 동작으로, 게임에서 쓰는 이름으로 지정합니다(RightStick, Fold, 조합키는 RightTrigger+RightStick 등). 기본값은 R2+R3이며 None은 끕니다. 각 버튼이 보내는 동작은 「패드 입력 기록」을 켜면 볼 수 있습니다." } }, { "Vendors tab", new string[10] { "Вкладка «Торговцы»", "Tab „Händler“", "Onglet « Marchands »", "Pestaña «Comerciantes»", "Aba «Comerciantes»", "Karta „Handlarze”", "«Tüccarlar» sekmesi", "「商人」タブ", "“商人”标签页", "「상인」 탭" } }, { "Show the Vendors tab: what each vendor you have met buys and sells.", new string[10] { "Показывать вкладку «Торговцы»: что покупает и продаёт каждый встреченный торговец.", "Zeigt den Tab „Händler“: was jeder getroffene Händler kauft und verkauft.", "Affiche l'onglet « Marchands » : ce que chaque marchand rencontré achète et vend.", "Muestra la pestaña «Comerciantes»: qué compra y vende cada comerciante que conoces.", "Mostra a aba «Comerciantes»: o que cada comerciante que você conheceu compra e vende.", "Pokazuje kartę „Handlarze”: co kupuje i sprzedaje każdy poznany handlarz.", "«Tüccarlar» sekmesini gösterir: tanıştığın her tüccarın alıp sattıkları.", "「商人」タブを表示:出会った商人ごとの売買品。", "显示“商人”标签页:你遇到的每位商人买卖什么。", "「상인」 탭을 표시합니다: 만난 상인마다 사고파는 물건." } }, { "Graveyard tab", new string[10] { "Вкладка «Кладбище»", "Tab „Friedhof“", "Onglet « Cimetière »", "Pestaña «Cementerio»", "Aba «Cemitério»", "Karta „Cmentarz”", "«Mezarlık» sekmesi", "「墓地」タブ", "“墓地”标签页", "「묘지」 탭" } }, { "Show the Graveyard tab: your graveyard, church and morgue, and what their quality is made of.", new string[10] { "Показывать вкладку «Кладбище»: ваше кладбище, церковь и морг и из чего складывается их качество.", "Zeigt den Tab „Friedhof“: dein Friedhof, die Kirche und die Leichenhalle und woraus ihre Qualität besteht.", "Affiche l'onglet « Cimetière » : votre cimetière, l'église et la morgue, et ce qui fait leur qualité.", "Muestra la pestaña «Cementerio»: tu cementerio, la iglesia y la morgue, y de qué depende su calidad.", "Mostra a aba «Cemitério»: seu cemitério, a igreja e o necrotério, e do que depende a qualidade deles.", "Pokazuje kartę „Cmentarz”: twój cmentarz, kościół i kostnicę oraz z czego wynika ich jakość.", "«Mezarlık» sekmesini gösterir: mezarlığın, kilisen ve morgun ve kalitelerinin neden oluştuğu.", "「墓地」タブを表示:墓地・教会・死体安置所と、その品質の内訳。", "显示“墓地”标签页:你的墓地、教堂和停尸房,以及它们的品质构成。", "「묘지」 탭을 표시합니다: 묘지, 교회, 시체 안치소와 그 품질의 구성." } }, { "Alchemy tab", new string[10] { "Вкладка «Алхимия»", "Tab „Alchemie“", "Onglet « Alchimie »", "Pestaña «Alquimia»", "Aba «Alquimia»", "Karta „Alchemia”", "«Simya» sekmesi", "「錬金術」タブ", "“炼金”标签页", "「연금술」 탭" } }, { "Show the Alchemy tab: only what this save has already discovered.", new string[10] { "Показывать вкладку «Алхимия»: только то, что уже открыто в этом сохранении.", "Zeigt den Tab „Alchemie“: nur, was dieser Spielstand schon entdeckt hat.", "Affiche l'onglet « Alchimie » : seulement ce que cette sauvegarde a déjà découvert.", "Muestra la pestaña «Alquimia»: solo lo que esta partida ya ha descubierto.", "Mostra a aba «Alquimia»: só o que este save já descobriu.", "Pokazuje kartę „Alchemia”: tylko to, co ten zapis już odkrył.", "«Simya» sekmesini gösterir: yalnızca bu kayıtta keşfedilmiş olanlar.", "「錬金術」タブを表示:このセーブで発見済みのものだけ。", "显示“炼金”标签页:仅显示本存档已发现的内容。", "「연금술」 탭을 표시합니다: 이 세이브에서 이미 발견한 것만." } }, { "Zombies tab", new string[10] { "Вкладка «Зомби»", "Tab „Zombies“", "Onglet « Zombies »", "Pestaña «Zombis»", "Aba «Zumbis»", "Karta „Zombie”", "«Zombiler» sekmesi", "「ゾンビ」タブ", "“僵尸”标签页", "「좀비」 탭" } }, { "Show the Zombies tab: your zombies, their talents and which station fits them.", new string[10] { "Показывать вкладку «Зомби»: ваши зомби, их таланты и какая станция им подходит.", "Zeigt den Tab „Zombies“: deine Zombies, ihre Talente und welche Station zu ihnen passt.", "Affiche l'onglet « Zombies » : vos zombies, leurs talents et le poste qui leur convient.", "Muestra la pestaña «Zombis»: tus zombis, sus talentos y qué estación les va.", "Mostra a aba «Zumbis»: seus zumbis, seus talentos e qual estação combina com eles.", "Pokazuje kartę „Zombie”: twoje zombie, ich talenty i które stanowisko do nich pasuje.", "«Zombiler» sekmesini gösterir: zombilerin, yetenekleri ve onlara uyan istasyon.", "「ゾンビ」タブを表示:ゾンビとその才能、向いている作業台。", "显示“僵尸”标签页:你的僵尸、它们的天赋以及适合的工作台。", "「좀비」 탭을 표시합니다: 좀비, 재능, 그리고 어울리는 작업대." } }, { "Buildings tab", new string[10] { "Вкладка «Постройки»", "Tab „Gebäude“", "Onglet « Bâtiments »", "Pestaña «Edificios»", "Aba «Construções»", "Karta „Budynki”", "«Binalar» sekmesi", "「建物」タブ", "“建筑”标签页", "「건물」 탭" } }, { "Show the Buildings tab.", new string[10] { "Показывать вкладку «Постройки».", "Zeigt den Tab „Gebäude“.", "Affiche l'onglet « Bâtiments ».", "Muestra la pestaña «Edificios».", "Mostra a aba «Construções».", "Pokazuje kartę „Budynki”.", "«Binalar» sekmesini gösterir.", "「建物」タブを表示します。", "显示“建筑”标签页。", "「건물」 탭을 표시합니다." } }, { "Items tab", new string[10] { "Вкладка «Предметы»", "Tab „Gegenstände“", "Onglet « Objets »", "Pestaña «Objetos»", "Aba «Itens»", "Karta „Przedmioty”", "«Eşyalar» sekmesi", "「アイテム」タブ", "“物品”标签页", "「아이템」 탭" } }, { "Show the Items tab.", new string[10] { "Показывать вкладку «Предметы».", "Zeigt den Tab „Gegenstände“.", "Affiche l'onglet « Objets ».", "Muestra la pestaña «Objetos».", "Mostra a aba «Itens».", "Pokazuje kartę „Przedmioty”.", "«Eşyalar» sekmesini gösterir.", "「アイテム」タブを表示します。", "显示“物品”标签页。", "「아이템」 탭을 표시합니다." } }, { "Window size", new string[10] { "Размер окна", "Fenstergröße", "Taille de la fenêtre", "Tamaño de la ventana", "Tamanho da janela", "Rozmiar okna", "Pencere boyutu", "ウィンドウの大きさ", "窗口大小", "창 크기" } }, { "Size of the codex window. 1 fills the screen; Text size changes what's inside.", new string[10] { "Размер окна кодекса. 1 — на весь экран; «Размер текста» меняет содержимое.", "Größe des Codex-Fensters. 1 füllt den Bildschirm; „Textgröße“ ändert den Inhalt.", "Taille de la fenêtre du codex. 1 remplit l'écran ; « Taille du texte » change le contenu.", "Tamaño de la ventana del códice. 1 llena la pantalla; «Tamaño del texto» cambia el contenido.", "Tamanho da janela do códice. 1 ocupa a tela; «Tamanho do texto» muda o conteúdo.", "Rozmiar okna kodeksu. 1 wypełnia ekran; „Rozmiar tekstu” zmienia zawartość.", "Kodeks penceresinin boyutu. 1 ekranı doldurur; «Yazı boyutu» içeriği değiştirir.", "図鑑ウィンドウの大きさ。1 で画面いっぱい。中身の大きさは「文字サイズ」で変わります。", "图鉴窗口的大小。1 为铺满屏幕;“文字大小”改变的是内容。", "도감 창의 크기입니다. 1은 화면 가득; 내용은 「글자 크기」로 바꿉니다." } }, { "Text size", new string[10] { "Размер текста", "Textgröße", "Taille du texte", "Tamaño del texto", "Tamanho do texto", "Rozmiar tekstu", "Yazı boyutu", "文字サイズ", "文字大小", "글자 크기" } }, { "How big the codex draws its contents. 1 is the size of the game's own text; bigger shows less at a time.", new string[10] { "Насколько крупно кодекс рисует содержимое. 1 — размер текста самой игры; крупнее — меньше помещается.", "Wie groß der Codex seinen Inhalt zeichnet. 1 ist die Textgröße des Spiels; größer zeigt weniger auf einmal.", "Taille du contenu du codex. 1 correspond au texte du jeu ; plus grand en montre moins à la fois.", "Tamaño del contenido del códice. 1 es el texto del juego; más grande muestra menos a la vez.", "Tamanho do conteúdo do códice. 1 é o texto do jogo; maior mostra menos de cada vez.", "Jak duża jest zawartość kodeksu. 1 to rozmiar tekstu gry; większy mieści mniej naraz.", "Kodeks içeriğinin ne kadar büyük çizileceği. 1 oyunun kendi yazı boyutudur; büyüdükçe bir seferde daha az görünür.", "図鑑の中身の大きさ。1 でゲームの文字と同じ。大きいほど一度に見える量は減ります。", "图鉴内容的大小。1 为游戏自身文字大小;越大一次显示得越少。", "도감 내용의 크기입니다. 1은 게임 글자 크기이며, 클수록 한 번에 보이는 양이 줄어듭니다." } }, { "Pause the game", new string[10] { "Ставить игру на паузу", "Spiel pausieren", "Mettre le jeu en pause", "Pausar el juego", "Pausar o jogo", "Pauzuj grę", "Oyunu duraklat", "ゲームを一時停止", "暂停游戏", "게임 일시정지" } }, { "The game stands still while the codex is open, as it does for its own windows.", new string[10] { "Пока открыт кодекс, игра стоит — как в её собственных окнах.", "Das Spiel steht still, solange der Codex offen ist, wie bei seinen eigenen Fenstern.", "Le jeu s'arrête tant que le codex est ouvert, comme pour ses propres fenêtres.", "El juego se detiene mientras el códice está abierto, como con sus propias ventanas.", "O jogo fica parado enquanto o códice está aberto, como nas janelas dele.", "Gra stoi, gdy kodeks jest otwarty, jak przy jej własnych oknach.", "Kodeks açıkken oyun, kendi pencerelerinde olduğu gibi durur.", "図鑑を開いている間、ゲーム本来のウィンドウと同じく時間が止まります。", "图鉴打开时游戏暂停,与游戏自身窗口一样。", "도감이 열려 있는 동안 게임 창처럼 게임이 멈춥니다." } }, { "Show all", new string[10] { "Показывать всё", "Alles zeigen", "Tout afficher", "Mostrar todo", "Mostrar tudo", "Pokaż wszystko", "Tümünü göster", "すべて表示", "显示全部", "전부 보기" } }, { "Show everything, not only what the loaded save has unlocked.", new string[10] { "Показывать всё, а не только то, что открыто в загруженном сохранении.", "Alles zeigen, nicht nur, was der geladene Spielstand freigeschaltet hat.", "Tout afficher, pas seulement ce que la sauvegarde chargée a débloqué.", "Mostrarlo todo, no solo lo que la partida cargada ha desbloqueado.", "Mostrar tudo, não só o que o save carregado desbloqueou.", "Pokazuj wszystko, nie tylko to, co odblokowano w wczytanym zapisie.", "Yalnızca yüklü kayıtta açılanları değil, her şeyi göster.", "読み込んだセーブで解放済みのものだけでなく、すべて表示します。", "显示全部内容,而不只是已加载存档解锁的部分。", "불러온 세이브에서 해금된 것만이 아니라 전부 보여줍니다." } }, { "Codex for the item under the cursor", new string[10] { "Справочник для предмета под курсором", "Handbuch für den Gegenstand unter dem Zeiger", "Codex pour l'objet sous le curseur", "Códice del objeto bajo el cursor", "Códice do item sob o cursor", "Kodeks dla przedmiotu pod kursorem", "İmleçteki eşya için rehber", "カーソル下のアイテムを図鑑で", "光标下物品的图鉴", "커서 아래 아이템 도감" } }, { "Opens the codex on the item under the mouse (or the gamepad focus) in any inventory, chest, craft or shop window.", new string[10] { "Открывает справочник на предмете под мышью (или в фокусе геймпада) в любом инвентаре, сундуке, окне крафта или магазина.", "Öffnet das Handbuch beim Gegenstand unter der Maus (oder im Gamepad-Fokus) in jedem Inventar, jeder Truhe, jedem Herstellungs- oder Ladenfenster.", "Ouvre le codex sur l'objet sous la souris (ou le focus de la manette) dans tout inventaire, coffre, fenêtre d'artisanat ou de boutique.", "Abre el códice en el objeto bajo el ratón (o el foco del mando) en cualquier inventario, cofre, ventana de fabricación o tienda.", "Abre o códice no item sob o mouse (ou o foco do controle) em qualquer inventário, baú, janela de criação ou loja.", "Otwiera kodeks na przedmiocie pod myszą (lub w fokusie pada) w każdym ekwipunku, skrzyni, oknie wytwarzania lub sklepu.", "Herhangi bir envanter, sandık, üretim ya da dükkân penceresinde farenin (veya gamepad odağının) altındaki eşyada rehberi açar.", "インベントリ・箱・製作・店のどの画面でも、マウス(またはゲームパッドのフォーカス)下のアイテムで図鑑を開きます。", "在任何背包、箱子、制作或商店窗口中,为鼠标(或手柄焦点)下的物品打开图鉴。", "인벤토리·상자·제작·상점 창 어디서든 마우스(또는 게임패드 포커스) 아래 아이템의 도감을 엽니다." } }, { "\"Codex\" in the inventory menu", new string[10] { "«Справочник» в меню инвентаря", "\"Handbuch\" im Inventarmenü", "« Codex » dans le menu de l'inventaire", "\"Códice\" en el menú del inventario", "\"Códice\" no menu do inventário", "\"Kodeks\" w menu ekwipunku", "Envanter menüsünde \"Rehber\"", "インベントリのメニューに「図鑑」", "背包菜单中的“图鉴”", "인벤토리 메뉴에 \"도감\"" } }, { "Adds \"Codex\" to the right-click menu of items in your inventory; it opens the codex on that item.", new string[10] { "Добавляет «Справочник» в меню предмета по правому клику в инвентаре; открывает справочник на этом предмете.", "Fügt \"Handbuch\" zum Rechtsklickmenü der Gegenstände im Inventar hinzu; es öffnet das Handbuch bei diesem Gegenstand.", "Ajoute « Codex » au menu clic droit des objets de l'inventaire ; il ouvre le codex sur cet objet.", "Añade \"Códice\" al menú de clic derecho de los objetos del inventario; abre el códice en ese objeto.", "Adiciona \"Códice\" ao menu do botão direito dos itens do inventário; abre o códice nesse item.", "Dodaje \"Kodeks\" do menu prawego kliknięcia przedmiotów w ekwipunku; otwiera kodeks na tym przedmiocie.", "Envanterdeki eşyaların sağ tık menüsüne \"Rehber\" ekler; rehberi o eşyada açar.", "インベントリのアイテムの右クリックメニューに「図鑑」を追加し、そのアイテムで図鑑を開きます。", "在背包物品的右键菜单中加入“图鉴”,可直接打开该物品的图鉴。", "인벤토리 아이템의 오른쪽 클릭 메뉴에 \"도감\"을 추가해 그 아이템의 도감을 엽니다." } }, { "Open the codex", new string[10] { "Открыть кодекс", "Codex öffnen", "Ouvrir le codex", "Abrir el códice", "Abrir o códice", "Otwórz kodeks", "Kodeksi aç", "図鑑を開く", "打开图鉴", "도감 열기" } }, { "Opens and closes the codex window.", new string[10] { "Открывает и закрывает окно кодекса.", "Öffnet und schließt das Codex-Fenster.", "Ouvre et ferme la fenêtre du codex.", "Abre y cierra la ventana del códice.", "Abre e fecha a janela do códice.", "Otwiera i zamyka okno kodeksu.", "Kodeks penceresini açar ve kapatır.", "図鑑ウィンドウを開閉します。", "打开和关闭图鉴窗口。", "도감 창을 열고 닫습니다." } }, { "In-game reference book: recipes, uses, raw materials, vendors and what each station can make.", new string[10] { "Справочник прямо в игре: рецепты, применение, сырьё, торговцы и что умеет делать каждая станция.", "Nachschlagewerk im Spiel: Rezepte, Verwendungen, Rohstoffe, Händler und was jede Station herstellen kann.", "Ouvrage de référence en jeu : recettes, usages, matières premières, marchands et ce que chaque poste peut fabriquer.", "Libro de consulta en el juego: recetas, usos, materias primas, comerciantes y qué puede hacer cada estación.", "Livro de consulta no jogo: receitas, usos, matérias-primas, comerciantes e o que cada estação pode fazer.", "Podręcznik w grze: przepisy, zastosowania, surowce, handlarze i co może zrobić każde stanowisko.", "Oyun içi başvuru kitabı: tarifler, kullanım yerleri, hammaddeler, tüccarlar ve her istasyonun neler yapabildiği.", "ゲーム内の図鑑:レシピ、用途、素材、商人、各作業台で作れるもの。", "游戏内参考手册:配方、用途、原料、商人以及每个工作台能制作什么。", "게임 속 참고서: 레시피, 용도, 재료, 상인, 그리고 작업대마다 만들 수 있는 것." } } }; private static int Index { get { if (_idx != -2) { return _idx; } string text = null; try { text = FrameworkLanguage(); } catch { } text = (text ?? "en").ToLowerInvariant(); _idx = -1; for (int i = 0; i < Langs.Length; i++) { if (text.StartsWith(Langs[i])) { _idx = i; break; } } return _idx; } } [MethodImpl(MethodImplOptions.NoInlining)] private static string FrameworkLanguage() { return FrameworkLocalization.CurrentLanguage; } public static string L(string en) { int index = Index; if (index < 0 || en == null || !Table.TryGetValue(en, out var value) || index >= value.Length || string.IsNullOrEmpty(value[index])) { return en; } return value[index]; } } internal static class GameApi { private static readonly Dictionary<string, List<ItemDef>> _groupCache = new Dictionary<string, List<ItemDef>>(); private static readonly HashSet<string> NotStorage = new HashSet<string> { "survey_wgo", "town_supplies", "well_garden_1", "well_garden_2" }; private static Dictionary<string, int> _stored; private static float _storedAt = -100f; private static readonly Dictionary<string, string> _wgoName = new Dictionary<string, string>(); private static readonly Dictionary<string, string> _wgoIcon = new Dictionary<string, string>(); private static readonly Dictionary<string, string> _techIcons = new Dictionary<string, string>(); private static readonly Dictionary<string, string> _runeIcons = new Dictionary<string, string>(); public static bool InGame() { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Invalid comparison between Unknown and I4 try { MainGame instance = MainGame.Instance; return (Object)(object)instance != (Object)null && (int)instance.gameState == 1 && MainGame.PlayerData != null; } catch { return false; } } public static string SlotKey() { try { string text = ((!((Object)(object)MainGame.Instance != (Object)null)) ? null : MainGame.Instance.SaveSlotData)?.slotName; if (string.IsNullOrEmpty(text)) { return null; } return Regex.Replace(text, "_backup_\\d+$", ""); } catch { return null; } } public static string Lang() { try { return LLBase.CurrentLang ?? ""; } catch { return ""; } } public static bool IsRussian() { try { return LLBase.CurrentLang?.StartsWith("ru") ?? false; } catch { return false; } } public static string L(string key) { try { return (!LLBase.HasL(key)) ? null : LLBase.L(key); } catch { return null; } } public static WGODef WgoDef(string id) { try { return ((GameBalanceBase)GameBalance.Me).GetDataOrNull<WGODef>(id); } catch { return null; } } public static ItemDef ItemDef(string id) { try { return ((GameBalanceBase)GameBalance.Me).GetDataOrNull<ItemDef>(id); } catch { return null; } } public static string ItemName(string id, bool isGroup) { if (isGroup) { string text = GroupName(id); if (text != null) { return text; } } ItemDef val = ((!isGroup) ? ItemDef(id) : null); if (val != null) { try { string header = val.GetHeader(); if (!string.IsNullOrEmpty(header)) { return header; } } catch { } } return L(id) ?? id; } public static string ItemIcon(string id, bool isGroup) { if (isGroup) { List<ItemDef> list = GroupDefs(id); return (list.Count <= 0) ? null : list[0].iconId; } return ItemDef(id)?.iconId; } public static Sprite Sprite(string iconId) { if (string.IsNullOrEmpty(iconId)) { return null; } try { return LazySingletonSO<EasySpritesCollection>.Instance.GetSprite(iconId, (string)null); } catch { return null; } } public static List<ItemDef> GroupDefs(string groupNeedId) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Expected O, but got Unknown if (_groupCache.TryGetValue(groupNeedId, out var value)) { return value; } value = new List<ItemDef>(); try { NeedItemData val = new NeedItemData(groupNeedId, 1); List<ItemDef> list = default(List<ItemDef>); if (val.TryGetGroupItemDefs(ref list) && list != null) { value = list; } } catch { } _groupCache[groupNeedId] = value; return value; } public static int StockCount(string id, bool isGroup) { int num = BackpackCount(id, isGroup); Dictionary<string, int> dictionary = StoredSnapshot(); if (!isGroup) { if (dictionary.TryGetValue(id, out var value)) { num += value; } return num; } foreach (ItemDef item in GroupDefs(id)) { if (item != null && dictionary.TryGetValue(((BalanceBaseObject)item).id, out var value2)) { num += value2; } } return num; } private static Dictionary<string, int> StoredSnapshot() { if (_stored != null && Time.unscaledTime - _storedAt < 2f) { return _stored; } Dictionary<string, int> dictionary = new Dictionary<string, int>(); try { WorldData worldData = MainGame.Instance.GameSave.worldData; foreach (GameSceneData gameSceneData in worldData.gameSceneDataList) { if (gameSceneData == null || gameSceneData.wgoDataList == null) { continue; } foreach (WgoData wgoData in gameSceneData.wgoDataList) { if (wgoData == null || string.IsNullOrEmpty(((ObjectLinkedToDefinition<WGODef>)(object)wgoData).id) || NotStorage.Contains(((ObjectLinkedToDefinition<WGODef>)(object)wgoData).id)) { continue; } WGODef val = null; try { val = ((ObjectLinkedToDefinition<WGODef>)(object)wgoData).Definition; } catch { } if (val != null && val.OpenInMultiInventory) { Inventory val2 = null; try { val2 = wgoData.Inventory; } catch { } if (val2 != null && val2.Data != null) { AddStored(val2.Data.Inventory, dictionary, 0); } } } } } catch (Exception ex) { Plugin.LogThrottled("Storage scan failed: " + ex.Message); } _stored = dictionary; _storedAt = Time.unscaledTime; return dictionary; } public static Dictionary<string, int> OwnedAll() { Dictionary<string, int> dictionary = new Dictionary<string, int>(StoredSnapshot()); try { Item data = MainGame.PlayerData.Inventory.Data; if (data != null) { AddStored(data.Inventory, dictionary, 0); } } catch { } return dictionary; } private static void AddStored(List<Item> items, Dictionary<string, int> map, int depth) { if (items == null) { return; } foreach (Item item in items) { if (item == null || item.IsEmpty) { continue; } string text = null; try { text = ((((ObjectLinkedToDefinition<ItemDef>)(object)item).Definition == null) ? null : ((BalanceBaseObject)((ObjectLinkedToDefinition<ItemDef>)(object)item).Definition).id); } catch { } if (!string.IsNullOrEmpty(text) && item.Count > 0) { map[text] = (map.TryGetValue(text, out var value) ? value : 0) + item.Count; } if (depth < 3 && item.IsBag) { try { AddStored(item.Inventory, map, depth + 1); } catch { } } } } public static int BackpackCount(string id, bool isGroup) { try { Item data = MainGame.PlayerData.Inventory.Data; if (data == null) { return 0; } if (!isGroup) { return data.GetTotalCountInInventory(id, (Item)null, false); } int num = 0; foreach (ItemDef item in GroupDefs(id)) { num += data.GetTotalCountInInventory(((BalanceBaseObject)item).id, (Item)null, false); } return num; } catch { return 0; } } public static string WgoNameOrNull(string wgoId) { if (string.IsNullOrEmpty(wgoId)) { return null; } if (_wgoName.TryGetValue(wgoId, out var value)) { return value; } value = Good(wgoId, L(wgoId)); if (value == null) { try { foreach (BuildingDef buildingDef in GameBalance.Me.buildingDefs) { if (buildingDef != null && buildingDef.wgoId == wgoId) { value = Good(((BalanceBaseObject)buildingDef).id, L(((BalanceBaseObject)buildingDef).id)); if (value != null) { break; } } } } catch { } } if (value == null) { string text = Regex.Replace(wgoId, "(_\\d+|_[a-z])$", ""); if (text != wgoId) { value = Good(text, L(text)); } } if (value == null) { try { WGODef dataOrNull = ((GameBalanceBase)GameBalance.Me).GetDataOrNull<WGODef>(wgoId); if (dataOrNull != null && !string.IsNullOrEmpty(dataOrNull.wgoGroup)) { value = Good(dataOrNull.wgoGroup, L(dataOrNull.wgoGroup)); } } catch { } } if (value != null && wgoId.StartsWith("test")) { value = null; } _wgoName[wgoId] = value; return value; } private static string Good(string key, string value) { if (string.IsNullOrEmpty(value) || value == key || value.StartsWith("[")) { return null; } return value; } public static bool HasWgoName(string wgoId) { return WgoNameOrNull(wgoId) != null; } public static string WgoName(string wgoId) { return WgoNameOrNull(wgoId) ?? wgoId ?? ""; } public static string GroupName(string groupId) { string text = Good(groupId, L(groupId)); if (text != null) { return text; } string text2 = null; bool flag = true; foreach (ItemDef item in GroupDefs(groupId)) { string value = null; try { value = item.GetHeader(); } catch { } value = Good(((BalanceBaseObject)item).id, value); if (value != null) { if (text2 == null) { text2 = value; } else if (value != text2) { flag = false; } } } if (text2 == null) { return null; } return (!flag) ? (text2 + "…") : text2; } public static bool IsLocalizedNeed(string id, bool isGroup) { return (!isGroup) ? IsLocalizedItem(id) : (GroupName(id) != null); } public static bool IsLocalizedItem(string id) { if (string.IsNullOrEmpty(id) || id.StartsWith("test")) { return false; } ItemDef val = ItemDef(id); if (val == null) { return false; } string value = null; try { value = val.GetHeader(); } catch { } return Good(id, value) != null; } public static string WgoIcon(string wgoId) { if (string.IsNullOrEmpty(wgoId)) { return null; } if (_wgoIcon.TryGetValue(wgoId, out var value)) { return value; } value = null; try { foreach (BuildingDef buildingDef in GameBalance.Me.buildingDefs) { if (buildingDef != null && buildingDef.wgoId == wgoId) { value = buildingDef.BuildResultIcon; break; } } if (string.IsNullOrEmpty(value)) { WGODef dataOrNull = ((GameBalanceBase)GameBalance.Me).GetDataOrNull<WGODef>(wgoId); if (dataOrNull != null && !string.IsNullOrEmpty(dataOrNull.craftIconId)) { value = dataOrNull.craftIconId; } } if (string.IsNullOrEmpty(value) && (Object)(object)Sprite("i_b_" + wgoId) != (Object)null) { value = "i_b_" + wgoId; } } catch { } _wgoIcon[wgoId] = value; return value; } public static string ItemDescription(ItemDef def) { if (def == null) { return null; } try { string description = def.GetDescription(); if (string.IsNullOrEmpty(description) || description.EndsWith("_d")) { return null; } return description; } catch { return null; } } public static string TechPoints(int red, int green, int blue, string fallbackRed, string fallbackGreen, string fallbackBlue) { List<string> list = new List<string>(); if (red > 0) { list.Add(Tech(red, "red", fallbackRed)); } if (green > 0) { list.Add(Tech(green, "green", fallbackGreen)); } if (blue > 0) { list.Add(Tech(blue, "blue", fallbackBlue)); } return string.Join(" ", list.ToArray()); } private static string Tech(int count, string colour, string word) { string text = TechIcon(colour); return (text.Length <= 0) ? (count + " " + word) : (count + text); } public static string TechIcon(string colour) { if (_techIcons.TryGetValue(colour, out var value)) { return value; } value = ""; try { value = UIExtensions.FontIcon("tech_" + colour) ?? ""; } catch (Exception ex) { Plugin.LogThrottled("Tech icon " + colour + ": " + ex.Message); } _techIcons[colour] = value; return value; } public static string RuneIcon(string colour) { if (_runeIcons.TryGetValue(colour, out var value)) { return value; } value = ""; try { value = UIExtensions.FontIcon("rune_" + colour) ?? ""; } catch (Exception ex) { Plugin.LogThrottled("Rune icon " + colour + ": " + ex.Message); } _runeIcons[colour] = value; return value; } public static string Runes(int red, int green, int blue, string fallbackRed, string fallbackGreen, string fallbackBlue) { List<string> list = new List<string>(); if (red > 0) { list.Add(Rune(red, "r", fallbackRed)); } if (green > 0) { list.Add(Rune(green, "g", fallbackGreen)); } if (blue > 0) { list.Add(Rune(blue, "b", fallbackBlue)); } return string.Join(" ", list.ToArray()); } private static string Rune(int count, string colour, string word) { string text = RuneIcon(colour); return (text.Length <= 0) ? (count + " " + word) : (count + text); } public static string Money(int value) { if (value <= 0) { return ""; } try { string text = Trading.FormatMoney(value, false, " ", (GameResIconType)null); if (!string.IsNullOrEmpty(text)) { return text; } } catch { } return value.ToString(); } public static void ResetCaches() { _groupCache.Clear(); _wgoIcon.Clear(); _wgoName.Clear(); _techIcons.Clear(); _runeIcons.Clear(); } } internal static class GamePause { private static bool _ours; private static MethodInfo _pause; private static MethodInfo _unpause; public static void Acquire() { //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Invalid comparison between Unknown and I4 if (_ours) { return; } Plugin instance = Plugin.Instance; if ((Object)(object)instance == (Object)null || instance.PauseGame == null || !instance.PauseGame.Value) { return; } try { MainGame instance2 = MainGame.Instance; if (!((Object)(object)instance2 == (Object)null) && (int)instance2.gameState == 1 && !MainGame.IsGamePaused) { if (_pause == null) { _pause = AccessTools.Method(typeof(MainGame), "PauseGame", (Type[])null, (Type[])null); } if (_pause == null) { Plugin.Log.LogWarning((object)"Pause: MainGame.PauseGame not found"); return; } _pause.Invoke(instance2, null); _ours = true; } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Pause failed: " + ex.Message)); } } public static void Release() { if (!_ours) { return; } _ours = false; try { if (LazyWindowsStackController.HasAnyModalWindowOpened) { return; } MainGame instance = MainGame.Instance; if (!((Object)(object)instance == (Object)null) && MainGame.IsGamePaused) { if (_unpause == null) { _unpause = AccessTools.Method(typeof(MainGame), "UnpauseGame", (Type[])null, (Type[])null); } if (_unpause == null) { Plugin.Log.LogWarning((object)"Pause: MainGame.UnpauseGame not found"); } else { _unpause.Invoke(instance, null); } } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Unpause failed: " + ex.Message)); } } } internal enum GraveSlot { Top, Bot } internal enum GraveWhy { Decor, NoDecor, Cap, Empty } internal sealed class GraveDecor { public GraveSlot Slot; public string ItemId; public string Name; public string IconId; public int Quality; } internal sealed class BodyPart { public string Id; public string Name; public string IconId; public int Count; public int Red; public int White; } internal sealed class GraveInfo { public int Number; public Vector2 Pos; public float Total; public bool Empty; public GraveDecor Top; public GraveDecor Bot; public int Other; public bool HasBody; public string BodyName; public int Red; public int White; public readonly List<BodyPart> Parts = new List<BodyPart>(); public string AreaName; public bool Counted = true; public GraveWhy Why; public int Gain; public ItemDef BestTop; public ItemDef BestBot; public int Best; public int DecorSum => ((Top != null) ? Top.Quality : 0) + ((Bot != null) ? Bot.Quality : 0); public int Calc => Graveyard.Score(DecorSum + Other, Red, White); } internal sealed class DecorItem { public string Id; public string Name; public string IconId; public int RedMin; public int RedMax; public int WhiteMin; public int WhiteMax; public int Variants = 1; } internal sealed class ZoneObj { public string Id; public string Name; public string IconId; public string Family; public float Quality; public Vector2 Pos; public bool Counted; } internal sealed class AreaInfo { public string Id; public string Name; public Rect Rect; public float Sum; public int Graves; } internal sealed class ZoneGoal { public string Zone; public float Level; public int Record; public readonly List<int> Miles = new List<int>(); public int Next; public int Prev; public string NextName; public int NextPrice; public int NextExp; public string Talent; public bool Hidden; public string ReadyName; public int ReadyPrice; public int QuestNeed; } internal sealed class Part { public string Name; public string IconId; public int Count; public float Sum; public bool Dim; } internal enum IdeaKind { GraveDecor, Upgrade, Replace, New, Area } internal sealed class Idea { public IdeaKind Kind; public string Name; public string Note; public string IconId; public int Gain = -1; public int Count = 1; public BuildingDef Building; } internal sealed class ZoneView { public ZoneGoal Goal; public HashSet<SGuid> CountedIds; public readonly List<ZoneObj> Objects = new List<ZoneObj>(); public readonly List<Part> Parts = new List<Part>(); public readonly List<Idea> Ideas = new List<Idea>(); public float Additional; public string WorldId; } internal sealed class GraveyardInfo { public ZoneView Yard; public ZoneView Church; public readonly List<GraveInfo> Graves = new List<GraveInfo>(); public readonly List<AreaInfo> Areas = new List<AreaInfo>(); public readonly List<string> AreasToBuild = new List<string>(); public readonly List<DecorItem> Catalogue = new List<DecorItem>(); public readonly List<ItemDef> DecorParts = new List<ItemDef>(); public Rect MapBounds; public bool HasChurch; public Vector2 ChurchDoor; public int OutsideCount; public float OutsideSum; public int GainTotal => Graves.Where((GraveInfo g) => g.Counted).Sum((GraveInfo g) => g.Gain); public int Raisable => Graves.Count((GraveInfo g) => g.Counted && g.Gain > 0); public int Capped => Graves.Count((GraveInfo g) => g.Counted && g.Why == GraveWhy.Cap); public int WithRed => Graves.Count((GraveInfo g) => g.Counted && g.Red > 0 && !g.Empty); public int EmptyCount => Graves.Count((GraveInfo g) => g.Counted && g.Empty); } internal static class Graveyard { private sealed class Tier { public BuildingDef B; public string Wgo; public int Q; } public const string GraveId = "grave_ground"; public const string EmptyGraveId = "grave_empty"; private const string TopPrefix = "grave_top_"; private const string BotPrefix = "grave_bot_"; private const string ModulePrefix = "graveyard_module_"; private const string BodyGroup = "body"; private const float CellW = 0.96f; private const float CellH = 1.8f; private static Dictionary<string, List<ItemDef>> _slots; public const float GraveCenterShift = 0.39999998f; public const float ChurchW = 7f; public const float ChurchD = 5f; public static int Score(int decor, int red, int white) { int num = decor - Mathf.Clamp(red, 0, 999); return Mathf.Clamp(num, -999, Mathf.Clamp(white, 0, 999)); } public static GraveyardInfo Collect(CodexIndex index) { GraveyardInfo graveyardInfo = new GraveyardInfo(); WorldZoneData val = Zone("graveyard"); if (val != null) { graveyardInfo.Yard = ReadZone(val, "graveyard", areas: true); ReadAreas(val, graveyardInfo); ReadGraves(val, graveyardInfo, index); graveyardInfo.Yard.Ideas.AddRange(Ideas("builder_graveyard", graveyardInfo.Yard, index, graveyardInfo)); FillParts(graveyardInfo.Yard, graveyardInfo); Bounds(graveyardInfo); ChurchSpot(graveyardInfo); if (graveyardInfo.HasChurch) { Bounds(graveyardInfo); } } WorldZoneData val2 = Zone("church"); if (val2 != null) { graveyardInfo.Church = ReadZone(val2, "church", areas: false); graveyardInfo.Church.Ideas.AddRange(Ideas("builder_church", graveyardInfo.Church, index, null)); FillParts(graveyardInfo.Church, null); } BuildCatalogue(graveyardInfo, index); return graveyardInfo; } private static ZoneView ReadZone(WorldZoneData zone, string id, bool areas) { //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Unknown result type (might be due to invalid IL or missing references) //IL_0140: Unknown result type (might be due to invalid IL or missing references) //IL_0145: Unknown result type (might be due to invalid IL or missing references) ZoneView zoneView = new ZoneView(); zoneView.Goal = Goal(zone, id); ZoneView zoneView2 = zoneView; try { zoneView2.Additional = zone.additionalQuality; } catch { } HashSet<SGuid> hashSet = null; if (areas) { hashSet = new HashSet<SGuid>(); try { foreach (SGuid customQualityWgoData in zone.customQualityWgoDataList) { hashSet.Add(customQualityWgoData); } } catch { } } zoneView2.CountedIds = hashSet; try { WorldData worldData = MainGame.Instance.GameSave.worldData; foreach (SGuid wgoData2 in zone.wgoDataList) { WgoData wgoData = worldData.GetWgoData(wgoData2); if (wgoData == null || string.IsNullOrEmpty(((ObjectLinkedToDefinition<WGODef>)(object)wgoData).id)) { continue; } ZoneObj zoneObj = new ZoneObj(); zoneObj.Id = ((ObjectLinkedToDefinition<WGODef>)(object)wgoData).id; zoneObj.Family = Family(((ObjectLinkedToDefinition<WGODef>)(object)wgoData).id); ZoneObj zoneObj2 = zoneObj; try { zoneObj2.Quality = wgoData.Quality; } catch { } try { Vector3 position = wgoData.Position; zoneObj2.Pos = new Vector2(position.x, position.z); } catch { } if (zoneView2.WorldId == null) { try { zoneView2.WorldId = wgoData.WorldId; } catch { } } zoneObj2.Counted = hashSet?.Contains(wgoData2) ?? true; zoneObj2.Name = GameApi.WgoName(((ObjectLinkedToDefinition<WGODef>)(object)wgoData).id); zoneObj2.IconId = GameApi.WgoIcon(((ObjectLinkedToDefinition<WGODef>)(object)wgoData).id); zoneView2.Objects.Add(zoneObj2); } } catch (Exception ex) { Plugin.LogThrottled("Zone " + id + ": " + ex.Message); } return zoneView2; } private static ZoneGoal Goal(WorldZoneData zone, string id) { ZoneGoal zoneGoal = new ZoneGoal(); zoneGoal.Zone = id; ZoneGoal zoneGoal2 = zoneGoal; try { zoneGoal2.Level = zone.GetTotalQuality(); } catch { } zoneGoal2.Record = Mathf.Max(Int(zone, "maxReachedQuality"), Mathf.FloorToInt(zoneGoal2.Level)); string text = "insp_" + id + "_quality"; List<InspirationDef> list = new List<InspirationDef>(); try { foreach (InspirationDef inspirationDef in GameBalance.Me.inspirationDefs) { if (inspirationDef != null && inspirationDef.idWithoutLvl == text) { list.Add(inspirationDef); } } } catch (Exception ex) { Plugin.LogThrottled("Inspirations: " + ex.Message); } list.Sort((InspirationDef a, InspirationDef b) => a.lvl.CompareTo(b.lvl)); foreach (InspirationDef item in list) { zoneGoal2.Miles.Add(item.completionGoalValue); } foreach (InspirationDef item2 in list) { if (item2.completionGoalValue <= zoneGoal2.Record) { zoneGoal2.Prev = item2.completionGoalValue; continue; } zoneGoal2.Next = item2.completionGoalValue; zoneGoal2.NextName = GameApi.L(((BalanceBaseObject)item2).id); zoneGoal2.NextPrice = item2.completionPrice; zoneGoal2.NextExp = item2.completionExp; zoneGoal2.Talent = item2.talentId; break; } if (zoneGoal2.Talent == null && list.Count > 0) { zoneGoal2.Talent = list[0].talentId; } try { object value = Traverse.Create(typeof(TalentSystemCache)).Property("Instance", (object[])null).GetValue(); IDictionary dictionary = ((value == null) ? null : (Traverse.Create(value).Field("inspirations").GetValue() as IDictionary)); object obj2 = ((dictionary == null || !dictionary.Contains(text)) ? null : dictionary[text]); if (obj2 != null) { Traverse val = Traverse.Create(obj2); bool flag = false; try { flag = val.Property("IsHidden", (object[])null).GetValue<bool>(); } catch { } zoneGoal2.Hidden = flag; bool value2 = val.Field("isAllLevelsBought").GetValue<bool>(); bool value3 = val.Property("IsCompleted", (object[])null).GetValue<bool>(); int lvl = val.Field("curLevel").GetValue<int>(); if (!flag && !value2 && value3) { InspirationDef val2 = ((IEnumerable<InspirationDef>)list).FirstOrDefault((Func<InspirationDef, bool>)((InspirationDef x) => x.lvl == lvl)); if (val2 != null) { zoneGoal2.ReadyName = GameApi.L(((BalanceBaseObject)val2).id); zoneGoal2.ReadyPrice = val2.completionPrice; } } } } catch { } zoneGoal2.QuestNeed = QuestNeed(id, zoneGoal2.Level); return zoneGoal2; } private static int QuestNeed(string zone, float level) { try { QuestSystemData questSystemData = MainGame.Instance.GameSave.questSystemData; int num = 0; foreach (QuestDef questDef in GameBalance.Me.questDefs) { if (questDef == null || !(Traverse.Create((object)questDef).Field("finishCheck").Field("phraseReqs") .GetValue() is IEnumerable enumerable)) { continue; } foreach (object item in enumerable) { Traverse val = Traverse.Create(item).Field("gameResAtom"); string text = null; try { text = val.Field("type").GetValue<string>(); } catch { } if (!(text != "wz_" + zone)) { float num2 = 0f; try { num2 = Convert.ToSingle(val.Field("value").GetValue()); } catch { } if (!(num2 <= level) && questSystemData.IsQuestInStatus(((BalanceBaseObject)questDef).id, (QuestStatus)2)) { num = ((num != 0) ? Mathf.Min(num, Mathf.CeilToInt(num2)) : Mathf.CeilToInt(num2)); } } } } return num; } catch (Exception ex) { Plugin.LogThrottled("Quest thresholds: " + ex.Message); return 0; } } private static void ReadAreas(WorldZoneData zone, GraveyardInfo info) { //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Unknown result type (might be due to invalid IL or missing references) List<Rect> list = null; try { list = Traverse.Create((object)zone).Field("customQualityZonesRectList").GetValue<List<Rect>>(); } catch { } if (list == null) { return; } List<ZoneObj> source = info.Yard.Objects.Where((ZoneObj o) => o.Id.StartsWith("graveyard_module_", StringComparison.Ordinal)).ToList(); foreach (Rect item in list) { Rect r = item; AreaInfo areaInfo = new AreaInfo(); areaInfo.Rect = r; AreaInfo areaInfo2 = areaInfo; ZoneObj zoneObj = source.OrderBy(delegate(ZoneObj o) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) Vector2 val = o.Pos - new Vector2(((Rect)(ref r)).x, ((Rect)(ref r)).y); return ((Vector2)(ref val)).sqrMagnitude; }).FirstOrDefault(delegate(ZoneObj o) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) Vector2 val = o.Pos - ((Rect)(ref r)).center; return ((Vector2)(ref val)).magnitude < Mathf.Max(((Rect)(ref r)).width, ((Rect)(ref r)).height); }); areaInfo2.Id = zoneObj?.Id; areaInfo2.Name = ((zoneObj == null) ? (GameApi.L("graveyard_module") ?? "") : zoneObj.Name); foreach (ZoneObj @object in info.Yard.Objects) { if (@object.Counted && Inside(r, @object.Pos)) { areaInfo2.Sum += @object.Quality; } } info.Areas.Add(areaInfo2); } } private static bool Inside(Rect r, Vector2 p) { return p.x >= ((Rect)(ref r)).xMin - 0.01f && p.x <= ((Rect)(ref r)).xMax + 0.01f && p.y >= ((Rect)(ref r)).yMin - 0.01f && p.y <= ((Rect)(ref r)).yMax + 0.01f; } private static void ReadGraves(WorldZoneData zone, GraveyardInfo info, CodexIndex index) { //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) WorldData worldData = MainGame.Instance.GameSave.worldData; try { foreach (SGuid wgoData2 in zone.wgoDataList) { WgoData wgoData = worldData.GetWgoData(wgoData2); if (wgoData != null && (!(((ObjectLinkedToDefinition<WGODef>)(object)wgoData).id != "grave_ground") || !(((ObjectLinkedToDefinition<WGODef>)(object)wgoData).id != "grave_empty"))) { GraveInfo g = new GraveInfo(); g.Counted = info.Yard.CountedIds == null || info.Yard.CountedIds.Contains(wgoData2); try { Vector3 position = wgoData.Position; g.Pos = new Vector2(position.x, position.z); } catch { } try { g.Total = wgoData.Quality; } catch { } if (((ObjectLinkedToDefinition<WGODef>)(object)wgoData).id == "grave_empty") { g.Empty = true; g.Why = GraveWhy.Empty; } else { ReadGrave(wgoData, g); } AreaInfo areaInfo = info.Areas.FirstOrDefault((AreaInfo a) => Inside(a.Rect, g.Pos)); if (areaInfo != null) { g.AreaName = areaInfo.Name; areaInfo.Graves++; } if (!g.Empty) { Improve(g, index); } info.Graves.Add(g); } } } catch (Exception ex) { Plugin.LogThrottled("Graves: " + ex.Message); } List<GraveInfo> list = (from graveInfo in info.Graves orderby Mathf.Round(graveInfo.Pos.y / 1.8f * 2f), graveInfo.Pos.x select graveInfo).ToList(); for (int num = 0; num < list.Count; num++) { list[num].Number = num + 1; } info.Graves.Sort((GraveInfo a, GraveInfo b) => a.Number.CompareTo(b.Number)); } private static void ReadGrave(WgoData w, GraveInfo g) { try { Inventory inventory = w.Inventory; Item val = ((inventory != null) ? inventory.Data : null); if (val == null || val.Inventory == null) { return; } foreach (Item item in val.Inventory) { if (item != null && !item.IsEmpty && !string.IsNullOrEmpty(((ObjectLinkedToDefinition<ItemDef>)(object)item).id) && !(((ObjectLinkedToDefinition<ItemDef>)(object)item).id == "empty")) { ItemDef val2 = GameApi.ItemDef(((ObjectLinkedToDefinition<ItemDef>)(object)item).id); if (((ObjectLinkedToDefinition<ItemDef>)(object)item).id.StartsWith("grave_top_", StringComparison.Ordinal)) { g.Top = Decor(((ObjectLinkedToDefinition<ItemDef>)(object)item).id, GraveSlot.Top, val2); } else if (((ObjectLinkedToDefinition<ItemDef>)(object)item).id.StartsWith("grave_bo