Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of CriticalPath v0.1.1
CriticalPath.dll
Decompiled a day agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using TMPro; using UnityEngine; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyVersion("0.0.0.0")] namespace CriticalPath; public enum Tier { Harvest, Food, Boss, Armor, Comfort, Gear } public class Suggestion { public string Id; public string Name; public string Reason; public string Detail; public Tier Tier; public string For = ""; public int Sequence; public bool Latest; public bool Ready; public float Value; } public class ArmorOption { public string Id; public int Slot; public float Gain; public int Cost; public float GainPerMaterial => Gain / (float)Math.Max(Cost, 1); } public class ComfortPiece { public string Id; public int Group; public int Comfort; public bool Built; } public class Making { public string Station = ""; public int Level = 1; public string Piece = ""; public List<string> Materials = new List<string>(); } public class Planner { private const int MaxDepth = 8; public const string GatherPrefix = "gather:"; public HashSet<string> Known = new HashSet<string>(); public Dictionary<string, int> StationLevels = new Dictionary<string, int>(); public HashSet<string> Built = new HashSet<string>(); public Dictionary<string, Making> Makings = new Dictionary<string, Making>(); public Dictionary<string, string> StationPieces = new Dictionary<string, string>(); public Dictionary<string, List<string>> Extensions = new Dictionary<string, List<string>>(); public Dictionary<string, string> Gathers = new Dictionary<string, string>(); private readonly HashSet<string> visiting = new HashSet<string>(); public string Make(string id) { visiting.Clear(); return MakeStep(id); } public string Get(string item) { visiting.Clear(); return MaterialStep(item); } private string MakeStep(string id) { if (!Makings.TryGetValue(id, out var value) || visiting.Count >= 8 || !visiting.Add(id)) { return null; } try { List<string> list = new List<string> { StationStep(value.Station, value.Level) }; if (value.Piece.Length > 0) { list.Add(PieceStep(value.Piece)); } list.AddRange(value.Materials.Select(MaterialStep)); return list.Contains(null) ? null : (list.FirstOrDefault((string step) => step != "") ?? ""); } finally { visiting.Remove(id); } } private string StationStep(string station, int level) { if (station.Length == 0) { return ""; } StationLevels.TryGetValue(station, out var value); if (value == 0) { string value2; string text = (StationPieces.TryGetValue(station, out value2) ? PieceStep(value2) : null); if (text != "") { return text; } value = 1; } if (value >= level) { return ""; } if (Extensions.TryGetValue(station, out var value3)) { foreach (string item in value3) { string text2 = PieceStep(item); if (text2 != null) { return text2; } } } return null; } private string PieceStep(string piece) { if (Built.Contains(piece)) { return ""; } string text = MakeStep(piece); if (!(text == "")) { return text; } return piece; } private string MaterialStep(string item) { if (Known.Contains(item)) { return ""; } if (Makings.ContainsKey(item)) { string text = MakeStep(item); if (!(text == "")) { return text; } return item; } if (Gathers.TryGetValue(item, out var value)) { string text2 = MaterialStep(value); if (!(text2 == "")) { return text2; } return "gather:" + item; } return null; } } public static class PathRules { private const int MaxPerTier = 2; private const int NoGroup = 0; private const char RecordSeparator = ';'; private const char FieldSeparator = '|'; private const char LevelSeparator = '@'; public static List<Suggestion> Choose(IEnumerable<Suggestion> suggestions, int count) { List<Suggestion> ranked = (from s in (from s in suggestions orderby s.Latest descending, s.Tier, s.Ready descending, s.Value descending, s.Sequence descending select s).ThenBy<Suggestion, string>((Suggestion s) => s.Name, StringComparer.Ordinal) group s by s.Id into sameStep select sameStep.First()).ToList(); List<Suggestion> list = ranked.Where((Suggestion s) => s.Latest).Take(count).ToList(); foreach (Suggestion suggestion in ranked.Where((Suggestion s) => !s.Latest)) { if (list.Count < count && list.Count((Suggestion s) => s.Tier == suggestion.Tier) < 2) { list.Add(suggestion); } } foreach (Suggestion item in ranked) { if (list.Count < count && !list.Contains(item)) { list.Add(item); } } return list.OrderBy((Suggestion s) => ranked.IndexOf(s)).ToList(); } public static HashSet<string> LatestBatch(List<KeyValuePair<string, string>> unlocks) { HashSet<string> hashSet = new HashSet<string>(); string text = ((unlocks.Count > 0) ? unlocks[unlocks.Count - 1].Value : ""); int num = unlocks.Count - 1; while (text.Length > 0 && num >= 0 && unlocks[num].Value == text) { hashSet.Add(unlocks[num].Key); num--; } return hashSet; } public static List<string> KeyArmor(IEnumerable<ArmorOption> options, float needed) { IOrderedEnumerable<ArmorOption> orderedEnumerable = from option in options where option.Gain > 0f group option by option.Slot into slot select (from option in slot orderby option.GainPerMaterial descending, option.Gain descending select option).First() into option orderby option.GainPerMaterial descending select option; List<string> list = new List<string>(); foreach (ArmorOption item in orderedEnumerable) { if (needed <= 0f) { break; } list.Add(item.Id); needed -= item.Gain; } return list; } public static List<Suggestion> WhenReady(IEnumerable<Suggestion> suggestions) { return suggestions.Where((Suggestion s) => s.Tier != Tier.Armor && s.Tier != Tier.Gear && s.Tier != Tier.Boss).ToList(); } public static string FoodGain(float health, float stamina, float eitr, float oldHealth, float oldStamina, float oldEitr, bool magic) { List<string> list = new List<string>(); if (magic && eitr > oldEitr) { list.Add("eitr"); } if (health > oldHealth) { list.Add("health"); } if (stamina > oldStamina) { list.Add("stamina"); } if (list.Count <= 0) { return "overall"; } return string.Join("/", list.ToArray()); } public static string ArmorRating(float armor, float minimum) { if (armor < minimum) { return "low"; } if (armor >= minimum * 1.6f) { return "great"; } if (!(armor >= minimum * 1.3f)) { return "min"; } return "good"; } public static bool HasAll(IEnumerable<KeyValuePair<string, int>> costs, Dictionary<string, int> stock) { int value; return costs.All((KeyValuePair<string, int> cost) => stock.TryGetValue(cost.Key, out value) && value >= cost.Value); } public static string StationEntry(string station, int level) { return station + "@" + level; } public static string NewDiscovery(HashSet<string> before, HashSet<string> now) { return (now.FirstOrDefault((string candidate) => !before.Contains(candidate)) ?? "").Split('@')[0]; } public static List<string> ComfortToBuild(IEnumerable<ComfortPiece> pieces) { List<string> list = new List<string>(); foreach (IGrouping<int, ComfortPiece> item in from p in pieces where p.Comfort > 0 group p by p.Group) { if (item.Key == 0) { list.AddRange(from p in item where !p.Built select p.Id); continue; } int num = (from p in item where p.Built select p.Comfort).DefaultIfEmpty(0).Max(); ComfortPiece comfortPiece = (from p in item where !p.Built orderby p.Comfort descending select p).FirstOrDefault(); if (comfortPiece != null && comfortPiece.Comfort > num) { list.Add(comfortPiece.Id); } } return list; } public static HashSet<string> ParseSet(string text) { return new HashSet<string>(from id in (text ?? "").Split(';') where id.Length > 0 select id); } public static string FormatSet(IEnumerable<string> ids) { return string.Join(';'.ToString(), ids.ToArray()); } public static List<KeyValuePair<string, string>> ParseUnlocks(string text) { return (from record in (text ?? "").Split(';') select record.Split('|') into fields where fields.Length == 2 && fields[0].Length > 0 select new KeyValuePair<string, string>(fields[0], fields[1])).ToList(); } public static string FormatUnlocks(IEnumerable<KeyValuePair<string, string>> unlocks) { return string.Join(';'.ToString(), unlocks.Select((KeyValuePair<string, string> pair) => pair.Key + "|" + pair.Value).ToArray()); } } [BepInPlugin("com.parallel53.criticalpath", "CriticalPath", "0.1.1")] public class CriticalPathPlugin : BaseUnityPlugin { private enum ShieldUse { None, Parry, Strongest } private class Gate { public readonly string Tool; public readonly string Target; public readonly string Where; public readonly string AfterBoss; public Gate(string tool, string target, string where, string afterBoss) { Tool = tool; Target = target; Where = where; AfterBoss = afterBoss; } } private class Boss { public readonly string Prefab; public readonly float TargetArmor; public readonly string Hint; public readonly DamageType Resist; public readonly ShieldUse Shield; public readonly Need[] Bring; public int Arrows = 20; public bool Ranged; public string Find = "Read a Vegvisir to mark its altar on the map."; public Boss(string prefab, float targetArmor, string hint, DamageType resist, ShieldUse shield, params Need[] bring) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) Prefab = prefab; TargetArmor = targetArmor; Hint = hint; Resist = resist; Shield = shield; Bring = bring; } } private class Need { public readonly string Prefab; public readonly int Count; public readonly string Where; public Need(string prefab, int count, string where) { Prefab = prefab; Count = count; Where = where; } } private class Shortfall { public string Item; public int Short; public string Where; } private class BossGoal { public string Name; public string Hint; public float Armor; public float TargetArmor; public List<Shortfall> Missing; public DamageType Resist; public int Have; public bool Resisted = true; public List<DamageType> Weak; public bool Armed = true; public ShieldUse Shield; public bool Shielded = true; public int Arrows; public bool Ranged; public bool Shooting = true; public bool Located; public string Find; public bool Ready { get { if (Armor >= TargetArmor && Missing.Count == 0 && Resisted && Armed && Shielded) { return Shooting; } return false; } } } private class Plan { public readonly Planner Planner = new Planner(); public readonly Dictionary<string, Recipe> Recipes; public readonly Dictionary<string, Piece> Pieces = new Dictionary<string, Piece>(); public readonly List<Gate> OpenGates = new List<Gate>(); private readonly Dictionary<string, Gate> gatesByTarget = new Dictionary<string, Gate>(); private readonly Dictionary<string, Suggestion> upgrades = new Dictionary<string, Suggestion>(); private readonly Dictionary<string, string> conversions = new Dictionary<string, string>(); public Plan(Player player, HashSet<string> built) { Planner.Known = new HashSet<string>(KnownMaterial.Invoke(player)); Planner.StationLevels = new Dictionary<string, int>(KnownStations.Invoke(player)); Planner.Built = built; Recipes = ObjectDB.instance.m_recipes.Where(IsUsable).GroupBy(ItemName).ToDictionary((IGrouping<string, Recipe> sameItem) => sameItem.Key, (IGrouping<string, Recipe> sameItem) => sameItem.OrderBy((Recipe recipe) => recipe.m_amount).First()); foreach (Recipe value in Recipes.Values) { Planner.Makings[ItemName(value)] = new Making { Station = (((Object)(object)value.m_craftingStation != (Object)null) ? value.m_craftingStation.m_name : ""), Level = Mathf.Max(value.m_minStationLevel, 1), Materials = Needs(value.m_resources, 1) }; } foreach (Piece item in from piece in HammerPieces() orderby built.Contains(piece.m_name) descending, Materials(piece.m_resources, 1) select piece) { AddPiece(item, built); } Gate[] gates = Gates; foreach (Gate gate in gates) { SharedData val = Shared(gate.Tool); SharedData val2 = Shared(gate.Target); if (val != null && val2 != null && (gate.AfterBoss.Length == 0 || Defeated(gate.AfterBoss))) { Planner.Gathers[val2.m_name] = val.m_name; gatesByTarget[val2.m_name] = gate; OpenGates.Add(gate); } } } private void AddPiece(Piece piece, HashSet<string> built) { if (Pieces.ContainsKey(piece.m_name)) { return; } Pieces[piece.m_name] = piece; Planner.Makings[piece.m_name] = new Making { Station = (((Object)(object)piece.m_craftingStation != (Object)null) ? piece.m_craftingStation.m_name : ""), Materials = Needs(piece.m_resources, 1) }; CraftingStation component = ((Component)piece).GetComponent<CraftingStation>(); if ((Object)(object)component != (Object)null && !Planner.StationPieces.ContainsKey(component.m_name)) { Planner.StationPieces[component.m_name] = piece.m_name; } StationExtension component2 = ((Component)piece).GetComponent<StationExtension>(); if ((Object)(object)component2 != (Object)null && (Object)(object)component2.m_craftingStation != (Object)null && !built.Contains(piece.m_name)) { string name = component2.m_craftingStation.m_name; if (!Planner.Extensions.ContainsKey(name)) { Planner.Extensions[name] = new List<string>(); } Planner.Extensions[name].Add(piece.m_name); } Smelter component3 = ((Component)piece).GetComponent<Smelter>(); if ((Object)(object)component3 != (Object)null) { foreach (ItemConversion item in component3.m_conversion) { AddConversion(piece, item.m_from, item.m_to); } } Fermenter component4 = ((Component)piece).GetComponent<Fermenter>(); if ((Object)(object)component4 != (Object)null) { foreach (ItemConversion item2 in component4.m_conversion) { AddConversion(piece, item2.m_from, item2.m_to); } } CookingStation component5 = ((Component)piece).GetComponent<CookingStation>(); if (!((Object)(object)component5 != (Object)null)) { return; } foreach (ItemConversion item3 in component5.m_conversion) { AddConversion(piece, item3.m_from, item3.m_to); } } private void AddConversion(Piece piece, ItemDrop from, ItemDrop to) { if (!((Object)(object)from == (Object)null) && !((Object)(object)to == (Object)null) && !Planner.Makings.ContainsKey(to.m_itemData.m_shared.m_name)) { conversions[to.m_itemData.m_shared.m_name] = "Make at " + Localized(piece.m_name) + " from " + Localized(from.m_itemData.m_shared.m_name); Planner.Makings[to.m_itemData.m_shared.m_name] = new Making { Piece = piece.m_name, Materials = new List<string> { from.m_itemData.m_shared.m_name } }; } } public string AddUpgrade(ItemData item) { Recipe recipe = ObjectDB.instance.GetRecipe(item); int next = item.m_quality + 1; if ((Object)(object)recipe == (Object)null || (Object)(object)recipe.m_craftingStation == (Object)null || next > item.m_shared.m_maxQuality) { return null; } string text = "upgrade:" + item.m_shared.m_name; string name = recipe.m_craftingStation.m_name; int requiredStationLevel = recipe.GetRequiredStationLevel(next); Planner.Makings[text] = new Making { Station = name, Level = requiredStationLevel, Materials = Needs(recipe.m_resources, next) }; string text2 = string.Join(", ", (from requirement in recipe.m_resources where (Object)(object)requirement.m_resItem != (Object)null && requirement.GetAmount(next) > 0 select requirement.GetAmount(next) + " " + Localized(requirement.m_resItem.m_itemData.m_shared.m_name)).ToArray()); upgrades[text] = new Suggestion { Id = text, Name = "Upgrade " + Localized(item.m_shared.m_name), Detail = "Upgrade at " + Localized(name) + " " + requiredStationLevel + ": " + text2 }; return text; } public Suggestion ForGoal(string goal, string ownStep, string step, Tier tier, string goalName, string reason) { if (step == null) { return null; } string text = ((step == "") ? ownStep : step); Suggestion suggestion = Describe(text, tier, (text == ownStep) ? reason : ("Needed for " + goalName + ".")); if (suggestion.Reason.Length == 0) { suggestion.Reason = "Needed for " + goalName + "."; } suggestion.For = goal; return suggestion; } public Suggestion Describe(string id, Tier tier, string reason) { Suggestion suggestion; Suggestion value; Piece value2; Recipe value3; if (id.StartsWith("gather:")) { string text = id.Substring("gather:".Length); Gate gate = gatesByTarget[text]; suggestion = new Suggestion { Name = "Find " + Localized(text), Detail = "", Reason = ((reason.Length > 0) ? reason : (gate.Where + " with your " + ItemTitle(gate.Tool) + ".")) }; } else if (upgrades.TryGetValue(id, out value)) { suggestion = new Suggestion { Name = value.Name, Detail = value.Detail, Reason = reason }; } else if (Pieces.TryGetValue(id, out value2)) { string text2 = Costs(value2.m_resources); string text3 = (((Object)(object)value2.m_craftingStation != (Object)null) ? (" at " + Localized(value2.m_craftingStation.m_name)) : ""); suggestion = new Suggestion { Name = Localized(value2.m_name), Detail = ((text2.Length > 0) ? ("Build" + text3 + ": " + text2) : ""), Reason = reason }; } else if (Recipes.TryGetValue(id, out value3)) { suggestion = new Suggestion { Name = Localized(id), Detail = CraftDetail(value3), Reason = reason }; } else { conversions.TryGetValue(id, out var value4); suggestion = new Suggestion { Name = Localized(id), Detail = (value4 ?? ""), Reason = reason }; } suggestion.Id = id; suggestion.For = id; suggestion.Tier = tier; return suggestion; } } private const string BuiltKey = "parallel53.criticalpath.built"; private const string UnlocksKey = "parallel53.criticalpath.unlocks"; private const string BuildTool = "Hammer"; private const float Padding = 14f; private const float Gap = 16f; private const float FontSize = 20f; private static readonly Color PanelBacking = new Color(0.12f, 0.09f, 0.07f, 0.96f); private const string QuickStackButton = "QuickStackButton"; private const float ButtonGap = 6f; private const float RefreshSeconds = 2f; private const float ScanRadius = 40f; private const float StaminaWorth = 0.5f; private const float EitrWorth = 0.5f; private static readonly FieldRef<Player, Dictionary<string, int>> KnownStations = AccessTools.FieldRefAccess<Player, Dictionary<string, int>>("m_knownStations"); private static readonly FieldRef<Minimap, List<PinData>> Pins = AccessTools.FieldRefAccess<Minimap, List<PinData>>("m_pins"); private static readonly FieldRef<Player, HashSet<string>> KnownMaterial = AccessTools.FieldRefAccess<Player, HashSet<string>>("m_knownMaterial"); private static readonly Gate[] Gates = new Gate[6] { new Gate("AxeFlint", "RoundLog", "Chop pine trees", ""), new Gate("PickaxeAntler", "CopperOre", "Mine copper deposits in the Black Forest", ""), new Gate("PickaxeAntler", "TinOre", "Mine tin on Black Forest shores", ""), new Gate("AxeBronze", "FineWood", "Chop birch and oak trees", ""), new Gate("CryptKey", "WitheredBone", "Search Sunken Crypts in the Swamp", ""), new Gate("PickaxeIron", "SilverOre", "Find silver in the Mountains with the Wishbone", "Bonemass") }; private static readonly Boss[] Bosses = new Boss[7] { new Boss("Eikthyr", 6f, "Offer 2 deer trophies at the altar. Parry his headbutts to stagger him.", (DamageType)0, ShieldUse.Parry, new Need("TrophyDeer", 2, "Hunt deer in the Meadows")) { Find = "Read the Vegvisir stone in the Meadows to mark his altar." }, new Boss("gd_king", 18f, "Offer 3 ancient seeds. Kite him with the bow from behind pillars.", (DamageType)0, ShieldUse.None, new Need("AncientSeed", 3, "Destroy greydwarf nests in the Black Forest")) { Arrows = 100, Find = "Read a Vegvisir in a Black Forest burial chamber to mark his altar." }, new Boss("Bonemass", 30f, "Offer 10 withered bones. Parry to stagger him and bring blunt weapons.", (DamageType)256, ShieldUse.Parry, new Need("WitheredBone", 10, "Search Sunken Crypts in the Swamp")) { Find = "Read a Vegvisir in a Swamp Sunken Crypt to mark his altar." }, new Boss("Dragon", 44f, "Offer 3 dragon eggs. Dodge her breath behind boulders.", (DamageType)64, ShieldUse.None, new Need("DragonEgg", 3, "Carry eggs one at a time from dragon nests in the Mountains")) { Arrows = 200, Ranged = true, Find = "Read a Vegvisir in the Mountains to mark her altar." }, new Boss("GoblinKing", 60f, "Offer 5 fuling totems. Sprint behind his stone pillars.", (DamageType)32, ShieldUse.None, new Need("GoblinTotem", 5, "Take totems from fuling villages in the Plains")) { Find = "Read a Vegvisir in a Plains fuling village to mark his altar." }, new Boss("SeekerQueen", 82f, "Open the sealed door with the Sealbreaker. Dodge rather than block.", (DamageType)0, ShieldUse.None, new Need("DvergrKey", 1, ""), new Need("CapeFeather", 1, ""), new Need("Demister", 1, "")) { Find = "Read the Vegvisir in a Mistlands infested mine to mark her lair." }, new Boss("Fader", 94f, "Offer 3 bells.", (DamageType)32, ShieldUse.Strongest) { Find = "Read a Vegvisir in an Ashlands fortress to mark its altar." } }; private const DamageType NoResistance = (DamageType)0; private const string DefaultFind = "Read a Vegvisir to mark its altar on the map."; private const int DefaultArrows = 20; private static readonly DamageType[] DamageKinds; private static readonly HashSet<ItemType> WeaponTypes; private const float LaterBossArmor = 96f; private const string LaterBossHint = "Offer 3 malicious bloods at the altar."; private static readonly ItemType[] ArmorSlots; private static readonly HashSet<ItemType> GearTypes; private static readonly HashSet<string> MissingItems; private static ManualLogSource log; private static ConfigEntry<int> suggestionCount; private static ConfigEntry<bool> notify; private static bool dirty; private static string lastCause; private static Player seenPlayer; private static HashSet<string> seenKnowledge; private string panelText; private string shownTop; private float nextRefresh; private RectTransform panel; private TMP_Text panelLabel; private void Awake() { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Expected O, but got Unknown //IL_0061: Unknown result type (might be due to invalid IL or missing references) log = ((BaseUnityPlugin)this).Logger; suggestionCount = ((BaseUnityPlugin)this).Config.Bind<int>("General", "Suggestions", 3, new ConfigDescription("How many suggestions the panel shows.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 6), Array.Empty<object>())); notify = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Notify", true, "Show a top-left message when the top suggestion changes."); new Harmony("com.parallel53.criticalpath").PatchAll(typeof(CriticalPathPlugin)); } private void Update() { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || (Object)(object)ObjectDB.instance == (Object)null) { panelText = null; shownTop = null; return; } bool flag = InventoryGui.IsVisible(); if (dirty || (flag && Time.unscaledTime >= nextRefresh)) { dirty = false; nextRefresh = Time.unscaledTime + 2f; Refresh(localPlayer); } } private void LateUpdate() { InventoryGui instance = InventoryGui.instance; bool flag = panelText != null && (Object)(object)instance != (Object)null && InventoryGui.IsVisible(); if ((Object)(object)panel == (Object)null) { if (!flag) { return; } CreatePanel(instance); } if (((Component)panel).gameObject.activeSelf != flag) { ((Component)panel).gameObject.SetActive(flag); } if (flag) { panelLabel.text = panelText; PlacePanel(instance); } } private void CreatePanel(InventoryGui gui) { //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Expected O, but got Unknown //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Expected O, but got Unknown //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Unknown result type (might be due to invalid IL or missing references) //IL_0112: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_0125: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_013a: Unknown result type (might be due to invalid IL or missing references) //IL_013b: 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_0141: Unknown result type (might be due to invalid IL or missing references) //IL_0148: Unknown result type (might be due to invalid IL or missing references) //IL_0166: Unknown result type (might be due to invalid IL or missing references) //IL_0180: Unknown result type (might be due to invalid IL or missing references) //IL_023f: Unknown result type (might be due to invalid IL or missing references) //IL_0244: Unknown result type (might be due to invalid IL or missing references) //IL_024b: Unknown result type (might be due to invalid IL or missing references) //IL_0251: Unknown result type (might be due to invalid IL or missing references) //IL_0256: Unknown result type (might be due to invalid IL or missing references) //IL_0263: Unknown result type (might be due to invalid IL or missing references) //IL_0264: Unknown result type (might be due to invalid IL or missing references) //IL_026e: Unknown result type (might be due to invalid IL or missing references) //IL_026f: Unknown result type (might be due to invalid IL or missing references) //IL_0279: Unknown result type (might be due to invalid IL or missing references) //IL_0284: Unknown result type (might be due to invalid IL or missing references) //IL_0298: Unknown result type (might be due to invalid IL or missing references) //IL_02a2: Unknown result type (might be due to invalid IL or missing references) //IL_01ef: Unknown result type (might be due to invalid IL or missing references) //IL_01f4: Unknown result type (might be due to invalid IL or missing references) //IL_0337: Unknown result type (might be due to invalid IL or missing references) Canvas rootCanvas = ((Component)gui.m_crafting).GetComponentInParent<Canvas>().rootCanvas; GameObject val = new GameObject("CriticalPathPanel", new Type[2] { typeof(RectTransform), typeof(Image) }); panel = (RectTransform)val.transform; ((Transform)panel).SetParent(((Component)rootCanvas).transform, false); RectTransform obj = panel; RectTransform obj2 = panel; Vector2 val2 = default(Vector2); ((Vector2)(ref val2))..ctor(0.5f, 0.5f); obj2.anchorMax = val2; obj.anchorMin = val2; panel.pivot = new Vector2(0f, 1f); Image component = val.GetComponent<Image>(); ((Graphic)component).color = PanelBacking; ((Graphic)component).raycastTarget = false; Image val3 = Background(gui.m_player) ?? Background(gui.m_crafting); if ((Object)(object)val3 != (Object)null) { GameObject val4 = new GameObject("Frame", new Type[2] { typeof(RectTransform), typeof(Image) }); RectTransform val5 = (RectTransform)val4.transform; ((Transform)val5).SetParent((Transform)(object)panel, false); val5.anchorMin = Vector2.zero; val5.anchorMax = Vector2.one; val2 = (val5.offsetMin = (val5.offsetMax = Vector2.zero)); Image component2 = val4.GetComponent<Image>(); component2.sprite = val3.sprite; component2.type = val3.type; component2.pixelsPerUnitMultiplier = val3.pixelsPerUnitMultiplier; ((Graphic)component2).color = ((Graphic)val3).color; if ((Object)(object)((Graphic)val3).material != (Object)(object)((Graphic)val3).defaultMaterial) { ((Graphic)component2).material = ((Graphic)val3).material; } ((Graphic)component2).raycastTarget = false; log.LogInfo((object)("Panel artwork from " + ((Object)val3).name + " (" + ((Object)val3.sprite).name + ", colour " + ((object)((Graphic)val3).color/*cast due to .constrained prefix*/).ToString() + ")")); } else { log.LogInfo((object)"No panel artwork found; using a plain backing"); } GameObject val7 = new GameObject("Text", new Type[1] { typeof(RectTransform) }); val7.SetActive(false); RectTransform val8 = (RectTransform)val7.transform; ((Transform)val8).SetParent((Transform)(object)panel, false); val8.anchorMin = Vector2.zero; val8.anchorMax = Vector2.one; val8.offsetMin = new Vector2(14f, 14f); val8.offsetMax = new Vector2(-14f, -14f); TextMeshProUGUI val9 = val7.AddComponent<TextMeshProUGUI>(); TMP_Text val10 = ((IEnumerable<TMP_Text>)((Component)gui.m_crafting).GetComponentsInChildren<TMP_Text>(true)).FirstOrDefault((Func<TMP_Text, bool>)((TMP_Text text) => (Object)(object)text.font != (Object)null)); if ((Object)(object)val10 != (Object)null) { ((TMP_Text)val9).font = val10.font; log.LogInfo((object)("Panel font " + ((Object)val10.font).name)); } ((TMP_Text)val9).fontSize = 20f; ((TMP_Text)val9).richText = true; ((TMP_Text)val9).enableWordWrapping = true; ((TMP_Text)val9).alignment = (TextAlignmentOptions)257; ((Graphic)val9).color = Color.white; ((Graphic)val9).raycastTarget = false; val7.SetActive(true); panelLabel = (TMP_Text)(object)val9; } private static Image Background(RectTransform window) { List<Image> source = (from image in ((Component)window).GetComponentsInChildren<Image>(true) where (Object)(object)image.sprite != (Object)null && ((Graphic)image).color.a > 0.5f select image).OrderByDescending(delegate(Image image) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) Rect rect = ((Graphic)image).rectTransform.rect; float width = ((Rect)(ref rect)).width; rect = ((Graphic)image).rectTransform.rect; return width * ((Rect)(ref rect)).height; }).ToList(); return ((IEnumerable<Image>)source).FirstOrDefault((Func<Image, bool>)((Image image) => ((Object)image).name.ToLowerInvariant().Contains("bkg") || ((Object)image).name.ToLowerInvariant().Contains("background"))) ?? source.FirstOrDefault(); } private void PlacePanel(InventoryGui gui) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Expected O, but got Unknown //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_0121: 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) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) RectTransform val = (RectTransform)((Transform)panel).parent; Rect val2 = LocalRect(val, gui.m_player); float num = ((Rect)(ref val2)).yMin; Rect val3; if ((Object)(object)gui.m_container != (Object)null && ((Component)gui.m_container).gameObject.activeInHierarchy) { float num2 = num; val3 = LocalRect(val, gui.m_container); num = Mathf.Min(num2, ((Rect)(ref val3)).yMin); } RectTransform val4 = (RectTransform)((Transform)gui.m_player).Find("QuickStackButton"); if ((Object)(object)val4 != (Object)null && ((Component)val4).gameObject.activeInHierarchy) { float num3 = num; val3 = LocalRect(val, val4); num = Mathf.Min(num3, ((Rect)(ref val3)).yMin + 16f - 6f); } float width = ((Rect)(ref val2)).width; float num4 = panelLabel.GetPreferredValues(panelText, width - 28f, 0f).y + 28f; panel.sizeDelta = new Vector2(width, num4); RectTransform obj = panel; Vector2 val5 = new Vector2(((Rect)(ref val2)).xMin, num - 16f); val3 = val.rect; obj.anchoredPosition = val5 - ((Rect)(ref val3)).center; } private static Rect LocalRect(RectTransform canvas, RectTransform window) { //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) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) Vector3[] array = (Vector3[])(object)new Vector3[4]; window.GetWorldCorners(array); Vector2 val = Vector2.op_Implicit(((Transform)canvas).InverseTransformPoint(array[0])); Vector2 val2 = Vector2.op_Implicit(((Transform)canvas).InverseTransformPoint(array[2])); return Rect.MinMaxRect(val.x, val.y, val2.x, val2.y); } private void Refresh(Player player) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) List<Piece> list = new List<Piece>(); Piece.GetAllPiecesInRadius(((Component)player).transform.position, 40f, list); RecordNearbyPieces(player, list); Dictionary<string, int> stock = Stock(player, list); BossGoal bossGoal = NextBoss(player, stock); List<Suggestion> list2 = PathRules.Choose(Candidates(player, stock, bossGoal), suggestionCount.Value); if (bossGoal != null && bossGoal.Ready && !bossGoal.Located) { list2.Insert(0, new Suggestion { Id = "find:" + bossGoal.Name, Name = "Find " + bossGoal.Name, Reason = bossGoal.Find, Detail = "" }); list2 = list2.Take(suggestionCount.Value).ToList(); } panelText = PanelText(list2, bossGoal); string text = ((list2.Count > 0) ? list2[0].Id : ""); if (shownTop != null && text != shownTop && list2.Count > 0) { ((BaseUnityPlugin)this).Logger.LogInfo((object)("Next: " + string.Join(", ", list2.Select((Suggestion s) => s.Name + " (" + s.Reason + ")").ToArray()))); if (notify.Value) { ((Character)player).Message((MessageType)1, "Critical Path: " + list2[0].Name, 0, (Sprite)null, false); } } shownTop = text; } private static string PanelText(List<Suggestion> suggestions, BossGoal goal) { StringBuilder stringBuilder = new StringBuilder("<b>Critical Path</b>"); string value = ((goal != null) ? (" Armor " + Mathf.FloorToInt(goal.Armor) + "/" + goal.TargetArmor + " (" + PathRules.ArmorRating(goal.Armor, goal.TargetArmor) + ").") : ""); if (goal != null && goal.Ready) { stringBuilder.Append("\n<color=#ffcc66>Ready for ").Append(goal.Name).Append(goal.Located ? ". Go fight it.</color>" : ". Find it next.</color>") .Append(value); stringBuilder.Append("\n").Append(goal.Hint); } else if (goal != null) { stringBuilder.Append("\nNext boss: ").Append(goal.Name).Append(".") .Append(value); if (!goal.Resisted) { stringBuilder.Append(" Needs ").Append(((object)Unsafe.As<DamageType, DamageType>(ref goal.Resist)/*cast due to .constrained prefix*/).ToString().ToLowerInvariant()).Append(" resistance."); } if (!goal.Shooting) { stringBuilder.Append(" Needs a bow and ").Append(goal.Arrows).Append(" arrows."); } if (!goal.Shielded) { stringBuilder.Append((goal.Shield == ShieldUse.Strongest) ? " Needs a stronger shield." : " Needs a shield."); } if (!goal.Armed) { stringBuilder.Append(" Needs a weapon that deals ").Append(WeakText(goal)).Append("."); } if (goal.Missing.Count > 0) { stringBuilder.Append(" Still to bring: ").Append(string.Join(", ", goal.Missing.Select((Shortfall missing) => missing.Short + " " + Localized(missing.Item)).ToArray())).Append("."); } } if (suggestions.Count == 0 && (goal == null || !goal.Ready)) { return stringBuilder.Append("\nNothing new to make right now. Explore further.").ToString(); } foreach (Suggestion suggestion in suggestions) { stringBuilder.Append("\n\n<color=#ffcc66>").Append(suggestion.Name).Append("</color>"); stringBuilder.Append("\n").Append(suggestion.Reason); if (suggestion.Detail.Length > 0) { stringBuilder.Append("\n").Append(suggestion.Detail); } } return stringBuilder.ToString(); } private static BossGoal NextBoss(Player player, Dictionary<string, int> stock) { //IL_013f: Unknown result type (might be due to invalid IL or missing references) //IL_0144: Unknown result type (might be due to invalid IL or missing references) //IL_0190: Unknown result type (might be due to invalid IL or missing references) //IL_019f: Unknown result type (might be due to invalid IL or missing references) foreach (Boss item in AllBosses()) { GameObject prefab = ZNetScene.instance.GetPrefab(item.Prefab); if ((Object)(object)prefab == (Object)null) { WarnMissingBoss(item.Prefab); continue; } Character character = prefab.GetComponent<Character>(); if (Defeated(item.Prefab)) { continue; } return new BossGoal { Name = Localized(character.m_name), Hint = item.Hint, Missing = (from missing in (from need in item.Bring select new { need = need, item = Shared(need.Prefab) } into pair where pair.item != null select pair).Select(pair => { stock.TryGetValue(pair.item.m_name, out var value); return new Shortfall { Item = pair.item.m_name, Short = pair.need.Count - value, Where = pair.need.Where }; }) where missing.Short > 0 select missing).ToList(), Resist = item.Resist, Shield = item.Shield, Arrows = item.Arrows, Ranged = item.Ranged, Weak = DamageKinds.Where((DamageType type) => IsWeakness(((DamageModifiers)(ref character.m_damageModifiers)).GetModifier(type))).ToList(), Have = (((int)item.Resist != 0) ? Strongest(player, stock, item.Resist) : 0), Armor = ((Character)player).GetBodyArmor(), TargetArmor = item.TargetArmor, Located = Located(character.m_name), Find = item.Find }; } return null; } private static IEnumerable<Boss> AllBosses() { HashSet<string> listed = new HashSet<string>(Bosses.Select((Boss boss) => boss.Prefab)); IEnumerable<Boss> second = ZNetScene.instance.m_prefabs.Where((GameObject prefab) => (Object)(object)prefab != (Object)null && !listed.Contains(((Object)prefab).name)).Where(delegate(GameObject prefab) { Character component = prefab.GetComponent<Character>(); return (Object)(object)component != (Object)null && component.m_boss && !string.IsNullOrEmpty(component.m_defeatSetGlobalKey); }).Select(delegate(GameObject prefab) { if (MissingItems.Add("boss:" + ((Object)prefab).name)) { log.LogInfo((object)("Found another boss: " + ((Object)prefab).name + " (" + Localized(prefab.GetComponent<Character>().m_name) + ")")); } return new Boss(((Object)prefab).name, 96f, "Offer 3 malicious bloods at the altar.", (DamageType)0, ShieldUse.None); }); return Bosses.Concat(second); } private static int Strongest(Player player, Dictionary<string, int> stock, DamageType type) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) int num = (from item in ((Humanoid)player).GetInventory().GetEquippedItems() select Strength(item.m_shared.m_damageModifiers, type)).DefaultIfEmpty(0).Max(); Dictionary<string, SharedData> items = ItemsByName(); int num2 = (from pair in stock where pair.Value > 0 && items.ContainsKey(pair.Key) select DrinkStrength(items[pair.Key], type)).DefaultIfEmpty(0).Max(); return Mathf.Max(num, num2); } private static int DrinkStrength(SharedData shared, DamageType type) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) StatusEffect consumeStatusEffect = shared.m_consumeStatusEffect; SE_Stats val = (SE_Stats)(object)((consumeStatusEffect is SE_Stats) ? consumeStatusEffect : null); if (!((Object)(object)val != (Object)null)) { return 0; } return Strength(val.m_mods, type); } private static int Strength(List<DamageModPair> mods, DamageType type) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) return mods?.Where((DamageModPair mod) => mod.m_type == type).Select(delegate(DamageModPair mod) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Expected I4, but got Unknown DamageModifier modifier = mod.m_modifier; return (modifier - 1) switch { 6 => 1, 0 => 2, 4 => 3, 2 => 4, _ => 0, }; }).DefaultIfEmpty(0) .Max() ?? 0; } private static Dictionary<string, SharedData> ItemsByName() { Dictionary<string, SharedData> dictionary = new Dictionary<string, SharedData>(); foreach (GameObject item in ObjectDB.instance.m_items) { ItemDrop val = (((Object)(object)item != (Object)null) ? item.GetComponent<ItemDrop>() : null); if ((Object)(object)val != (Object)null && !dictionary.ContainsKey(val.m_itemData.m_shared.m_name)) { dictionary[val.m_itemData.m_shared.m_name] = val.m_itemData.m_shared; } } return dictionary; } private static void WarnMissingBoss(string prefab) { if (MissingItems.Add(prefab)) { log.LogWarning((object)("No boss named " + prefab)); } } private static bool Located(string name) { if ((Object)(object)Minimap.instance == (Object)null) { return true; } List<PinData> source = (from pin in Pins.Invoke(Minimap.instance) where (int)pin.m_type == 9 select pin).ToList(); foreach (PinData item in source.Where((PinData pin) => MissingItems.Add("pin:" + pin.m_name))) { log.LogInfo((object)("Boss pin on the map: " + item.m_name)); } string localized = Localized(name); return source.Any((PinData pin) => pin.m_name == name || string.Equals(Localized(pin.m_name), localized, StringComparison.OrdinalIgnoreCase)); } private static bool Defeated(string prefab) { GameObject prefab2 = ZNetScene.instance.GetPrefab(prefab); if ((Object)(object)prefab2 == (Object)null) { WarnMissingBoss(prefab); return false; } return ZoneSystem.instance.GetGlobalKey(prefab2.GetComponent<Character>().m_defeatSetGlobalKey); } private static List<Suggestion> Candidates(Player player, Dictionary<string, int> stock, BossGoal goal) { //IL_0042: Unknown result type (might be due to invalid IL or missing references) List<KeyValuePair<string, string>> list = Unlocks(player); HashSet<string> hashSet = PathRules.LatestBatch(list); HashSet<string> built = Built(player); Plan plan = new Plan(player, built); Suggestion suggestion = ((goal != null && (int)goal.Resist != 0) ? ResistGoal(plan, stock, goal) : null); if (suggestion != null) { goal.Resisted = false; } Suggestion suggestion2 = ((goal != null && goal.Weak.Count > 0) ? WeaponGoal(plan, stock, goal) : null); if (suggestion2 != null) { goal.Armed = false; } Suggestion suggestion3 = ((goal != null && goal.Shield != ShieldUse.None) ? ShieldGoal(plan, stock, goal) : null); if (suggestion3 != null) { goal.Shielded = false; } Suggestion suggestion4 = ((goal != null && goal.Ranged) ? RangedGoal(plan, stock, goal) : null); if (suggestion4 != null) { goal.Shooting = false; } List<Suggestion> list2 = new List<Suggestion>(); list2.AddRange(GatherGoals(player, plan)); list2.AddRange(FoodGoals(player, plan, stock)); if (goal != null && !goal.Ready) { list2.AddRange(ArmorGoals(player, plan, goal.TargetArmor - goal.Armor)); list2.AddRange(BringGoals(plan, stock, goal)); if (suggestion != null) { list2.Add(suggestion); } if (suggestion2 != null) { list2.Add(suggestion2); } if (suggestion3 != null) { list2.Add(suggestion3); } if (suggestion4 != null) { list2.Add(suggestion4); } } list2.AddRange(Comfort(plan.Pieces.Values.Where((Piece piece) => player.IsRecipeKnown(piece.m_name)).ToList(), built)); list2.AddRange(from recipe in plan.Recipes.Values where GearTypes.Contains(recipe.m_item.m_itemData.m_shared.m_itemType) && player.IsRecipeKnown(ItemName(recipe)) && !player.IsMaterialKnown(ItemName(recipe)) select plan.Describe(ItemName(recipe), Tier.Gear, "New gear.")); foreach (Suggestion suggestion5 in list2) { bool flag = suggestion5.Tier == Tier.Food && suggestion5.Id == suggestion5.For; suggestion5.Latest = (hashSet.Contains(suggestion5.Id) || hashSet.Contains(suggestion5.For)) && (!flag || suggestion5.Ready); int num = list.FindLastIndex((KeyValuePair<string, string> pair) => pair.Key == suggestion5.Id || pair.Key == suggestion5.For); if (num >= 0) { suggestion5.Sequence = num + 1; if (list[num].Value.Length > 0 && list[num].Key == suggestion5.Id) { Suggestion suggestion6 = suggestion5; suggestion6.Reason = suggestion6.Reason + " Unlocked by " + Localized(list[num].Value) + "."; } } } if (goal == null || !goal.Ready) { return list2; } return PathRules.WhenReady(list2); } private static IEnumerable<Suggestion> GatherGoals(Player player, Plan plan) { HashSet<string> offerings = new HashSet<string>(from need in Bosses.SelectMany((Boss boss) => boss.Bring) select need.Prefab); foreach (Gate openGate in plan.OpenGates) { string name = Shared(openGate.Target).m_name; if (!player.IsMaterialKnown(name) && !offerings.Contains(openGate.Target)) { Suggestion suggestion = plan.ForGoal(name, "gather:" + name, plan.Planner.Get(name), Tier.Harvest, Localized(name), ""); if (suggestion != null) { yield return suggestion; } } } } private static IEnumerable<Suggestion> FoodGoals(Player player, Plan plan, Dictionary<string, int> stock) { Dictionary<string, SharedData> items = ItemsByName(); bool magic = stock.Any((KeyValuePair<string, int> pair) => pair.Value > 0 && items.ContainsKey(pair.Key) && UsesEitr(items[pair.Key])); SharedData val = (from shared2 in items.Values where shared2.m_food > 0f && player.IsMaterialKnown(shared2.m_name) orderby FoodValue(shared2, magic) descending select shared2).Skip(2).FirstOrDefault(); float num = ((val != null) ? FoodValue(val, magic) : 0f); Suggestion suggestion = null; foreach (Recipe value in plan.Recipes.Values) { SharedData shared = value.m_item.m_itemData.m_shared; if (shared.m_food <= 0f || player.IsMaterialKnown(shared.m_name) || FoodValue(shared, magic) <= num) { continue; } string text = plan.Planner.Make(shared.m_name); bool flag = text == "" && CanMake(value, stock); string text2 = ((val != null) ? PathRules.FoodGain(shared.m_food, shared.m_foodStamina, shared.m_foodEitr, val.m_food, val.m_foodStamina, val.m_foodEitr, magic) : PathRules.FoodGain(shared.m_food, shared.m_foodStamina, shared.m_foodEitr, 0f, 0f, 0f, magic)); Suggestion suggestion2 = plan.ForGoal(shared.m_name, shared.m_name, text, Tier.Food, Localized(shared.m_name) + " (" + text2 + ")", "New food (" + text2 + ")." + (flag ? " You have the ingredients." : "")); if (suggestion2 != null) { suggestion2.Ready = flag; suggestion2.Value = FoodValue(shared, magic); if (suggestion == null || suggestion2.Value > suggestion.Value || (suggestion2.Value == suggestion.Value && flag && !suggestion.Ready)) { suggestion = suggestion2; } } } if (suggestion != null) { yield return suggestion; } } private static float FoodValue(SharedData shared, bool magic) { return shared.m_food + shared.m_foodStamina * 0.5f + (magic ? (shared.m_foodEitr * 0.5f) : 0f); } private static bool UsesEitr(SharedData shared) { if (shared.m_attack == null || !(shared.m_attack.m_attackEitr > 0f)) { if (shared.m_secondaryAttack != null) { return shared.m_secondaryAttack.m_attackEitr > 0f; } return false; } return true; } private static IEnumerable<Suggestion> BringGoals(Plan plan, Dictionary<string, int> stock, BossGoal goal) { bool armored = goal.Armor >= goal.TargetArmor; foreach (Shortfall item in goal.Missing) { if (armored || plan.Planner.Makings.ContainsKey(item.Item)) { Suggestion suggestion = BringSuggestion(plan, stock, goal, item, "Bring it to fight " + goal.Name + "."); if (suggestion != null) { yield return suggestion; } } } } private static Suggestion ResistGoal(Plan plan, Dictionary<string, int> stock, BossGoal goal) { string resist = ((object)Unsafe.As<DamageType, DamageType>(ref goal.Resist)/*cast due to .constrained prefix*/).ToString().ToLowerInvariant() + " resist"; string reason = "Gives " + resist + " for " + goal.Name + "."; return (from pair in ItemsByName() where plan.Planner.Makings.ContainsKey(pair.Key) select new { drink = DrinkStrength(pair.Value, goal.Resist), wear = Strength(pair.Value.m_damageModifiers, goal.Resist), item = pair.Key } into provider where Mathf.Max(provider.drink, provider.wear) > goal.Have select new { strength = Mathf.Max(provider.drink, provider.wear), mead = (provider.drink > 0), suggestion = BringSuggestion(plan, stock, goal, new Shortfall { Item = provider.item, Short = 1, Where = "" }, reason, " (" + resist + ")") } into provider where provider.suggestion != null orderby provider.strength descending, provider.suggestion.Id == provider.suggestion.For descending, provider.mead descending select provider.suggestion).FirstOrDefault(); } private static bool IsWeakness(DamageModifier modifier) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Invalid comparison between Unknown and I4 //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Invalid comparison between Unknown and I4 //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Invalid comparison between Unknown and I4 if ((int)modifier != 2 && (int)modifier != 6) { return (int)modifier == 8; } return true; } private unsafe static string WeakText(BossGoal goal) { return string.Join(" or ", goal.Weak.Select((DamageType type) => ((object)(*(DamageType*)(&type))/*cast due to .constrained prefix*/).ToString().ToLowerInvariant()).ToArray()); } private static float Damage(DamageTypes damages, DamageType type) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Invalid comparison between Unknown and I4 //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Invalid comparison between Unknown and I4 //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Invalid comparison between Unknown and I4 //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Invalid comparison between Unknown and I4 //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Invalid comparison between Unknown and I4 //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Invalid comparison between Unknown and I4 //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Expected I4, but got Unknown //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Invalid comparison between Unknown and I4 //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Invalid comparison between Unknown and I4 //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Invalid comparison between Unknown and I4 //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_006a: 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_0023: Invalid comparison between Unknown and I4 //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) if ((int)type <= 32) { if ((int)type <= 8) { switch (type - 1) { default: if ((int)type != 8) { break; } return damages.m_chop; case 0: return damages.m_blunt; case 1: return damages.m_slash; case 3: return damages.m_pierce; case 2: break; } } else { if ((int)type == 16) { return damages.m_pickaxe; } if ((int)type == 32) { return damages.m_fire; } } } else if ((int)type <= 128) { if ((int)type == 64) { return damages.m_frost; } if ((int)type == 128) { return damages.m_lightning; } } else { if ((int)type == 256) { return damages.m_poison; } if ((int)type == 512) { return damages.m_spirit; } } return 0f; } private static float WeakDamage(SharedData shared, BossGoal goal) { return goal.Weak.Sum((DamageType type) => Damage(shared.m_damages, type)); } private static bool Melee(SharedData shared) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) if (WeaponTypes.Contains(shared.m_itemType)) { return string.IsNullOrEmpty(shared.m_ammoType); } return false; } private static bool IsAmmo(SharedData shared) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Invalid comparison between Unknown and I4 //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Invalid comparison between Unknown and I4 if ((int)shared.m_itemType == 9 || (int)shared.m_itemType == 23) { return !string.IsNullOrEmpty(shared.m_ammoType); } return false; } private static bool Ammo(SharedData shared, HashSet<string> launchers) { if (IsAmmo(shared)) { return launchers.Contains(shared.m_ammoType); } return false; } private static Suggestion WeaponGoal(Plan plan, Dictionary<string, int> stock, BossGoal goal) { Dictionary<string, SharedData> items = ItemsByName(); List<SharedData> source = (from pair in stock where pair.Value > 0 && items.ContainsKey(pair.Key) select items[pair.Key]).ToList(); HashSet<string> launchers = new HashSet<string>(from shared in source where WeaponTypes.Contains(shared.m_itemType) && !string.IsNullOrEmpty(shared.m_ammoType) select shared.m_ammoType); if (source.Any((SharedData shared) => WeakDamage(shared, goal) > 0f && (Melee(shared) || (Ammo(shared, launchers) && stock[shared.m_name] >= goal.Arrows)))) { return null; } string reason = "Hits " + goal.Name + "'s weakness to " + WeakText(goal) + "."; return (from option in items.Where((KeyValuePair<string, SharedData> pair) => plan.Planner.Makings.ContainsKey(pair.Key) && WeakDamage(pair.Value, goal) > 0f && (Melee(pair.Value) || IsAmmo(pair.Value))).Select(delegate(KeyValuePair<string, SharedData> pair) { Suggestion suggestion; if (IsAmmo(pair.Value) && !launchers.Contains(pair.Value.m_ammoType)) { suggestion = Launcher(plan, stock, goal, items, pair.Value, "Fires " + Localized(pair.Key) + ", which " + reason.Substring(0, 1).ToLowerInvariant() + reason.Substring(1)); } else { stock.TryGetValue(pair.Key, out var value); int num = (Melee(pair.Value) ? 1 : (goal.Arrows - value)); suggestion = BringSuggestion(plan, stock, goal, new Shortfall { Item = pair.Key, Short = num, Where = "" }, reason); } return new { damage = WeakDamage(pair.Value, goal), suggestion = suggestion }; }) where option.suggestion != null orderby option.suggestion.Id == option.suggestion.For descending, option.damage descending select option.suggestion).FirstOrDefault(); } private static Suggestion RangedGoal(Plan plan, Dictionary<string, int> stock, BossGoal goal) { Dictionary<string, SharedData> items = ItemsByName(); List<SharedData> source = (from pair in stock where pair.Value > 0 && items.ContainsKey(pair.Key) select items[pair.Key]).ToList(); HashSet<string> launchers = new HashSet<string>(from shared in source where WeaponTypes.Contains(shared.m_itemType) && !string.IsNullOrEmpty(shared.m_ammoType) select shared.m_ammoType); if (source.Any((SharedData shared) => Ammo(shared, launchers) && stock[shared.m_name] >= goal.Arrows)) { return null; } string reason = goal.Name + " is fought at range. Bring " + goal.Arrows + " arrows."; return (from option in items.Where((KeyValuePair<string, SharedData> pair) => IsAmmo(pair.Value) && plan.Planner.Makings.ContainsKey(pair.Key)).Select(delegate(KeyValuePair<string, SharedData> pair) { Suggestion suggestion; if (launchers.Contains(pair.Value.m_ammoType)) { stock.TryGetValue(pair.Key, out var value); suggestion = BringSuggestion(plan, stock, goal, new Shortfall { Item = pair.Key, Short = goal.Arrows - value, Where = "" }, reason); } else { suggestion = Launcher(plan, stock, goal, items, pair.Value, "Fires " + Localized(pair.Key) + ". " + reason); } return new { weak = WeakDamage(pair.Value, goal), total = DamageKinds.Sum((DamageType type) => Damage(pair.Value.m_damages, type)), suggestion = suggestion }; }) where option.suggestion != null orderby option.suggestion.Id == option.suggestion.For descending, option.weak descending, option.total descending select option.suggestion).FirstOrDefault(); } private static Suggestion ShieldGoal(Plan plan, Dictionary<string, int> stock, BossGoal goal) { Dictionary<string, SharedData> items = ItemsByName(); float owned = (from pair in stock where pair.Value > 0 && items.ContainsKey(pair.Key) && (int)items[pair.Key].m_itemType == 5 select items[pair.Key].m_blockPower).DefaultIfEmpty(-1f).Max(); if (goal.Shield == ShieldUse.Parry && owned >= 0f) { return null; } string reason = ((goal.Shield == ShieldUse.Parry) ? ("Parry " + goal.Name + "'s attacks to stagger it for double damage.") : ("A safety net against " + goal.Name + "'s heavy hits.")); var source = from pair in items where (int)pair.Value.m_itemType == 5 && plan.Planner.Makings.ContainsKey(pair.Key) && pair.Value.m_blockPower > owned select new { block = pair.Value.m_blockPower, suggestion = BringSuggestion(plan, stock, goal, new Shortfall { Item = pair.Key, Short = 1, Where = "" }, reason) } into option where option.suggestion != null select option; return ((goal.Shield == ShieldUse.Parry) ? (from option in source orderby option.suggestion.Id == option.suggestion.For descending, option.block select option) : (from option in source orderby option.block descending, option.suggestion.Id == option.suggestion.For descending select option)).Select(option => option.suggestion).FirstOrDefault(); } private static Suggestion Launcher(Plan plan, Dictionary<string, int> stock, BossGoal goal, Dictionary<string, SharedData> items, SharedData ammo, string reason) { return (from pair in items where plan.Planner.Makings.ContainsKey(pair.Key) && WeaponTypes.Contains(pair.Value.m_itemType) && pair.Value.m_ammoType == ammo.m_ammoType select BringSuggestion(plan, stock, goal, new Shortfall { Item = pair.Key, Short = 1, Where = "" }, reason) into suggestion where suggestion != null orderby suggestion.Id == suggestion.For descending select suggestion).FirstOrDefault(); } private static Suggestion BringSuggestion(Plan plan, Dictionary<string, int> stock, BossGoal goal, Shortfall missing, string reason, string label = "") { string item = missing.Item; string text; if (plan.Planner.Makings.TryGetValue(item, out var value)) { text = plan.Planner.Make(item); int value2; string text2 = ((text == "") ? value.Materials.FirstOrDefault((string material) => !stock.TryGetValue(material, out value2) || value2 <= 0) : null); if (text2 != null) { string text3 = plan.Planner.Make(text2); text = ((text3 == "") ? text2 : text3); } } else { if (!plan.Planner.Gathers.ContainsKey(item) || !(plan.Planner.Get(item) != "")) { return new Suggestion { Id = "collect:" + item, For = item, Name = "Collect " + missing.Short + " more " + Localized(item), Tier = Tier.Boss, Reason = missing.Where + ", to summon " + goal.Name + ".", Detail = "" }; } text = plan.Planner.Get(item); } bool flag = text?.StartsWith("gather:") ?? false; return plan.ForGoal(item, flag ? text : item, text, Tier.Boss, Localized(item) + label, flag ? "" : reason); } private static IEnumerable<Suggestion> ArmorGoals(Player player, Plan plan, float needed) { //IL_0050: Unknown result type (might be due to invalid IL or missing references) List<ItemData> equippedItems = ((Humanoid)player).GetInventory().GetEquippedItems(); List<ArmorOption> options = new List<ArmorOption>(); Dictionary<string, Suggestion> offers = new Dictionary<string, Suggestion>(); for (int i = 0; i < ArmorSlots.Length; i++) { ItemType type = ArmorSlots[i]; ItemData val = ((IEnumerable<ItemData>)equippedItems).FirstOrDefault((Func<ItemData, bool>)((ItemData item) => item.m_shared.m_itemType == type)); float num = ((val != null) ? val.GetArmor() : 0f); foreach (Recipe item in plan.Recipes.Values.Where((Recipe r) => r.m_item.m_itemData.m_shared.m_itemType == type && !player.IsMaterialKnown(ItemName(r)))) { string text = ItemName(item); float num2 = item.m_item.m_itemData.m_shared.m_armor - num; Suggestion suggestion = plan.ForGoal(text, text, plan.Planner.Make(text), Tier.Armor, Localized(text), "Adds " + Mathf.RoundToInt(num2) + " armor."); if (suggestion != null) { options.Add(new ArmorOption { Id = text, Slot = i, Gain = num2, Cost = Materials(item.m_resources, 1) }); offers[text] = suggestion; } } string text2 = ((val != null) ? plan.AddUpgrade(val) : null); if (text2 != null) { float armorPerLevel = val.m_shared.m_armorPerLevel; Suggestion suggestion2 = plan.ForGoal(text2, text2, plan.Planner.Make(text2), Tier.Armor, "Upgrade " + Localized(val.m_shared.m_name), "Adds " + Mathf.RoundToInt(armorPerLevel) + " armor."); if (suggestion2 != null) { Recipe recipe = ObjectDB.instance.GetRecipe(val); options.Add(new ArmorOption { Id = text2, Slot = i, Gain = armorPerLevel, Cost = Materials(recipe.m_resources, val.m_quality + 1) }); offers[text2] = suggestion2; } } } return PathRules.KeyArmor(options, needed).Select(delegate(string id) { offers[id].Value = options.First((ArmorOption option) => option.Id == id).GainPerMaterial; return offers[id]; }); } private static List<string> Needs(Requirement[] requirements, int quality) { return (from requirement in requirements where (Object)(object)requirement.m_resItem != (Object)null && requirement.GetAmount(quality) > 0 select requirement.m_resItem.m_itemData.m_shared.m_name).ToList(); } private static bool IsUsable(Recipe recipe) { if ((Object)(object)recipe != (Object)null && recipe.m_enabled) { return (Object)(object)recipe.m_item != (Object)null; } return false; } private static string ItemName(Recipe recipe) { return recipe.m_item.m_itemData.m_shared.m_name; } private static IEnumerable<Piece> HammerPieces() { GameObject itemPrefab = ObjectDB.instance.GetItemPrefab("Hammer"); if ((Object)(object)itemPrefab == (Object)null) { return Enumerable.Empty<Piece>(); } return from prefab in itemPrefab.GetComponent<ItemDrop>().m_itemData.m_shared.m_buildPieces.m_pieces where (Object)(object)prefab != (Object)null select prefab.GetComponent<Piece>() into piece where (Object)(object)piece != (Object)null && piece.m_enabled select piece; } private static IEnumerable<Suggestion> Comfort(List<Piece> pieces, HashSet<string> built) { List<Piece> comfort = pieces.Where((Piece piece) => piece.m_comfort > 0).ToList(); return PathRules.ComfortToBuild(comfort.Select((Piece piece) => new ComfortPiece { Id = piece.m_name, Group = (int)piece.m_comfortGroup, Comfort = piece.m_comfort, Built = built.Contains(piece.m_name) }).ToList()).Select(delegate(string id) { Piece val = comfort.First((Piece candidate) => candidate.m_name == id); string text = Costs(val.m_resources); return new Suggestion { Id = id, For = id, Name = Localized(id), Tier = Tier.Comfort, Reason = "Adds " + val.m_comfort + " comfort.", Detail = ((text.Length > 0) ? ("Build: " + text) : "") }; }); } private static Dictionary<string, int> Stock(Player player, List<Piece> nearby) { IEnumerable<ItemData> enumerable = (from piece in nearby select ((Component)piece).GetComponent<Container>() into container where (Object)(object)container != (Object)null select container).SelectMany((Container container) => container.GetInventory().GetAllItems()).Concat(((Humanoid)player).GetInventory().GetAllItems()); Dictionary<string, int> dictionary = new Dictionary<string, int>(); foreach (ItemData item in enumerable) { dictionary.TryGetValue(item.m_shared.m_name, out var value); dictionary[item.m_shared.m_name] = value + item.m_stack; } return dictionary; } private static bool CanMake(Recipe recipe, Dictionary<string, int> stock) { return PathRules.HasAll(from requirement in recipe.m_resources where (Object)(object)requirement.m_resItem != (Object)null && requirement.m_amount > 0 select new KeyValuePair<string, int>(requirement.m_resItem.m_itemData.m_shared.m_name, requirement.m_amount), stock); } private static string CraftDetail(Recipe recipe) { string text = (((Object)(object)recipe.m_craftingStation != (Object)null) ? (" at " + Localized(recipe.m_craftingStation.m_name) + " " + Mathf.Max(recipe.m_minStationLevel, 1)) : ""); return "Craft" + text + ": " + Costs(recipe.m_resources); } private static int Materials(Requirement[] requirements, int quality) { return requirements.Where((Requirement requirement) => (Object)(object)requirement.m_resItem != (Object)null).Sum((Requirement requirement) => requirement.GetAmount(quality)); } private static SharedData Shared(string prefab) { GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(prefab); if ((Object)(object)itemPrefab == (Object)null) { if (MissingItems.Add(prefab)) { log.LogWarning((object)("No item named " + prefab)); } return null; } return itemPrefab.GetComponent<ItemDrop>().m_itemData.m_shared; } private static string ItemTitle(string prefab) { SharedData val = Shared(prefab); if (val == null) { return prefab; } return Localized(val.m_name); } private static string Costs(Requirement[] requirements) { return string.Join(", ", (from requirement in requirements where (Object)(object)requirement.m_resItem != (Object)null && requirement.m_amount > 0 select requirement.m_amount + " " + Localized(requirement.m_resItem.m_itemData.m_shared.m_name)).ToArray()); } private static string Localized(string name) { return Localization.instance.Localize(name); } private static void RecordNearbyPieces(Player player, List<Piece> nearby) { HashSet<string> hashSet = Built(player); int count = hashSet.Count; foreach (Piece item in nearby) { if (item.IsPlacedByPlayer()) { hashSet.Add(item.m_name); } } if (hashSet.Count != count) { player.m_customData["parallel53.criticalpath.built"] = PathRules.FormatSet(hashSet); } } private static HashSet<string> Built(Player player) { player.m_customData.TryGetValue("parallel53.criticalpath.built", out var value); return PathRules.ParseSet(value); } private static List<KeyValuePair<string, string>> Unlocks(Player player) { player.m_customData.TryGetValue("parallel53.criticalpath.unlocks", out var value); return PathRules.ParseUnlocks(value); } private static void RecordUnlock(Player player, string id) { List<KeyValuePair<string, string>> list = Unlocks(player); list.Add(new KeyValuePair<string, string>(id, lastCause)); player.m_customData["parallel53.criticalpath.unlocks"] = PathRules.FormatUnlocks(list); dirty = true; } private static HashSet<string> Knowledge(Player player) { HashSet<string> hashSet = new HashSet<string>(KnownMaterial.Invoke(player)); foreach (KeyValuePair<string, int> item in KnownStations.Invoke(player)) { hashSet.Add(PathRules.StationEntry(item.Key, item.Value)); } return hashSet; } [HarmonyPostfix] [HarmonyPatch(typeof(Player), "Load")] private static void AfterLoad(Player __instance) { seenPlayer = __instance; seenKnowledge = Knowledge(__instance); } [HarmonyPrefix] [HarmonyPatch(typeof(Player), "UpdateKnownRecipesList")] private static void BeforeRecipeCheck(Player __instance) { if ((Object)(object)__instance == (Object)(object)seenPlayer) { lastCause = PathRules.NewDiscovery(seenKnowledge, Knowledge(__instance)); dirty |= lastCause.Length > 0; } } [HarmonyPostfix] [HarmonyPatch(typeof(Player), "UpdateKnownRecipesList")] private static void AfterRecipeCheck(Player __instance) { if ((Object)(object)__instance == (Object)(object)seenPlayer) { seenKnowledge = Knowledge(__instance); lastCause = ""; } } [HarmonyPrefix] [HarmonyPatch(typeof(Player), "AddKnownRecipe")] private static void BeforeRecipe(Player __instance, Recipe recipe, out bool __state) { __state = __instance.IsRecipeKnown(recipe.m_item.m_itemData.m_shared.m_name); } [HarmonyPostfix] [HarmonyPatch(typeof(Player), "AddKnownRecipe")] private static void AfterRecipe(Player __instance, Recipe recipe, bool __state) { if (!__state && (Object)(object)__instance == (Object)(object)Player.m_localPlayer) { RecordUnlock(__instance, recipe.m_item.m_itemData.m_shared.m_name); } } [HarmonyPrefix] [HarmonyPatch(typeof(Player), "AddKnownPiece")] private static void BeforePiece(Player __instance, Piece piece, out bool __state) { __state = __instance.IsRecipeKnown(piece.m_name); } [HarmonyPostfix] [HarmonyPatch(typeof(Player), "AddKnownPiece")] private static void AfterPiece(Player __instance, Piece piece, bool __state) { if (!__state && (Object)(object)__instance == (Object)(object)Player.m_localPlayer) { RecordUnlock(__instance, piece.m_name); } } [HarmonyPostfix] [HarmonyPatch(typeof(Player), "PlacePiece")] private static void AfterPlacePiece(Player __instance, Piece piece) { if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer)) { HashSet<string> hashSet = Built(__instance); if (hashSet.Add(piece.m_name)) { __instance.m_customData["parallel53.criticalpath.built"] = PathRules.FormatSet(hashSet); dirty = true; } } } static CriticalPathPlugin() { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) DamageType[] array = new DamageType[10]; RuntimeHelpers.InitializeArray(array, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/); DamageKinds = (DamageType[])(object)array; WeaponTypes = new HashSet<ItemType> { (ItemType)3, (ItemType)14, (ItemType)22, (ItemType)4 }; ItemType[] array2 = new ItemType[3]; RuntimeHelpers.InitializeArray(array2, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/); ArmorSlots = (ItemType[])(object)array2; GearTypes = new HashSet<ItemType> { (ItemType)3, (ItemType)14, (ItemType)22, (ItemType)4, (ItemType)5, (ItemType)17, (ItemType)19 }; MissingItems = new HashSet<string>(); dirty = true; lastCause = ""; } }