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 RavenScout v1.0.0
plugins/RavenScout.dll
Decompiled a day ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Jotunn.Configs; using Jotunn.Entities; using Jotunn.Managers; using Jotunn.Utils; using Microsoft.CodeAnalysis; using Splatform; using UnityEngine; using UnityEngine.Events; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")] [assembly: AssemblyCompany("RavenScout")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+6752175819ff0a5d3eae7a28de14a8a6f53b57c2")] [assembly: AssemblyProduct("RavenScout")] [assembly: AssemblyTitle("RavenScout")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace RavenScout { internal class Cost { public string Prefab; public int Amount; } internal static class Costs { public static List<Cost> Parse(string text) { List<Cost> list = new List<Cost>(); string[] array = (text ?? "").Split(new char[1] { ',' }); for (int i = 0; i < array.Length; i++) { string[] array2 = array[i].Split(new char[1] { ':' }); if (array2.Length == 2 && array2[0].Trim().Length > 0 && int.TryParse(array2[1].Trim(), out var result) && result > 0) { list.Add(new Cost { Prefab = array2[0].Trim(), Amount = result }); } } return list; } public static ItemData ItemData(string prefabName) { if (!Object.op_Implicit((Object)(object)ObjectDB.instance)) { return null; } GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(prefabName); ItemDrop val = (Object.op_Implicit((Object)(object)itemPrefab) ? itemPrefab.GetComponent<ItemDrop>() : null); if (!Object.op_Implicit((Object)(object)val)) { return null; } return val.m_itemData; } public static string DisplayName(string prefabName) { ItemData val = ItemData(prefabName); if (val == null) { return prefabName; } return Localization.instance.Localize(val.m_shared.m_name); } public static List<string> Missing(List<Cost> costs) { return (from c in costs where ItemData(c.Prefab) == null select c.Prefab).ToList(); } public static string Describe(List<Cost> costs) { if (costs.Count != 0) { return string.Join(", ", costs.Select((Cost c) => $"{c.Amount} {DisplayName(c.Prefab)}")); } return "free"; } public static bool TryPay(Player player, List<Cost> costs) { List<string> list = Missing(costs); if (list.Count > 0) { ((Character)player).Message((MessageType)2, "Needs items this game doesn't have (" + string.Join(", ", list) + ") - see the config", 0, (Sprite)null, false); return false; } Inventory inventory = ((Humanoid)player).GetInventory(); foreach (Cost cost in costs) { ItemData val = ItemData(cost.Prefab); if (inventory.CountItems(val.m_shared.m_name, -1, true) < cost.Amount) { ((Character)player).Message((MessageType)2, $"Not enough {DisplayName(cost.Prefab)} ({cost.Amount} needed)", 0, (Sprite)null, false); return false; } } foreach (Cost cost2 in costs) { inventory.RemoveItem(ItemData(cost2.Prefab).m_shared.m_name, cost2.Amount, -1, true); } return true; } } internal static class FeatherGuide { private class Target { public string Label; public Vector3 Position; } private const float ArrivedDistance = 40f; public static void Use(Player player, ItemData item) { //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_0104: 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_011d: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_012d: 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) if (!Plugin.FeatherGuideOn.Value) { ((Character)player).Message((MessageType)2, "Read it in the tooltip", 0, (Sprite)null, false); return; } List<Target> list = Targets(item); if (list.Count == 0) { ((Character)player).Message((MessageType)2, "This report has nothing for the feather to point to", 0, (Sprite)null, false); return; } int num = UsesLeft(item); if (num <= 0) { ((Character)player).Message((MessageType)2, "The feather has gone still - it can't show the way any more", 0, (Sprite)null, false); return; } Vector3 position = ((Component)player).transform.position; Target target = null; float num2 = float.MaxValue; foreach (Target item2 in list) { float num3 = Scouting.DistanceXZ(position, item2.Position); if (num3 < num2) { num2 = num3; target = item2; } } if (num2 < 40f) { ((Character)player).Message((MessageType)2, "The feather trembles: " + target.Label + " is right here", 0, (Sprite)null, false); return; } item.m_customData["ravenscout_guideuses"] = (num - 1).ToString(); Vector3 val = target.Position - position; val.y = 0f; Vector3 forward = ((Component)player).transform.forward; forward.y = 0f; float num4 = Vector3.SignedAngle(forward, val, Vector3.up); string text = ((Mathf.Abs(num4) <= 30f) ? "ahead" : ((Mathf.Abs(num4) >= 150f) ? "behind you" : ((num4 > 0f) ? "to your right" : "to your left"))); string text2 = num switch { 2 => "1 more time", 1 => "it goes still", _ => $"{num - 1} more times", }; ((Character)player).Message((MessageType)2, "The raven's feather points " + text + ", toward the " + target.Label + " (" + Far(num2) + ") - " + text2, 0, (Sprite)null, false); } public static string TooltipLine(ItemData item) { if (!Plugin.FeatherGuideOn.Value || Targets(item).Count == 0) { return ""; } int num = UsesLeft(item); if (num <= 0) { return "\n\n<color=grey>The feather has gone still.</color>"; } return string.Format("\n\n<color=grey>Use it: the raven's feather points toward the nearest find ({0} use{1} left).</color>", num, (num == 1) ? "" : "s"); } private static string Far(float metres) { if (Plugin.ReportPrecision.Value == Plugin.Precision.Exact) { return $"{Mathf.RoundToInt(metres)} m"; } if (metres < 150f) { return "close"; } if (metres < 700f) { return "not far"; } if (!(metres < 2000f)) { return "far away"; } return "a good walk"; } private static int UsesLeft(ItemData item) { if (!item.m_customData.TryGetValue("ravenscout_guideuses", out var value) || !int.TryParse(value, out var result)) { return 0; } return result; } private static List<Target> Targets(ItemData item) { //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) List<Target> list = new List<Target>(); if (!item.m_customData.TryGetValue("ravenscout_guide", out var value) || string.IsNullOrEmpty(value)) { return list; } string[] array = value.Split(new char[1] { ';' }); for (int i = 0; i < array.Length; i++) { string[] array2 = array[i].Split(new char[1] { '|' }); if (array2.Length == 3 && float.TryParse(array2[1], NumberStyles.Float, CultureInfo.InvariantCulture, out var result) && float.TryParse(array2[2], NumberStyles.Float, CultureInfo.InvariantCulture, out var result2)) { list.Add(new Target { Label = array2[0], Position = new Vector3(result, 0f, result2) }); } } return list; } } [HarmonyPatch(typeof(Container), "GetHoverText")] internal static class ContainerHoverTextPatch { private static void Postfix(Container __instance, ref string __result) { RavenRoost component = ((Component)__instance).GetComponent<RavenRoost>(); if (Object.op_Implicit((Object)(object)component)) { __result += component.GetExtraHoverText(); } } } [HarmonyPatch(typeof(Game), "Start")] internal static class GameStartPatch { private static void Postfix() { ScoutNet.Register(); } } [HarmonyPatch(typeof(Container), "UseItem")] internal static class ContainerUseItemPatch { private static bool Prefix(Container __instance, Humanoid user, ItemData item, ref bool __result) { RavenRoost component = ((Component)__instance).GetComponent<RavenRoost>(); if (Object.op_Implicit((Object)(object)component)) { Player val = (Player)(object)((user is Player) ? user : null); if (val != null) { __result = component.Feed(val, item); return false; } } return true; } } [HarmonyPatch(typeof(ItemData), "GetTooltip", new Type[] { typeof(ItemData), typeof(int), typeof(bool), typeof(float), typeof(int), typeof(bool) })] internal static class ReportTooltipPatch { private static void Postfix(ItemData item, ref string __result) { if (ScoutReportItem.IsReport(item)) { __result += ScoutReportItem.Tooltip(item); } } } [HarmonyPatch(typeof(Humanoid), "UseItem")] internal static class ReportUsePatch { private static bool Prefix(Humanoid __instance, Inventory inventory, ItemData item) { if (ScoutReportItem.IsReport(item)) { Player val = (Player)(object)((__instance is Player) ? __instance : null); if (val != null && !((Object)(object)val != (Object)(object)Player.m_localPlayer)) { if (inventory != null && inventory != ((Humanoid)val).GetInventory()) { return true; } FeatherGuide.Use(val, item); return false; } } return true; } } [HarmonyPatch(typeof(Container), "CanBeRemoved")] internal static class ContainerCanBeRemovedPatch { private static void Postfix(Container __instance, ref bool __result) { if (__result) { RavenRoost component = ((Component)__instance).GetComponent<RavenRoost>(); if (Object.op_Implicit((Object)(object)component) && component.IsAway) { __result = false; } } } } [BepInPlugin("com.ravenscout.valheim", "Raven Scout", "1.0.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] [NetworkCompatibility(/*Could not decode attribute arguments.*/)] public class Plugin : BaseUnityPlugin { internal enum HungryBehaviour { FlySlowly, Refuse } internal enum PinGrouping { Area, Patch, Each } internal enum NoMapBehaviour { Auto, MapOnly, ReportOnly, Both } internal enum Precision { Vague, Rough, Exact } internal enum ShareMode { Private, Everyone } public const string ModGuid = "com.ravenscout.valheim"; public const string ModName = "Raven Scout"; public const string ModVersion = "1.0.0"; public const string RoostPrefabName = "piece_ravenroost"; internal static ManualLogSource Log; internal static ConfigEntry<float> BaseRange; internal static ConfigEntry<float> MinDistance; internal static ConfigEntry<float> SecondsPer100m; internal static ConfigEntry<float> MinTripSeconds; internal static ConfigEntry<int> MaxTripsPerDay; internal static ConfigEntry<float> RevealRadius; internal static ConfigEntry<float> SpotRadius; internal static ConfigEntry<bool> SpotDungeons; internal static ConfigEntry<string> DungeonNames; internal static ConfigEntry<bool> SpotBosses; internal static ConfigEntry<string> BossNames; internal static ConfigEntry<bool> SpotTraders; internal static ConfigEntry<string> TraderNames; internal static ConfigEntry<bool> SpotOther; internal static ConfigEntry<string> OtherNames; internal static ConfigEntry<bool> SpotOres; internal static ConfigEntry<string> OreNames; internal static ConfigEntry<bool> SpotFood; internal static ConfigEntry<string> FoodNames; internal static ConfigEntry<float> FoodPinSpacing; internal static ConfigEntry<float> OrePinSpacing; internal static ConfigEntry<bool> LiveTracking; internal static ConfigEntry<bool> ShowRavenOnMap; internal static ConfigEntry<bool> LiveMessages; internal static ConfigEntry<PinGrouping> FoodPinGrouping; internal static ConfigEntry<PinGrouping> OrePinGrouping; internal static ConfigEntry<ShareMode> ShareFindings; internal static ConfigEntry<NoMapBehaviour> NoMapMode; internal static ConfigEntry<Precision> ReportPrecision; internal static ConfigEntry<KeyCode> RenameKey; internal static ConfigEntry<bool> ShareReports; internal static ConfigEntry<string> CopyCost; internal static ConfigEntry<bool> FeatherGuideOn; internal static ConfigEntry<int> FeatherGuideUses; internal static ConfigEntry<bool> UseFood; internal static ConfigEntry<HungryBehaviour> Hungry; internal static ConfigEntry<float> HungrySpeed; internal static ConfigEntry<int> MaxMeals; internal static ConfigEntry<float> MaxFoodSpeed; internal static ConfigEntry<float> RawMeatSpeed; internal static ConfigEntry<string> RawMeatItems; private Harmony _harmony; private const string OtherNamesV1 = "GoblinCamp=Fuling Village,DrakeNest=Drake Nest,TarPit=Tar Pit,Mistlands_Giant=Giant Remains,Mistlands_Excavation=Dvergr Excavation,WoodVillage=Abandoned Village,StoneHenge=Stone Circle,NorthVillage=Northern Village"; private const string OtherNamesDefault = "GoblinCamp=Fuling Village,DrakeNest=Drake Nest,TarPit=Tar Pit,Mistlands_Giant=Giant Remains,Mistlands_Excavation=Dvergr Excavation,WoodVillage=Abandoned Village,StoneHenge=Stone Circle,NorthVillage=Northern Village,Mistlands_GuardTower=Dvergr Tower,Mistlands_Lighthouse=Dvergr Lighthouse,MorgenHole=Morgen Hole,CharredRuins=Charred Ruins,CharredTowerRuins=Charred Tower,VoltureNest=Volture Nest,HotSpring=Hot Spring,NorthMemorialPlace=Memorial Site"; private void UpgradeDefault(ConfigEntry<string> entry, string oldDefault) { string value = entry.Value; string text = (string)((ConfigEntryBase)entry).DefaultValue; if (value != text && value == oldDefault) { entry.Value = text; Log.LogInfo((object)(((ConfigEntryBase)entry).Definition.Key + ": updated to the new default list")); } } internal static ConfigDescription Synced(string description) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Expected O, but got Unknown //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Expected O, but got Unknown return new ConfigDescription(description, (AcceptableValueBase)null, new object[1] { (object)new ConfigurationManagerAttributes { IsAdminOnly = true } }); } private void Awake() { //IL_0669: Unknown result type (might be due to invalid IL or missing references) //IL_0673: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; BaseRange = ((BaseUnityPlugin)this).Config.Bind<float>("Trip", "BaseRange", 1500f, Synced("How far (metres) a raven with an ordinary meal (x1.0) can fly out. The meal's speed multiplies it: x1.6 food = 2400 m.")); MinDistance = ((BaseUnityPlugin)this).Config.Bind<float>("Trip", "MinDistance", 200f, Synced("Shortest scouting trip, in metres.")); SecondsPer100m = ((BaseUnityPlugin)this).Config.Bind<float>("Trip", "SecondsPer100m", 15f, Synced("Seconds a raven at x1.0 speed needs per 100 m. The trip is there and back, so 1 km out takes 300 s at x1.0. In-game time: sleeping speeds it up.")); MinTripSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Trip", "MinTripSeconds", 30f, Synced("Shortest possible trip, in seconds.")); MaxTripsPerDay = ((BaseUnityPlugin)this).Config.Bind<int>("Trip", "MaxTripsPerDay", 0, Synced("How many trips each roost can start per in-game day. 0 = unlimited (a roost always has only one raven out).")); NoMapMode = ((BaseUnityPlugin)this).Config.Bind<NoMapBehaviour>("Report", "NoMapMode", NoMapBehaviour.Auto, Synced("What the raven brings back. Auto: map reveal and pins, or a Scout Report item in no-map worlds (and for players who turned their own map off). MapOnly, ReportOnly, Both: as named. A no-map world never gets map changes: there MapOnly gives a report instead.")); ReportPrecision = ((BaseUnityPlugin)this).Config.Bind<Precision>("Report", "ReportPrecision", Precision.Vague, Synced("How exact Scout Reports are. Vague: \"about 300 m\", \"far, over 2 km\", north-east. Rough: to the nearest 100 m. Exact: metres and compass degrees.")); RenameKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Keys", "Rename", (KeyCode)110, "While looking at a roost: give it a name (shown on its reports)."); ShareFindings = ((BaseUnityPlugin)this).Config.Bind<ShareMode>("Report", "ShareFindings", ShareMode.Private, Synced("Who gets a returning raven's map reveal and pins. Private: only the player who sent it. Everyone: every player; those offline get the latest trips of every roost when they join. Live tracking (the moving feather) is always just for the sender.")); ShareReports = ((BaseUnityPlugin)this).Config.Bind<bool>("Report", "ShareReports", true, Synced("Show a Copy report button at the roost: anyone can take a copy of its last report, to keep or give to a friend.")); CopyCost = ((BaseUnityPlugin)this).Config.Bind<string>("Report", "CopyCost", "Feathers:1", Synced("What a copy costs, as PrefabName:Amount, comma-separated. Empty = free.")); FeatherGuideOn = ((BaseUnityPlugin)this).Config.Bind<bool>("Report", "FeatherGuide", true, Synced("Using a Scout Report (hotbar key or right-click) makes the raven's feather point left, right, ahead or behind toward the nearest find on it. Never a marker or compass. Turn off for hardcore servers.")); FeatherGuideUses = ((BaseUnityPlugin)this).Config.Bind<int>("Report", "FeatherGuideUses", 3, Synced("How many times each report's feather can point the way.")); RevealRadius = ((BaseUnityPlugin)this).Config.Bind<float>("Spotting", "RevealRadius", 120f, Synced("Half the width of the strip of map the raven reveals along its route, in metres.")); SpotRadius = ((BaseUnityPlugin)this).Config.Bind<float>("Spotting", "SpotRadius", 150f, Synced("How far from its route (metres) the raven spots places and ore.")); SpotDungeons = ((BaseUnityPlugin)this).Config.Bind<bool>("Spotting", "SpotDungeons", true, Synced("Pin dungeons: crypts, caves, mines.")); DungeonNames = ((BaseUnityPlugin)this).Config.Bind<string>("Spotting", "DungeonNames", "Crypt=Burial Chambers,HalfBurried_ForestCrypt=Burial Chambers,SunkenCrypt=Sunken Crypt,TrollCave=Troll Cave,MountainCave=Frost Cave,Mistlands_DvergrTownEntrance=Infested Mine,CharredFortress=Charred Fortress,Hildir_crypt=Smouldering Tomb,Hildir_cave=Howling Cavern,Hildir_plainsfortress=Sealed Tower", Synced("Location prefab name (or start of it) = pin label, comma-separated.")); SpotBosses = ((BaseUnityPlugin)this).Config.Bind<bool>("Spotting", "SpotBosses", true, Synced("Pin boss altars.")); BossNames = ((BaseUnityPlugin)this).Config.Bind<string>("Spotting", "BossNames", "Eikthyrnir=Eikthyr,GDKing=The Elder,Bonemass=Bonemass,Dragonqueen=Moder,GoblinKing=Yagluth,Mistlands_DvergrBossEntrance=The Queen,FaderLocation=Fader,DN_Bossroom=Deep North boss", Synced("Location prefab name (or start of it) = pin label, comma-separated.")); SpotTraders = ((BaseUnityPlugin)this).Config.Bind<bool>("Spotting", "SpotTraders", true, Synced("Pin traders.")); TraderNames = ((BaseUnityPlugin)this).Config.Bind<string>("Spotting", "TraderNames", "Vendor_BlackForest=Haldor,Hildir_camp=Hildir,BogWitch_Camp=Bog Witch", Synced("Location prefab name (or start of it) = pin label, comma-separated.")); SpotOther = ((BaseUnityPlugin)this).Config.Bind<bool>("Spotting", "SpotOther", true, Synced("Pin other interesting places.")); OtherNames = ((BaseUnityPlugin)this).Config.Bind<string>("Spotting", "OtherNames", "GoblinCamp=Fuling Village,DrakeNest=Drake Nest,TarPit=Tar Pit,Mistlands_Giant=Giant Remains,Mistlands_Excavation=Dvergr Excavation,WoodVillage=Abandoned Village,StoneHenge=Stone Circle,NorthVillage=Northern Village,Mistlands_GuardTower=Dvergr Tower,Mistlands_Lighthouse=Dvergr Lighthouse,MorgenHole=Morgen Hole,CharredRuins=Charred Ruins,CharredTowerRuins=Charred Tower,VoltureNest=Volture Nest,HotSpring=Hot Spring,NorthMemorialPlace=Memorial Site", Synced("Location prefab name (or start of it) = pin label, comma-separated.")); UpgradeDefault(OtherNames, "GoblinCamp=Fuling Village,DrakeNest=Drake Nest,TarPit=Tar Pit,Mistlands_Giant=Giant Remains,Mistlands_Excavation=Dvergr Excavation,WoodVillage=Abandoned Village,StoneHenge=Stone Circle,NorthVillage=Northern Village"); SpotOres = ((BaseUnityPlugin)this).Config.Bind<bool>("Spotting", "SpotOres", true, Synced("Pin ore deposits. Only land someone has already been near has ore in it, so far wild land shows none.")); OreNames = ((BaseUnityPlugin)this).Config.Bind<string>("Spotting", "OreNames", "rock4_copper=Copper,rock4_copper_frac=Copper,MineRock_Copper=Copper,MineRock_Tin=Tin,Pickable_Tin=Tin,rock3_silver=Silver,rock3_silver_frac=Silver,silvervein=Silver,silvervein_frac=Silver,MineRock_Obsidian=Obsidian,mudpile=Iron Scrap,mudpile2=Iron Scrap,mudpile_frac=Iron Scrap,mudpile2_frac=Iron Scrap,mudpile_beacon=Iron Scrap,giant_brain=Soft Tissue,giant_brain_frac=Soft Tissue,MineRock_Meteorite=Flametal,FlametalRockstand=Flametal,FlametalRockstand_frac=Flametal,goldvein=Gold,goldvein_frac=Gold", Synced("Ore object prefab name (exact) = pin label, comma-separated. Add :N to the label to need at least N close together.")); SpotFood = ((BaseUnityPlugin)this).Config.Bind<bool>("Spotting", "SpotFood", false, Synced("Pin wild food: berries, mushrooms, thistle, wild seeds. Off by default: like ore, plants only exist in land someone has already been near, so it's a rough guide, not a full list. Picked plants and plants next to anything player-built (farms) are skipped.")); FoodNames = ((BaseUnityPlugin)this).Config.Bind<string>("Spotting", "FoodNames", "RaspberryBush=Raspberries:3,BlueberryBush=Blueberries:3,CloudberryBush=Cloudberries:3,LingonberryBush=Lingonberries:3,Pickable_Mushroom=Mushrooms:2,Pickable_Mushroom_yellow=Yellow Mushrooms,Pickable_Mushroom_blue=Blue Mushrooms,Pickable_Mushroom_Magecap=Magecap,Pickable_Mushroom_JotunPuffs=Jotun Puffs,Pickable_SmokePuff=Smoke Puffs,Pickable_Thistle=Thistle:2,Pickable_Dandelion=Dandelions:3,Pickable_SeedCarrot=Carrot Seeds,Pickable_SeedTurnip=Turnip Seeds,Pickable_SeedOnion=Onion Seeds,Pickable_Barley_Wild=Wild Barley,Pickable_Flax_Wild=Wild Flax,Pickable_Fiddlehead=Fiddlehead,Pickable_RoyalJelly=Royal Jelly", Synced("Plant prefab name (exact) = pin label:N, comma-separated. N = how many must grow close together (within 60 m) for a pin; it stops single raspberry bushes from covering the map. Remove an entry to never pin it.")); FoodPinSpacing = ((BaseUnityPlugin)this).Config.Bind<float>("Spotting", "FoodPinSpacing", 300f, Synced("Within this many metres, patches of the same food share one pin (at the biggest patch, with the total count).")); OrePinSpacing = ((BaseUnityPlugin)this).Config.Bind<float>("Spotting", "OrePinSpacing", 100f, Synced("Within this many metres, deposits of the same ore share one pin (with the total count).")); FoodPinGrouping = ((BaseUnityPlugin)this).Config.Bind<PinGrouping>("Spotting", "FoodPinGrouping", PinGrouping.Area, Synced("How food is pinned. Area: one pin per food within FoodPinSpacing, with the total count. Patch: one pin per patch (plants within 60 m, at least the :N in FoodNames). Each: a pin on every single plant (many pins).")); OrePinGrouping = ((BaseUnityPlugin)this).Config.Bind<PinGrouping>("Spotting", "OrePinGrouping", PinGrouping.Area, Synced("How ore is pinned. Area: one pin per ore within OrePinSpacing, with the total count. Patch: one pin per group of deposits within 60 m. Each: a pin on every single deposit.")); LiveTracking = ((BaseUnityPlugin)this).Config.Bind<bool>("Live", "LiveTracking", true, Synced("Reveal the map and add pins while the raven flies, instead of all at once when it returns.")); ShowRavenOnMap = ((BaseUnityPlugin)this).Config.Bind<bool>("Live", "ShowRavenOnMap", true, "Show your flying raven as a feather on the map and minimap (your own choice, not synced)."); LiveMessages = ((BaseUnityPlugin)this).Config.Bind<bool>("Live", "LiveMessages", true, "Show a top-left message when your flying raven spots something (your own choice, not synced)."); UseFood = ((BaseUnityPlugin)this).Config.Bind<bool>("Food", "UseFood", true, Synced("Ravens eat one stored meal per trip; better food flies farther and faster. Turn off to ignore food entirely.")); Hungry = ((BaseUnityPlugin)this).Config.Bind<HungryBehaviour>("Food", "HungryRaven", HungryBehaviour.FlySlowly, Synced("What a raven does with no meals stored: FlySlowly (at HungrySpeed) or Refuse to fly.")); HungrySpeed = ((BaseUnityPlugin)this).Config.Bind<float>("Food", "HungrySpeed", 0.5f, Synced("Range and speed multiplier for a hungry raven when HungryRaven = FlySlowly.")); MaxMeals = ((BaseUnityPlugin)this).Config.Bind<int>("Food", "MaxMeals", 10, Synced("How many meals a roost can store.")); MaxFoodSpeed = ((BaseUnityPlugin)this).Config.Bind<float>("Food", "MaxFoodSpeed", 4f, Synced("Biggest multiplier a meal can give. Cooked food gives 1 + health / 50, e.g. 30 hp = x1.6, 75 hp = x2.5.")); RawMeatSpeed = ((BaseUnityPlugin)this).Config.Bind<float>("Food", "RawMeatSpeed", 1.25f, Synced("Multiplier for raw meat.")); RawMeatItems = ((BaseUnityPlugin)this).Config.Bind<string>("Food", "RawMeatItems", "RawMeat,DeerMeat,NeckTail,WolfMeat,LoxMeat,SerpentMeat,BugMeat,HareMeat,ChickenMeat,AsksvinMeat,VoltureMeat,BoneMawSerpentMeat", Synced("Comma-separated item prefab names ravens eat raw.")); RoostTiers.Bind(((BaseUnityPlugin)this).Config); PrefabManager.OnVanillaPrefabsAvailable += AddRoostPiece; _harmony = new Harmony("com.ravenscout.valheim"); _harmony.PatchAll(typeof(Plugin).Assembly); Log.LogInfo((object)"Raven Scout 1.0.0 loaded"); } private void AddRoostPiece() { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Expected O, but got Unknown //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Expected O, but got Unknown //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Expected O, but got Unknown //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Expected O, but got Unknown //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Expected O, but got Unknown PrefabManager.OnVanillaPrefabsAvailable -= AddRoostPiece; PieceConfig val = new PieceConfig(); val.Name = "Raven Roost"; val.Description = "Send a raven on a long scouting trip. It maps the land it flies over and pins what it sees."; val.PieceTable = "Hammer"; val.Category = "Misc"; val.CraftingStation = "piece_workbench"; val.Requirements = (RequirementConfig[])(object)new RequirementConfig[3] { new RequirementConfig("Wood", 10, 0, true), new RequirementConfig("Feathers", 4, 0, true), new RequirementConfig("FineWood", 4, 0, true) }; PieceConfig val2 = val; CustomPiece val3 = new CustomPiece("piece_ravenroost", "piece_chest_wood", val2); GameObject piecePrefab = val3.PiecePrefab; Container component = piecePrefab.GetComponent<Container>(); component.m_name = "Raven Roost"; component.m_width = 4; component.m_height = 2; piecePrefab.AddComponent<RavenRoost>(); RavenFlight.Prepare(); try { RoostModel.Decorate(piecePrefab); } catch (Exception arg) { Log.LogError((object)$"Couldn't build the roost model, using the plain chest look: {arg}"); } PieceManager.Instance.AddPiece(val3); ScoutReportItem.Register(); } private void Update() { ScoutWindow.Tick(); if (Scouting.IsActive) { ScoutNet.ServerTick(); ScoutLive.Tick(); HandleKeys(); } } private static void HandleKeys() { //IL_0069: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; if (Object.op_Implicit((Object)(object)localPlayer) && !InventoryGui.IsVisible() && !Menu.IsVisible() && !TextInput.IsVisible() && !Console.IsVisible() && (!Object.op_Implicit((Object)(object)Chat.instance) || !Chat.instance.HasFocus())) { GameObject hoverObject = ((Humanoid)localPlayer).GetHoverObject(); RavenRoost ravenRoost = (Object.op_Implicit((Object)(object)hoverObject) ? hoverObject.GetComponentInParent<RavenRoost>() : null); if (Object.op_Implicit((Object)(object)ravenRoost) && ZInput.GetKeyDown(RenameKey.Value, false)) { TextInput.instance.RequestText((TextReceiver)(object)ravenRoost, "Name this roost", 24); } } } private void OnDestroy() { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } } internal class RavenFlight : MonoBehaviour { private enum Mode { TakeOff, Land } public const float LandingSeconds = 6f; public const float TakeOffSeconds = 12f; private const float MinTakeOffSeconds = 3f; private const float CruiseSpeed = 5f; private const float MaxCruiseSpeed = 30f; private const float ClimbSpeed = 1.6f; private const float CircleSeconds = 3f; private const float FadeSeconds = 1.5f; private const float LandingDistance = 30f; private const float LandingHeight = 14f; private static readonly string[] s_candidatePrefabs = new string[3] { "Crow", "Raven", "Seagal" }; private static GameObject s_template; private static GameObject s_perchedTemplate; private static bool s_warned; private Mode _mode; private Vector3 _origin; private Vector3 _target; private Vector3 _heading; private float _duration; private float _circleSeconds = 3f; private float _cruiseSpeed = 5f; private float _age; private Vector3 _baseScale; public static GameObject Template { get { Prepare(); return s_template; } } public static GameObject PerchedTemplate { get { Prepare(); return s_perchedTemplate; } } public static void Prepare() { //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Expected O, but got Unknown if (Object.op_Implicit((Object)(object)s_template)) { return; } GameObject val = null; string[] array = s_candidatePrefabs; foreach (string text in array) { val = PrefabManager.Instance.GetPrefab(text); if (Object.op_Implicit((Object)(object)val)) { Plugin.Log.LogInfo((object)("Raven visual uses prefab '" + text + "'")); break; } } if (!Object.op_Implicit((Object)(object)val)) { if (!s_warned) { s_warned = true; Plugin.Log.LogWarning((object)"No bird prefab found; ravens will be invisible."); } } else { GameObject val2 = new GameObject("RavenScout_Templates"); val2.SetActive(false); Object.DontDestroyOnLoad((Object)(object)val2); s_template = BuildTemplate(val, val2.transform, landed: false); s_perchedTemplate = BuildTemplate(val, val2.transform, landed: true); } } private static GameObject BuildTemplate(GameObject source, Transform holder, bool landed) { GameObject val = Object.Instantiate<GameObject>(source, holder); ((Object)val).name = (landed ? "RavenScout_RavenPerched" : "RavenScout_Raven"); RandomFlyingBird component = val.GetComponent<RandomFlyingBird>(); if (Object.op_Implicit((Object)(object)component) && !component.m_singleModel && Object.op_Implicit((Object)(object)component.m_flyingModel) && Object.op_Implicit((Object)(object)component.m_landedModel)) { component.m_flyingModel.SetActive(!landed); component.m_landedModel.SetActive(landed); } MonoBehaviour[] componentsInChildren = val.GetComponentsInChildren<MonoBehaviour>(true); for (int i = 0; i < componentsInChildren.Length; i++) { Object.DestroyImmediate((Object)(object)componentsInChildren[i]); } Collider[] componentsInChildren2 = val.GetComponentsInChildren<Collider>(true); for (int i = 0; i < componentsInChildren2.Length; i++) { Object.DestroyImmediate((Object)(object)componentsInChildren2[i]); } Rigidbody[] componentsInChildren3 = val.GetComponentsInChildren<Rigidbody>(true); for (int i = 0; i < componentsInChildren3.Length; i++) { Object.DestroyImmediate((Object)(object)componentsInChildren3[i]); } val.AddComponent<BirdPose>().Flying = !landed; return val; } private static RavenFlight Spawn(Vector3 position, Vector3 facing) { //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_006c: 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) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)s_template)) { Prepare(); if (!Object.op_Implicit((Object)(object)s_template)) { return null; } } facing.y = 0f; if (((Vector3)(ref facing)).sqrMagnitude < 0.01f) { facing = Vector3.forward; } GameObject val = Object.Instantiate<GameObject>(s_template, position, Quaternion.LookRotation(((Vector3)(ref facing)).normalized)); val.SetActive(true); RavenFlight ravenFlight = val.AddComponent<RavenFlight>(); ravenFlight._baseScale = val.transform.localScale; return ravenFlight; } public static float Launch(Vector3 from, Vector3 to, float flightSeconds, float speedMultiplier) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0018: 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) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) float num = Mathf.Clamp(flightSeconds * 0.8f, 3f, 12f); Vector3 val = to - from; val.y = 0f; val = ((((Vector3)(ref val)).sqrMagnitude < 0.01f) ? Vector3.forward : ((Vector3)(ref val)).normalized); RavenFlight ravenFlight = Spawn(from, val); if (!Object.op_Implicit((Object)(object)ravenFlight)) { return 0f; } ravenFlight._mode = Mode.TakeOff; ravenFlight._origin = from; ravenFlight._heading = val; ravenFlight._duration = num; ravenFlight._circleSeconds = Mathf.Min(3f, num * 0.3f); ravenFlight._cruiseSpeed = Mathf.Min(30f, 5f * Mathf.Max(1f, speedMultiplier)); return num; } public static void Land(Vector3 roost, Vector3 fromPos, float seconds) { //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_0002: 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_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_002f: 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_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0065: 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_006f: 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_007e: 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_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0085: 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_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: 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_00b6: Unknown result type (might be due to invalid IL or missing references) Vector3 val = fromPos - roost; val.y = 0f; if (fromPos == Vector3.zero || ((Vector3)(ref val)).sqrMagnitude < 0.01f) { val = Random.insideUnitSphere; val.y = 0f; } val = ((((Vector3)(ref val)).sqrMagnitude < 0.01f) ? Vector3.back : ((Vector3)(ref val)).normalized); Vector3 val2 = roost + val * 30f + Vector3.up * 14f; RavenFlight ravenFlight = Spawn(val2, -val); if (Object.op_Implicit((Object)(object)ravenFlight)) { ravenFlight._mode = Mode.Land; ravenFlight._origin = val2; ravenFlight._target = roost; ravenFlight._heading = -val; ravenFlight._duration = Mathf.Max(1f, seconds); } } private void Update() { _age += Time.deltaTime; if (_mode == Mode.TakeOff) { UpdateTakeOff(); } else { UpdateLanding(); } } private void UpdateTakeOff() { //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_011f: 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_012a: 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) //IL_013d: Unknown result type (might be due to invalid IL or missing references) //IL_0142: Unknown result type (might be due to invalid IL or missing references) //IL_014f: Unknown result type (might be due to invalid IL or missing references) //IL_0154: Unknown result type (might be due to invalid IL or missing references) //IL_0159: Unknown result type (might be due to invalid IL or missing references) //IL_015b: Unknown result type (might be due to invalid IL or missing references) //IL_0160: 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_0045: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0075: 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_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_0167: Unknown result type (might be due to invalid IL or missing references) //IL_0179: Unknown result type (might be due to invalid IL or missing references) //IL_017e: Unknown result type (might be due to invalid IL or missing references) //IL_017f: Unknown result type (might be due to invalid IL or missing references) //IL_018f: Unknown result type (might be due to invalid IL or missing references) Vector3 position; Vector3 val2; if (_age < _circleSeconds) { float num = _age / _circleSeconds; float num2 = num * (float)Math.PI * 2f; float num3 = Mathf.Lerp(0.5f, 3f, num); Vector3 val = Quaternion.LookRotation(_heading) * new Vector3(Mathf.Sin(num2), 0f, 0f - Mathf.Cos(num2)) * num3; position = _origin + val + Vector3.up * (_age * 1.6f * 1.5f); val2 = Quaternion.LookRotation(_heading) * new Vector3(Mathf.Cos(num2), 0f, Mathf.Sin(num2)); } else { float num4 = _age - _circleSeconds; position = _origin + Quaternion.LookRotation(_heading) * new Vector3(0f, 0f, -3f) + Vector3.up * (_circleSeconds * 1.6f * 1.5f) + _heading * (num4 * _cruiseSpeed) + Vector3.up * (num4 * 1.6f); val2 = _heading; } ((Component)this).transform.position = position; ((Component)this).transform.rotation = Quaternion.Slerp(((Component)this).transform.rotation, Quaternion.LookRotation(val2), Time.deltaTime * 6f); FadeAndExpire(_duration); } private void UpdateLanding() { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_006c: 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) float num = Mathf.Clamp01(_age / _duration); float num2 = 1f - (1f - num) * (1f - num); Vector3 position = Vector3.Lerp(_origin, _target, num2); position.y += Mathf.Sin(num * (float)Math.PI) * 2f; ((Component)this).transform.position = position; ((Component)this).transform.rotation = Quaternion.LookRotation(_heading); if (_age >= _duration) { Object.Destroy((Object)(object)((Component)this).gameObject); } } private void FadeAndExpire(float lifetime) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) float num = lifetime - _age; if (num <= 0f) { Object.Destroy((Object)(object)((Component)this).gameObject); } else if (num < 1.5f) { ((Component)this).transform.localScale = _baseScale * (num / 1.5f); } } } internal class BirdPose : MonoBehaviour { private static readonly int s_flyingHash = Animator.StringToHash("flying"); private static readonly int s_flappingHash = Animator.StringToHash("flapping"); public bool Flying; private Animator _animator; private bool _hasFlying; private bool _hasFlapping; private void Start() { _animator = ((Component)this).GetComponentInChildren<Animator>(); if (Object.op_Implicit((Object)(object)_animator)) { AnimatorControllerParameter[] parameters = _animator.parameters; foreach (AnimatorControllerParameter val in parameters) { _hasFlying |= val.nameHash == s_flyingHash; _hasFlapping |= val.nameHash == s_flappingHash; } Apply(); } } private void Update() { Apply(); } private void Apply() { if (Object.op_Implicit((Object)(object)_animator) && ((Behaviour)_animator).isActiveAndEnabled) { if (_hasFlying) { _animator.SetBool(s_flyingHash, Flying); } if (_hasFlapping) { _animator.SetBool(s_flappingHash, Flying); } } } } internal static class RavenFood { public const string MealsKey = "ravenscout_meals"; private static HashSet<string> s_rawMeats; private static string s_rawMeatsSource; public static float SpeedFor(ItemData item) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Invalid comparison between Unknown and I4 if (item == null) { return 0f; } SharedData shared = item.m_shared; if ((int)shared.m_itemType == 2 && shared.m_food > 0f) { return Mathf.Min(Plugin.MaxFoodSpeed.Value, 1f + shared.m_food / 50f); } string item2 = (Object.op_Implicit((Object)(object)item.m_dropPrefab) ? ((Object)item.m_dropPrefab).name : ""); if (!RawMeats().Contains(item2)) { return 0f; } return Plugin.RawMeatSpeed.Value; } private static HashSet<string> RawMeats() { string text = Plugin.RawMeatItems.Value ?? ""; if (s_rawMeats == null || text != s_rawMeatsSource) { s_rawMeatsSource = text; s_rawMeats = new HashSet<string>(from x in text.Split(new char[1] { ',' }) select x.Trim() into x where x.Length > 0 select x); } return s_rawMeats; } public static ItemData BestFood(Inventory inventory) { ItemData result = null; float num = 0f; foreach (ItemData allItem in inventory.GetAllItems()) { float num2 = SpeedFor(allItem); if (num2 > num && !allItem.m_equipped) { result = allItem; num = num2; } } return result; } public static List<float> ReadMeals(ZDO zdo) { List<float> list = new List<float>(); string[] array = zdo.GetString("ravenscout_meals", "").Split(new char[1] { ';' }); for (int i = 0; i < array.Length; i++) { if (float.TryParse(array[i], NumberStyles.Float, CultureInfo.InvariantCulture, out var result) && result > 0f) { list.Add(result); } } list.Sort((float a, float b) => b.CompareTo(a)); return list; } public static void WriteMeals(ZDO zdo, List<float> meals) { meals.Sort((float a, float b) => b.CompareTo(a)); zdo.Set("ravenscout_meals", string.Join(";", meals.Select((float m) => m.ToString("0.###", CultureInfo.InvariantCulture)))); } public static string FormatSpeed(float speed) { return $"x{speed:0.##}"; } } public class RavenRoost : MonoBehaviour, TextReceiver { private const string TripsLogKey = "ravenscout_trips"; private const float LandingViewDistance = 80f; private ZNetView _nview; private Container _container; private static readonly HashSet<string> s_landingsShown = new HashSet<string>(); public float Heading { get; private set; } public float Distance { get; private set; } = 1000f; public int TierIndex { get { if (Zdo == null) { return 0; } return Mathf.Clamp(Zdo.GetInt("ravenscout_tier", 0), 0, RoostTiers.Max); } } internal RoostTier Tier => RoostTiers.Get(TierIndex); internal RoostTier NextTier { get { if (TierIndex >= RoostTiers.Max) { return null; } return RoostTiers.Get(TierIndex + 1); } } private ZDO Zdo { get { if (!Object.op_Implicit((Object)(object)_nview) || !_nview.IsValid()) { return null; } return _nview.GetZDO(); } } internal ScoutTrip Trip { get { if (Zdo == null) { return null; } return Scouting.ReadTrip(Zdo); } } public bool IsAway { get { ScoutTrip trip = Trip; if (trip != null && Scouting.IsActive) { return trip.IsAway(Scouting.Now); } return false; } } public string LastReport { get { ZDO zdo = Zdo; return ((zdo != null) ? zdo.GetString("ravenscout_report", "") : null) ?? ""; } } public string RoostName { get { if (Zdo == null) { return "Raven Roost"; } return Scouting.RoostName(Zdo); } } public bool HasReportToCopy { get { if (Zdo != null) { return !string.IsNullOrEmpty(Zdo.GetString("ravenscout_lasttext", "")); } return false; } } public int PendingReports { get { if (Zdo == null) { return 0; } return Scouting.ReadPending(Zdo).Count; } } public float MaxRange => Scouting.RangeFor(NextMealSpeed * Tier.RangeMultiplier); public float FlightSpeed => NextMealSpeed * Tier.SpeedMultiplier; public float PlannedDistance => Mathf.Clamp(Distance, Plugin.MinDistance.Value, Mathf.Max(Plugin.MinDistance.Value, MaxRange)); public Vector3 PlannedTarget => Scouting.TargetPoint(((Component)this).transform.position, Heading, PlannedDistance); public float PlannedSeconds => Scouting.TripSeconds(Scouting.DistanceXZ(((Component)this).transform.position, PlannedTarget), FlightSpeed); public List<float> Meals { get { if (Zdo == null) { return new List<float>(); } return RavenFood.ReadMeals(Zdo); } } public float NextMealSpeed { get { if (!Plugin.UseFood.Value) { return 1f; } List<float> meals = Meals; if (meals.Count <= 0) { return Mathf.Max(0.05f, Plugin.HungrySpeed.Value); } return meals[0]; } } public bool IsHungry { get { if (Plugin.UseFood.Value) { return Meals.Count == 0; } return false; } } public bool RefusesToFly { get { if (IsHungry) { return Plugin.Hungry.Value == Plugin.HungryBehaviour.Refuse; } return false; } } public string MealsStatus { get { if (!Plugin.UseFood.Value) { return ""; } List<float> meals = Meals; if (meals.Count > 0) { return $"Meals: {meals.Count}/{Mathf.Max(1, Plugin.MaxMeals.Value)} (next {RavenFood.FormatSpeed(meals[0])})"; } if (!RefusesToFly) { return "<color=#ff9a40>Hungry - flies short and slow (" + RavenFood.FormatSpeed(NextMealSpeed) + ")</color>"; } return "<color=#ff6060>Hungry - won't fly until fed</color>"; } } public static bool TripsLimited => Plugin.MaxTripsPerDay.Value > 0; public int TripsToday { get { ZDO zdo = Zdo; string[] array = (((zdo != null) ? zdo.GetString("ravenscout_trips", "") : null) ?? "").Split(new char[1] { ':' }); if (array.Length == 2 && int.TryParse(array[0], out var result) && int.TryParse(array[1], out var result2) && result == Scouting.Day(Scouting.Now)) { return result2; } return 0; } } public int TripsLeft { get { if (!TripsLimited) { return int.MaxValue; } return Mathf.Max(0, Plugin.MaxTripsPerDay.Value - TripsToday); } } public string TripsStatus { get { if (!TripsLimited) { return ""; } int value = Plugin.MaxTripsPerDay.Value; if (TripsLeft <= 0) { return $"<color=#ff6060>Tired ({value}/{value} today) - flies again tomorrow</color>"; } return $"Trips today: {TripsToday}/{value}"; } } private Transform SeatedRaven => ((Component)this).transform.Find("RavenScout_PerchedRaven"); public Vector3 SeatPosition { get { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001a: 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) Transform seatedRaven = SeatedRaven; if (!Object.op_Implicit((Object)(object)seatedRaven)) { return ((Component)this).transform.position + Vector3.up * 1.5f; } return seatedRaven.position; } } private void Awake() { _nview = ((Component)this).GetComponent<ZNetView>(); _container = ((Component)this).GetComponent<Container>(); if (Object.op_Implicit((Object)(object)_nview) && _nview.GetZDO() != null) { ((MonoBehaviour)this).InvokeRepeating("UpdateSeat", 0.5f, 0.5f); ((MonoBehaviour)this).InvokeRepeating("DeliverReports", 1f, 2f); RoostTiers.ValidateItems(); } } public void Upgrade(Player player) { RoostTier nextTier = NextTier; if (Zdo != null && Object.op_Implicit((Object)(object)player) && nextTier != null && EnsureOwner(player) && Costs.TryPay(player, nextTier.Requirements())) { Zdo.Set("ravenscout_tier", nextTier.Index); ((Character)player).Message((MessageType)2, $"Upgraded to {nextTier.Name}: range x{nextTier.RangeMultiplier:0.##}, speed x{nextTier.SpeedMultiplier:0.##}", 0, (Sprite)null, false); Plugin.Log.LogInfo((object)("'" + RoostName + "' upgraded to " + nextTier.Name)); } } public string GetText() { ZDO zdo = Zdo; return ((zdo != null) ? zdo.GetString("ravenscout_name", "") : null) ?? ""; } public void SetText(string text) { if (Zdo != null && EnsureOwner(Player.m_localPlayer)) { Zdo.Set("ravenscout_name", (text ?? "").Trim()); } } public void CopyReport(Player player) { if (Zdo == null || !Object.op_Implicit((Object)(object)player) || !Plugin.ShareReports.Value) { return; } string text = Zdo.GetString("ravenscout_lasttext", ""); if (string.IsNullOrEmpty(text)) { ((Character)player).Message((MessageType)2, "No report to copy yet - send the raven first", 0, (Sprite)null, false); return; } ItemData val = ScoutReportItem.Create(text, RoostName, Zdo.GetInt("ravenscout_lastday", 0), Zdo.GetString("ravenscout_lastguide", "")); Inventory inventory = ((Humanoid)player).GetInventory(); if (val == null || !inventory.CanAddItem(val, -1)) { ((Character)player).Message((MessageType)2, "Your bag is full", 0, (Sprite)null, false); } else if (Costs.TryPay(player, Costs.Parse(Plugin.CopyCost.Value))) { inventory.AddItem(val); ((Character)player).Message((MessageType)2, "A copy of the report is in your bag", 0, (Sprite)null, false); } } private void DeliverReports() { if (Zdo == null || !_nview.IsOwner() || !Scouting.IsActive || _container.GetInventory() == null) { return; } List<PendingReport> list = Scouting.ReadPending(Zdo); if (list.Count == 0) { return; } Inventory inventory = _container.GetInventory(); List<PendingReport> list2 = new List<PendingReport>(); foreach (PendingReport item in list) { ItemData val = ScoutReportItem.Create(item.Text, item.Roost, item.Day, item.Guide); if (val == null || !inventory.CanAddItem(val, -1) || !inventory.AddItem(val)) { list2.Add(item); } } if (list2.Count != list.Count) { Scouting.WritePending(Zdo, list2); } } public void SetHeading(float degrees) { Heading = Mathf.Repeat(Mathf.Round(degrees), 360f); } public void SetDistance(float metres) { Distance = Mathf.Clamp(Mathf.Round(metres / 50f) * 50f, Plugin.MinDistance.Value, Mathf.Max(Plugin.MinDistance.Value, MaxRange)); } public bool Feed(Player player, ItemData item) { if (Zdo == null || !Object.op_Implicit((Object)(object)player)) { return false; } float num = RavenFood.SpeedFor(item); if (num <= 0f) { return false; } if (!EnsureOwner(player)) { return true; } List<float> meals = Meals; int num2 = Mathf.Max(1, Plugin.MaxMeals.Value); if (meals.Count >= num2) { ((Character)player).Message((MessageType)2, "The raven is full", 0, (Sprite)null, false); return true; } if (!((Humanoid)player).GetInventory().RemoveOneItem(item)) { return true; } meals.Add(num); RavenFood.WriteMeals(Zdo, meals); ((Character)player).Message((MessageType)2, Localization.instance.Localize($"The raven ate {item.m_shared.m_name} ({RavenFood.FormatSpeed(num)}: {Scouting.FormatDistance(Scouting.RangeFor(num * Tier.RangeMultiplier))} range) - {meals.Count}/{num2} meals"), 0, (Sprite)null, false); return true; } public void FeedBest(Player player) { ItemData val = RavenFood.BestFood(((Humanoid)player).GetInventory()); if (val == null) { ((Character)player).Message((MessageType)2, "You have nothing the raven will eat", 0, (Sprite)null, false); } else { Feed(player, val); } } private bool EnsureOwner(Player player) { if (Zdo == null) { return false; } if (_nview.IsOwner()) { return true; } if (!_container.IsInUse() && Zdo.GetInt(ZDOVars.s_inUse, 0) == 0) { _nview.ClaimOwnership(); } if (!_nview.IsOwner()) { if (Object.op_Implicit((Object)(object)player)) { ((Character)player).Message((MessageType)2, "Someone else is using this roost", 0, (Sprite)null, false); } return false; } return true; } public void Send(Player player) { //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_0115: 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_015b: Unknown result type (might be due to invalid IL or missing references) //IL_01d2: Unknown result type (might be due to invalid IL or missing references) //IL_01d7: Unknown result type (might be due to invalid IL or missing references) //IL_01e3: Unknown result type (might be due to invalid IL or missing references) //IL_01e8: Unknown result type (might be due to invalid IL or missing references) //IL_01fe: Unknown result type (might be due to invalid IL or missing references) //IL_0203: Unknown result type (might be due to invalid IL or missing references) //IL_028d: Unknown result type (might be due to invalid IL or missing references) if (Zdo == null || !Object.op_Implicit((Object)(object)player) || !Scouting.IsActive || !EnsureOwner(player)) { return; } double now = Scouting.Now; ScoutTrip trip = Trip; if (trip != null && trip.IsAway(now)) { ((Character)player).Message((MessageType)2, "The raven is still out scouting - back in " + Scouting.FormatDuration(trip.ReturnAt - now), 0, (Sprite)null, false); return; } if (TripsLimited && TripsLeft <= 0) { ((Character)player).Message((MessageType)2, "The raven is tired - it rests until tomorrow", 0, (Sprite)null, false); return; } if (RefusesToFly) { ((Character)player).Message((MessageType)2, "Your raven is hungry - feed it first", 0, (Sprite)null, false); return; } float nextMealSpeed = NextMealSpeed; float flightSpeed = FlightSpeed; Vector3 plannedTarget = PlannedTarget; float num = Scouting.DistanceXZ(((Component)this).transform.position, plannedTarget); if (num < Plugin.MinDistance.Value * 0.5f) { ((Character)player).Message((MessageType)2, "That way is the edge of the world", 0, (Sprite)null, false); return; } float num2 = Scouting.TripSeconds(num, flightSpeed); ScoutTrip trip2 = new ScoutTrip { SentAt = now, ReturnAt = now + (double)num2, From = ((Component)this).transform.position, Target = plannedTarget, Sender = player.GetPlayerName(), Meal = nextMealSpeed, SenderNoMap = Game.m_noMap, Tier = TierIndex }; Scouting.WriteTrip(Zdo, trip2); ScoutNet.TripStarted(Zdo.m_uid); List<float> meals = Meals; if (Plugin.UseFood.Value && meals.Count > 0) { meals.RemoveAt(0); RavenFood.WriteMeals(Zdo, meals); } if (TripsLimited) { Zdo.Set("ravenscout_trips", $"{Scouting.Day(now)}:{TripsToday + 1}"); } RavenFlight.Launch(SeatPosition, plannedTarget, num2, flightSpeed); ScoutNet.BroadcastTakeOff(SeatPosition, plannedTarget, num2, flightSpeed); UpdateSeat(); string text = Scouting.CompassName(Scouting.Heading(((Component)this).transform.position, plannedTarget)); ((Character)player).Message((MessageType)2, "Raven flies " + Scouting.FormatDistance(num) + " " + text + " - back in " + Scouting.FormatDuration(num2), 0, (Sprite)null, false); Plugin.Log.LogInfo((object)$"Scout sent {num:0} m {text} ({Heading:0} deg) from {((Component)this).transform.position}, meal {RavenFood.FormatSpeed(nextMealSpeed)}, {num2:0} s"); } private void UpdateSeat() { //IL_00a0: 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_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) Transform seatedRaven = SeatedRaven; if (!Object.op_Implicit((Object)(object)seatedRaven) || !Scouting.IsActive) { return; } ScoutTrip trip = Trip; double now = Scouting.Now; bool flag = trip?.IsAway(now) ?? false; if (((Component)seatedRaven).gameObject.activeSelf == flag) { ((Component)seatedRaven).gameObject.SetActive(!flag); } if (flag) { Player localPlayer = Player.m_localPlayer; double num = trip.ReturnAt - now; float num2 = Mathf.Clamp((float)(trip.ReturnAt - trip.SentAt) * 0.5f, 1.5f, 6f); if (Object.op_Implicit((Object)(object)localPlayer) && num <= (double)num2 && Vector3.Distance(((Component)localPlayer).transform.position, ((Component)this).transform.position) <= 80f && s_landingsShown.Add(trip.Key(Zdo.m_uid))) { RavenFlight.Land(SeatPosition, trip.Target, (float)Math.Max(0.5, num)); } } } public string GetExtraHoverText() { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) if (Zdo == null || !Scouting.IsActive) { return ""; } StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append($"\n<color=orange>{RoostName}</color> <color=grey>{Tier.Name}</color> [<color=yellow><b>{Plugin.RenameKey.Value}</b></color>] Rename"); ScoutTrip trip = Trip; double now = Scouting.Now; if (trip != null && trip.IsAway(now)) { string text = Scouting.CompassName(Scouting.Heading(trip.From, trip.Target)); stringBuilder.Append("\nRaven out scouting " + Scouting.FormatDistance(trip.Distance) + " " + text + " - back in " + Scouting.FormatDuration(trip.ReturnAt - now)); } else if (trip != null && !trip.Reported) { stringBuilder.Append("\n<color=yellow>Raven is back - its news is for " + trip.Sender + "</color>"); } else { stringBuilder.Append("\nRaven ready <color=grey>range " + Scouting.FormatDistance(MaxRange) + "</color>"); } if (Plugin.UseFood.Value) { stringBuilder.Append("\n" + MealsStatus); stringBuilder.Append("\n<color=grey>Use food from your hotbar on the roost to feed</color>"); } if (TripsLimited) { stringBuilder.Append("\n" + TripsStatus); } int pendingReports = PendingReports; if (pendingReports > 0) { stringBuilder.Append(string.Format("\n<color=yellow>{0} Scout Report{1} waiting - make room in the box</color>", pendingReports, (pendingReports == 1) ? "" : "s")); } return stringBuilder.ToString(); } } internal static class RoostModel { private static readonly string[] s_poleCandidates = new string[3] { "wood_pole2", "wood_pole", "wood_pole_log" }; private const float PoleHeight = 2.2f; private const float PoleThickness = 0.2f; private const float BirdScale = 1f; public static void Decorate(GameObject roost) { //IL_0007: 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_000e: Unknown result type (might be due to invalid IL or missing references) Bounds chestBounds = LocalBounds(roost, roost.transform); Vector3? poleTop = AddPole(roost, chestBounds); AddBird(roost, poleTop); } private static Vector3? AddPole(GameObject roost, Bounds chestBounds) { //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_0106: 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_011c: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_014b: Unknown result type (might be due to invalid IL or missing references) //IL_015a: Unknown result type (might be due to invalid IL or missing references) //IL_018d: Unknown result type (might be due to invalid IL or missing references) //IL_019a: Unknown result type (might be due to invalid IL or missing references) //IL_01ab: Unknown result type (might be due to invalid IL or missing references) //IL_01b0: Unknown result type (might be due to invalid IL or missing references) //IL_01b5: Unknown result type (might be due to invalid IL or missing references) //IL_01c1: Unknown result type (might be due to invalid IL or missing references) //IL_01d4: Unknown result type (might be due to invalid IL or missing references) //IL_01e1: Unknown result type (might be due to invalid IL or missing references) //IL_01ed: Unknown result type (might be due to invalid IL or missing references) //IL_01fa: Unknown result type (might be due to invalid IL or missing references) //IL_0206: Unknown result type (might be due to invalid IL or missing references) //IL_021d: Unknown result type (might be due to invalid IL or missing references) //IL_0222: Unknown result type (might be due to invalid IL or missing references) //IL_0224: Unknown result type (might be due to invalid IL or missing references) //IL_0235: Unknown result type (might be due to invalid IL or missing references) //IL_023a: Unknown result type (might be due to invalid IL or missing references) //IL_023d: Unknown result type (might be due to invalid IL or missing references) //IL_0249: Unknown result type (might be due to invalid IL or missing references) //IL_0255: Unknown result type (might be due to invalid IL or missing references) //IL_025f: Unknown result type (might be due to invalid IL or missing references) GameObject val = null; string[] array = s_poleCandidates; foreach (string text in array) { val = PrefabManager.Instance.GetPrefab(text); if (Object.op_Implicit((Object)(object)val)) { Plugin.Log.LogInfo((object)("Roost post uses prefab '" + text + "'")); break; } } if (!Object.op_Implicit((Object)(object)val)) { Plugin.Log.LogWarning((object)"No wooden pole prefab found; roost keeps the plain chest look."); return null; } WearNTear component = val.GetComponent<WearNTear>(); GameObject val2 = Object.Instantiate<GameObject>((Object.op_Implicit((Object)(object)component) && Object.op_Implicit((Object)(object)component.m_new)) ? component.m_new : val, roost.transform); ((Object)val2).name = "RavenScout_Pole"; val2.SetActive(true); MonoBehaviour[] componentsInChildren = val2.GetComponentsInChildren<MonoBehaviour>(true); for (int i = 0; i < componentsInChildren.Length; i++) { Object.DestroyImmediate((Object)(object)componentsInChildren[i]); } val2.transform.localPosition = Vector3.zero; val2.transform.localRotation = Quaternion.identity; val2.transform.localScale = Vector3.one; Bounds val3 = LocalBounds(val2, roost.transform); if (((Bounds)(ref val3)).size.y < 0.01f) { Object.DestroyImmediate((Object)(object)val2); return null; } float num = Mathf.Max(new float[3] { ((Bounds)(ref val3)).size.x, ((Bounds)(ref val3)).size.z, 0.01f }); float num2 = 0.2f / num; val2.transform.localScale = new Vector3(num2, 2.2f / ((Bounds)(ref val3)).size.y, num2); val3 = LocalBounds(val2, roost.transform); Vector3 val4 = default(Vector3); ((Vector3)(ref val4))..ctor(((Bounds)(ref chestBounds)).max.x + ((Bounds)(ref val3)).extents.x + 0.05f - ((Bounds)(ref val3)).center.x, ((Bounds)(ref chestBounds)).min.y - ((Bounds)(ref val3)).min.y, ((Bounds)(ref chestBounds)).center.z - ((Bounds)(ref val3)).center.z); Transform transform = val2.transform; transform.localPosition += val4; val3 = LocalBounds(val2, roost.transform); return new Vector3(((Bounds)(ref val3)).center.x, ((Bounds)(ref val3)).max.y, ((Bounds)(ref val3)).center.z); } private static void AddBird(GameObject roost, Vector3? poleTop) { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0049: 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_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0074: 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_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: 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_00f4: Unknown result type (might be due to invalid IL or missing references) GameObject perchedTemplate = RavenFlight.PerchedTemplate; if (Object.op_Implicit((Object)(object)perchedTemplate)) { GameObject obj = Object.Instantiate<GameObject>(perchedTemplate, roost.transform); ((Object)obj).name = "RavenScout_PerchedRaven"; obj.SetActive(true); obj.transform.localRotation = Quaternion.identity; obj.transform.localScale = perchedTemplate.transform.localScale * 1f; obj.transform.localPosition = Vector3.zero; Bounds val = LocalBounds(obj, roost.transform); Vector3 value = default(Vector3); if (poleTop.HasValue) { value = poleTop.Value; } else { Bounds val2 = LocalBounds(roost, roost.transform); ((Vector3)(ref value))..ctor(((Bounds)(ref val2)).center.x, ((Bounds)(ref val2)).max.y, ((Bounds)(ref val2)).center.z); } obj.transform.localPosition = value - new Vector3(((Bounds)(ref val)).center.x, ((Bounds)(ref val)).min.y, ((Bounds)(ref val)).center.z); } } public static Bounds LocalBounds(GameObject part, Transform space) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //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_00b7: Unknown result type (might be due to invalid IL or missing references) bool any = false; Bounds result = default(Bounds); Matrix4x4 toSpace = space.worldToLocalMatrix; MeshFilter[] componentsInChildren = part.GetComponentsInChildren<MeshFilter>(true); foreach (MeshFilter val in componentsInChildren) { if (Object.op_Implicit((Object)(object)((Component)val).GetComponent<MeshRenderer>()) && IsShown(((Component)val).transform, part.transform)) { Add(val.sharedMesh, ((Component)val).transform); } } SkinnedMeshRenderer[] componentsInChildren2 = part.GetComponentsInChildren<SkinnedMeshRenderer>(true); foreach (SkinnedMeshRenderer val2 in componentsInChildren2) { if (IsShown(((Component)val2).transform, part.transform)) { Add(val2.sharedMesh, ((Component)val2).transform); } } return result; void Add(Mesh mesh, Transform t) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001a: 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_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002f: 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_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0065: 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_0095: Unknown result type (might be due to invalid IL or missing references) //IL_0075: 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_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) if (Object.op_Implicit((Object)(object)mesh)) { Matrix4x4 val3 = toSpace * t.localToWorldMatrix; Bounds bounds = mesh.bounds; for (int j = 0; j < 8; j++) { Vector3 val4 = ((Bounds)(ref bounds)).center + Vector3.Scale(((Bounds)(ref bounds)).extents, new Vector3((float)(((j & 1) != 0) ? 1 : (-1)), (float)(((j & 2) != 0) ? 1 : (-1)), (float)(((j & 4) != 0) ? 1 : (-1)))); Vector3 val5 = ((Matrix4x4)(ref val3)).MultiplyPoint3x4(val4); if (!any) { result = new Bounds(val5, Vector3.zero); any = true; } else { ((Bounds)(ref result)).Encapsulate(val5); } } } } } private static bool IsShown(Transform t, Transform root) { while (Object.op_Implicit((Object)(object)t) && (Object)(object)t != (Object)(object)root) { if (!((Component)t).gameObject.activeSelf) { return false; } t = t.parent; } return true; } } internal class RoostTier { public int Index; public string Name; public string Biome; public ConfigEntry<string> Cost; public ConfigEntry<float> Range; public ConfigEntry<float> Speed; public ConfigEntry<float> Sight; public float RangeMultiplier => Mathf.Max(0.1f, Range.Value); public float SpeedMultiplier => Mathf.Max(0.1f, Speed.Value); public float SightMultiplier => Mathf.Max(0.1f, Sight.Value); public List<Cost> Requirements() { return Costs.Parse(Cost.Value); } } internal static class RoostTiers { public const string TierKey = "ravenscout_tier"; private static readonly List<RoostTier> s_tiers = new List<RoostTier>(); private static bool s_validated; internal static ConfigEntry<int> OresFromTier; internal static ConfigEntry<int> FoodFromTier; public static IReadOnlyList<RoostTier> All => s_tiers; public static int Max => s_tiers.Count - 1; public static RoostTier Get(int index) { return s_tiers[Mathf.Clamp(index, 0, s_tiers.Count - 1)]; } public static void Bind(ConfigFile config) { OresFromTier = config.Bind<int>("Tiers", "OresFromTier", 1, Plugin.Synced("Lowest roost tier whose raven spots ore (0 = Wooden, 1 = Bronze, ...). 0 = every roost.")); FoodFromTier = config.Bind<int>("Tiers", "FoodFromTier", 2, Plugin.Synced("Lowest roost tier whose raven spots wild food (also needs SpotFood = true). 0 = every roost.")); Add(config, "Wooden Roost", "Meadows", "", 1f, 1f, 1f); Add(config, "Bronze Roost", "Black Forest", "Bronze:5,BronzeNails:10", 1.25f, 1.1f, 1f); Add(config, "Iron Roost", "Swamp", "Iron:10,Guck:5", 1.5f, 1.2f, 1.25f); Add(config, "Silver Roost", "Mountains", "Silver:10,WolfPelt:4", 1.75f, 1.35f, 1.25f); Add(config, "Black Metal Roost", "Plains", "BlackMetal:10,LinenThread:10", 2f, 1.5f, 1.25f); Add(config, "Eitr Roost", "Mistlands", "BlackCore:2,Eitr:10,Carapace:10", 2.5f, 1.75f, 1.5f); Add(config, "Flametal Roost", "Ashlands", "FlametalNew:10,CelestialFeather:3", 3f, 2f, 1.5f); Add(config, "Deep North Roost", "Deep North", "FlametalNew:30,MoltenCore:2", 4f, 2.5f, 1.5f); } private static void Add(ConfigFile config, string name, string biome, string cost, float range, float speed, float sight) { int count = s_tiers.Count; string text = $"Tier {count} - {name}"; s_tiers.Add(new RoostTier { Index = count, Name = name, Biome = biome, Cost = config.Bind<string>(text, "UpgradeCost", cost, Plugin.Synced((count == 0) ? "Not used: this is the tier you build." : "Items to upgrade to this tier, as PrefabName:Amount, comma-separated.")), Range = config.Bind<float>(text, "Range", range, Plugin.Synced("Range multiplier; multiplies with BaseRange and the meal.")), Speed = config.Bind<float>(text, "Speed", speed, Plugin.Synced("Speed multiplier; multiplies with the meal.")), Sight = config.Bind<float>(text, "Sight", sight, Plugin.Synced("Multiplies RevealRadius and SpotRadius: how wide a strip this raven maps and sees.")) }); } public static bool SpotsOre(int tier) { if (Plugin.SpotOres.Value) { return tier >= OresFromTier.Value; } return false; } public static bool SpotsFood(int tier) { if (Plugin.SpotFood.Value) { return tier >= FoodFromTier.Value; } return false; } public static void ValidateItems() { if (s_validated || !Object.op_Implicit((Object)(object)ObjectDB.instance)) { return; } s_validated = true; foreach (RoostTier item in s_tiers.Skip(1)) { List<string> list = Costs.Missing(item.Requirements()); if (list.Count > 0) { Plugin.Log.LogWarning((object)(item.Name + ": unknown upgrade item(s) " + string.Join(", ", list) + " - this tier can't be reached until its UpgradeCost is changed in the config.")); } } } } internal class ScoutTrip { public double SentAt; public double ReturnAt; public Vector3 From; public Vector3 Target; public string Sender = ""; public float Meal = 1f; public bool Reported; public bool SenderNoMap; public int Tier; public float Distance => Scouting.DistanceXZ(From, Target); public bool IsAway(double now) { return now < ReturnAt; } public string Key(ZDOID roost) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) return $"{roost}:{SentAt:R}"; } } internal class Finding { public enum Kind { Dungeon, Boss, Trader, Other, Ore, Food } public Kind Type; public string Label; public Vector3 Position; public int Count = 1; } internal class TripResult { public string Key; public double SentAt; public double ReturnAt; public Vector3 From; public Vector3 Target; public float RevealRadius; public string Report; public bool ApplyMap = true; public bool GaveReport; public string RoostName = ""; public string Sender = ""; public List<Finding> Findings = new List<Finding>(); public string ReportText; public string Guide; } internal class PendingReport { public string Text; public string Roost; public int Day; public string Guide = ""; } internal static class Scouting { private class NameRule { public readonly Finding.Kind Kind; public readonly Dictionary<string, string> Names; public NameRule(Finding.Kind kind, string names) { Kind = kind; Names = ParseNames(names); } } private class ObjectRule { public Finding.Kind Kind; public string Label; public int MinCount; } public const string TripKey = "ravenscout_trip"; public const string ReportKey = "ravenscout_report"; public const string NameKey = "ravenscout_name"; public const string PendingKey = "ravenscout_pending"; public const string LastTextKey = "ravenscout_lasttext"; public const string LastDayKey = "ravenscout_lastday"; public const string LastGuideKey = "ravenscout_lastguide"; public const string SharedKey = "ravenscout_shared"; public const int SharedHistory = 10; private const int PendingVersionMarker = -2; public const string DefaultRoostName = "Raven Roost"; private const int TripVersion = 3; private const int ResultVersion = 4; private const float WorldRadius = 10000f; private static readonly FieldInfo s_objectsBySector = AccessTools.Field(typeof(ZDOMan), "m_objectsBySector"); private static readonly HashSet<string> s_reportedUnknownLocations = new HashSet<string>(); private static readonly HashSet<string> s_checkedNames = new HashSet<string>(); private const int GuideTargets = 8; private const float ClusterDistance = 60f; public static bool IsActive { get { if (Object.op_Implicit((Object)(object)ZNet.instance) && ZDOMan.instance != null) { return ZRoutedRpc.instance != null; } return false; } } public static bool IsServer { get { if (Object.op_Implicit((Object)(object)ZNet.instance)) { return ZNet.instance.IsServer(); } return false; } } public static double Now => ZNet.instance.GetTimeSeconds(); public static bool WorldHasNoMap { get { if (Object.op_Implicit((Object)(object)ZoneSystem.instance)) { return ZoneSystem.instance.GetGlobalKey((GlobalKeys)31); } return false; } } public static int Day(double time) { if (!Object.op_Implicit((Object)(object)EnvMan.instance)) { return 0; } return EnvMan.instance.GetDay(time); } public static ScoutTrip ReadTrip(ZDO zdo) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Expected O, but got Unknown //IL_006d: 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_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) byte[] array = ((zdo != null) ? zdo.GetByteArray("ravenscout_trip", (byte[])null) : null); if (array == null || array.Length == 0) { return null; } ZPackage val = new ZPackage(array); int num = val.ReadInt(); if (num < 1 || num > 3) { Plugin.Log.LogWarning((object)$"Unknown trip version {num}; ignoring."); return null; } ScoutTrip scoutTrip = new ScoutTrip { SentAt = val.ReadDouble(), ReturnAt = val.ReadDouble(), From = val.ReadVector3(), Target = val.ReadVector3(), Sender = val.ReadString(), Meal = val.ReadSingle(), Reported = val.ReadBool() }; if (num >= 2) { scoutTrip.SenderNoMap = val.ReadBool(); } if (num >= 3) { scoutTrip.Tier = val.ReadInt(); } return scoutTrip; } public static void WriteTrip(ZDO zdo, ScoutTrip trip) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown //IL_0027: 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) ZPackage val = new ZPackage(); val.Write(3); val.Write(trip.SentAt); val.Write(trip.ReturnAt); val.Write(trip.From); val.Write(trip.Target); val.Write(trip.Sender ?? ""); val.Write(trip.Meal); val.Write(trip.Reported); val.Write(trip.SenderNoMap); val.Write(trip.Tier); zdo.Set("ravenscout_trip", val.GetArray()); } public static string RoostName(ZDO zdo) { string text = ((zdo != null) ? zdo.GetString("ravenscout_name", "") : null); if (!string.IsNullOrEmpty(text)) { return text; } return "Raven Roost"; } public static List<PendingReport> ReadPending(ZDO zdo) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Expected O, but got Unknown List<PendingReport> list = new List<PendingReport>(); byte[] array = ((zdo != null) ? zdo.GetByteArray("ravenscout_pending", (byte[])null) : null); if (array == null || array.Length == 0) { return list; } ZPackage val = new ZPackage(array); int num = val.ReadInt(); bool flag = num == -2; int num2 = (flag ? val.ReadInt() : num); for (int i = 0; i < num2; i++) { PendingReport pendingReport = new PendingReport { Text = val.ReadString(), Roost = val.ReadString(), Day = val.ReadInt() }; if (flag) { pendingReport.Guide = val.ReadString(); } list.Add(pendingReport); } return list; } public static void WritePending(ZDO zdo, List<PendingReport> list) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown ZPackage val = new ZPackage(); val.Write(-2); val.Write(list.Count); foreach (PendingReport item in list) { val.Write(item.Text ?? ""); val.Write(item.Roost ?? ""); val.Write(item.Day); val.Write(item.Guide ?? ""); } zdo.Set("ravenscout_pending", val.GetArray()); } public static List<byte[]> ReadShared(ZDO zdo) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Expected O, but got Unknown List<byte[]> list = new List<byte[]>(); byte[] array = ((zdo != null) ? zdo.GetByteArray("ravenscout_shared", (byte[])null) : null); if (array == null || array.Length == 0) { return list; } ZPackage val = new ZPackage(array); int num = val.ReadInt(); for (int i = 0; i < num; i++) { list.Add(val.ReadByteArray()); } return list; } public static void AddShared(ZDO zdo, byte[] result) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Expected O, but got Unknown List<byte[]> list = ReadShared(zdo); list.Add(result); if (list.Count > 10) { list.RemoveRange(0, list.Count - 10); } ZPackage val = new ZPackage(); val.Write(list.Count); foreach (byte[] item in list) { val.Write(item); } zdo.Set("ravenscout_shared", val.GetArray()); } public static List<ZDO> GetAllRoostZdos() { List<ZDO> list = new List<ZDO>(); int num = 0; while (!ZDOMan.instance.GetAllZDOsWithPrefabIterative("piece_ravenroost", list, ref num)) { } list.RemoveAll((ZDO z) => z == null || !z.IsValid()); return list; } public static float DistanceXZ(Vector3 a, Vector3 b) { //IL_0000: 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_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_0017: 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) return Vector2.Distance(new Vector2(a.x, a.z), new Vector2(b.x, b.z)); } public static float RangeFor(float meal) { return Mathf.Max(Plugin.MinDistance.Value, Plugin.BaseRange.Value * meal); } public static float TripSeconds(float distance, float meal) { return Mathf.Max(Plugin.MinTripSeconds.Value, 2f * distance / 100f * Plugin.SecondsPer100m.Value / Mathf.Max(0.05f, meal)); } public static Vector3 TargetPoint(Vector3 from, float degrees, float distance) { //IL_0020: 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: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0045: 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_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004d: 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) float num = degrees * ((float)Math.PI / 180f); Vector3 val = default(Vector3); ((Vector3)(ref val))..ctor(Mathf.Sin(num), 0f, Mathf.Cos(num)); Vector3 val2 = from + val * distance; for (int i = 0; i < 400; i++) { Vector2 val3 = new Vector2(val2.x, val2.z); if (!(((Vector2)(ref val3)).magnitude > 10000f) || !(distance > 0f)) { break; } distance = Mathf.Max(0f, distance - 50f); val2 = from + val * distance; } val2.y = from.y; return val2; } public static string CompassName(float degrees) { string[] obj = new string[8] { "N", "NE", "E", "SE", "S", "SW", "W", "NW" }; int num = Mathf.RoundToInt(Mathf.Repeat(degrees, 360f) / 45f) % 8; return obj[num]; } public static float Heading(Vector3 from, Vector3 to) { //IL_0000: 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_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) return Mathf.Repeat(Mathf.Atan2(to.x - from.x, to.z - from.z) * 57.29578f, 360f); } public static string FormatDuration(double seconds) { if (seconds < 0.0) { seconds = 0.0; } int num = Mathf.CeilToInt((float)seconds); if (num < 3600) { return $"{num / 60}:{num % 60:00}"; } return $"{num / 3600}h {num % 3600 / 60:00}m"; } public static string FormatDistance(float metres) { if (!(metres >= 1000f)) { return $"{Mathf.RoundToInt(metres)} m"; } return $"{metres / 1000f:0.0} km"; } private static float DistanceToRoute(Vector3 p, Vector3 a, Vector3 b) { //IL_0002: 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) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0023: 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_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0074: 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) Vector2 val = default(Vector2); ((Vector2)(ref val))..ctor(p.x - a.x, p.z - a.z); Vector2 val2 = default(Vector2); ((Vector2)(ref val2))..ctor(b.x - a.x, b.z - a.z); float num = ((((Vector2)(ref val2)).sqrMagnitude < 0.01f) ? 0f : Mathf.Clamp01(Vector2.Dot(val, val2) / ((Vector2)(ref val2)).sqrMagnitude)); Vector2 val3 = val - val2 * num; return ((Vector2)(ref val3)).magnitude; } public static TripResult Survey(ScoutTrip trip, string key) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) TripResult tripResult = new TripResult { Key = key, SentAt = trip.SentAt, ReturnAt = trip.ReturnAt, From = trip.From, Target = trip.Target, RevealRadius = Plugin.RevealRadius.Value * RoostTiers.Get(trip.Tier).SightMultiplier }; float radius = Mathf.Max(0f, Plugin.SpotRadius.Value * RoostTiers.Get(trip.Tier).SightMultiplier); FindPlaces(trip, radius, tripResult.Findings); bool wildLand = FindObjects(trip, radius, tripResult.Findings); tripResult.Report = Report(trip, tripResult.Findings, wildLand); tripResult.ReportText = ScoutWords.Write(trip, tripResult.Findings, wildLand); tripResult.Guide = GuideFor(tripResult.Findings); return tripResult; } public static string GuideFor(List<Finding> findings) { Finding.Kind[] order = new Finding.Kind[6] { Finding.Kind.Boss, Finding.Kind.Dungeon, Finding.Kind.Trader, Finding.Kind.Other, Finding.Kind.Ore, Finding.Kind.Food }; IEnumerable<Finding> source = findings.OrderBy((Finding f) => Array.IndexOf(order, f.Type)).Take(8); return string.Join(";", source.Select((Finding f) => (f.Label ?? "").Replace("|", "/").Replace(";", ",") + "|" + f.Position.x.ToString("0.#", CultureInfo.InvariantCulture) + "|" + f.Position.z.ToString("0.#", CultureInfo.InvariantCulture))); } private static void FindPlaces(ScoutTrip trip, float radius, List<Finding> findings) { //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: 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_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_014a: Unknown result type (might be due to invalid IL or missing references) //IL_014c: Unknown result type (might be due to invalid IL or missing references) //IL_0151: Unknown result type (might be due to invalid IL or missing references) List<NameRule> list = new List<NameRule>(); if (Plugin.SpotDungeons.Value) { list.Add(new NameRule(Finding.Kind.Dungeon, Plugin.DungeonNames.Value)); } if (Plugin.SpotBosses.Value) { list.Add(new NameRule(Finding.Kind.Boss, Plugin.BossNames.Value)); } if (Plugin.SpotTraders.Value) { list.Add(new NameRule(Finding.Kind.Trader, Plugin.TraderNames.Value)); } if (Plugin.SpotOther.Value) { list.Add(new NameRule(Finding.Kind.Other, Plugin.OtherNames.Value)); } if (list.Count == 0 || !Object.op_Implicit((Object)(object)ZoneSystem.instance)) { return; } HashSet<string> hashSet = new HashSet<string>(); foreach (LocationInstance location in ZoneSystem.instance.GetLocationList()) { if (location.m_location != null && !(DistanceToRoute(location.m_position, trip.From, trip.Target) > radius)) { string text = location.m_location.m_prefab.Name ?? location.m_location.m_prefabName ?? ""; if (!Match(list, text, out var kind, out var label)) { hashSet.Add(text); continue; } findings.Add(new Finding { Type = kind, Label = label, Position = location.m_position }); } } List<string> list2 = (from n in hashSet where s_reportedUnknownLocations.Add(n) orderby n select n).ToList(); if (list2.Count > 0) { Plugin.Log.LogInfo((object)("Raven flew past places it doesn't pin: " + string.Join(", "