Decompiled source of How to Webfish v1.0.1
BepInEx/plugins/WFxHTF/WFxHTF.Adapter.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; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Text; using System.Threading; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using FishNet; using FishNet.Connection; using FishNet.Managing.Client; using FishNet.Managing.Object; using FishNet.Object; using HarmonyLib; using Steamworks; using UnityEngine; using UnityEngine.SceneManagement; using WFxHTF.Core; using WFxHTF.Data; using WFxHTF.Runtime; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("WFxHTF")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.1.0.0")] [assembly: AssemblyInformationalVersion("0.1.0+166b9aae0d28865ba70c4894450aeb4db4b21aeb")] [assembly: AssemblyProduct("WFxHTF.Adapter")] [assembly: AssemblyTitle("WFxHTF.Adapter")] [assembly: AssemblyVersion("0.1.0.0")] namespace WFxHTF.Adapter; [BepInPlugin("com.wfxhtf.adapter", "WFxHTF Adapter", "1.0.1")] [BepInDependency(/*Could not decode attribute arguments.*/)] public sealed class AdapterPlugin : BaseUnityPlugin { public const string Guid = "com.wfxhtf.adapter"; public static ManualLogSource Log; private readonly List<string> _ok = new List<string>(); private readonly List<string> _failed = new List<string>(); private Harmony _harmony; private ConfigEntry<KeyCode> _placeKey; private ConfigEntry<bool> _economyLog; private ConfigEntry<KeyCode> _lureKey; private ConfigEntry<KeyCode> _charmKey; private void Awake() { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; _harmony = new Harmony("com.wfxhtf.adapter"); _placeKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Keys", "PlaceAquarium", (KeyCode)112, "Host: place the packed aquarium."); _lureKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Keys", "WebFishingLure", (KeyCode)108, "Switch your Web Fishing Lure on and off (once you've bought it)."); _charmKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Keys", "ShinyCharm", (KeyCode)108, "Ctrl + this switches your Shiny Charm on and off (once you've bought it)."); _economyLog = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "EconomyLog", false, "Host: log catches, sales and tank income to %LOCALAPPDATA%\\WFxHTF\\logs (economy check)."); Hook("native journal", delegate { GameHooks.NativeJournalOpen = StaticBool("PlayerThinking", "IsThinking"); GameHooks.NativeJournalHint = "Open your journal (Tab) first, then press " + (((object)Unsafe.As<KeyCode, KeyCode>(ref Plugin.Instance.JournalPanel?.ToggleKey)/*cast due to .constrained prefix*/).ToString() ?? "J"); }); Hook("input block", delegate { InputBlockPatch.Install(_harmony); }); Hook("cursor", delegate { InputBlockPatch.InstallCursor(); }); bool num = Hook("world aquarium", InstallTank); if (num) { Hook("save hook", delegate { SavePatch.Install(_harmony); }); } if (num) { Hook("interact prompt", delegate { TankInteractable.Check(); }); } if (num) { Hook("WF fish", InstallWfFish); } if (num) { Hook("slot machine", delegate { SlotMachinePatch.Install(_harmony); }); } if (num) { Hook("shiny creatures", delegate { NativeShiny.Install(_harmony); Plugin instance2 = Plugin.Instance; if ((Object)(object)((instance2 != null) ? instance2.Console : null) != (Object)null) { NativeShiny.Instance.RegisterCommands(Plugin.Instance.Console); } }); } if (num) { Hook("TNT fish", delegate { if (WfFishService.Instance == null) { throw new InvalidOperationException("needs WF fish"); } WfFishService.InstallTnt(_harmony); }); } if (num) { Hook("hotbar icon", delegate { HotbarIconPatch.Install(_harmony); }); } if (num) { Hook("rain", delegate { WeatherHooks.Install(); TankService.Instance.RainOk = true; }); } if (num) { Hook("tank attacker", delegate { ThreatController.Check(); TankService.Instance.ThreatHookOk = WfFishService.Instance != null; }); } if (num) { Hook("multiplayer", InstallNet); } if (num) { Hook("shop", delegate { //IL_002a: 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_003b: 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) if (WfFishService.Instance == null) { throw new InvalidOperationException("needs WF fish"); } KeyItemService.Check(); KeyItemService keyItemService = ((Component)this).gameObject.AddComponent<KeyItemService>(); keyItemService.LureKey = _lureKey.Value; keyItemService.CharmKey = _charmKey.Value; Plugin instance2 = Plugin.Instance; if ((Object)(object)((instance2 != null) ? instance2.Console : null) != (Object)null) { keyItemService.RegisterCommands(Plugin.Instance.Console); } }); } if (num) { Hook("economy log", delegate { EconomyLog.Install(_harmony, _economyLog.Value); Plugin instance2 = Plugin.Instance; if ((Object)(object)((instance2 != null) ? instance2.Console : null) != (Object)null) { EconomyLog.Instance.RegisterCommands(Plugin.Instance.Console); } }); } string text = $"{_ok.Count} hooks ok" + ((_ok.Count > 0) ? (" (" + string.Join(", ", _ok) + ")") : "") + ((_failed.Count > 0) ? ("; FAILED: " + string.Join("; ", _failed)) : ""); Plugin instance = Plugin.Instance; if (instance != null) { instance.Feature("adapter", text); } if (_failed.Count > 0) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("adapter: " + text)); } else { ((BaseUnityPlugin)this).Logger.LogInfo((object)("adapter: " + text)); } } private void InstallTank() { //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) Game.Check(); GamePorts.Check(); GameHooks.NativeFishModel = NativeFish.Model; GameHooks.NativeFishName = NativeFish.Name; TankService tankService = ((Component)this).gameObject.AddComponent<TankService>(); tankService.PlaceKey = _placeKey.Value; Plugin instance = Plugin.Instance; if ((Object)(object)((instance != null) ? instance.Console : null) != (Object)null) { tankService.RegisterCommands(Plugin.Instance.Console); } } private void InstallNet() { NetService.Check(); NetService netService = ((Component)this).gameObject.AddComponent<NetService>(); Plugin instance = Plugin.Instance; if ((Object)(object)((instance != null) ? instance.Console : null) != (Object)null) { netService.RegisterCommands(Plugin.Instance.Console); } } private void InstallWfFish() { WfFishService.Install(_harmony); Plugin instance = Plugin.Instance; if ((Object)(object)((instance != null) ? instance.Console : null) != (Object)null) { WfFishService.Instance.RegisterCommands(Plugin.Instance.Console); } } private bool Hook(string feature, Action resolve) { try { resolve(); _ok.Add(feature); return true; } catch (Exception ex) { _failed.Add(feature + ": " + ex.GetType().Name + ": " + ex.Message); return false; } } private static Func<bool> StaticBool(string typeName, string property) { PropertyInfo propertyInfo = (AccessTools.TypeByName(typeName) ?? throw new MissingMemberException("type " + typeName + " not found")).GetProperty(property, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) ?? throw new MissingMemberException(typeName + "." + property + " not found"); if (propertyInfo.PropertyType != typeof(bool)) { throw new MissingMemberException(typeName + "." + property + " is " + propertyInfo.PropertyType.Name + ", not bool"); } MethodInfo method = propertyInfo.GetGetMethod(nonPublic: true) ?? throw new MissingMemberException(typeName + "." + property + " has no getter"); return (Func<bool>)Delegate.CreateDelegate(typeof(Func<bool>), method); } } internal sealed class EconomyLog { private bool _enabled; private SessionTally _t = new SessionTally(); private JsonLinesLog _log; private readonly HashSet<Item> _hooked = new HashSet<Item>(); private float _nextSnapshot; private bool _warned; private const int KeepLogs = 40; public static EconomyLog Instance { get; private set; } public static string Dir => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "WFxHTF", "logs"); private static double Now => Game.PlaytimeSeconds; public static void Install(Harmony h, bool enabled) { //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Expected O, but got Unknown Game.Require(typeof(MoneyManager), "SellItem", "Instance", "_money"); Game.Require(typeof(Item), "TotalWorth", "Currency", "Creature"); Instance = new EconomyLog { _enabled = enabled }; h.Patch((MethodBase)AccessTools.Method(typeof(MoneyManager), "SellItem", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(EconomyLog), "SellPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } private void Write(string kind, params (string, object)[] fields) { try { _t.Seen(Now); JsonLinesLog log = _log; if (log != null) { log.Write(Now, kind, fields); } } catch (Exception ex) { if (!_warned) { _warned = true; AdapterPlugin.Log.LogWarning((object)("economy log: " + ex.Message)); } } } public void OnWorldLoaded(string world) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Expected O, but got Unknown Close(); _t = new SessionTally(); _hooked.Clear(); if (!_enabled) { return; } try { Prune(); _log = new JsonLinesLog(Path.Combine(Dir, $"economy_{DateTime.Now:yyyyMMdd_HHmmss}.jsonl")); Write("start", ("world", world), ("mod", "1.0.1"), ("game", Application.version)); AdapterPlugin.Log.LogInfo((object)("economy log: " + _log.Path)); } catch (Exception ex) { AdapterPlugin.Log.LogWarning((object)("economy log off: " + ex.Message)); _log = null; } } public void OnWorldUnloaded() { if (_log != null) { Write("end", ("summary", _t.Report())); } Close(); } private void Close() { try { JsonLinesLog log = _log; if (log != null) { log.Dispose(); } } catch { } _log = null; } public void Tick(Aquarium tank) { if (!(Time.unscaledTime < _nextSnapshot)) { _nextSnapshot = Time.unscaledTime + 60f; if (tank != null) { _t.Tray(tank.State.TrayCoins); } int num = (((Object)(object)MoneyManager.Instance != (Object)null) ? MoneyManager.Instance._money.Value : MoneyManager.Money); Write("snapshot", ("money", num), ("tray", (tank != null) ? Math.Round(tank.State.TrayCoins, 2) : 0.0), ("tankFish", tank?.State.Fish.Count ?? 0), ("players", Game.PlayerIds.Length)); } } private static void Prune() { try { if (!Directory.Exists(Dir)) { return; } foreach (FileInfo item in (from f in new DirectoryInfo(Dir).GetFiles("economy_*.jsonl") orderby f.Name descending select f).Skip(39)) { item.Delete(); } } catch (Exception ex) { AdapterPlugin.Log.LogWarning((object)("economy log: couldn't tidy old logs: " + ex.Message)); } } public void OnBite(Item item, WfItemTag tag, string bait, bool generalLure, ulong by = 0uL, float shinyChance = 0f) { //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Expected I4, but got Unknown if ((Object)(object)item == (Object)null) { return; } if (_hooked.Count > 200) { _hooked.RemoveWhere((Item i) => (Object)(object)i == (Object)null); } _hooked.Add(item); if (tag != null) { SessionTally t = _t; t.WfBites++; _t.WfQualities[Mathf.Clamp((int)tag.Quality, 0, _t.WfQualities.Length - 1)]++; if (tag.Shiny) { SessionTally t2 = _t; t2.ShinyBites++; } Write("bite", ("wf", true), ("species", tag.SpeciesId), ("quality", ((object)Unsafe.As<Quality, Quality>(ref tag.Quality)/*cast due to .constrained prefix*/).ToString()), ("shiny", tag.Shiny), ("kg", Math.Round(tag.WeightKg, 2)), ("worth", (int)((float)tag.Worth * tag.WeightMultiplier)), ("lure", tag.LureTier), ("bait", bait), ("by", by.ToString()), ("shinyChance", shinyChance)); } else { if ((Object)(object)item.Creature != (Object)null) { SessionTally t3 = _t; t3.NativeBites++; } Write("bite", ("wf", false), ("species", NativeFish.IdOf(item)), ("creature", (Object)(object)item.Creature != (Object)null), ("worth", item.TotalWorth), ("bait", bait), ("generalLure", generalLure), ("shiny", NativeShiny.Instance?.Is(item) ?? false), ("by", by.ToString()), ("shinyChance", shinyChance)); } } public void OnTnt(WfItemTag tag) { SessionTally t = _t; t.TntWf++; Write("tnt", ("species", tag.SpeciesId), ("quality", ((object)Unsafe.As<Quality, Quality>(ref tag.Quality)/*cast due to .constrained prefix*/).ToString()), ("shiny", tag.Shiny), ("worth", (int)((float)tag.Worth * tag.WeightMultiplier))); } public void OnDeath(Creature c, WfItemTag tag) { if (!((Object)(object)c == (Object)null) && _hooked.Remove((Item)(object)c)) { if (tag != null) { SessionTally t = _t; t.WfLanded++; } else { SessionTally t2 = _t; t2.NativeLanded++; } Write("landed", ("wf", tag != null), ("species", tag?.SpeciesId ?? NativeFish.IdOf((Item)(object)c))); } } private static void SellPostfix(Item item) { try { if (Game.IsHost) { Instance?.OnSold(item); } } catch (Exception ex) { AdapterPlugin.Log.LogWarning((object)("economy log (sale): " + ex.Message)); } } private void OnSold(Item item) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Invalid comparison between Unknown and I4 //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_0136: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)item == (Object)null)) { WfItemTag val = WfFishService.Instance?.TagOf(item); int totalWorth = item.TotalWorth; bool flag = (int)item.Currency == 0; if (val != null && flag) { SessionTally t = _t; t.WfSold++; SessionTally t2 = _t; t2.WfIncome += totalWorth; } else if ((Object)(object)item.Creature != (Object)null && flag) { SessionTally t3 = _t; t3.NativeSold++; SessionTally t4 = _t; t4.NativeIncome += totalWorth; } else { SessionTally t5 = _t; t5.OtherSold++; SessionTally t6 = _t; t6.OtherIncome += (flag ? totalWorth : 0); } Write("sold", ("wf", val != null), ("species", val?.SpeciesId ?? NativeFish.IdOf(item)), ("worth", totalWorth), ("currency", ((object)item.Currency/*cast due to .constrained prefix*/).ToString()), ("quality", ((object)Unsafe.As<Quality, Quality>(ref val?.Quality)/*cast due to .constrained prefix*/).ToString()), ("shiny", val?.Shiny ?? false)); } } public void OnTankResult(ulong sender, AqRequest req, AqResult r, Aquarium tank) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Expected I4, but got Unknown if (req == null || r == null || !r.Ok) { return; } AqRequestKind kind = req.Kind; switch ((int)kind) { case 0: { AqFish val = tank?.State.Fish.Find((AqFish x) => x.Uid == r.Amount); SessionTally t3 = _t; t3.Deposits++; SessionTally t4 = _t; t4.DepositedValue += val?.Value ?? 0; Write("deposit", ("species", val?.SpeciesId), ("value", val?.Value ?? 0), ("shiny", val != null && (val.Shiny || (val.Drip && !NativeFish.IsNative(val.SpeciesId)))), ("by", sender.ToString())); break; } case 3: { SessionTally t = _t; t.Upgrades++; SessionTally t2 = _t; t2.UpgradeSpent += r.Amount; Write("upgrade", ("type", ((object)Unsafe.As<UpgradeType, UpgradeType>(ref req.Upgrade)/*cast due to .constrained prefix*/).ToString()), ("cost", r.Amount)); break; } case 2: Write("collect", ("by", sender.ToString()), ("theirShare", r.Amount)); break; case 8: Write("takeout", ("uid", req.Uid), ("by", sender.ToString())); break; case 4: Write("zen", ("on", tank?.State.Zen ?? false)); break; case 1: case 5: case 6: case 7: break; } } public void OnFood(Item item) { if (!((Object)(object)item == (Object)null)) { SessionTally t = _t; t.FoodItems++; SessionTally t2 = _t; t2.FoodValue += item.TotalWorth; WfItemTag val = WfFishService.Instance?.TagOf(item); Write("food", ("wf", val != null), ("species", val?.SpeciesId ?? NativeFish.IdOf(item)), ("worth", item.TotalWorth)); } } public void OnPurchase(ulong player, string item, int price) { Write("purchase", ("by", player.ToString()), ("item", item), ("price", price)); } public void OnPaid(ulong player, int coins) { SessionTally t = _t; t.TankPaid += coins; Write("payout", ("to", player.ToString()), ("coins", coins)); } public void OnTankEvents(List<AqEvent> events) { //IL_0019: 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_003c: Invalid comparison between Unknown and I4 if (events == null) { return; } foreach (AqEvent @event in events) { if ((int)@event.Kind == 0) { SessionTally t = _t; t.TankDeaths++; } else if ((int)@event.Kind == 2) { if (@event.Defeated) { SessionTally t2 = _t; t2.AttacksWon++; } else { SessionTally t3 = _t; t3.AttacksLost++; } } Write("tank", ("event", ((object)Unsafe.As<AqEventKind, AqEventKind>(ref @event.Kind)/*cast due to .constrained prefix*/).ToString()), ("species", @event.SpeciesId), ("reason", @event.Reason), ("defeated", @event.Defeated)); } } public void RegisterCommands(DebugConsole c) { c.Register("econ.summary", "economy totals for this session (host) next to the simulator's limits", (Func<string[], string>)delegate { if (!Game.IsHost) { return "host only (the economy runs on the host)"; } Aquarium val = TankService.Instance?.HostTank; if (val != null) { _t.Tray(val.State.TrayCoins); } _t.Seen(Now); string text = _t.Report(); JsonLinesLog log = _log; return text + "\nlog: " + (((log != null) ? log.Path : null) ?? (_enabled ? "none yet (load a world)" : "off (config Debug.EconomyLog)")); }); c.Register("econ.reset", "start a fresh economy tally and log file (host)", (Func<string[], string>)delegate { if (!Game.IsHost) { return "host only"; } OnWorldUnloaded(); OnWorldLoaded(Game.WorldName); JsonLinesLog log = _log; return "new tally; log: " + (((log != null) ? log.Path : null) ?? "off"); }); } } internal static class Game { public static bool IsHost { get { if ((Object)(object)Server.Instance != (Object)null) { return ((NetworkBehaviour)Server.Instance).IsServerInitialized; } return false; } } public static bool InHostedWorld { get { if (IsHost && SaveManager.CurServerSave != null) { return (Object)(object)Player.LocalPlayer != (Object)null; } return false; } } public static bool InClientWorld { get { if (!IsHost && (Object)(object)Player.LocalPlayer != (Object)null) { return InstanceFinder.IsClientStarted; } return false; } } public static string WorldId { get { ServerSaveObject curServerSave = SaveManager.CurServerSave; ulong steamId = Identity.SteamId; if (curServerSave != null && steamId != 0L) { return steamId + ":" + curServerSave.Name; } return null; } } public static string WorldName => SaveManager.CurServerSave?.Name; public static double PlaytimeSeconds => SaveManager.GetTotalPlaytime(); public static int ActiveMainIsland { get { if ((Object)(object)OnlineIslandManager.Instance == (Object)null || IslandManager.IsLoading || (Object)(object)Island.CurIsland == (Object)null) { return -1; } byte activeIsland = OnlineIslandManager.ActiveIsland; if (!IslandManager.IsMainIsland(activeIsland) || Island.CurIsland.IsMini) { return -1; } return activeIsland; } } public static int CurrentMainIsland { get { if (!((Object)(object)OnlineIslandManager.Instance != (Object)null)) { return 0; } return OnlineIslandManager.CurIsland; } } public static int MainIslandCount { get { object obj = AccessTools.Field(typeof(IslandManager), "_instance")?.GetValue(null); return ((obj == null) ? null : (AccessTools.Field(typeof(IslandManager), "_islandInfos")?.GetValue(obj) as Array))?.Length ?? 5; } } public static Vector3 IslandOrigin => Island.IslandPos; public static LayerMask Ground => GameInfo.LevelLayer; public static LayerMask PlacementBlockers => LayerMask.op_Implicit(~LayerMask.GetMask(new string[3] { "Ignore Raycast", "UI", "Water" })); public static bool LocalInputBlocked { get { if (!((Object)(object)Player.LocalPlayer == (Object)null)) { return Player.LocalPlayer.BlockInputs; } return true; } } public static ulong[] PlayerIds => (from p in PlayerManager.Players where (Object)(object)p != (Object)null select p.SteamID).ToArray(); public static void Check() { Require(typeof(SaveManager), "CurServerSave", "GetTotalPlaytime", "SaveServer"); Require(typeof(ServerSaveObject), "Name"); Require(typeof(Server), "Instance"); Require(typeof(Player), "LocalPlayer", "SteamID", "SteamName", "Rigidbody", "BlockInputs", "Transform"); Require(typeof(PlayerManager), "Players"); Require(typeof(OnlineIslandManager), "Instance", "ActiveIsland", "CurIsland", "TpToSpecificIsland"); Require(typeof(IslandManager), "_instance", "_islandInfos"); Require(typeof(IslandManager), "IsLoading", "IsMainIsland"); Require(typeof(Island), "CurIsland", "IslandPos"); Require(typeof(GameInfo), "LevelLayer"); Require(typeof(WaterManager), "GetWaterHeight"); Require(typeof(ChatManager), "ChatMessage"); } public static void Require(Type t, params string[] members) { foreach (string text in members) { if (t.GetMember(text, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).Length == 0) { throw new MissingMemberException(t.Name + "." + text + " not found"); } } } public static float WaterHeightAt(Vector3 p) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) return WaterManager.GetWaterHeight(p); } public static bool IsLocalPlayerCollider(Collider c) { Player localPlayer = Player.LocalPlayer; if ((Object)(object)localPlayer == (Object)null || (Object)(object)c == (Object)null) { return false; } if (!((Component)c).transform.IsChildOf(((Component)localPlayer).transform)) { if ((Object)(object)c.attachedRigidbody != (Object)null) { return (Object)(object)c.attachedRigidbody == (Object)(object)localPlayer.Rigidbody; } return false; } return true; } public static string NameOf(ulong id) { Player? obj = ((IEnumerable<Player>)PlayerManager.Players).FirstOrDefault((Func<Player, bool>)((Player p) => (Object)(object)p != (Object)null && p.SteamID == id)); if (obj == null) { return null; } return obj.SteamName; } public static void LocalChat(string message) { ChatManager.ChatMessage("<color=#e6b422>[WFxHTF]</color> " + message); } } internal sealed class GamePorts : IAquariumPorts { public const float BossReach = 12f; private readonly Dictionary<HeldFish, int> _values = new Dictionary<HeldFish, int>(); private static FieldRef<Item, float> _weight; private const float MaxTakeOutDistance = 30f; private static Aquarium Tank => TankService.Instance?.Tank; public static void Check() { Game.Require(typeof(Player), "Holding"); Game.Require(typeof(PlayerHolding), "HeldItem"); Game.Require(typeof(Item), "SyncedHolder", "Creature", "DestroyItem", "IsDestroying", "TotalWorth", "RandomizedWeight", "_weight"); Game.Require(typeof(Creature), "DeadCreatures", "BossType", "IsDead", "ServerKillOnSpawn", "_isDrip", "_syncedRandomWeight"); Game.Require(typeof(Player), "Transform"); Game.Require(typeof(Server), "Spawn"); Game.Require(typeof(MoneyManager), "Money", "AddMoney", "RemoveMoney", "Instance", "_money"); NativeFish.Check(); _weight = AccessTools.FieldRefAccess<Item, float>("_weight"); } private static Player PlayerOf(ulong id) { return ((IEnumerable<Player>)PlayerManager.Players).FirstOrDefault((Func<Player, bool>)((Player p) => (Object)(object)p != (Object)null && p.SteamID == id)); } public static Item HeldDeadCreature(ulong id) { Player val = PlayerOf(id); Item val2 = (((Object)(object)val != (Object)null && (Object)(object)val.Holding != (Object)null) ? val.Holding.HeldItem : null); if ((Object)(object)val2 == (Object)null || val2.IsDestroying || (Object)(object)val2.SyncedHolder != (Object)(object)val) { return null; } Creature creature = val2.Creature; if (!((Object)(object)creature != (Object)null) || !creature.IsDead) { return null; } return val2; } public static Item Offered(ulong id) { Item val = HeldDeadCreature(id); if ((Object)(object)val != (Object)null) { return val; } Aquarium tank = Tank; Vector3? at = TankService.Instance?.TankPosition; if (tank == null || !AllowsBosses(tank) || !at.HasValue) { return null; } return (Item)(object)(from c in Creature.DeadCreatures where (Object)(object)c != (Object)null && (int)c.BossType != 0 && !((Item)c).IsDestroying && !((NetworkBehaviour)c).IsDeinitializing && Vector3.Distance(((Component)c).transform.position, at.Value) <= 12f orderby Vector3.Distance(((Component)c).transform.position, at.Value) select c).FirstOrDefault(); } public static bool AllowsBosses(Aquarium tank) { return tank.State.CapacityLevel >= tank.Config.MaxCapacityLevel; } public static HeldFish Describe(Item item, out int value) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0046: 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_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0059: 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_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Expected O, but got Unknown //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Invalid comparison between Unknown and I4 //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_0136: Unknown result type (might be due to invalid IL or missing references) //IL_013e: 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_0156: 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_0173: Unknown result type (might be due to invalid IL or missing references) //IL_018e: 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_01a6: Unknown result type (might be due to invalid IL or missing references) //IL_01b3: Expected O, but got Unknown value = -1; if ((Object)(object)item == (Object)null) { return null; } WfItemTag val = WfFishService.Instance?.TagOf(item); if (val != null) { value = (int)((float)val.Worth * val.WeightMultiplier); return new HeldFish { SpeciesId = val.SpeciesId, WeightKg = val.WeightKg, Quality = val.Quality, Drip = val.Shiny, Shiny = val.Shiny, Cookness = item.Cookness, BetMultiplier = item.BettingMultiplier, KillScore = item.KillScoreMultiplier }; } Creature creature = item.Creature; bool num; if (!((Object)(object)creature != (Object)null) || (int)creature.BossType <= 0) { num = !NativeFish.CanLiveInTank(creature); } else { if (Tank == null) { goto IL_00dd; } num = !AllowsBosses(Tank); } if (!num) { int num2 = WfFishService.Instance?.MaxRegularWorth ?? int.MaxValue; value = Mathf.Min(item.TotalWorth, num2); float num3 = _weight.Invoke(item) * item.RandomizedWeight; string speciesId = NativeFish.IdOf(item); NativeFish.RememberName(speciesId, NativeShiny.Plain(item.GetName())); return new HeldFish { SpeciesId = speciesId, WeightKg = ((num3 > 0f) ? num3 : 0.01f), Quality = (Quality)0, Drip = ((Object)(object)creature != (Object)null && creature.IsDrip), Shiny = (NativeShiny.Instance != null && NativeShiny.Instance.Is(item)), Cookness = item.Cookness, BetMultiplier = item.BettingMultiplier, KillScore = item.KillScoreMultiplier }; } goto IL_00dd; IL_00dd: return null; } public HeldFish PeekHeldFish(ulong playerId) { _values.Clear(); int value; HeldFish val = Describe(Offered(playerId), out value); if (val != null) { _values[val] = value; } return val; } public bool TakeHeldFish(ulong playerId, HeldFish fish) { //IL_0027: 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) Item val = Offered(playerId); int value; HeldFish val2 = Describe(val, out value); if (val2 == null || val2.SpeciesId != fish.SpeciesId || val2.Quality != fish.Quality || val2.WeightKg != fish.WeightKg || val2.Drip != fish.Drip || val2.Shiny != fish.Shiny) { return false; } WfFishService.Instance?.Forget(val); val.DestroyItem((byte)0, byte.MaxValue); return true; } public int ValueOf(HeldFish fish) { if (!_values.TryGetValue(fish, out var value)) { return -1; } return value; } public int TakeFood(ulong playerId, int units) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) if (units <= 0) { return 0; } Item val = HeldDeadCreature(playerId); if ((Object)(object)val == (Object)null || (int)val.Creature.BossType != 0) { return 0; } try { EconomyLog.Instance?.OnFood(val); } catch { } WfFishService.Instance?.Forget(val); val.DestroyItem((byte)3, byte.MaxValue); return Plugin.Instance.Aquarium.FoodUnitsPerItem; } public bool TryCharge(ulong playerId, int coins) { int num = (((Object)(object)MoneyManager.Instance != (Object)null) ? MoneyManager.Instance._money.Value : MoneyManager.Money); if (coins < 0 || num < coins) { return false; } MoneyManager.RemoveMoney(coins, PlayerOf(playerId), (Currency)0); return true; } public void Pay(ulong playerId, int coins) { if (coins <= 0) { return; } MoneyManager.AddMoney(coins, PlayerOf(playerId), (Currency)0); try { EconomyLog.Instance?.OnPaid(playerId, coins); } catch { } } public bool GiveFish(ulong playerId, AqFish fish) { //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_0062: 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_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_0081: 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_0087: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: 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_00bb: Unknown result type (might be due to invalid IL or missing references) //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_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: 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_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_014e: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_0155: Unknown result type (might be due to invalid IL or missing references) //IL_0156: 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_0288: Unknown result type (might be due to invalid IL or missing references) //IL_028e: Unknown result type (might be due to invalid IL or missing references) Player val = PlayerOf(playerId); if ((Object)(object)val == (Object)null || (Object)(object)val.Transform == (Object)null || fish == null || (Object)(object)Server.Instance == (Object)null) { return false; } Vector3 position = val.Transform.position; Vector3? val2 = TankService.Instance?.TankPosition; if (val2.HasValue) { Vector3 val3 = val2.Value - position; if (!(((Vector3)(ref val3)).sqrMagnitude > 900f)) { Vector3 val4 = val2.Value - position; val4.y = 0f; if (((Vector3)(ref val4)).sqrMagnitude < 0.001f) { val4 = Vector3.forward; } ((Vector3)(ref val4)).Normalize(); Vector3 val5 = position + val4 * 0.8f + Vector3.up * 1.2f; FishDef val6 = null; Creature val7; if (NativeFish.IsNative(fish.SpeciesId)) { val7 = NativeFish.Prefab(fish.SpeciesId); } else { val6 = Plugin.Instance.Cache.Find(fish.SpeciesId); if (val6 == null || val6.Id != fish.SpeciesId || WfFishService.Instance == null) { return false; } val7 = WfFishService.Instance.BodyOf(val6); } if ((Object)(object)val7 == (Object)null) { return false; } Creature val8 = Object.Instantiate<Creature>(val7, val5, Quaternion.LookRotation(Vector3.Cross(Vector3.up, val4))); try { if (val6 != null) { WfFishService.Instance.TagTakenOut(val8, fish, val6); } else { float num = _weight.Invoke((Item)(object)val8); if (num > 0f) { float num2 = Mathf.Clamp(fish.WeightKg / num, 0.05f, 50f); ((Item)val8)._syncedRandomWeight.Value = ((num2 == 1f) ? 1.0002f : num2); } val8._isDrip.Value = fish.Drip; if (fish.Shiny) { NativeShiny.Instance?.Mark((Item)(object)val8, "taken out of the tank"); } } ((Item)val8)._cookness.Value = Finite(fish.Cookness, 0f, 0f, 10f); ((Item)val8)._bettingMultiplier.Value = Finite(fish.BetMultiplier, 1f, 0f, 1000f); ((Item)val8)._killScoreMultiplier.Value = Finite(fish.KillScore, 1f, 0f, 100f); val8.ServerKillOnSpawn(); ((NetworkBehaviour)Server.Instance).Spawn(((Component)val8).gameObject, (NetworkConnection)null, default(Scene)); return true; } catch (Exception ex) { if ((Object)(object)val8 != (Object)null && ((NetworkBehaviour)val8).IsSpawned) { AdapterPlugin.Log.LogWarning((object)("take-out: the fish is out, but spawning reported: " + ex.Message)); return true; } AdapterPlugin.Log.LogWarning((object)("take-out failed, the fish stays in the tank: " + ex.Message)); if ((Object)(object)val8 != (Object)null) { WfFishService.Instance?.Forget((Item)(object)val8); NativeShiny.Instance?.Forget((Item)(object)val8); Object.Destroy((Object)(object)((Component)val8).gameObject); } return false; } } } return false; } private static float Finite(float v, float fallback, float min, float max) { if (!float.IsNaN(v) && !float.IsInfinity(v)) { return Mathf.Clamp(v, min, max); } return fallback; } public IReadOnlyCollection<ulong> PlayersInLobby() { return (IReadOnlyCollection<ulong>)(object)Game.PlayerIds; } public string NameOf(ulong playerId) { return Game.NameOf(playerId) ?? "player"; } public bool IsHost(ulong playerId) { if (Game.IsHost) { return playerId == Identity.SteamId; } return false; } } internal static class HotbarIconPatch { private static FieldRef<InventorySlot, MeshFilter> _filter; private static FieldRef<InventorySlot, Renderer> _renderer; private static readonly Dictionary<InventorySlot, Material> Original = new Dictionary<InventorySlot, Material>(); private static readonly Dictionary<string, Material> Materials = new Dictionary<string, Material>(); public static void Install(Harmony h) { //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Expected O, but got Unknown Game.Require(typeof(InventorySlot), "SetItem", "_filter", "_renderer"); Game.Require(typeof(Item), "Mesh", "InventoryMeshScale"); _filter = AccessTools.FieldRefAccess<InventorySlot, MeshFilter>("_filter"); _renderer = AccessTools.FieldRefAccess<InventorySlot, Renderer>("_renderer"); h.Patch((MethodBase)AccessTools.Method(typeof(InventorySlot), "SetItem", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(HotbarIconPatch), "Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } private static void Postfix(InventorySlot __instance, Item item) { try { Apply(__instance, item); } catch (Exception ex) { AdapterPlugin.Log.LogWarning((object)("hotbar icon: " + ex.Message)); } } private static void Apply(InventorySlot slot, Item item) { //IL_014e: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Unknown result type (might be due to invalid IL or missing references) //IL_01fb: Unknown result type (might be due to invalid IL or missing references) //IL_020c: Unknown result type (might be due to invalid IL or missing references) //IL_018b: Unknown result type (might be due to invalid IL or missing references) //IL_0190: Unknown result type (might be due to invalid IL or missing references) //IL_0194: Unknown result type (might be due to invalid IL or missing references) //IL_01a0: Unknown result type (might be due to invalid IL or missing references) //IL_01a5: Unknown result type (might be due to invalid IL or missing references) //IL_01a9: 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_01ba: Unknown result type (might be due to invalid IL or missing references) //IL_01be: Unknown result type (might be due to invalid IL or missing references) //IL_0286: Unknown result type (might be due to invalid IL or missing references) //IL_024e: Unknown result type (might be due to invalid IL or missing references) //IL_025a: 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) //IL_026b: Unknown result type (might be due to invalid IL or missing references) //IL_0270: Unknown result type (might be due to invalid IL or missing references) MeshFilter filter = _filter.Invoke(slot); Renderer val = _renderer.Invoke(slot); if ((Object)(object)filter == (Object)null || (Object)(object)val == (Object)null) { return; } WfItemTag val2 = ((!((Object)(object)item != (Object)null)) ? null : WfFishService.Instance?.TagOf(item)); SpriteCache cache = Plugin.Instance.Cache; FishDef val3 = ((val2 != null) ? cache.Find(val2.SpeciesId) : null); if (val3 == null) { if (Original.TryGetValue(slot, out var value)) { val.sharedMaterial = value; Original.Remove(slot); } return; } Mesh val4 = cache.FrameMesh(val3, 0); Texture2D val5 = cache.Frame(val3, 0, val2.Shiny); if ((Object)(object)val4 == (Object)null || (Object)(object)val5 == (Object)null) { return; } if (!Original.ContainsKey(slot)) { Original[slot] = val.sharedMaterial; } string key = val3.Id + "|" + ((object)Unsafe.As<Quality, Quality>(ref val2.Quality)/*cast due to .constrained prefix*/).ToString() + (val2.Shiny ? "|shiny" : ""); if (!Materials.TryGetValue(key, out var value2) || (Object)(object)value2 == (Object)null) { value2 = (Materials[key] = SpriteMaterial.Create(val5, SpriteFish.Tint(val2.Quality))); } if (!((Object)(object)value2 == (Object)null)) { float num = 1f; Mesh mesh = item.Mesh; if ((Object)(object)mesh != (Object)null) { Bounds bounds = mesh.bounds; float x = ((Bounds)(ref bounds)).size.x; bounds = mesh.bounds; float y = ((Bounds)(ref bounds)).size.y; bounds = mesh.bounds; num = Mathf.Max(x, Mathf.Max(y, ((Bounds)(ref bounds)).size.z)) * item.InventoryMeshScale; } filter.mesh = val4; val.sharedMaterial = value2; ((Component)filter).transform.localScale = Vector3.one * Mathf.Max(0.05f, num); Camera val7 = ((IEnumerable<Camera>)Camera.allCameras).FirstOrDefault((Func<Camera, bool>)((Camera c) => (Object)(object)c != (Object)null && (Object)(object)c.targetTexture != (Object)null && (c.cullingMask & (1 << ((Component)filter).gameObject.layer)) != 0)); if ((Object)(object)val7 != (Object)null) { ((Component)filter).transform.rotation = Quaternion.LookRotation(((Component)filter).transform.position - ((Component)val7).transform.position, ((Component)val7).transform.up); } else { ((Component)filter).transform.localRotation = Quaternion.identity; } } } } internal sealed class KeyItemService : MonoBehaviour { public KeyCode LureKey = (KeyCode)108; public KeyCode CharmKey = (KeyCode)108; private int _standIsland = -2; private int _standTries; private float _nextTry; private float _errorAt; private ShopStand _lureStand; private ShopStand _charmStand; private readonly Dictionary<ulong, GlowOrb> _orbs = new Dictionary<ulong, GlowOrb>(); private readonly Dictionary<ulong, GameObject> _invertedBaits = new Dictionary<ulong, GameObject>(); private static readonly Color CharmColour = new Color(0.75f, 0.92f, 1f); private bool _standsHere; public static KeyItemService Instance { get; private set; } private Plugin P => Plugin.Instance; public static List<PlayerKeyItems> Players { get { object obj = TankService.Instance?.KeyItems?.Players; if (obj == null) { NetService instance = NetService.Instance; if (instance == null) { return null; } KeyItemsState clientKeyItems = instance.ClientKeyItems; if (clientKeyItems == null) { return null; } obj = clientKeyItems.Players; } return (List<PlayerKeyItems>)obj; } } private static PlayerKeyItems Me => Of(Identity.SteamId); public static void Check() { Game.Require(typeof(BaitPurchasable), "_bait"); Game.Require(typeof(ItemPurchasable), "_itemToPurchase"); Game.Require(typeof(Item), "FishingRod"); Game.Require(typeof(FishingRod), "Bait"); Game.Require(typeof(BaitInfo), "Mesh"); Game.Require(typeof(GameInfo), "AllBaits"); } private void Awake() { Instance = this; } public static PlayerKeyItems Of(ulong id) { return Players?.FirstOrDefault((Func<PlayerKeyItems, bool>)((PlayerKeyItems p) => p != null && p.SteamId == id)); } private void Update() { if (!Game.InHostedWorld && !Game.InClientWorld) { ClearAll(); return; } try { UpdateStands(); UpdateVisuals(); UpdateInput(); } catch (Exception ex) { if (Time.unscaledTime >= _errorAt) { AdapterPlugin.Log.LogError((object)("shop: " + ex)); _errorAt = Time.unscaledTime + 10f; } } } private void UpdateStands() { int activeMainIsland = Game.ActiveMainIsland; if (activeMainIsland != _standIsland) { _standsHere = false; DestroyStands(); _standIsland = activeMainIsland; _standTries = 0; _nextTry = Time.unscaledTime + 2f; } if (activeMainIsland < 0) { return; } int num = ((P.Mapping.WfLureIsland < 0) ? (Game.MainIslandCount - 1) : P.Mapping.WfLureIsland); bool flag = activeMainIsland == num && (Object)(object)_lureStand == (Object)null; bool flag2 = activeMainIsland == P.Mapping.ShinyCharmIsland && (Object)(object)_charmStand == (Object)null; if (!_standsHere && (flag || flag2) && !(Time.unscaledTime < _nextTry) && _standTries < 10) { _nextTry = Time.unscaledTime + 3f; if (++_standTries == 10) { AdapterPlugin.Log.LogWarning((object)$"shop: no How to Fish shop or NPC found on island {activeMainIsland}; the mod's stand isn't placed there"); } Material palette; Purchasable val = FindShop(out palette); if (flag && (Object)(object)val != (Object)null) { _lureStand = BuildLure(val, palette); } if (flag2) { _charmStand = BuildCharm(val); } } } private ShopStand BuildLure(Purchasable counter, Material palette) { return Build((Component)(object)counter, BesideCounter(((Component)counter).transform, 1), "WFxHTF Web Fishing Lure", LureDisplay(palette), LurePrompt, UseLure); } private ShopStand BuildCharm(Purchasable counter) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) NPC val = FindNpc(); if ((Object)(object)val == (Object)null && (Object)(object)counter == (Object)null) { return null; } GameObject gameObject = ((Component)GlowOrb.Create("Shiny Charm display", 0.12f, CharmColour)).gameObject; if (!((Object)(object)val != (Object)null)) { return Build((Component)(object)counter, BesideCounter(((Component)counter).transform, -1), "WFxHTF Shiny Charm", gameObject, CharmPrompt, UseCharm); } return Build((Component)(object)val, BehindNpc(((Component)val).transform), "WFxHTF Shiny Charm", gameObject, CharmPrompt, UseCharm); } private static NPC FindNpc() { //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) Vector3 origin = Game.IslandOrigin; return (from n in Object.FindObjectsByType<NPC>((FindObjectsInactive)0) where (Object)(object)n != (Object)null && ((Behaviour)n).isActiveAndEnabled && Flat(((Component)n).transform.position - origin) < 500f orderby Flat(((Component)n).transform.position - origin) select n).FirstOrDefault(); } private static (Vector3 Right, Vector3 Fwd) Axes(Transform a) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0034: 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_0050: 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_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) Vector3 right = a.right; right.y = 0f; Vector3 forward = a.forward; forward.y = 0f; if (((Vector3)(ref right)).sqrMagnitude < 0.001f) { right = Vector3.right; } if (((Vector3)(ref forward)).sqrMagnitude < 0.001f) { forward = Vector3.forward; } return (Right: ((Vector3)(ref right)).normalized, Fwd: ((Vector3)(ref forward)).normalized); } private static Vector3[] BesideCounter(Transform a, int side) { //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_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_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_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0041: 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_004d: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0058: 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_0064: 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_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_007d: 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_0088: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) var (val, val2) = Axes(a); return (Vector3[])(object)new Vector3[6] { val * 1.6f * (float)side, val * 2.6f * (float)side, -val * 1.6f * (float)side, val2 * 1.6f, -val2 * 1.6f, val * 3.6f * (float)side }; } private static Vector3[] BehindNpc(Transform a) { //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_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //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_0033: Unknown result type (might be due to invalid IL or missing references) //IL_003d: 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_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0059: 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_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0069: 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_0071: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0097: 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_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) var (val, val2) = Axes(a); return (Vector3[])(object)new Vector3[7] { -val2 * 1.4f, -val2 * 2f, -val2 * 1.4f + val * 0.9f, -val2 * 1.4f - val * 0.9f, -val2 * 2.6f, val * 1.6f, -val * 1.6f }; } private static Purchasable FindShop(out Material palette) { //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) palette = null; Vector3 origin = Game.IslandOrigin; List<Purchasable> all = Counters(); List<Purchasable> source = all.Where((Purchasable p) => p is BaitPurchasable || p is ItemPurchasable).ToList(); Purchasable val = (from p in source where p is BaitPurchasable orderby Flat(((Component)p).transform.position - origin) select p).FirstOrDefault() ?? (from p in source orderby Neighbours(p, all) descending, Flat(((Component)p).transform.position - origin) select p).FirstOrDefault(); if ((Object)(object)val != (Object)null) { palette = (from r in ((Component)val).GetComponentsInChildren<Renderer>() select r.sharedMaterial).FirstOrDefault((Func<Material, bool>)((Material m) => (Object)(object)m != (Object)null)); } return val; } private static List<Purchasable> Counters() { //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) Vector3 origin = Game.IslandOrigin; Transform boat = (((Object)(object)BoatManager.Boat != (Object)null) ? ((Component)BoatManager.Boat).transform : null); return (from p in Object.FindObjectsByType<Purchasable>((FindObjectsInactive)0) where (Object)(object)p != (Object)null && ((Behaviour)p).isActiveAndEnabled && (!((Object)(object)boat != (Object)null) || !((Component)p).transform.IsChildOf(boat)) && Flat(((Component)p).transform.position - origin) < 500f select p).ToList(); } private static int Neighbours(Purchasable p, List<Purchasable> all) { return all.Count((Purchasable o) => (Object)(object)o != (Object)(object)p && Flat(((Component)o).transform.position - ((Component)p).transform.position) < 12f); } private static float Flat(Vector3 d) { d.y = 0f; return ((Vector3)(ref d)).magnitude; } private static string FromMe(Vector3 at) { //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0060: 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_0066: 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) //IL_00b0: 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_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) Transform val = (((Object)(object)Player.LocalPlayer != (Object)null) ? Player.LocalPlayer.Transform : null); Transform val2 = (((Object)(object)Camera.main != (Object)null) ? ((Component)Camera.main).transform : val); if ((Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null) { return $"at {at}"; } Vector3 val3 = at - val.position; float num = Flat(val3); if (num < 2f) { return "right here"; } Vector3 forward = val2.forward; forward.y = 0f; val3.y = 0f; float num2 = Vector3.SignedAngle((((Vector3)(ref forward)).sqrMagnitude > 0.0001f) ? forward : Vector3.forward, val3, Vector3.up); string arg = (new string[8] { "ahead", "ahead-right", "right", "behind-right", "behind", "behind-left", "left", "ahead-left" })[(int)Mathf.Repeat(Mathf.Round(num2 / 45f), 8f)]; return $"{num:0} m {arg}"; } private ShopStand Build(Component anchor, Vector3[] offsets, string name, GameObject display, Func<string> prompt, Action<Player> use) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_01c5: 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_01dc: 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_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002b: 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_0032: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0041: 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_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: 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_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: 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_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: 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_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_0119: 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_0152: 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) Transform transform = anchor.transform; Vector3 item = Axes(transform).Fwd; RaycastHit val2 = default(RaycastHit); foreach (Vector3 val in offsets) { if (!Physics.Raycast(transform.position + val + Vector3.up * 1.5f, Vector3.down, ref val2, 6f, LayerMask.op_Implicit(Game.Ground), (QueryTriggerInteraction)1) || ((RaycastHit)(ref val2)).normal.y < 0.7f || ((RaycastHit)(ref val2)).point.y > transform.position.y + 0.5f) { continue; } Vector3 point = ((RaycastHit)(ref val2)).point; if (!Physics.CheckBox(point + Vector3.up * 0.8f, new Vector3(0.35f, 0.7f, 0.35f), Quaternion.identity, LayerMask.op_Implicit(Game.PlacementBlockers) & ~LayerMask.op_Implicit(Game.Ground), (QueryTriggerInteraction)1)) { ShopStand val3 = ShopStand.Create(name, point, Quaternion.LookRotation(item, Vector3.up), display, 1.05f); Transform transform2 = new GameObject("InteractPoint").transform; transform2.SetParent(((Component)val3).transform, false); transform2.localPosition = new Vector3(0f, val3.Size.y / 2f, 0f); try { TankInteractable.Attach(transform2, val3.Size, val3.Outline, prompt, use); } catch (Exception ex) { AdapterPlugin.Log.LogWarning((object)("shop stand prompt unavailable: " + ex.Message)); } AdapterPlugin.Log.LogInfo((object)$"shop: {name} next to {((Object)transform).name} ({((object)anchor).GetType().Name} at {transform.position}) at {point}, {FromMe(point)}"); return val3; } } Object.Destroy((Object)(object)display); AdapterPlugin.Log.LogWarning((object)("shop: no free ground next to " + ((Object)transform).name + " for " + name)); return null; } private GameObject LureDisplay(Material palette) { //IL_007e: 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_0099: Expected O, but got Unknown //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Expected O, but got Unknown //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_0145: 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_0164: Unknown result type (might be due to invalid IL or missing references) //IL_0169: Unknown result type (might be due to invalid IL or missing references) //IL_0174: 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_0183: 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_0192: Unknown result type (might be due to invalid IL or missing references) BaitInfo val = (from b in GameInfo.AllBaits where (Object)(object)b != (Object)null && (Object)(object)b.Mesh != (Object)null && WfFishService.Instance != null && WfFishService.Instance.IsGeneralLure(b) orderby b.Cost descending select b).FirstOrDefault(); if ((Object)(object)val == (Object)null || (Object)(object)palette == (Object)null) { return ((Component)GlowOrb.Create("Web Fishing Lure display", 0.08f, new Color(1f, 0.6f, 0.9f))).gameObject; } GameObject val2 = new GameObject("Web Fishing Lure display"); GameObject val3 = new GameObject("Lure"); val3.transform.SetParent(val2.transform, false); val3.AddComponent<MeshFilter>().sharedMesh = val.Mesh; ((Renderer)val3.AddComponent<MeshRenderer>()).sharedMaterial = palette; Bounds bounds = val.Mesh.bounds; float x = ((Bounds)(ref bounds)).size.x; bounds = val.Mesh.bounds; float y = ((Bounds)(ref bounds)).size.y; bounds = val.Mesh.bounds; float num = Mathf.Max(x, Mathf.Max(y, ((Bounds)(ref bounds)).size.z)); val3.transform.localScale = Vector3.one * (0.35f / Mathf.Max(0.01f, num)); Transform transform = val3.transform; bounds = val.Mesh.bounds; transform.localPosition = -((Bounds)(ref bounds)).center * val3.transform.localScale.x + Vector3.up * 0.15f; ShinyLook.Apply(val2, true); return val2; } private void DestroyStands() { if ((Object)(object)_lureStand != (Object)null) { Object.Destroy((Object)(object)((Component)_lureStand).gameObject); } if ((Object)(object)_charmStand != (Object)null) { Object.Destroy((Object)(object)((Component)_charmStand).gameObject); } _lureStand = (_charmStand = null); } private string LurePrompt() { //IL_0037: Unknown result type (might be due to invalid IL or missing references) PlayerKeyItems me = Me; if (me == null || !me.WfLure) { return $"Web Fishing Lure\n${P.Mapping.WfLurePrice}"; } return string.Format("Web Fishing Lure (yours)\n[{0}] switch {1}", LureKey, me.WfLureOn ? "off" : "on"); } private string CharmPrompt() { //IL_0037: Unknown result type (might be due to invalid IL or missing references) PlayerKeyItems me = Me; if (me == null || !me.ShinyCharm) { return $"Shiny Charm\n${P.Mapping.ShinyCharmPrice}"; } return string.Format("Shiny Charm (yours)\n[Ctrl+{0}] switch {1}", CharmKey, me.ShinyCharmOff ? "on" : "off"); } private void UseLure(Player player) { if ((Object)(object)player != (Object)(object)Player.LocalPlayer) { return; } PlayerKeyItems me = Me; if (me != null && me.WfLure) { SetLure(!Me.WfLureOn); return; } TankService.Instance?.RequestKeyItem((AqRequestKind)9, on: false, delegate(AqResult r) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) Chat(r.Ok ? $"<b>You bought the Web Fishing Lure!</b> While it's on, only WEBFISHING fish bite. Press {LureKey} to switch it off and on." : ("Couldn't buy the Web Fishing Lure: " + r.Error)); }); } private void UseCharm(Player player) { if ((Object)(object)player != (Object)(object)Player.LocalPlayer) { return; } PlayerKeyItems me = Me; if (me != null && me.ShinyCharm) { SetCharm(Me.ShinyCharmOff); return; } TankService.Instance?.RequestKeyItem((AqRequestKind)10, on: false, delegate(AqResult r) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) Chat(r.Ok ? $"<b><color=#fff6a0>You bought the Shiny Charm!</color></b> Shiny fish are now 1 in 500 for you (bosses stay 1 in 100). Ctrl+{CharmKey} switches it off and on." : ("Couldn't buy the Shiny Charm: " + r.Error)); }); } private void SetLure(bool on) { TankService.Instance?.RequestKeyItem((AqRequestKind)11, on, delegate(AqResult r) { Chat((!r.Ok) ? ("Web Fishing Lure: " + r.Error) : (on ? "Web Fishing Lure <b>on</b>: only WEBFISHING fish bite." : "Web Fishing Lure <b>off</b>.")); }); } private void SetCharm(bool on) { TankService.Instance?.RequestKeyItem((AqRequestKind)12, on, delegate(AqResult r) { Chat((!r.Ok) ? ("Shiny Charm: " + r.Error) : (on ? "Shiny Charm <b>on</b>: shiny fish are 1 in 500 for you." : "Shiny Charm <b>off</b>.")); }); } private void UpdateInput() { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) PlayerKeyItems me = Me; if (me != null && me.WfLureOn) { Hud.ShowSmall("wflure", $"Web Fishing Lure: on [{LureKey}]"); } else { Hud.Hide("wflure"); } if (me != null && me.ShinyCharm && !me.ShinyCharmOff) { Hud.ShowSmall("wfshinycharm", $"Shiny Charm: on [Ctrl+{CharmKey}]"); } else { Hud.Hide("wfshinycharm"); } if (me != null && !Game.LocalInputBlocked) { bool ctrlHeld = Keys.CtrlHeld; if (ctrlHeld && me.ShinyCharm && Keys.Down(CharmKey)) { SetCharm(me.ShinyCharmOff); } else if (!ctrlHeld && me.WfLure && Keys.Down(LureKey)) { SetLure(!me.WfLureOn); } } } private static void Chat(string text) { try { Game.LocalChat(text); } catch { } } public void RegisterCommands(DebugConsole c) { c.Register("shop.status", "the mod's shop: stands on this island and who owns what", (Func<string[], string>)delegate { //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Unknown result type (might be due to invalid IL or missing references) int num = ((P.Mapping.WfLureIsland < 0) ? (Game.MainIslandCount - 1) : P.Mapping.WfLureIsland); List<string> list = new List<string> { string.Format("Web Fishing Lure: island {0} for ${1}, stand {2}", num, P.Mapping.WfLurePrice, ((Object)(object)_lureStand != (Object)null) ? FromMe(((Component)_lureStand).transform.position) : "not on this island"), string.Format("Shiny Charm: island {0} for ${1}, stand {2}", P.Mapping.ShinyCharmIsland, P.Mapping.ShinyCharmPrice, ((Object)(object)_charmStand != (Object)null) ? FromMe(((Component)_charmStand).transform.position) : "not on this island"), $"you are on island {Game.ActiveMainIsland}" }; foreach (PlayerKeyItems item in Players ?? new List<PlayerKeyItems>()) { list.Add(" " + DisplayText.VisibleOr(Game.NameOf(item.SteamId), item.SteamId.ToString()) + ": lure " + (item.WfLure ? (item.WfLureOn ? "on" : "off") : "no") + ", charm " + (item.ShinyCharm ? (item.ShinyCharmOff ? "off" : "on") : "no")); } return string.Join("\n", list); }); c.Register("shop.give", "DEBUG (host): <lure|charm> give yourself a shop item for free ('shop.give none' takes both back)", (Func<string[], string>)delegate(string[] a) { KeyItems val = TankService.Instance?.KeyItems; if (val == null) { return "host only (load a world)"; } string text = ((a.Length != 0) ? a[0].ToLowerInvariant() : ""); switch (text) { case "lure": val.Grant(Identity.SteamId, (bool?)true, (bool?)null); break; case "charm": val.Grant(Identity.SteamId, (bool?)null, (bool?)true); break; case "none": val.Grant(Identity.SteamId, (bool?)false, (bool?)false); break; default: return "usage: shop.give <lure|charm|none>"; } NetService.Instance?.OnKeyItemsChanged(); return (!(text == "none")) ? ("you have the " + ((text == "lure") ? "Web Fishing Lure (on; L switches it)" : "Shiny Charm")) : "taken back"; }); c.Register("shop.list", "the shop counters on this island, nearest first (where the mod's stands can go)", (Func<string[], string>)delegate { //IL_00d9: Unknown result type (might be due to invalid IL or missing references) if (Game.ActiveMainIsland < 0) { return "stand on a main island"; } List<Purchasable> all = Counters(); Transform me = (((Object)(object)Player.LocalPlayer != (Object)null) ? Player.LocalPlayer.Transform : null); Material palette; Purchasable pick = FindShop(out palette); List<string> list = (from p in all.OrderBy((Purchasable p) => (!((Object)(object)me != (Object)null)) ? 0f : Flat(((Component)p).transform.position - me.position)).Take(20) select string.Format("{0}{1} [{2}], {3}, {4} counters around", ((Object)(object)p == (Object)(object)pick) ? "* " : " ", ((Object)p).name.Replace("(Purchasable)", "").Trim(), ((object)p).GetType().Name, FromMe(((Component)p).transform.position), Neighbours(p, all))).ToList(); list.Insert(0, $"island {Game.ActiveMainIsland}: {all.Count} counters (* = where the Web Fishing Lure stand goes)"); NPC val = FindNpc(); list.Add(((Object)(object)val != (Object)null) ? ("NPC (the Shiny Charm stand goes behind it): " + ((Object)val).name + ", " + FromMe(((Component)val).transform.position)) : "no NPC on this island"); return string.Join("\n", list); }); c.Register("shop.here", "DEBUG: put both shop stands on this island (next to its shop) until you leave it", (Func<string[], string>)delegate { //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) if (Game.ActiveMainIsland < 0) { return "stand on a main island"; } DestroyStands(); Material palette; Purchasable val = FindShop(out palette); if ((Object)(object)val != (Object)null) { _lureStand = BuildLure(val, palette); } _charmStand = BuildCharm(val); _standsHere = true; return "Web Fishing Lure: " + (((Object)(object)_lureStand != (Object)null) ? FromMe(((Component)_lureStand).transform.position) : "no room or no shop") + "; Shiny Charm: " + (((Object)(object)_charmStand != (Object)null) ? FromMe(((Component)_charmStand).transform.position) : "no room or no shop/NPC"); }); } private void UpdateVisuals() { //IL_019a: Unknown result type (might be due to invalid IL or missing references) //IL_019f: Unknown result type (might be due to invalid IL or missing references) //IL_01a9: Unknown result type (might be due to invalid IL or missing references) //IL_01ae: Unknown result type (might be due to invalid IL or missing references) //IL_017b: Unknown result type (might be due to invalid IL or missing references) HashSet<ulong> seen = new HashSet<ulong>(); foreach (Player player in PlayerManager.Players) { if ((Object)(object)player == (Object)null) { continue; } ulong steamID = player.SteamID; seen.Add(steamID); PlayerKeyItems val = Of(steamID); Item val2 = (((Object)(object)player.Holding != (Object)null) ? player.Holding.HeldItem : null); FishingRod val3 = (((Object)(object)val2 != (Object)null) ? val2.FishingRod : null); GameObject val4 = (((Object)(object)val3 != (Object)null && (Object)(object)val3.Bait != (Object)null) ? ((Component)val3.Bait).gameObject : null); bool flag = val != null && val.WfLureOn && (Object)(object)val4 != (Object)null; if (_invertedBaits.TryGetValue(steamID, out var value) && ((Object)(object)value != (Object)(object)val4 || !flag)) { if ((Object)(object)value != (Object)null) { ShinyLook.Remove(value); } _invertedBaits.Remove(steamID); } if (flag && !ShinyLook.Is(val4)) { ShinyLook.Apply(val4, false); _invertedBaits[steamID] = val4; } bool num = val != null && val.ShinyCharm && !val.ShinyCharmOff && (Object)(object)player.Transform != (Object)null; _orbs.TryGetValue(steamID, out var value2); if (num) { if ((Object)(object)value2 == (Object)null) { value2 = (_orbs[steamID] = GlowOrb.Create("WFxHTF Shiny Charm", 0.07f, CharmColour)); } ((Component)value2).transform.position = player.Transform.position + Vector3.up * 2.05f; } else if ((Object)(object)value2 != (Object)null || _orbs.ContainsKey(steamID)) { if ((Object)(object)value2 != (Object)null) { Object.Destroy((Object)(object)((Component)value2).gameObject); } _orbs.Remove(steamID); } } foreach (ulong item in _orbs.Keys.Where((ulong id) => !seen.Contains(id)).ToList()) { if ((Object)(object)_orbs[item] != (Object)null) { Object.Destroy((Object)(object)((Component)_orbs[item]).gameObject); } _orbs.Remove(item); } foreach (ulong item2 in _invertedBaits.Keys.Where((ulong id) => !seen.Contains(id)).ToList()) { if ((Object)(object)_invertedBaits[item2] != (Object)null) { ShinyLook.Remove(_invertedBaits[item2]); } _invertedBaits.Remove(item2); } } private void ClearAll() { DestroyStands(); _standIsland = -2; foreach (GlowOrb value in _orbs.Values) { if ((Object)(object)value != (Object)null) { Object.Destroy((Object)(object)((Component)value).gameObject); } } _orbs.Clear(); foreach (GameObject value2 in _invertedBaits.Values) { if ((Object)(object)value2 != (Object)null) { ShinyLook.Remove(value2); } } _invertedBaits.Clear(); Hud.Hide("wflure"); Hud.Hide("wfshinycharm"); } } internal static class NativeFish { public const string Prefix = "htf:"; private static FieldRef<Creature, GameObject> _normalLook; private static FieldRef<Creature, GameObject> _dripLook; private static FieldRef<Item, Mesh> _iconMesh; private static readonly Dictionary<string, string> Names = new Dictionary<string, string>(); public static void Check() { Game.Require(typeof(GameInfo), "GetSpawnable"); Game.Require(typeof(Creature), "_disableOnShiny", "_enableOnShiny"); Game.Require(typeof(Item), "_mesh"); _normalLook = AccessTools.FieldRefAccess<Creature, GameObject>("_disableOnShiny"); _dripLook = AccessTools.FieldRefAccess<Creature, GameObject>("_enableOnShiny"); _iconMesh = AccessTools.FieldRefAccess<Item, Mesh>("_mesh"); } public static bool IsNative(string speciesId) { return speciesId?.StartsWith("htf:", StringComparison.Ordinal) ?? false; } public static string IdOf(Item item) { Item spawnable = GameInfo.GetSpawnable(item.ID); return "htf:" + (((Object)(object)spawnable != (Object)null) ? ((Object)spawnable).name : ((Object)item).name.Replace("(Clone)", "").Trim()); } public static Creature Prefab(string speciesId) { if (!IsNative(speciesId)) { return null; } Item spawnable = GameInfo.GetSpawnable(speciesId.Substring("htf:".Length).Replace(" ", "").ToLowerInvariant()); return (Creature)(object)((spawnable is Creature) ? spawnable : null); } public static bool CanLiveInTank(Creature c) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)c != (Object)null && (int)c.BossType == 0) { if (!(c is Fish)) { return c is Crab; } return true; } return false; } public static void RememberName(string speciesId, string name) { if (!string.IsNullOrEmpty(speciesId) && !string.IsNullOrEmpty(name)) { Names[speciesId] = name; } } public static string Name(string speciesId) { if (speciesId == null) { return null; } if (Names.TryGetValue(speciesId, out var value)) { return value; } if (!IsNative(speciesId)) { return null; } string text = speciesId.Substring("htf:".Length); StringBuilder stringBuilder = new StringBuilder(text.Length + 4); for (int i = 0; i < text.Length; i++) { if (i > 0 && char.IsUpper(text[i]) && char.IsLower(text[i - 1])) { stringBuilder.Append(' '); } stringBuilder.Append(text[i]); } return stringBuilder.ToString(); } public static GameObject Model(string speciesId, bool drip) { //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Expected O, but got Unknown //IL_0256: Unknown result type (might be due to invalid IL or missing references) //IL_025b: Unknown result type (might be due to invalid IL or missing references) //IL_026d: Unknown result type (might be due to invalid IL or missing references) //IL_027b: Unknown result type (might be due to invalid IL or missing references) //IL_0280: Unknown result type (might be due to invalid IL or missing references) //IL_028a: Unknown result type (might be due to invalid IL or missing references) //IL_0296: Unknown result type (might be due to invalid IL or missing references) //IL_029b: Unknown result type (might be due to invalid IL or missing references) //IL_02a2: Unknown result type (might be due to invalid IL or missing references) //IL_02a7: Unknown result type (might be due to invalid IL or missing references) //IL_02b7: Unknown result type (might be due to invalid IL or missing references) //IL_02bc: Unknown result type (might be due to invalid IL or missing references) //IL_02be: Unknown result type (might be due to invalid IL or missing references) //IL_02c6: Unknown result type (might be due to invalid IL or missing references) //IL_02d0: Unknown result type (might be due to invalid IL or missing references) //IL_02da: Unknown result type (might be due to invalid IL or missing references) //IL_02e4: Unknown result type (might be due to invalid IL or missing references) //IL_02ee: Unknown result type (might be due to invalid IL or missing references) //IL_02f8: Unknown result type (might be due to invalid IL or missing references) //IL_0300: Unknown result type (might be due to invalid IL or missing references) //IL_030a: Unknown result type (might be due to invalid IL or missing references) //IL_0371: Unknown result type (might be due to invalid IL or missing references) Creature val = Prefab(speciesId); if ((Object)(object)val == (Object)null) { AdapterPlugin.Log.LogWarning((object)("tank: no How to Fish creature named " + speciesId)); return null; } GameObject val2 = new GameObject("WFxHTF " + ((Object)val).name + (drip ? " (drip)" : "")); Transform src = ((Component)val).transform; GameObject val3 = _normalLook.Invoke(val); GameObject val4 = _dripLook.Invoke(val); GameObject on = ((drip && (Object)(object)val4 != (Object)null) ? val4 : val3); GameObject off = (((Object)(object)on == (Object)(object)val4) ? val3 : val4); if (drip && (Object)(object)val4 == (Object)null) { AdapterPlugin.Log.LogWarning((object)("tank: " + speciesId + " has no drip look; showing the normal one")); } List<(Mesh, Material[], Transform)> list = new List<(Mesh, Material[], Transform)>(); MeshFilter[] componentsInChildren = ((Component)val).GetComponentsInChildren<MeshFilter>(true); foreach (MeshFilter val5 in componentsInChildren) { MeshRenderer component = ((Component)val5).GetComponent<MeshRenderer>(); if ((Object)(object)component != (Object)null && (Object)(object)val5.sharedMesh != (Object)null && Include(((Component)val5).transform)) { list.Add((val5.sharedMesh, ((Renderer)component).sharedMaterials, ((Component)val5).transform)); } } SkinnedMeshRenderer[] componentsInChildren2 = ((Component)val).GetComponentsInChildren<SkinnedMeshRenderer>(true); foreach (SkinnedMeshRenderer val6 in componentsInChildren2) { if ((Object)(object)val6.sharedMesh != (Object)null && Include(((Component)val6).transform)) { list.Add((val6.sharedMesh, ((Renderer)val6).sharedMaterials, ((Component)val6).transform)); } } if (list.Count == 0) { Mesh val7 = _iconMesh.Invoke((Item)(object)val); Renderer val8 = ((IEnumerable<Renderer>)((Component)val).GetComponentsInChildren<Renderer>(true)).FirstOrDefault((Func<Renderer, bool>)((Renderer r) => r is MeshRenderer || r is SkinnedMeshRenderer)); if ((Object)(object)val7 != (Object)null && (Object)(object)val8 != (Object)null) { list.Add((val7, val8.sharedMaterials, ((Component)val8).transform)); } } foreach (var item4 in list) { Mesh item = item4.Item1; Material[] item2 = item4.Item2; Transform item3 = item4.Item3; GameObject val9 = new GameObject(((Object)item).name); val9.transform.SetParent(val2.transform, false); val9.transform.localPosition = src.InverseTransformPoint(item3.position); val9.transform.localRotation = Quaternion.Inverse(src.rotation) * item3.rotation; Vector3 lossyScale = src.lossyScale; val9.transform.localScale = new Vector3(item3.lossyScale.x / lossyScale.x, item3.lossyScale.y / lossyScale.y, item3.lossyScale.z / lossyScale.z); val9.AddComponent<MeshFilter>().sharedMesh = item; ((Renderer)val9.AddComponent<MeshRenderer>()).sharedMaterials = item2; } if (list.Count == 0) { Object.Destroy((Object)(object)val2); AdapterPlugin.Log.LogWarning((object)("tank: no visible mesh found for " + speciesId)); return null; } val2.transform.localScale = src.localScale; AdapterPlugin.Log.LogInfo((object)string.Format("tank: model for {0}{1}: {2} mesh(es) ({3})", speciesId, drip ? " (drip)" : "", list.Count, string.Join(", ", list.Select(((Mesh, Material[], Transform) p) => ((Object)p.Item1).name)))); return val2; bool Include(Transform t) { if ((Object)(object)off != (Object)null && (Object)(object)off != (Object)(object)on && t.IsChildOf(off.transform)) { return false; } return Shown(t, src, ((Object)(object)on != (Object)null) ? on.transform : null); } } private static bool Shown(Transform t, Transform root, Transform switchedOnAtSpawn) { Transform val = t; while ((Object)(object)val != (Object)null) { if (!((Component)val).gameObject.activeSelf && (Object)(object)val != (Object)(object)switchedOnAtSpawn) { return false; } val = (((Object)(object)val == (Object)(object)root) ? null : val.parent); } return true; } } internal sealed class NativeShiny { private readonly Rng _rng = new Rng((ulong)(DateTime.UtcNow.Ticks ^ 0x5348494E59L)); private readonly HashSet<Item> _live = new HashSet<Item>(); private readonly List<NativeShinyKey> _keys = new List<NativeShinyKey>(); private readonly List<NativeShinyKey> _unmatched = new List<NativeShinyKey>(); private readonly List<(Creature Body, float Weight)> _pendingLoads = new List<(Creature, float)>(); private readonly List<(ShinyMark Msg, float At)> _remotePending = new List<(ShinyMark, float)>(); private bool _worldLoaded; private bool _forceNext; private const string NamePrefix = "Shiny "; public static NativeShiny Instance { get; private set; } private Plugin P => Plugin.Instance; public static void Install(Harmony h) { //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Expected O, but got Unknown //IL_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Expected O, but got Unknown //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Expected O, but got Unknown Game.Require(typeof(BossManager), "InitializeBossFight"); Game.Require(typeof(Creature), "LoadFromSave", "_syncedRandomWeight", "BossType"); Game.Require(typeof(Item), "GetName", "ID"); Instance = new NativeShiny(); h.Patch((MethodBase)AccessTools.Method(typeof(BossManager), "InitializeBossFight", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(NativeShiny), "BossPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); h.Patch((MethodBase)AccessTools.Method(typeof(Creature), "LoadFromSave", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(NativeShiny), "LoadPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); h.Patch((MethodBase)AccessTools.Method(typeof(Item), "GetName", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(NativeShiny), "NamePostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } public bool Is(Item item) { if ((Object)(object)item != (Object)null) { return _live.Contains(item); } return false; } public void Forget(Item item) { if ((Object)(object)item != (Object)null) { _live.Remove(item); } } public bool Roll(Item item, float chance, string why) { if ((Object)(object)item == (Object)null || (Object)(object)item.Creature == (Object)null || !Game.IsHost || Is(item)) { return false; } if (_forceNext) { _forceNext = false; } else if (_rng.Next01() >= (double)chance) { return false; } Mark(item, why); return true; } public void Mark(Item item, string why) { if (!((Object)(object)item == (Object)null) && _live.Add(item)) { if (Game.IsHost && (Object)(object)item.Creature != (Object)null && why != "loaded from the save") { MakeKeyUnique(item.Creature); } try { ShinyLook.Apply(((Component)item).gameObject, true); } catch (Exception ex) { AdapterPlugin.Log.LogWarning((object)("shiny look: " + ex.Message)); } if (Game.IsHost) { NetService.Instance?.OnShinyMarked(item); } AdapterPlugin.Log.LogInfo((object)("shiny How to Fish creature: " + ((Object)item).name.Replace("(Clone)", "") + " (" + why + ")")); } } private void MakeKeyUnique(Creature c) { float w = ((Item)c)._syncedRandomWeight.Value; if (w == 1f || _keys.Any((NativeShinyKey k) => k.Matches((int)((Item)c).ID, w)) || Creature.DeadCreatures.Any((Creature o) => (Object)(object)o != (Object)null && (Object)(object)o != (Object)(object)c && ((Item)o).ID == ((Item)c).ID && Mathf.Abs(((Item)o)._syncedRandomWeight.Value - w) <= 1E-05f * Mathf.Max(1f, w))) { ((Item)c)._syncedRandomWeight.Value = w * (1.0002f + (float)_rng.Next01() * 0.0008f); } } public void OnWorldLoaded(List<NativeShinyKey> saved) { List<(Creature, float)> list = _pendingLoads.Where(((Creature Body, float Weight) p) => (Object)(object)p.Body != (Object)null).ToList(); _pendingLoads.Clear(); OnWorldUnloaded(); foreach (NativeShinyKey item in saved ?? new List<NativeShinyKey>()) { if (item != null && item.ItemId >= 0 && item.ItemId <= 255 && item.Weight > 0f && !float.IsInfinity(item.Weight)) { _keys.Add(item); } } _unmatched.AddRange(_keys); _worldLoaded = true; foreach (var (c, weight) in list) { Reload(c, weight); } if (_keys.Count > 0) { AdapterPlugin.Log.LogInfo((object)$"shiny: {_keys.Count} shiny creature(s) in the world file, {_keys.Count - _unmatched.Count} found among the {list.Count} loaded so far"); } } public void OnWorldUnloaded() { _live.Clear(); _keys.Clear(); _unmatched.Clear(); _pendingLoads.RemoveAll(((Creature Body, float Weight) p) => (Object)(object)p.Body == (Object)null); _remotePending.Clear(); _worldLoaded = false; } public List<NativeShinyKey> KeysInSave(ServerSaveObject save) { //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_01fa: Unknown result type (might be due to invalid IL or missing references) //IL_020c: Expected O, but got Unknown List<SavedItem> saved = new List<SavedItem>(); if (save?.WorldItems != null) { saved.AddRange(from val in save.WorldItems where val?.Item != null select val.Item); } if (save?.Players != null) { foreach (SavedPlayer item2 in save.Players.Where((SavedPlayer p) => p != null)) { if (item2.HeldItem != null) { saved.Add(item2.HeldItem); } if (item2.InventoryItems != null) { saved.AddRange(item2.InventoryItems.Where((SavedItem i) => i != null)); } } } _live.RemoveWhere((Item i) => (Object)(object)i == (Object)null); List<NativeShinyKey> list = new List<NativeShinyKey>(_keys); foreach (Item item in _live.Where((Item i) => (Object)(object)i != (Object)null && (Object)(object)i.Creature != (Object)null)) { float w = ((Item)item.Creature)._syncedRandomWeight.Value; if (!list.Any((NativeShinyKey k) => k.Matches((int)item.ID, w))) { list.Add(new NativeShinyKey { ItemId = item.ID, Weight = w }); } } List<NativeShinyKey> list2 = list.Where((NativeShinyKey k) => saved.Any((SavedItem s) => s.Exists && k.Matches((int)s.ItemID, s.Weight))).ToList(); _keys.Clear(); _keys.AddRange(list2); return list2; } private static void LoadPostfix(Creature __instance, SavedItem savedItem) { NativeShiny instance = Instance; if (instance == null || (Object)(object)__instance == (Object)null || savedItem == null || !InstanceFinder.IsServerStarted) { return; } if (!instance._worldLoaded) { instance._pendingLoads.RemoveAll(((Creature Body, float Weight) p) => (Object)(object)p.Body == (Object)null); instance._pendingLoads.Add((__instance, savedItem.Weight)); } else { instance.Reload(__instance, savedItem.Weight); } } private void Reload(Creature c, float weight) { if (!((Object)(object)c == (Object)null)) { int num = _unmatched.FindIndex((NativeShinyKey k) => k.Matches((int)((Item)c).ID, weight)); if (num >= 0) { _unmatched.RemoveAt(num); Mark((Item)(object)c, "loaded from the save"); } } } private static void BossPostfix(Creature creature) { try { NativeShiny instance = Instance; if (instance != null && !((Object)(object)creature == (Object)null) && Game.IsHost && (instance.Is((Item)(object)creature) || instance.Roll((Item)(object)creature, instance.P.Mapping.BossShinyChance, "boss"))) { TryChat("<b><color=#fff6a0>A SHINY boss appeared!</color></b>"); } } catch (Exception ex) { AdapterPlugin.Log.LogWarning((object)("shiny boss: " + ex.Message)); } } private static void NamePostfix(Item __instance, ref string __result) { if (Instance != null && (Object)(object)__instance != (Object)null && Instance._live.Contains(__instance) && __result != null && !__result.StartsWith("Shiny ")) { __result = "Shiny " + __result; } } public static string Plain(string name) { if (name == null || !name.StartsWith("Shiny ")) { return name; } return name.Substring("Shiny ".Length); } private static void TryChat(string text) { try { Game.LocalChat(text); } catch { } } public void RegisterCommands(DebugConsole c) { c.Register("shiny.held", "DEBUG (host): make the How to Fish creature in your hand shiny", (Func<string[], string>)delegate { if (!Game.IsHost) { return "host only"; } Item val = (((Object)(object)Player.LocalPlayer != (Object)null && (Object)(object)Player.LocalPlayer.Holding != (Object)null) ? Player.LocalPlayer.Holding.HeldItem : null); if ((Object)(object)val == (Object)null || (Object)(object)val.Creature == (Object)null) { return "hold a How to Fish creature first"; } if (WfFishService.Instance?.TagOf(val) != null) { return "that's a WEBFISHING fish (use wf.nextbite <name> shiny)"; } Mark(val, "shiny.held"); return val.GetName() + " is shiny now"; }); c.Register("shiny.next", "DEBUG (host): the next How to Fish bite, TNT fish or boss is shiny", (Func<string[], string>)delegate { if (!Game.IsHost) { return "host only"; } _forceNext = true; return "the next How to Fish bite, TNT fish or boss will be shiny"; }); } public void OnClientJoined() { OnWorldUnloaded(); } public void OnClientLeft() { OnWorldUnloaded(); } public IEnumerable<(Item Item, ShinyMark Msg)> LiveMarks() { return from i in _live.Where((Item i) => (Object)(object)i != (Object)null).ToList() select ((Item i, ShinyMark))(i: i, new ShinyMark { ItemId = i.ID }); } public void OnRemote(ShinyMark m) { if (!Game.IsHost && m != null && !TryApplyRemote(m)) { _remotePending.RemoveAll(((ShinyMark Msg, float At) p) => p.Msg.ObjectId == m.ObjectId); if (_remotePending.Count >= 300) { _remotePending.RemoveAt(0); } _remotePending.Add((m, Time.unscaledTime)); } } public void RetryRemote() { for (int num = _remotePending.Count - 1; num >= 0; num--) { if (TryApplyRemote(_remotePending[num].Msg)) { _remotePending.RemoveAt(num); } else if (Time.unscaledTime - _remotePending[num].At > 60f) { _remotePending.RemoveAt(num); } } } private bool TryApplyRemote(ShinyMark m) { //IL_0088: Unknown result type (might be due to invalid IL or missing references) ClientManager clientManager = InstanceFinder.ClientManager; if ((Object)(object)clientManager == (Object)null || clientManager.Objects == null || !((ManagedObjects)clientManager.Objects).Spawned.TryGetValue(m.ObjectId, out var value) || (Object)(object)value == (Object)null) { return false; } Item component = ((Component)value).GetComponent<Item>(); if ((Object)(object)component == (Object)null || component.ID != m.ItemId) { return false; } bool num = !Is(component); Mark(component, "from the host"); if (num && (Object)(object)component.Creature != (Object)null && (int)component.Creature.BossType != 0 && !component.Creature.IsDead) { TryChat("<b><color=#fff6a0>A SHINY boss appeared!</color></b>"); } return true; } } internal sealed class NetService : MonoBehaviour { public enum Role { Off, Host, Client } private sealed class Peer { public ulong Id; public string Name; public NetCompat Compat; public readonly RateLimit Rate = new RateLimit(); public double LastSnapshot = double.NegativeInfinity; public double LastWorldSent = double.NegativeInfinity; public int Dropped; public bool Warned; public bool HasCache; public bool Linked { get { if (Compat != null) { return Compat.Linked; } return false; } } } private readonly SteamLink _link = new SteamLink(); private Role _role; private float _errorLogAt; private readonly Dictionary<ulong, Peer> _peers = new Dictionary<ulong, Peer>(); private readonly HashSet<ulong> _present = new HashSet<ulong>(); private const int MaxFromClient = 16384; private readonly AqBroadcaster _bc = new AqBroadcaster(); private readonly List<(Item Item, SpawnTag Msg, float At)> _outTags = new List<(Item, SpawnTag, float)>(); private readonly List<(Item Item, ShinyMark Msg, float At)> _outMarks = new List<(Item, ShinyMark, float)>(); private bool? _sentRain; private AqMirror _loop; private int _loopDeltas; private int _loopTags; private int _loopRain; private string _loopProblem; private ulong _hostId; private bool _hostAnswered; private NetCompat _hostCompat; private double _helloAt = double.NegativeInfinity; private int _helloTries; private AqMirror _mirror; private readonly PendingRequests _pending = new PendingRequests(); private bool? _clientRain; private const int MaxHelloTries = 40; private const int MaxKeyItemOwners = 256; public static NetService Instance { get; private set; } private Plugin P => Plugin.Instance; private double Now => Time.realtimeSinceStartupAsDouble; public KeyItemsState ClientKeyItems { get; private set; } public Role Current => _role; private bool ClientLinked { get { if (_role == Role.Client && _hostCompat != null) { return _hostCompat.Linked; } return false; } } public AqMirror ClientMirror { get { if (!ClientLinked || _mirror == null || _mirror.State == null) { return null; } return _mirror; } } private bool AnyoneListening { get { if (!_peers.Values.Any((Peer p) => p.Linked)) { return _loop != null; } return true; } } public static void Che
BepInEx/plugins/WFxHTF/WFxHTF.Core.dll
Decompiled a day agousing System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using System.Threading; using Newtonsoft.Json; using Newtonsoft.Json.Converters; using Newtonsoft.Json.Linq; using WFxHTF.Data; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("WFxHTF")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.1.0.0")] [assembly: AssemblyInformationalVersion("0.1.0+166b9aae0d28865ba70c4894450aeb4db4b21aeb")] [assembly: AssemblyProduct("WFxHTF.Core")] [assembly: AssemblyTitle("WFxHTF.Core")] [assembly: AssemblyVersion("0.1.0.0")] namespace WFxHTF.Core; [JsonConverter(typeof(StringEnumConverter))] public enum AqEventKind { FishDied, AttackStarted, AttackEnded } public sealed class AqEvent { public AqEventKind Kind; public long Uid; public string SpeciesId; public string Reason; public bool Defeated; } public sealed class Aquarium { private const float TimerEps = 0.0001f; public readonly AquariumConfig Config; public AquariumState State; public int Players = 1; public const float MaxCoordinate = 100000f; public int Capacity => Config.BaseCapacity + State.CapacityLevel * Config.CapacityPerLevel; public float SizeScale { get { float num = (float.IsNaN(Config.MaxSizeScale) ? 1f : Math.Max(1f, Math.Min(3f, Config.MaxSizeScale))); if (Config.MaxCapacityLevel <= 0) { return 1f; } float num2 = (float)Math.Max(0, Math.Min(Config.MaxCapacityLevel, State.CapacityLevel)) / (float)Config.MaxCapacityLevel; return 1f + (num - 1f) * num2; } } public float PlayerIncomeFactor => 1f + Config.IncomePerExtraPlayer * (float)Math.Max(0, Players - 1); public int HopperCapacity => State.AutoFeederLevel * Config.HopperPerLevel; public bool AttackActive => State.Attack != null; public bool IsPlaced { get { if (State.Placement != null) { return State.Placement.Placed; } return false; } } public Aquarium(AquariumConfig config, AquariumState state = null) { Config = config ?? new AquariumConfig(); State = state ?? new AquariumState { NextAttackInDays = Config.AttackIntervalDays }; } public bool TryDeposit(string speciesId, float weightKg, Quality quality, ulong depositorId, string depositorName, int value, out long uid, out string error, bool drip = false, bool shiny = false, float cookness = 0f, float betMultiplier = 1f, float killScore = 1f) { //IL_0058: 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_00b2: Unknown result type (might be due to invalid IL or missing references) uid = 0L; if (string.IsNullOrEmpty(speciesId)) { error = "no species"; return false; } if (State.Fish.Count >= Capacity) { error = "tank full"; return false; } if (value < 0 || !(weightKg > 0f) || float.IsInfinity(weightKg) || !Enum.IsDefined(typeof(Quality), quality)) { error = "bad fish"; return false; } uid = State.NextUid++; State.Fish.Add(new AqFish { Uid = uid, SpeciesId = speciesId, WeightKg = weightKg, Quality = quality, Drip = drip, Shiny = shiny, DepositorId = depositorId, DepositorName = depositorName, Value = value, Cookness = Finite(cookness, 0f, 0f, 10f), BetMultiplier = Finite(betMultiplier, 1f, 0f, 1000f), KillScore = Finite(killScore, 1f, 0f, 100f) }); State.Seq++; error = null; return true; } private static float Finite(float v, float fallback, float min, float max) { if (!float.IsNaN(v) && !float.IsInfinity(v)) { return Math.Max(min, Math.Min(max, v)); } return fallback; } public int Feed(int units) { int i; for (i = 0; i < units; i++) { AqFish aqFish = (from f in State.Fish where f.Hunger < Config.FeedBelow orderby f.Hunger, f.Uid select f).FirstOrDefault(); if (aqFish == null) { break; } aqFish.Hunger = 1f; aqFish.StarvedDays = 0f; } int num = Math.Min(units - i, HopperCapacity - State.HopperFood); if (num > 0) { State.HopperFood += num; i += num; } if (i > 0) { State.Seq++; } return i; } public Dictionary<ulong, int> Collect(IReadOnlyCollection<ulong> presentPlayers) { Dictionary<ulong, int> dictionary = PlanCollect(presentPlayers); foreach (KeyValuePair<ulong, int> item in dictionary) { Withdraw(item.Value); } return dictionary; } public Dictionary<ulong, int> PlanCollect(IReadOnlyCollection<ulong> presentPlayers) { Dictionary<ulong, int> dictionary = new Dictionary<ulong, int>(); List<ulong> list = presentPlayers?.Distinct().ToList() ?? new List<ulong>(); if (list.Count == 0) { return dictionary; } int num = (int)Math.Floor(State.TrayCoins / (double)list.Count); if (num <= 0) { return dictionary; } foreach (ulong item in list) { dictionary[item] = num; } return dictionary; } public void Withdraw(int coins) { if (coins > 0) { State.TrayCoins = Math.Max(0.0, State.TrayCoins - (double)coins); State.Seq++; } } public AqFish Remove(long uid) { int num = State.Fish.FindIndex((AqFish f) => f.Uid == uid); if (num < 0) { return null; } AqFish result = State.Fish[num]; State.Fish.RemoveAt(num); State.Seq++; return result; } public int? UpgradeCost(UpgradeType type) { int num = Level(type); int[] array = Prices(type); if (num >= array.Length) { return null; } return array[num]; } public bool ApplyUpgrade(UpgradeType type) { if (!UpgradeCost(type).HasValue) { return false; } switch (type) { case UpgradeType.Capacity: State.CapacityLevel++; break; case UpgradeType.AutoFeeder: State.AutoFeederLevel++; break; case UpgradeType.Decor: State.DecorLevel++; break; } State.Seq++; return true; } public bool IsLiveOn(int activeIsland) { if (IsPlaced) { return State.Placement.Island == activeIsland; } return false; } public bool TryPlace(int island, float x, float y, float z, float rotY, out string error) { error = null; if (AttackActive) { error = "can't move the tank during an attack"; return false; } if (island < 0 || island > 255) { error = "bad island"; return false; } float[] array = new float[4] { x, y, z, rotY }; foreach (float num in array) { if (float.IsNaN(num) || float.IsInfinity(num) || Math.Abs(num) > 100000f) { error = "bad position"; return false; } } bool isPlaced = IsPlaced; State.Placement = new TankPlacement { Placed = true, Island = island, X = x, Y = y, Z = z, RotY = (rotY % 360f + 360f) % 360f }; if (!isPlaced) { State.NextAttackInDays = Math.Max(State.NextAttackInDays, Config.AttackGraceDays); } State.Seq++; return true; } public bool TryPack(out string error) { error = null; if (AttackActive) { error = "can't pack the tank during an attack"; return false; } if (!IsPlaced) { error = "the tank is already packed"; return false; } State.Placement.Placed = false; State.Seq++; return true; } public void Hold(double tankDay) { State.LastTickDay = tankDay; } public bool TryToggleZen(out string error) { if (AttackActive) { error = "can't change Zen mode during an attack"; return false; } if (State.ZenCooldownDays > 0f) { error = "Zen mode was changed recently: try again in " + RealTimeText(State.ZenCooldownDays) + " (counts down while the tank is on your island)"; return false; } State.Zen = !State.Zen; State.ZenCooldownDays = (State.Zen ? Config.ZenToggleCooldownDays : Math.Max(Config.ZenToggleCooldownDays, Config.AttackGraceDays * 1.5f)); if (!State.Zen) { State.NextAttackInDays = Math.Max(State.NextAttackInDays, Config.AttackGraceDays); } State.Seq++; error = null; return true; } public List<AqEvent> ReportThreatHp(float hp, float maxHp) { List<AqEvent> list = new List<AqEvent>(); if (State.Attack == null) { return list; } State.Attack.ThreatMaxHp = Math.Max(maxHp, 0f); State.Attack.ThreatHp = Math.Max(hp, 0f); if (hp <= 0f) { EndAttack(list, defeated: true); } State.Seq++; return list; } public List<AqEvent> TickTo(double tankDay) { double num = tankDay - State.LastTickDay; State.LastTickDay = tankDay; if (num <= 0.0) { return new List<AqEvent>(); } return Advance(Math.Min(num, Config.MaxTickGapDays)); } public void OnWorldLoaded(double tankDay) { State.LastTickDay = tankDay; State.ZenCooldownDays = Math.Min(State.ZenCooldownDays, Math.Max(Config.ZenToggleCooldownDays, Config.AttackGraceDays * 1.5f)); State.Seq++; } public string RealTimeText(double days) { double num = Math.Max(0.0, days * (double)Config.TankDayRealMinutes * 60.0); return $"{(int)(num / 60.0)}:{(int)(num % 60.0):00}"; } public double TankDays(double playtimeSeconds) { return playtimeSeconds / 60.0 / (double)Config.TankDayRealMinutes; } public List<AqEvent> Advance(double days, bool live = true) { List<AqEvent> list = new List<AqEvent>(); if (days <= 0.0) { return list; } double num = days; bool flag = false; while (num > 1E-09) { float num2 = (float)Math.Min(Config.StepDays, num); Step(num2, list, live && !flag); if (!flag && list.Exists((AqEvent e) => e.Kind == AqEventKind.AttackStarted)) { flag = true; } num -= (double)num2; } State.Seq++; return list; } private void Step(float dt, List<AqEvent> events, bool live) { AquariumState state = State; AquariumConfig config = Config; if (state.ZenCooldownDays > 0f) { state.ZenCooldownDays = Math.Max(0f, state.ZenCooldownDays - dt); } float num = config.IncomeRate * (1f + (float)state.DecorLevel * config.DecorIncomeBonusPerLevel) * (state.Zen ? config.ZenIncomeRate : 1f) * PlayerIncomeFactor; for (int num2 = state.Fish.Count - 1; num2 >= 0; num2--) { AqFish aqFish = state.Fish[num2]; aqFish.Hunger = Math.Max(0f, aqFish.Hunger - config.HungerPerDay * (state.Zen ? config.ZenHungerRate : 1f) * dt); if (aqFish.Hunger < config.AutoFeedBelow && state.AutoFeederLevel > 0 && state.HopperFood > 0) { state.HopperFood--; aqFish.Hunger = 1f; aqFish.StarvedDays = 0f; } if (aqFish.Hunger > 0f) { state.TrayCoins += (float)aqFish.Value * num * dt; if (state.Attack == null) { aqFish.Health = Math.Min(1f, aqFish.Health + config.HealthRegenPerDay * dt); } } else { aqFish.StarvedDays += dt; if (aqFish.StarvedDays >= config.StarveDeathDays - 0.0001f) { Kill(num2, "starved", events); continue; } } if (state.Attack != null && live) { aqFish.Health -= config.AttackDrainPerDay * dt; if (aqFish.Health <= 0f) { aqFish.Health = 0f; Kill(num2, "attack", events); } } } if (state.Attack != null) { if (live) { state.Attack.ElapsedDays += dt; if (state.Fish.Count == 0) { EndAttack(events, defeated: false); } else if (state.Attack.ElapsedDays >= config.AttackTimeoutDays - 0.0001f) { int index = WeakestIndex(); Kill(index, "attack", events); EndAttack(events, defeated: false); } } } else { if (state.Zen) { return; } state.NextAttackInDays -= dt; if (!live) { state.NextAttackInDays = Math.Max(state.NextAttackInDays, config.AttackGraceDays); } else if (state.NextAttackInDays <= 0.0001f) { if (state.Fish.Count > 0) { state.Attack = new AqAttack { ThreatHp = 1f, ThreatMaxHp = 1f }; events.Add(new AqEvent { Kind = AqEventKind.AttackStarted }); } else { state.NextAttackInDays = config.AttackIntervalDays; } } } } public List<AqEvent> ForceAttack() { List<AqEvent> list = new List<AqEvent>(); if (State.Attack != null || State.Fish.Count == 0) { return list; } State.Attack = new AqAttack { ThreatHp = 1f, ThreatMaxHp = 1f }; list.Add(new AqEvent { Kind = AqEventKind.AttackStarted }); State.Seq++; return list; } private int WeakestIndex() { int num = 0; for (int i = 1; i < State.Fish.Count; i++) { AqFish aqFish = State.Fish[i]; AqFish aqFish2 = State.Fish[num]; if (aqFish.Health < aqFish2.Health || (aqFish.Health == aqFish2.Health && (aqFish.Value < aqFish2.Value || (aqFish.Value == aqFish2.Value && aqFish.Uid < aqFish2.Uid)))) { num = i; } } return num; } private void Kill(int index, string reason, List<AqEvent> events) { AqFish aqFish = State.Fish[index]; State.Fish.RemoveAt(index); events.Add(new AqEvent { Kind = AqEventKind.FishDied, Uid = aqFish.Uid, SpeciesId = aqFish.SpeciesId, Reason = reason }); } private void EndAttack(List<AqEvent> events, bool defeated) { State.Attack = null; State.NextAttackInDays = Config.AttackIntervalDays; events.Add(new AqEvent { Kind = AqEventKind.AttackEnded, Defeated = defeated }); } private int Level(UpgradeType t) { return t switch { UpgradeType.AutoFeeder => State.AutoFeederLevel, UpgradeType.Capacity => State.CapacityLevel, _ => State.DecorLevel, }; } private int[] Prices(UpgradeType t) { int[] array = t switch { UpgradeType.AutoFeeder => Config.AutoFeederPrices, UpgradeType.Capacity => Config.CapacityPrices, _ => Config.DecorPrices, }; int num = t switch { UpgradeType.AutoFeeder => Config.MaxAutoFeederLevel, UpgradeType.Capacity => Config.MaxCapacityLevel, _ => Config.MaxDecorLevel, }; if (array.Length <= num) { return array; } return array.Take(num).ToArray(); } public string Hash() { return AquariumSync.HashOf(State); } public bool IsValid(out string why) { why = null; AquariumState s = State; if (s.Schema < 1 || s.Schema > 1) { why = $"schema {s.Schema}"; return false; } if (s.Fish == null) { why = "no fish list"; return false; } if (s.Fish.Any((AqFish f) => f == null || string.IsNullOrEmpty(f.SpeciesId))) { why = "fish without a species"; return false; } if (s.TrayCoins < 0.0 || double.IsNaN(s.TrayCoins)) { why = "bad tray"; return false; } if (s.Fish.Select((AqFish f) => f.Uid).Distinct().Count() != s.Fish.Count) { why = "duplicate fish uid"; return false; } if (s.Fish.Any((AqFish f) => f.Uid >= s.NextUid)) { why = "uid counter behind fish"; return false; } if (s.Placement != null) { TankPlacement placement = s.Placement; if (placement.Island < -1 || placement.Island > 255) { why = "bad placement island"; return false; } float[] array = new float[4] { placement.X, placement.Y, placement.Z, placement.RotY }; foreach (float num2 in array) { if (float.IsNaN(num2) || float.IsInfinity(num2) || Math.Abs(num2) > 100000f) { why = "bad placement"; return false; } } if (placement.Placed && placement.Island < 0) { why = "placed without an island"; return false; } } return true; } } public sealed class HeldFish { public string SpeciesId; public float WeightKg; public Quality Quality; public bool Drip; public bool Shiny; public float Cookness; public float BetMultiplier = 1f; public float KillScore = 1f; } public interface IAquariumPorts { HeldFish PeekHeldFish(ulong playerId); bool TakeHeldFish(ulong playerId, HeldFish fish); int ValueOf(HeldFish fish); int TakeFood(ulong playerId, int units); bool TryCharge(ulong playerId, int coins); void Pay(ulong playerId, int coins); IReadOnlyCollection<ulong> PlayersInLobby(); string NameOf(ulong playerId); bool IsHost(ulong playerId); bool GiveFish(ulong playerId, AqFish fish); } public sealed class AquariumHost { public const int MaxRequestIdLength = 64; public const int MaxFoodPerRequest = 1000; public readonly Aquarium Tank; private readonly IAquariumPorts _ports; private readonly Dictionary<string, AqResult> _seen = new Dictionary<string, AqResult>(); private readonly Queue<string> _seenOrder = new Queue<string>(); private const int SeenLimit = 1024; public AquariumHost(Aquarium tank, IAquariumPorts ports) { Tank = tank; _ports = ports; } public AqResult Handle(ulong senderId, AqRequest req) { if (req == null) { return Fail("no request"); } if (!Enum.IsDefined(typeof(AqRequestKind), req.Kind)) { return Fail("unknown request"); } bool flag = req.Kind != AqRequestKind.SnapshotPlease; if (flag && string.IsNullOrEmpty(req.RequestId)) { return Fail("request id required"); } if (req.RequestId != null && req.RequestId.Length > 64) { return Fail("request id too long"); } string key = senderId + ":" + req.RequestId; if (flag && _seen.TryGetValue(key, out var value)) { return value; } AqResult aqResult; try { aqResult = Execute(senderId, req); } catch (Exception ex) { return new AqResult { RequestId = req.RequestId, Ok = false, Error = "host error: " + ex.GetType().Name }; } aqResult.RequestId = req.RequestId; if (flag) { Remember(key, aqResult); } return aqResult; } private AqResult Execute(ulong sender, AqRequest req) { //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_0136: Unknown result type (might be due to invalid IL or missing references) switch (req.Kind) { case AqRequestKind.Deposit: { if (Tank.State.Fish.Count >= Tank.Capacity) { return Fail("tank full"); } HeldFish heldFish = _ports.PeekHeldFish(sender); if (heldFish == null) { return Fail("not holding a fish"); } if (string.IsNullOrEmpty(heldFish.SpeciesId) || !IsFinitePositive(heldFish.WeightKg) || !Enum.IsDefined(typeof(Quality), heldFish.Quality)) { return Fail("bad fish"); } int num = _ports.ValueOf(heldFish); if (num < 0) { return Fail("bad fish"); } string depositorName = _ports.NameOf(sender); if (!_ports.TakeHeldFish(sender, heldFish)) { return Fail("fish is gone"); } Tank.TryDeposit(heldFish.SpeciesId, heldFish.WeightKg, heldFish.Quality, sender, depositorName, num, out var uid, out var _, heldFish.Drip, heldFish.Shiny, heldFish.Cookness, heldFish.BetMultiplier, heldFish.KillScore); return new AqResult { Ok = true, Amount = uid }; } case AqRequestKind.Feed: { if (req.FoodUnits <= 0 || req.FoodUnits > 1000) { return Fail("bad food amount"); } int num2 = FeedableUnits(req.FoodUnits); if (num2 == 0) { return Fail("no fish need food and the hopper is full"); } int num3 = _ports.TakeFood(sender, num2); if (num3 <= 0) { return Fail("no food in inventory"); } return new AqResult { Ok = true, Amount = Tank.Feed(Math.Min(num3, num2)) }; } case AqRequestKind.Collect: { int num5 = 0; string text = null; foreach (KeyValuePair<ulong, int> item in Tank.PlanCollect(_ports.PlayersInLobby())) { try { _ports.Pay(item.Key, item.Value); Tank.Withdraw(item.Value); if (item.Key == sender) { num5 = item.Value; } } catch (Exception ex) { text = text ?? $"paying {item.Key} failed: {ex.GetType().Name}"; } } return new AqResult { Ok = (text == null), Error = text, Amount = num5 }; } case AqRequestKind.Upgrade: { if (!Enum.IsDefined(typeof(UpgradeType), req.Upgrade)) { return Fail("unknown upgrade"); } int? num4 = Tank.UpgradeCost(req.Upgrade); if (!num4.HasValue) { return Fail("already at max level"); } if (!_ports.TryCharge(sender, num4.Value)) { return Fail("not enough coins"); } Tank.ApplyUpgrade(req.Upgrade); return new AqResult { Ok = true, Amount = num4.Value }; } case AqRequestKind.ToggleZen: { if (!Tank.TryToggleZen(out var error3)) { return Fail(error3); } return new AqResult { Ok = true }; } case AqRequestKind.Place: { if (!_ports.IsHost(sender)) { return Fail("only the host can move the tank"); } if (!Tank.TryPlace(req.Island, req.X, req.Y, req.Z, req.RotY, out var error4)) { return Fail(error4); } return new AqResult { Ok = true }; } case AqRequestKind.Pack: { if (!_ports.IsHost(sender)) { return Fail("only the host can move the tank"); } if (!Tank.TryPack(out var error2)) { return Fail(error2); } return new AqResult { Ok = true }; } case AqRequestKind.TakeOut: { if (!Tank.IsPlaced) { return Fail("the aquarium is packed up"); } if (Tank.AttackActive) { return Fail("not while the tank is under attack"); } AqFish aqFish = Tank.State.Fish.Find((AqFish f) => f.Uid == req.Uid); if (aqFish == null) { return Fail("that fish isn't in the tank"); } if (!_ports.GiveFish(sender, aqFish)) { return Fail("couldn't hand the fish over (stand next to the aquarium)"); } Tank.Remove(req.Uid); return new AqResult { Ok = true, Amount = req.Uid }; } case AqRequestKind.SnapshotPlease: return new AqResult { Ok = true }; default: return Fail("unknown request"); } } private int FeedableUnits(int offered) { int num = 0; foreach (AqFish item in Tank.State.Fish) { if (item.Hunger < Tank.Config.FeedBelow) { num++; } } int val = Tank.HopperCapacity - Tank.State.HopperFood; return Math.Min(offered, num + Math.Max(0, val)); } private static bool IsFinitePositive(float v) { if (!float.IsNaN(v) && !float.IsInfinity(v)) { return v > 0f; } return false; } private static AqResult Fail(string error) { return new AqResult { Ok = false, Error = error }; } private void Remember(string key, AqResult r) { if (!_seen.ContainsKey(key)) { _seen[key] = r; _seenOrder.Enqueue(key); while (_seenOrder.Count > 1024) { _seen.Remove(_seenOrder.Dequeue()); } } } } [JsonConverter(typeof(StringEnumConverter))] public enum UpgradeType { Capacity, AutoFeeder, Decor } public sealed class AquariumConfig { public int BaseCapacity = 6; public int CapacityPerLevel = 3; public int MaxCapacityLevel = 5; public float MaxSizeScale = 1.75f; public int MaxAutoFeederLevel = 3; public int MaxDecorLevel = 5; public int FoodUnitsPerItem = 8; public int HopperPerLevel = 10; public float AutoFeedBelow = 0.3f; public float HungerPerDay = 0.5f; public float FeedBelow = 0.5f; public float StarveDeathDays = 1f; public float IncomeRate = 0.22f; public float IncomePerExtraPlayer = 0.35f; public float DecorIncomeBonusPerLevel = 0.05f; public float ZenIncomeRate = 0.1f; public float ZenHungerRate = 0.1f; public float ZenToggleCooldownDays = 0.25f; public float AttackIntervalDays = 2f; public float AttackGraceDays = 0.1f; public float AttackTimeoutDays = 0.5f; public float AttackDrainPerDay = 1f; public int ThreatHp = 400; public float HealthRegenPerDay = 0.5f; public bool TankCanBeHitByBosses; public float TankDayRealMinutes = 20f; public float MaxTickGapDays = 0.05f; public int[] CapacityPrices = new int[5] { 200, 400, 800, 1600, 3200 }; public int[] AutoFeederPrices = new int[3] { 300, 900, 2000 }; public int[] DecorPrices = new int[5] { 100, 250, 500, 1000, 2000 }; public double StepDays = 1.0 / 96.0; } public sealed class AqFish { public long Uid; public string SpeciesId; public float WeightKg; public Quality Quality; public bool Drip; public bool Shiny; public ulong DepositorId; public string DepositorName; public int Value; public float Cookness; public float BetMultiplier = 1f; public float KillScore = 1f; public float Hunger = 1f; public float Health = 1f; public float StarvedDays; } public sealed class AqAttack { public float ThreatHp; public float ThreatMaxHp; public float ElapsedDays; } public sealed class AquariumState { public const int CurrentSchema = 1; public int Schema = 1; public long Seq; public long NextUid = 1L; public List<AqFish> Fish = new List<AqFish>(); public double TrayCoins; public int CapacityLevel; public int AutoFeederLevel; public int DecorLevel; public int HopperFood; public bool Zen; public float ZenCooldownDays; public float NextAttackInDays = 2f; public AqAttack Attack; public double LastTickDay; public TankPlacement Placement; } public sealed class TankPlacement { public bool Placed; public int Island = -1; public float X; public float Y; public float Z; public float RotY; } public static class AquariumSync { public static AquariumState Clone(AquariumState s) { return Hashing.Clone<AquariumState>(s); } public static string HashOf(AquariumState s) { return Hashing.Short((object)Clone(s)); } public static AqSnapshot Snapshot(AquariumState s) { AquariumState aquariumState = Clone(s); return new AqSnapshot { State = aquariumState, Hash = Hashing.Short((object)aquariumState) }; } public static AqHash HashMessage(AquariumState s) { return new AqHash { Seq = s.Seq, Hash = HashOf(s) }; } public static AqDelta Diff(AquariumState prev, AquariumState next) { Dictionary<long, string> dictionary = prev.Fish.ToDictionary((AqFish f) => f.Uid, (AqFish f) => Hashing.CanonicalJson((object)f)); AqDelta aqDelta = new AqDelta { BaseSeq = prev.Seq, Seq = next.Seq, Hash = HashOf(next) }; foreach (AqFish item in next.Fish) { if (!dictionary.TryGetValue(item.Uid, out var value) || value != Hashing.CanonicalJson((object)item)) { aqDelta.Upserts.Add(Clone(item)); } } HashSet<long> nowUids = new HashSet<long>(next.Fish.Select((AqFish f) => f.Uid)); aqDelta.Removed.AddRange(from f in prev.Fish where !nowUids.Contains(f.Uid) select f.Uid); aqDelta.Scalars = Clone(next); aqDelta.Scalars.Fish = new List<AqFish>(); return aqDelta; } public static AquariumState Apply(AquariumState mirror, AqDelta d) { if (mirror == null || d == null || d.Scalars == null || mirror.Seq != d.BaseSeq) { return null; } Dictionary<long, AqFish> byUid = mirror.Fish.ToDictionary((AqFish f) => f.Uid, Clone); foreach (long item in d.Removed) { byUid.Remove(item); } foreach (AqFish upsert in d.Upserts) { byUid[upsert.Uid] = Clone(upsert); } AquariumState aquariumState = Clone(d.Scalars); List<long> order = mirror.Fish.Select((AqFish f) => f.Uid).Where(byUid.ContainsKey).ToList(); order.AddRange(from u in byUid.Keys where !order.Contains(u) orderby u select u); aquariumState.Fish = order.Select((long u) => byUid[u]).ToList(); if (!(HashOf(aquariumState) == d.Hash)) { return null; } return aquariumState; } private static AqFish Clone(AqFish f) { return Hashing.Clone<AqFish>(f); } } public sealed class EconomyConfig { public float[] QualityMultiplier = new float[6] { 1f, 1.2f, 1.5f, 2f, 2.5f, 3f }; public float[][] QualityChancesByBaitTier = new float[4][] { new float[1] { 1f }, new float[3] { 1f, 0.15f, 0.05f }, new float[5] { 1f, 0.8f, 0.45f, 0.15f, 0.05f }, new float[6] { 1f, 0.98f, 0.75f, 0.55f, 0.25f, 0.05f } }; public Quality DripVisualMinQuality = (Quality)4; } public static class Economy { public static int SellValue(FishDef def, LureStats lure, float weightKg, Quality q, EconomyConfig eco) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) float val = BaseValue(def, lure, weightKg) * eco.QualityMultiplier[q]; int val2 = (int)Math.Ceiling(Math.Max(0f, val)); return Math.Max(1, Math.Min(val2, lure.MaxRegularWorth)); } public static float BaseValue(FishDef def, LureStats lure, float weightKg) { float val = ((def.Htf.WeightKgMean > 0f) ? (weightKg / def.Htf.WeightKgMean) : 1f); return lure.AvgWorth * def.Htf.RelativeWorth * (float)Math.Sqrt(Math.Max(0.01f, val)); } public static int MaxHp(FishDef def, LureStats lure) { return Math.Max(1, (int)Math.Round(lure.AvgHp * def.Htf.RelativeHp)); } public static Quality RollQuality(int baitTier, EconomyConfig eco, Rng rng) { //IL_0033: Unknown result type (might be due to invalid IL or missing references) float[][] qualityChancesByBaitTier = eco.QualityChancesByBaitTier; if (qualityChancesByBaitTier.Length == 0) { return (Quality)0; } int num = Math.Max(0, Math.Min(qualityChancesByBaitTier.Length - 1, baitTier)); return QualityInfo.Roll(qualityChancesByBaitTier[num], (Func<double>)rng.Next01, 1f); } public static float RollWeightKg(FishDef def, Rng rng) { HtfTargets htf = def.Htf; double val = (double)htf.WeightKgMean * (1.0 + rng.NextGaussian() * (double)htf.WeightSpread); return (float)Math.Max(htf.WeightKgMin, Math.Min(htf.WeightKgMax, val)); } public static bool ShowsDripVisual(Quality q, EconomyConfig eco) { //IL_0000: 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) return q >= eco.DripVisualMinQuality; } public static bool ForcesHighTierSkin(FishDef def) { return def.Htf.UltraRare; } } public sealed class JournalEntry { public bool Caught; public int Count; public string FirstCatchUtc; public float BestWeightKg; public Quality BestQuality; public ulong FirstCatcherId; public string FirstCatcherName; public string FirstIsland; public string FirstBait; public int ShinyCount; } public sealed class Journal { public const int CurrentSchema = 1; public const string ScopePlayer = "player"; public const string ScopeWorld = "world"; public int Schema = 1; public string Scope = "player"; public string OwnerId = ""; public SortedDictionary<string, JournalEntry> Entries = new SortedDictionary<string, JournalEntry>(StringComparer.Ordinal); public int CaughtCount { get { int num = 0; foreach (KeyValuePair<string, JournalEntry> entry in Entries) { if (entry.Value.Caught) { num++; } } return num; } } public static Journal ForPlayer(ulong steamId) { return new Journal { Scope = "player", OwnerId = steamId.ToString() }; } public static Journal ForWorld(string worldId) { return new Journal { Scope = "world", OwnerId = worldId }; } public bool Record(CatchEvent e, DateTime utc) { //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: 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_010b: Unknown result type (might be due to invalid IL or missing references) if (string.IsNullOrEmpty(e.SpeciesId)) { throw new ArgumentException("speciesId required"); } if (Scope == "player" && e.HookerId.ToString() != OwnerId) { return false; } if (!Entries.TryGetValue(e.SpeciesId, out var value)) { value = (Entries[e.SpeciesId] = new JournalEntry()); } bool num = !value.Caught; value.Count++; if (num) { value.Caught = true; value.FirstCatchUtc = utc.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ"); value.FirstCatcherId = e.HookerId; value.FirstCatcherName = e.HookerName; value.FirstIsland = e.Island; value.FirstBait = e.Bait; } if (e.WeightKg > value.BestWeightKg) { value.BestWeightKg = e.WeightKg; } if (e.Quality > value.BestQuality) { value.BestQuality = e.Quality; } if (e.Shiny) { value.ShinyCount++; } return num; } public bool IsValid(out string why) { why = null; if (Schema < 1 || Schema > 1) { why = $"schema {Schema}"; return false; } if (Scope != "player" && Scope != "world") { why = "scope " + Scope; return false; } if (Entries == null) { why = "no entries"; return false; } foreach (KeyValuePair<string, JournalEntry> entry in Entries) { if (entry.Value == null || entry.Value.Count < 0 || (entry.Value.Caught && entry.Value.Count == 0)) { why = "bad entry " + entry.Key; return false; } } return true; } } public sealed class PlayerKeyItems { public ulong SteamId; public bool WfLure; public bool WfLureOn; public bool ShinyCharm; public bool ShinyCharmOff; } public sealed class KeyItems { public readonly List<PlayerKeyItems> Players; public KeyItems(List<PlayerKeyItems> players) { Players = players ?? new List<PlayerKeyItems>(); Players.RemoveAll((PlayerKeyItems p) => p == null || p.SteamId == 0); } public PlayerKeyItems Find(ulong id) { return Players.FirstOrDefault((PlayerKeyItems p) => p.SteamId == id); } private PlayerKeyItems Get(ulong id) { PlayerKeyItems playerKeyItems = Find(id); if (playerKeyItems == null) { List<PlayerKeyItems> players = Players; PlayerKeyItems obj = new PlayerKeyItems { SteamId = id }; playerKeyItems = obj; players.Add(obj); } return playerKeyItems; } public bool LureOn(ulong id) { return Find(id)?.WfLureOn ?? false; } public bool HasCharm(ulong id) { return Find(id)?.ShinyCharm ?? false; } public bool CharmOn(ulong id) { PlayerKeyItems playerKeyItems = Find(id); if (playerKeyItems != null && playerKeyItems.ShinyCharm) { return !playerKeyItems.ShinyCharmOff; } return false; } public AqResult Handle(ulong sender, AqRequest req, int lurePrice, int charmPrice, Func<int, bool> tryCharge) { if (req == null || sender == 0L) { return Fail("no request"); } switch (req.Kind) { case AqRequestKind.BuyWfLure: { PlayerKeyItems playerKeyItems4 = Find(sender); if (playerKeyItems4 != null && playerKeyItems4.WfLure) { return Fail("you already have the Web Fishing Lure"); } if (!tryCharge(lurePrice)) { return Fail($"it costs ${lurePrice}"); } PlayerKeyItems playerKeyItems5 = Get(sender); playerKeyItems5.WfLure = true; playerKeyItems5.WfLureOn = true; return new AqResult { RequestId = req.RequestId, Ok = true, Amount = lurePrice }; } case AqRequestKind.BuyShinyCharm: { PlayerKeyItems playerKeyItems2 = Find(sender); if (playerKeyItems2 != null && playerKeyItems2.ShinyCharm) { return Fail("you already have the Shiny Charm"); } if (!tryCharge(charmPrice)) { return Fail($"it costs ${charmPrice}"); } PlayerKeyItems playerKeyItems3 = Get(sender); playerKeyItems3.ShinyCharm = true; playerKeyItems3.ShinyCharmOff = false; return new AqResult { RequestId = req.RequestId, Ok = true, Amount = charmPrice }; } case AqRequestKind.SetShinyCharm: { PlayerKeyItems playerKeyItems6 = Find(sender); if (playerKeyItems6 == null || !playerKeyItems6.ShinyCharm) { return Fail("you don't have the Shiny Charm"); } playerKeyItems6.ShinyCharmOff = !req.On; return new AqResult { RequestId = req.RequestId, Ok = true }; } case AqRequestKind.SetWfLure: { PlayerKeyItems playerKeyItems = Find(sender); if (playerKeyItems == null || !playerKeyItems.WfLure) { return Fail("you don't have the Web Fishing Lure"); } playerKeyItems.WfLureOn = req.On; return new AqResult { RequestId = req.RequestId, Ok = true }; } default: return Fail("not a shop request"); } AqResult Fail(string why) { return new AqResult { RequestId = req.RequestId, Ok = false, Error = why }; } } public void Grant(ulong id, bool? lure, bool? charm) { PlayerKeyItems playerKeyItems = Get(id); if (lure.HasValue) { playerKeyItems.WfLure = lure.Value; playerKeyItems.WfLureOn = lure.Value; } if (charm.HasValue) { playerKeyItems.ShinyCharm = charm.Value; playerKeyItems.ShinyCharmOff = false; } } public static bool IsKeyItemRequest(AqRequestKind k) { if (k != AqRequestKind.BuyWfLure && k != AqRequestKind.BuyShinyCharm && k != AqRequestKind.SetWfLure) { return k == AqRequestKind.SetShinyCharm; } return true; } public KeyItemsState State() { return new KeyItemsState { Players = Players.Select((PlayerKeyItems p) => new PlayerKeyItems { SteamId = p.SteamId, WfLure = p.WfLure, WfLureOn = p.WfLureOn, ShinyCharm = p.ShinyCharm, ShinyCharmOff = p.ShinyCharmOff }).ToList() }; } } public sealed class KeyItemsState : IMessage { public List<PlayerKeyItems> Players = new List<PlayerKeyItems>(); } public static class MessageCodec { public const int Protocol = 1; public const int MaxBytes = 524288; private static readonly Dictionary<string, Type> Types; private static readonly JsonSerializer Serializer; static MessageCodec() { //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_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Expected O, but got Unknown Types = new Dictionary<string, Type>(); Serializer = JsonSerializer.Create(new JsonSerializerSettings { NullValueHandling = (NullValueHandling)1, MissingMemberHandling = (MissingMemberHandling)0 }); Type[] array = new Type[13] { typeof(Handshake), typeof(SpawnTag), typeof(CatchEvent), typeof(JournalSnapshot), typeof(AqSnapshot), typeof(AqDelta), typeof(AqHash), typeof(AqRequest), typeof(AqResult), typeof(AttackState), typeof(RainMessage), typeof(ShinyMark), typeof(KeyItemsState) }; foreach (Type type in array) { Types[type.Name] = type; } } public static byte[] Encode(IMessage msg) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Expected O, but got Unknown JObject val = new JObject { ["v"] = JToken.op_Implicit(1), ["t"] = JToken.op_Implicit(msg.GetType().Name), ["p"] = (JToken)(object)JObject.FromObject((object)msg, Serializer) }; byte[] bytes = Encoding.UTF8.GetBytes(((JToken)val).ToString((Formatting)0, Array.Empty<JsonConverter>())); if (bytes.Length > 524288) { throw new InvalidOperationException($"{msg.GetType().Name} is {bytes.Length} bytes; limit {524288}"); } return bytes; } public static IMessage Decode(byte[] bytes, out string error) { int protocol; return Decode(bytes, out error, out protocol); } public static IMessage Decode(byte[] bytes, out string error, out int protocol) { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Expected O, but got Unknown error = null; protocol = -1; if (bytes == null || bytes.Length == 0) { error = "empty"; return null; } if (bytes.Length > 524288) { error = "too large"; return null; } try { JObject val2; using (StringReader stringReader = new StringReader(Encoding.UTF8.GetString(bytes))) { JsonTextReader val = new JsonTextReader((TextReader)stringReader) { DateParseHandling = (DateParseHandling)0, MaxDepth = 64 }; try { val2 = JObject.Load((JsonReader)(object)val); } finally { ((IDisposable)val)?.Dispose(); } } int num = (protocol = ((JToken)val2).Value<int?>((object)"v") ?? (-1)); string text = ((JToken)val2).Value<string>((object)"t"); if (num != 1 && text != "Handshake") { error = $"protocol {num}, expected {1}"; return null; } if (text == null || !Types.TryGetValue(text, out var value)) { error = "unknown type " + text; return null; } JToken obj = val2["p"]; JObject val3 = (JObject)(object)((obj is JObject) ? obj : null); if (val3 == null) { error = "no payload"; return null; } return (IMessage)((JToken)val3).ToObject(value, Serializer); } catch (Exception ex) { error = "malformed: " + ex.Message; return null; } } } public interface IMessage { } public sealed class Handshake : IMessage { public string ModVersion; public int Protocol; public string GameBuild; public string FishDefHash; public bool HasCache; public ulong SteamId; public string Name; } public sealed class SpawnTag : IMessage { public int ObjectId; public WfItemTag Tag; public bool Threat; } public sealed class CatchEvent : IMessage { public string SpeciesId; public float WeightKg; public Quality Quality; public ulong HookerId; public string HookerName; public ulong KillerId; public string Island; public string Bait; public bool Shiny; } public sealed class JournalSnapshot : IMessage { public Journal Journal; } public sealed class AqSnapshot : IMessage { public AquariumState State; public string Hash; public AquariumConfig Config; } public sealed class AqDelta : IMessage { public long BaseSeq; public long Seq; public List<AqFish> Upserts = new List<AqFish>(); public List<long> Removed = new List<long>(); public AquariumState Scalars; public string Hash; public List<AqEvent> Events = new List<AqEvent>(); } public sealed class AqHash : IMessage { public long Seq; public string Hash; } [JsonConverter(typeof(StringEnumConverter))] public enum AqRequestKind { Deposit, Feed, Collect, Upgrade, ToggleZen, SnapshotPlease, Place, Pack, TakeOut, BuyWfLure, BuyShinyCharm, SetWfLure, SetShinyCharm } public sealed class AqRequest : IMessage { public string RequestId; public AqRequestKind Kind; public int FoodUnits; public UpgradeType Upgrade; public int Island; public float X; public float Y; public float Z; public float RotY; public long Uid; public bool On; } public sealed class AqResult : IMessage { public string RequestId; public bool Ok; public string Error; public long Amount; } public sealed class ShinyMark : IMessage { public int ObjectId; public int ItemId; } public sealed class RainMessage : IMessage { public bool Active; public float SecondsLeft; } [JsonConverter(typeof(StringEnumConverter))] public enum AttackPhase { None, Active, Defeated, TimedOut } public sealed class AttackState : IMessage { public AttackPhase Phase; public float Hp; public float MaxHp; } public sealed class NetCompat { public bool Linked; public readonly List<string> Warnings = new List<string>(); public static NetCompat Check(Handshake mine, Handshake theirs, string theirName) { NetCompat netCompat = new NetCompat(); string text = (string.IsNullOrEmpty(theirName) ? "the other player" : Clean(theirName, 32)); if (mine == null || theirs == null) { netCompat.Warnings.Add("no handshake"); return netCompat; } if (theirs.Protocol != mine.Protocol || !SameText(theirs.ModVersion, mine.ModVersion)) { netCompat.Warnings.Add("WFxHTF versions differ (" + text + " has " + Show(theirs.ModVersion) + ", you have " + Show(mine.ModVersion) + "): the aquarium, WEBFISHING fish and rain aren't shared this session. Install the same version to play together."); return netCompat; } netCompat.Linked = true; if (!SameText(theirs.GameBuild, mine.GameBuild)) { netCompat.Warnings.Add("How to Fish versions differ (" + text + " " + Show(theirs.GameBuild) + ", you " + Show(mine.GameBuild) + "); things may misbehave."); } if (!theirs.HasCache) { netCompat.Warnings.Add(text + " hasn't extracted the WEBFISHING fish yet, so they see How to Fish fish in their place."); } else if (mine.HasCache && !SameText(theirs.FishDefHash, mine.FishDefHash)) { netCompat.Warnings.Add(text + "'s WEBFISHING fish data differs from yours; a few fish may look like How to Fish fish."); } return netCompat; } private static bool SameText(string a, string b) { return string.Equals(a ?? "", b ?? "", StringComparison.Ordinal); } private static string Show(string s) { if (!string.IsNullOrEmpty(s)) { return Clean(s, 24); } return "unknown"; } public static string Clean(string s, int max) { if (string.IsNullOrEmpty(s)) { return ""; } StringBuilder stringBuilder = new StringBuilder(Math.Min(s.Length, max)); foreach (char c in s) { if (stringBuilder.Length >= max) { stringBuilder.Append("..."); break; } if (c != '<' && c != '>' && !char.IsControl(c)) { stringBuilder.Append(c); } } return stringBuilder.ToString(); } } public sealed class AqBroadcaster { public double IntervalSeconds = 1.0; private AquariumState _baseline; private double _lastSent = double.NegativeInfinity; private bool _poked; private readonly List<AqEvent> _events = new List<AqEvent>(); public bool HasBaseline => _baseline != null; public void AddEvents(IEnumerable<AqEvent> events) { if (events != null && _baseline != null) { _events.AddRange(events); } } public void Poke() { _poked = true; } public AqSnapshot SnapshotFor(AquariumState current, AquariumConfig config) { if (_baseline == null) { _baseline = AquariumSync.Clone(current); } AqSnapshot aqSnapshot = AquariumSync.Snapshot(_baseline); aqSnapshot.Config = config; return aqSnapshot; } public AqDelta Tick(AquariumState current, double now) { if (_baseline == null || current == null) { return null; } if ((!_poked && _events.Count <= 0 && !(now - _lastSent >= IntervalSeconds)) || (_baseline.Seq == current.Seq && _events.Count == 0)) { return null; } AqDelta aqDelta = AquariumSync.Diff(_baseline, current); aqDelta.Events = new List<AqEvent>(_events); _events.Clear(); _baseline = AquariumSync.Clone(current); _lastSent = now; _poked = false; return aqDelta; } public void Reset() { _baseline = null; _events.Clear(); _poked = false; _lastSent = double.NegativeInfinity; } } public sealed class AqMirror { public double RetrySeconds = 5.0; private bool _awaiting = true; private double _askedAt = double.NegativeInfinity; private readonly List<AqEvent> _events = new List<AqEvent>(); public const int MaxFish = 500; private static readonly List<AqEvent> NoEvents = new List<AqEvent>(); public AquariumState State { get; private set; } public AquariumConfig Config { get; private set; } public int Applied { get; private set; } public int Resyncs { get; private set; } public string LastProblem { get; private set; } public bool InSync { get { if (State != null) { return !_awaiting; } return false; } } public void OnSnapshot(AqSnapshot s) { if (s?.State == null) { LastProblem = "empty snapshot"; return; } if (AquariumSync.HashOf(s.State) != s.Hash) { LastProblem = "snapshot hash mismatch"; _awaiting = true; return; } if (!Sane(s.State, s.Config ?? Config, out var why)) { LastProblem = "bad snapshot: " + why; _awaiting = true; return; } State = s.State; if (s.Config != null) { Config = s.Config; } _awaiting = false; LastProblem = null; } public void OnDelta(AqDelta d) { if (State == null || _awaiting || d == null) { return; } if ((d.Upserts?.Count ?? 0) > 500 || (d.Removed?.Count ?? 0) > 2000) { _awaiting = true; _askedAt = double.NegativeInfinity; Resyncs++; LastProblem = "oversized delta"; return; } AquariumState aquariumState = AquariumSync.Apply(State, d); string why; string text = ((aquariumState != null) ? ((!Sane(aquariumState, Config, out why)) ? ("bad delta: " + why) : null) : ((d.BaseSeq != State.Seq) ? $"delta base {d.BaseSeq} != mine {State.Seq}" : "delta hash mismatch")); if (text != null) { _awaiting = true; _askedAt = double.NegativeInfinity; Resyncs++; LastProblem = text; return; } State = aquariumState; Applied++; if (d.Events != null) { _events.AddRange(d.Events); } } private static bool Sane(AquariumState s, AquariumConfig cfg, out string why) { if (s.Fish != null && s.Fish.Count > 500) { why = $"{s.Fish.Count} fish"; return false; } return new Aquarium(cfg, s).IsValid(out why); } public bool ShouldAskForSnapshot(double now) { if (!_awaiting || now - _askedAt < RetrySeconds) { return false; } _askedAt = now; return true; } public List<AqEvent> TakeEvents() { if (_events.Count == 0) { return NoEvents; } List<AqEvent> result = new List<AqEvent>(_events); _events.Clear(); return result; } } public sealed class PendingRequests { public double TimeoutSeconds = 10.0; private readonly Dictionary<string, (Action<AqResult> Done, double At)> _open = new Dictionary<string, (Action<AqResult>, double)>(); public int Count => _open.Count; public void Add(AqRequest req, Action<AqResult> done, double now) { if (req == null || string.IsNullOrEmpty(req.RequestId)) { throw new ArgumentException("request id required"); } _open[req.RequestId] = (done, now); } public bool Resolve(AqResult r) { if (r == null || r.RequestId == null || !_open.TryGetValue(r.RequestId, out (Action<AqResult>, double) value)) { return false; } _open.Remove(r.RequestId); value.Item1?.Invoke(r); return true; } public void Expire(double now) { foreach (string item in (from kv in _open where now - kv.Value.At >= TimeoutSeconds select kv.Key).ToList()) { Fail(item, "no answer from the host"); } } public void FailAll(string error) { foreach (string item in _open.Keys.ToList()) { Fail(item, error); } } private void Fail(string id, string error) { (Action<AqResult> Done, double At) tuple = _open[id]; _open.Remove(id); tuple.Done?.Invoke(new AqResult { RequestId = id, Ok = false, Error = error }); } } public sealed class RateLimit { public double Burst = 40.0; public double PerSecond = 20.0; private double _tokens = double.NaN; private double _at; public bool Allow(double now) { if (double.IsNaN(_tokens)) { _tokens = Burst; _at = now; } _tokens = Math.Min(Burst, _tokens + Math.Max(0.0, now - _at) * PerSecond); _at = now; if (_tokens < 1.0) { return false; } _tokens -= 1.0; return true; } } public sealed class RainConfig { public bool Enabled = true; public float MeanMinutesBetween = 30f; public float MinDurationMinutes = 4f; public float MaxDurationMinutes = 8f; } public sealed class RainState { public bool Active; public double NextChangeAt; public ulong RngState = 790741uL; } public sealed class RainScheduler { public readonly RainConfig Config; public readonly RainState State; public bool Raining { get { if (Config.Enabled) { return State.Active; } return false; } } public RainScheduler(RainConfig config, RainState state) { Config = config ?? new RainConfig(); State = state ?? new RainState(); } public bool Update(double playtimeSeconds) { if (!Config.Enabled) { bool active = State.Active; State.Active = false; return active; } if (State.NextChangeAt <= 0.0) { State.NextChangeAt = playtimeSeconds + DryGap(); return false; } bool flag = false; for (int i = 0; i < 2; i++) { if (!(playtimeSeconds >= State.NextChangeAt)) { break; } State.Active = !State.Active; State.NextChangeAt = Math.Max(State.NextChangeAt, playtimeSeconds - 1.0) + (State.Active ? Shower() : DryGap()); flag = !flag; } return flag; } public RainMessage Message(double playtimeSeconds) { return new RainMessage { Active = Raining, SecondsLeft = (float)Math.Max(0.0, State.NextChangeAt - playtimeSeconds) }; } private double DryGap() { return 60.0 * (double)Config.MeanMinutesBetween * (0.5 + Next01()); } private double Shower() { return 60.0 * ((double)Config.MinDurationMinutes + (double)(Config.MaxDurationMinutes - Config.MinDurationMinutes) * Next01()); } private double Next01() { Rng rng = new Rng(State.RngState); double result = rng.Next01(); State.RngState = rng.State; return result; } } public sealed class Rng { public ulong State; public Rng(ulong seed) { State = ((seed == 0L) ? 11400714819323198485uL : seed); } public ulong NextU64() { ulong state = State; state ^= state >> 12; state ^= state << 25; return (State = state ^ (state >> 27)) * 2685821657736338717L; } public double Next01() { return (double)(NextU64() >> 11) * 1.1102230246251565E-16; } public double NextGaussian() { double d = 1.0 - Next01(); double num = Next01(); return Math.Sqrt(-2.0 * Math.Log(d)) * Math.Cos(Math.PI * 2.0 * num); } } public sealed class SessionTally { public const double MaxAquariumShare = 0.3; public const double MaxWfIncomeShare = 0.6; public const double MaxWfValueRatio = 1.5; public double StartPlaytime = double.NaN; public double LastPlaytime; public int NativeBites; public int WfBites; public int ShinyBites; public int NativeLanded; public int WfLanded; public int TntWf; public readonly int[] WfQualities = new int[6]; public int NativeSold; public int WfSold; public int OtherSold; public long NativeIncome; public long WfIncome; public long OtherIncome; public int Deposits; public int FoodItems; public int Upgrades; public int TankDeaths; public int AttacksWon; public int AttacksLost; public long DepositedValue; public long FoodValue; public long TankPaid; public long UpgradeSpent; public double TrayStart = double.NaN; public double TrayNow; public double Minutes { get { if (!double.IsNaN(StartPlaytime)) { return Math.Max(0.0, LastPlaytime - StartPlaytime) / 60.0; } return 0.0; } } public double Hours => Math.Max(1.0 / 60.0, Minutes / 60.0); public double TankGross => (double)TankPaid + (double.IsNaN(TrayStart) ? 0.0 : (TrayNow - TrayStart)); public double TankNet => TankGross - (double)FoodValue; public double WfShareOfBites { get { if (NativeBites + WfBites <= 0) { return 0.0; } return (double)WfBites / (double)(NativeBites + WfBites); } } public double NativeAvgSale { get { if (NativeSold <= 0) { return 0.0; } return (double)NativeIncome / (double)NativeSold; } } public double WfAvgSale { get { if (WfSold <= 0) { return 0.0; } return (double)WfIncome / (double)WfSold; } } public double WfValueRatio { get { if (!(NativeAvgSale > 0.0)) { return 0.0; } return WfAvgSale / NativeAvgSale; } } public double WfIncomeShare { get { if (NativeIncome <= 0) { return 0.0; } return (double)WfIncome / (double)NativeIncome; } } public double AquariumShare { get { if (NativeIncome <= 0) { return 0.0; } return TankNet / (double)NativeIncome; } } public void Seen(double playtime) { if (double.IsNaN(StartPlaytime)) { StartPlaytime = playtime; } LastPlaytime = Math.Max(LastPlaytime, playtime); } public void Tray(double coins) { if (double.IsNaN(TrayStart)) { TrayStart = coins; } TrayNow = coins; } private static string Flag(double v, double limit) { if (!(v > limit)) { return "ok"; } return "HIGH"; } public string Report() { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine($"session: {Minutes:0} min of play"); stringBuilder.AppendLine(string.Format("bites: {0} (How to Fish {1}, WEBFISHING {2} = {3:P0}{4})", NativeBites + WfBites, NativeBites, WfBites, WfShareOfBites, (ShinyBites > 0) ? $", {ShinyBites} shiny" : "")); stringBuilder.AppendLine(" WEBFISHING qualities: " + string.Join(" / ", WfQualities) + " (Normal..Alpha)"); stringBuilder.AppendLine(string.Format("landed (killed after hooking): How to Fish {0}, WEBFISHING {1}{2}", NativeLanded, WfLanded, (TntWf > 0) ? $"; WEBFISHING fish from TNT {TntWf}" : "")); stringBuilder.AppendLine($"sold: How to Fish {NativeSold} fish ${NativeIncome} (avg ${NativeAvgSale:0}), WEBFISHING {WfSold} fish ${WfIncome} (avg ${WfAvgSale:0})" + ((OtherSold > 0) ? $", other items {OtherSold} ${OtherIncome}" : "")); stringBuilder.AppendLine($" WEBFISHING avg sale / How to Fish avg sale: {WfValueRatio:0.00} (sim limit {1.5}) {Flag(WfValueRatio, 1.5)}"); stringBuilder.AppendLine($" WEBFISHING income / How to Fish income: {WfIncomeShare:0.00} (sim limit {0.6}) {Flag(WfIncomeShare, 0.6)}"); stringBuilder.AppendLine($"tank: {Deposits} fish put in (worth ${DepositedValue}), {FoodItems} fed (worth ${FoodValue}), paid out ${TankPaid}, tray {(double.IsNaN(TrayStart) ? 0.0 : (TrayNow - TrayStart)):+0;-0;0}, upgrades {Upgrades} for ${UpgradeSpent}"); stringBuilder.AppendLine($" tank net ${TankNet:0} = {AquariumShare:0.00} of How to Fish income (sim limit {0.3}) {Flag(AquariumShare, 0.3)}; deaths {TankDeaths}, attacks won {AttacksWon} lost {AttacksLost}"); stringBuilder.Append($"per hour: How to Fish ${(double)NativeIncome / Hours:0}, WEBFISHING ${(double)WfIncome / Hours:0}, tank net ${TankNet / Hours:0}"); return stringBuilder.ToString(); } } public sealed class JsonLinesLog : IDisposable { private readonly StreamWriter _w; public string Path { get; } public JsonLinesLog(string path) { Path = path; Directory.CreateDirectory(System.IO.Path.GetDirectoryName(System.IO.Path.GetFullPath(path))); _w = new StreamWriter(path, append: true, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false)) { AutoFlush = true }; } public void Write(double t, string kind, params (string Key, object Value)[] fields) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: 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_002e: Expected O, but got Unknown JObject val = new JObject { ["t"] = JToken.op_Implicit(Math.Round(t, 1)), ["kind"] = JToken.op_Implicit(kind) }; for (int i = 0; i < fields.Length; i++) { var (text, obj) = fields[i]; val[text] = (JToken)((obj == null) ? ((object)JValue.CreateNull()) : ((object)JToken.FromObject(obj))); } _w.WriteLine(((JToken)val).ToString((Formatting)0, Array.Empty<JsonConverter>())); } public void Dispose() { _w.Dispose(); } } public enum LoadStatus { Loaded, Recovered, Fresh, Unsupported, Unreadable } public sealed class LoadResult<T> { public T Value; public LoadStatus Status; public string Source; public string Problem; public bool Recovered => Status == LoadStatus.Recovered; public bool CanSave { get { if (Status != LoadStatus.Unsupported) { return Status != LoadStatus.Unreadable; } return false; } } } public sealed class SidecarKind<T> where T : class { public Func<T> Fresh; public Func<T, string> Validate; public Func<JObject, string> NewerVersion; } public static class Sidecars { public static SidecarKind<Journal> PlayerJournal(ulong steamId) { string why; return new SidecarKind<Journal> { Fresh = () => Journal.ForPlayer(steamId), Validate = (Journal j) => (!j.IsValid(out why)) ? why : null, NewerVersion = (JObject o) => Newer(o, "journal", 1) }; } public static SidecarKind<WorldSidecar> World(string worldId, string modVersion, AquariumConfig cfg) { return new SidecarKind<WorldSidecar> { Fresh = () => WorldSidecar.New(worldId, modVersion, cfg), Validate = (WorldSidecar w) => w.Validate(worldId), NewerVersion = (JObject o) => Newer(o, "world file", 1) ?? Newer((JObject)/*isinst with value type is only supported in some contexts*/, "world journal", 1) ?? Newer((JObject)/*isinst with value type is only supported in some contexts*/, "aquarium", 1) }; } private static string Newer(JObject o, string what, int supported) { int valueOrDefault = ((int?)((o != null) ? o["Schema"] : null)).GetValueOrDefault(); if (valueOrDefault <= supported) { return null; } return $"{what} has schema {valueOrDefault}; this mod version supports {supported}"; } } public static class SidecarStore { public const int Backups = 5; public const int MaxCorruptKept = 3; public static readonly TimeSpan DefaultBackupInterval = TimeSpan.FromMinutes(5.0); private static readonly Dictionary<string, object> Locks = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase); public static string BackupPath(string path, int n) { return $"{path}.bak{n}"; } private static object LockFor(string path) { string fullPath = Path.GetFullPath(path); lock (Locks) { if (!Locks.TryGetValue(fullPath, out var value)) { value = (Locks[fullPath] = new object()); } return value; } } public static void Save<T>(string path, T value, SidecarKind<T> kind, TimeSpan? minBackupInterval = null) where T : class { lock (LockFor(path)) { if (File.Exists(path)) { string text = NewerVersion(path, kind); if (text != null) { throw new InvalidOperationException("refusing to overwrite " + Path.GetFileName(path) + ": " + text); } } try { if (File.Exists(path) && RotationDue(path, minBackupInterval ?? DefaultBackupInterval) && BackupsMovable(path)) { Rotate(path); } } catch (IOException) { } catch (UnauthorizedAccessException) { } JsonFile.WriteAtomic<T>(path, value); } } private static bool RotationDue(string path, TimeSpan interval) { string path2 = BackupPath(path, 1); if (File.Exists(path2)) { return DateTime.UtcNow - File.GetLastWriteTimeUtc(path2) >= interval; } return true; } private static bool BackupsMovable(string path) { for (int i = 1; i <= 5; i++) { string path2 = BackupPath(path, i); if (!File.Exists(path2)) { continue; } try { using (new FileStream(path2, FileMode.Open, FileAccess.ReadWrite, FileShare.None)) { } } catch (IOException) { return false; } catch (UnauthorizedAccessException) { return false; } } return true; } private static void Rotate(string path) { string path2 = BackupPath(path, 5); if (File.Exists(path2)) { File.Delete(path2); } for (int num = 4; num >= 1; num--) { string text = BackupPath(path, num); if (File.Exists(text)) { File.Move(text, BackupPath(path, num + 1)); } } JsonFile.WriteDurable(BackupPath(path, 1), File.ReadAllBytes(path)); } public static LoadResult<T> Load<T>(string path, SidecarKind<T> kind) where T : class { lock (LockFor(path)) { string text = JsonFile.TempPath(path); bool flag = File.Exists(path) && File.Exists(text) && File.GetLastWriteTimeUtc(text) > File.GetLastWriteTimeUtc(path); List<(string, string)> list = new List<(string, string)>(); if (flag) { list.Add((text, "tmp")); } list.Add((path, "main")); if (!flag) { list.Add((text, "tmp")); } for (int i = 1; i <= 5; i++) { list.Add((BackupPath(path, i), $"bak{i}")); } string text2 = (flag ? "the last save was interrupted; its complete temp file is newer than the main file" : null); foreach (var (text3, text4) in list) { if (!File.Exists(text3)) { if (text4 == "main") { text2 = "main file missing"; } continue; } string text5 = NewerVersion(text3, kind); if (text5 != null) { return new LoadResult<T> { Value = kind.Fresh(), Status = LoadStatus.Unsupported, Source = text4, Problem = Path.GetFileName(text3) + ": " + text5 + ". Update the mod. Saving is disabled." }; } string problem; bool ioError; T val = TryRead(text3, kind.Validate, out problem, out ioError); int num = 0; while (val == null && ioError && text4 == "main" && num < 3) { Thread.Sleep(150); val = TryRead(text3, kind.Validate, out problem, out ioError); num++; } if (val == null && ioError && text4 == "main") { return new LoadResult<T> { Value = kind.Fresh(), Status = LoadStatus.Unreadable, Source = text4, Problem = Path.GetFileName(text3) + " couldn't be read (" + problem + "). Close whatever has it open and reload. Saving is disabled." }; } if (val != null) { return new LoadResult<T> { Value = val, Status = ((!(text4 == "main")) ? LoadStatus.Recovered : LoadStatus.Loaded), Source = text4, Problem = ((text4 == "main") ? null : text2) }; } if (text4 == "main") { text2 = "main: " + problem; Quarantine(path); } else { text2 = text2 ?? (text4 + ": " + problem); } } return new LoadResult<T> { Value = kind.Fresh(), Status = LoadStatus.Fresh, Source = "new", Problem = ((text2 == "main file missing") ? null : text2) }; } } private static string NewerVersion<T>(string file, SidecarKind<T> kind) where T : class { if (kind.NewerVersion == null) { return null; } try { return kind.NewerVersion(JObject.Parse(File.ReadAllText(file))); } catch { return null; } } private static void Quarantine(string path) { try { File.Move(path, $"{path}.corrupt-{DateTime.UtcNow:yyyyMMddTHHmmssfffZ}"); foreach (string item in Directory.GetFiles(Path.GetDirectoryName(Path.GetFullPath(path)), Path.GetFileName(path) + ".corrupt-*").OrderByDescending<string, string>((string f) => f, StringComparer.Ordinal).Skip(3)) { File.Delete(item); } } catch (IOException) { } catch (UnauthorizedAccessException) { } } private static T TryRead<T>(string path, Func<T, string> validate, out string problem, out bool ioError) where T : class { problem = null; ioError = false; try { T val = JsonFile.Read<T>(path); if (val == null) { problem = "empty file"; return null; } string text = validate?.Invoke(val); if (text != null) { problem = text; return null; } return val; } catch (Exception ex) { ioError = (ex is IOException && !(ex is FileNotFoundException)) || ex is UnauthorizedAccessException; problem = ex.GetType().Name + ": " + ex.Message; return null; } } } public sealed class WfItemTag { public string SpeciesId; public Quality Quality; public float WeightKg; public int ItemId; public float WeightMultiplier; public int Worth; public int MaxHp; public int LureTier; public bool Drip; public bool Shiny; public ulong HookerId; public string HookerName; public bool Matches(int itemId, float weightMultiplier) { if (ItemId == itemId) { return Math.Abs(WeightMultiplier - weightMultiplier) <= 1E-05f * Math.Max(1f, Math.Abs(weightMultiplier)); } return false; } public bool IsValid(out string why) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) why = null; if (string.IsNullOrEmpty(SpeciesId)) { why = "no species"; } else if (!Enum.IsDefined(typeof(Quality), Quality)) { why = "bad quality"; } else if (ItemId < 0 || ItemId > 255) { why = "bad item id"; } else if (!(WeightMultiplier > 0f) || float.IsInfinity(WeightMultiplier) || !(WeightKg > 0f) || float.IsInfinity(WeightKg)) { why = "bad weight"; } else if (Worth < 0 || MaxHp < 0) { why = "bad stats"; } return why == null; } } public sealed class NativeShinyKey { public int ItemId; public float Weight; public bool Matches(int itemId, float weight) { if (ItemId == itemId) { return Math.Abs(Weight - weight) <= 1E-05f * Math.Max(1f, Math.Abs(weight)); } return false; } } public static class WfCatch { public static List<FishDef> Eligible(IEnumerable<FishDef> defs, int island, int mainIslandCount, bool rain, int lureTier) { return defs.Where((FishDef d) => FishMapper.CanSpawn(d, island, mainIslandCount, rain, lureTier)).OrderBy<FishDef, string>((FishDef d) => d.Id, StringComparer.Ordinal).ToList(); } public static FishDef Roll(LureStats lure, IList<FishDef> eligible, MappingConfig cfg, double r1, double r2) { if (lure == null || eligible == null || eligible.Count == 0) { return null; } Dictionary<string, float> dictionary = FishMapper.InjectedWeights(lure, (IEnumerable<FishDef>)eligible, cfg); double num = dictionary.Values.Sum(); if (num <= 0.0) { return null; } if (r1 >= num / (num + (double)lure.NativeWeightTotal)) { return null; } double num2 = r2 * num; FishDef result = null; foreach (FishDef item in eligible) { if (dictionary.TryGetValue(item.Id, out var value) && !(value <= 0f)) { result = item; num2 -= (double)value; if (num2 < 0.0) { return item; } } } return result; } public static WfItemTag Make(FishDef def, LureStats lure, EconomyConfig eco, Rng rng, int itemId, ulong hookerId, string hookerName, Quality? quality = null) { //IL_001e: 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_005e: 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_007a: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) Quality val = (Quality)(((??)quality) ?? Economy.RollQuality(lure.Tier, eco, rng)); float num = Economy.RollWeightKg(def, rng); float num2 = ((def.Htf.WeightKgMean > 0f) ? def.Htf.WeightKgMean : num); float num3 = Math.Max(0.01f, num / num2); int num4 = Economy.SellValue(def, lure, num, val, eco); return new WfItemTag { SpeciesId = def.Id, Quality = val, WeightKg = num, ItemId = itemId, WeightMultiplier = num3, Worth = Math.Max(1, (int)Math.Round((float)num4 / num3)), MaxHp = Economy.MaxHp(def, lure), LureTier = lure.Tier, Drip = (def.Htf.UltraRare || Economy.ShowsDripVisual(val, eco)), HookerId = hookerId, HookerName = hookerName }; } public static void MakeKeyUnique(WfItemTag tag, IEnumerable<WfItemTag> existing) { List<WfItemTag> source = existing.ToList(); for (int i = 0; i < 100; i++) { if (tag.WeightMultiplier != 1f && !source.Any((WfItemTag t) => t != tag && t.Matches(tag.ItemId, tag.WeightMultiplier))) { break; } tag.WeightMultiplier += 0.0001f; } } public static WfItemTag Find(IEnumerable<WfItemTag> tags, int itemId, float weightMultiplier) { return (from t in tags?.Where((WfItemTag t) => t.Matches(itemId, weightMultiplier)) orderby Math.Abs(t.WeightMultiplier - weightMultiplier) select t).FirstOrDefault(); } } public sealed class WorldSidecar { public const int CurrentSchema = 1; public int Schema = 1; public string WorldId; public string ModVersion; public Journal Journal; public AquariumState Aquarium; public RainState Rain = new RainState(); public List<WfItemTag> Items = new List<WfItemTag>(); public List<NativeShinyKey> ShinyNatives = new List<NativeShinyKey>(); public List<PlayerKeyItems> KeyItems = new List<PlayerKeyItems>(); public static WorldSidecar New(string worldId, string modVersion, AquariumConfig cfg) { return new WorldSidecar { WorldId = worldId, ModVersion = modVersion, Journal = Journal.ForWorld(worldId), Aquarium = new Aquarium(cfg).State }; } public static string FileName(string worldId) { return "world_" + Sanitize(worldId) + "-" + Hashing.Short((object)(worldId ?? "")).Substring(0, 8) + ".wfxhtf.json"; } public static string JournalFileName(ulong steamId) { return $"journal_{steamId}.json"; } public string Validate(string expectedWorldId) { if (Schema < 1 || Schema > 1) { return $"schema {Schema}"; } if (WorldId != expectedWorldId) { return "world id " + WorldId + ", expected " + expectedWorldId; } if (Journal == null) { return "journal missing"; } if (!Journal.IsValid(out var why)) { return "journal: " + why; } if (Aquarium == null) { return "aquarium missing"; } if (!new Aquarium(null, Aquarium).IsValid(out var why2)) { return "aquarium: " + why2; } return null; } private static string Sanitize(string id) { char[] array = (id ?? "unknown").ToCharArray(); for (int i = 0; i < array.Length; i++) { if (!char.IsLetterOrDigit(array[i]) && array[i] != '-' && array[i] != '_') { array[i] = '_'; } } return new string(array); } }
BepInEx/plugins/WFxHTF/WFxHTF.Data.dll
Decompiled a day agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security.Cryptography; using System.Text; using Newtonsoft.Json; using Newtonsoft.Json.Converters; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("WFxHTF")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.1.0.0")] [assembly: AssemblyInformationalVersion("0.1.0+166b9aae0d28865ba70c4894450aeb4db4b21aeb")] [assembly: AssemblyProduct("WFxHTF.Data")] [assembly: AssemblyTitle("WFxHTF.Data")] [assembly: AssemblyVersion("0.1.0.0")] namespace WFxHTF.Data; public static class DisplayText { public static string VisibleOr(string s, string fallback) { if (string.IsNullOrEmpty(s)) { return fallback; } int num = 0; while (num < s.Length) { switch (CharUnicodeInfo.GetUnicodeCategory(s, num)) { default: return s; case UnicodeCategory.NonSpacingMark: case UnicodeCategory.EnclosingMark: case UnicodeCategory.SpaceSeparator: case UnicodeCategory.LineSeparator: case UnicodeCategory.ParagraphSeparator: case UnicodeCategory.Control: case UnicodeCategory.Format: case UnicodeCategory.Surrogate: case UnicodeCategory.PrivateUse: case UnicodeCategory.OtherNotAssigned: num += ((!char.IsSurrogatePair(s, num)) ? 1 : 2); break; } } return fallback; } } public static class FishArgs { public static string Split(string[] args, out Quality? quality, out double? number) { quality = null; number = null; if (args == null || args.Length == 0) { return ""; } int num = args.Length; if (num > 1 && IsNumber(args[num - 1], out var v)) { number = v; num--; } if (num > 1 && !IsNumber(args[num - 1], out var _) && Enum.TryParse<Quality>(args[num - 1], ignoreCase: true, out var result) && Enum.IsDefined(typeof(Quality), result)) { quality = result; num--; } return string.Join(" ", args, 0, num); } private static bool IsNumber(string s, out double v) { return double.TryParse(s, NumberStyles.Float, CultureInfo.InvariantCulture, out v); } } [JsonConverter(typeof(StringEnumConverter))] public enum SizeClass { Small, Medium, Eel, Flat, Shark, Giant } [JsonConverter(typeof(StringEnumConverter))] public enum ZoneRule { Freshwater, Saltwater, AnyDuringRain, Anywhere } [JsonConverter(typeof(StringEnumConverter))] public enum Aggression { Passive, Flee, Bite } [JsonConverter(typeof(StringEnumConverter))] public enum RainCondition { ModRain, Storm, Night, Either } public sealed class HtfTargets { public SizeClass SizeClass; public ZoneRule Zone; public float SpawnWeight; public int MinBaitTier; public float RelativeWorth; public float RelativeHp; public Aggression Aggression; public float WeightKgMean; public float WeightSpread; public float WeightKgMin; public float WeightKgMax; public float WfWorth; public bool UltraRare; } public sealed class FishVisual { public string[] Frames; public float Fps; public int Width; public int Height; public string Filter = "point"; public bool Billboard = true; } public sealed class FishDef { public string Id; public string Name; public WfFish Wf; public HtfTargets Htf; public FishVisual Visual; } public static class FishMapper { private static readonly HashSet<string> Eels = new HashSet<string> { "wf:ocean_eel", "wf:lake_leech" }; private static readonly HashSet<string> Flats = new HashSet<string> { "wf:ocean_flounder", "wf:ocean_stingray", "wf:ocean_manta_ray", "wf:ocean_golden_manta_ray" }; private static readonly HashSet<string> Sharks = new HashSet<string> { "wf:lake_bullshark", "wf:ocean_greatwhiteshark", "wf:ocean_hammerhead_shark", "wf:ocean_dogfish", "wf:ocean_sawfish", "wf:rain_heliocoprion", "wf:lake_alligator" }; private static readonly HashSet<string> ExtraBiters = new HashSet<string> { "wf:rain_leedsichthys" }; public static List<FishDef> MapAll(WfCatalog catalog, MappingConfig cfg) { List<FishDef> list = catalog.Fish.Select((WfFish f) => Map(f, cfg)).ToList(); double num = list.Sum((FishDef d) => d.Htf.SpawnWeight); if (num <= 0.0) { return list; } double e = cfg.ValueSpreadExponent; double num2 = list.Sum((FishDef d) => (double)d.Htf.SpawnWeight * Math.Pow(Math.Max(0.01, d.Htf.WfWorth), e)) / num; double num3 = list.Sum((FishDef d) => (double)d.Htf.SpawnWeight * Math.Max(0.1, d.Wf.CatchDifficulty)) / num; foreach (FishDef item in list) { item.Htf.RelativeWorth = Clamp((float)(Math.Pow(Math.Max(0.01, item.Htf.WfWorth), e) / num2), cfg.RelativeWorthMin, cfg.RelativeWorthMax); item.Htf.RelativeHp = Clamp((float)(Math.Max(0.1, item.Wf.CatchDifficulty) / num3), cfg.RelativeHpMin, cfg.RelativeHpMax); } return list; } public static FishDef Map(WfFish wf, MappingConfig cfg) { int num = Math.Max(0, Math.Min(2, wf.Tier)); SizeClass sizeClass = ClassifySize(wf, cfg); float num2 = Clamp(cfg.WeightCoeff * (float)Math.Pow(wf.AvgSizeCm, cfg.WeightExponent), cfg.MinWeightKg, cfg.MaxMeanWeightKg); HtfTargets htf = new HtfTargets { SizeClass = sizeClass, Zone = ZoneFor(wf.Pool), SpawnWeight = wf.LootWeight * cfg.SpawnWeightScale * PoolScale(wf.Pool, cfg), MinBaitTier = cfg.MinBaitTierByWfTier[num], RelativeWorth = 1f, RelativeHp = 1f, Aggression = ((sizeClass == SizeClass.Shark || ExtraBiters.Contains(wf.Id)) ? Aggression.Bite : ((wf.CatchDifficulty >= cfg.FleeDifficulty) ? Aggression.Flee : Aggression.Passive)), WeightKgMean = num2, WeightSpread = cfg.WeightSpread, WeightKgMin = Math.Max(cfg.MinWeightKg, num2 * cfg.WeightRollMin), WeightKgMax = num2 * cfg.WeightRollMax, WfWorth = WfWorth(wf), UltraRare = wf.Rare }; return new FishDef { Id = wf.Id, Name = wf.Name, Wf = wf, Htf = htf, Visual = new FishVisual { Frames = (wf.Sprite?.Frames?.ToArray() ?? Array.Empty<string>()), Fps = (wf.Sprite?.Fps ?? 0f), Width = (wf.Sprite?.Width ?? 0), Height = (wf.Sprite?.Height ?? 0), Filter = (wf.Sprite?.Filter ?? "point") } }; } public static SizeClass ClassifySize(WfFish wf, MappingConfig cfg) { if (Eels.Contains(wf.Id)) { return SizeClass.Eel; } if (Flats.Contains(wf.Id)) { return SizeClass.Flat; } if (Sharks.Contains(wf.Id)) { return SizeClass.Shark; } if (wf.AvgSizeCm >= cfg.GiantMinCm) { return SizeClass.Giant; } if (!(wf.AvgSizeCm <= cfg.SmallMaxCm)) { return SizeClass.Medium; } return SizeClass.Small; } public static ZoneRule ZoneFor(WfPool pool) { return pool switch { WfPool.Freshwater => ZoneRule.Freshwater, WfPool.Saltwater => ZoneRule.Saltwater, WfPool.Rain => ZoneRule.AnyDuringRain, _ => ZoneRule.Anywhere, }; } public static bool IslandIsSaltwater(int islandIndex, int islandCount) { return islandIndex >= islandCount - 2; } public static bool CanSpawn(FishDef def, int islandIndex, int islandCount, bool rainConditionActive, int baitTier) { if (baitTier < def.Htf.MinBaitTier) { return false; } return def.Htf.Zone switch { ZoneRule.Freshwater => !IslandIsSaltwater(islandIndex, islandCount), ZoneRule.Saltwater => IslandIsSaltwater(islandIndex, islandCount), ZoneRule.AnyDuringRain => rainConditionActive, _ => true, }; } public static Dictionary<string, float> InjectedWeights(LureStats lure, IEnumerable<FishDef> eligible, MappingConfig cfg) { List<FishDef> list = eligible.Where((FishDef d) => d.Htf.SpawnWeight > 0f).ToList(); Dictionary<string, float> dictionary = new Dictionary<string, float>(); float num = Clamp(cfg.WfCatchShare, 0f, 0.95f); double num2 = list.Sum((FishDef d) => d.Htf.SpawnWeight); if (num2 <= 0.0 || num <= 0f || lure.NativeWeightTotal <= 0f) { return dictionary; } double num3 = lure.NativeWeightTotal * num / (1f - num); foreach (FishDef item in list) { dictionary[item.Id] = (float)(num3 * (double)item.Htf.SpawnWeight / num2); } return dictionary; } public static bool RainConditionActive(RainCondition cond, bool modRain, bool storm = false, bool night = false) { return cond switch { RainCondition.ModRain => modRain, RainCondition.Storm => storm, RainCondition.Night => night, _ => storm || night, }; } public static float WfWorth(WfFish wf) { if (!wf.GenerateWorth) { return wf.SellValue; } double num = 1.0 + 0.25 * (double)wf.Tier; double num2 = wf.LootWeight; double num3 = Math.Pow(5.0 * num, 2.5 - num2); if (num2 < 0.4) { num3 *= 1.1; } if (num2 < 0.15) { num3 *= 1.25; } if (num2 < 0.05) { num3 *= 1.5; } return (float)num3; } private static float PoolScale(WfPool pool, MappingConfig cfg) { return pool switch { WfPool.Misc => cfg.MiscSpawnScale, WfPool.Rain => cfg.RainSpawnScale, _ => 1f, }; } private static float Clamp(float v, float lo, float hi) { if (!(v < lo)) { if (!(v > hi)) { return v; } return hi; } return lo; } } public static class Hashing { private static readonly JsonSerializer Canonical = JsonSerializer.Create(new JsonSerializerSettings { Formatting = (Formatting)0, NullValueHandling = (NullValueHandling)0 }); private static readonly JsonSerializer Reader = JsonSerializer.Create(new JsonSerializerSettings { DateParseHandling = (DateParseHandling)0 }); public static string CanonicalJson(object value) { StringWriter stringWriter = new StringWriter(CultureInfo.InvariantCulture); Canonical.Serialize((TextWriter)stringWriter, value); return stringWriter.ToString(); } public static T Clone<T>(T value) { return FromJson<T>(CanonicalJson(value)); } public static T FromJson<T>(string json) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Expected O, but got Unknown using StringReader stringReader = new StringReader(json); JsonTextReader val = new JsonTextReader((TextReader)stringReader); try { return Reader.Deserialize<T>((JsonReader)(object)val); } finally { ((IDisposable)val)?.Dispose(); } } public static string Short(object value) { using SHA256 sHA = SHA256.Create(); byte[] array = sHA.ComputeHash(Encoding.UTF8.GetBytes(CanonicalJson(value))); StringBuilder stringBuilder = new StringBuilder(16); for (int i = 0; i < 8; i++) { stringBuilder.Append(array[i].ToString("x2")); } return stringBuilder.ToString(); } public static string FishDefHash(WfCatalog catalog) { return Short(catalog.Fish); } } public static class JsonFile { public static readonly JsonSerializerSettings Settings = new JsonSerializerSettings { Formatting = (Formatting)1, NullValueHandling = (NullValueHandling)1, MissingMemberHandling = (MissingMemberHandling)0, FloatFormatHandling = (FloatFormatHandling)2 }; private static readonly JsonSerializer Serializer = JsonSerializer.Create(Settings); public static T Read<T>(string path) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Expected O, but got Unknown using StringReader stringReader = new StringReader(File.ReadAllText(path, Encoding.UTF8)); JsonTextReader val = new JsonTextReader((TextReader)stringReader); try { return Serializer.Deserialize<T>((JsonReader)(object)val); } finally { ((IDisposable)val)?.Dispose(); } } public static string ToJson<T>(T value) { StringWriter stringWriter = new StringWriter(CultureInfo.InvariantCulture); Serializer.Serialize((TextWriter)stringWriter, (object)value); return stringWriter.ToString(); } public static string TempPath(string path) { return path + ".tmp"; } public static void WriteAtomic<T>(string path, T value) { Directory.CreateDirectory(Path.GetDirectoryName(Path.GetFullPath(path))); string text = TempPath(path); WriteDurable(text, Encoding.UTF8.GetBytes(ToJson(value))); if (File.Exists(path)) { File.Replace(text, path, null, ignoreMetadataErrors: true); } else { File.Move(text, path); } } public static void WriteDurable(string path, byte[] bytes) { using FileStream fileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None); fileStream.Write(bytes, 0, bytes.Length); fileStream.Flush(flushToDisk: true); } } public sealed class MappingConfig { public float WfCatchShare = 0.25f; public float SpawnWeightScale = 1f; public float RainSpawnScale = 1f; public float MiscSpawnScale = 0.02f; public RainCondition RainCondition; public float ShinyChance = 0.0005f; public float TntWfShare = 0.25f; public float BossShinyChance = 0.01f; public float ShinyCharmChance = 0.002f; public int WfLurePrice = 1000; public int WfLureIsland = -1; public int ShinyCharmPrice = 9999; public int ShinyCharmIsland; public int[] MinBaitTierByWfTier = new int[3] { 0, 0, 1 }; public float ValueSpreadExponent = 0.5f; public float RelativeWorthMin = 0.25f; public float RelativeWorthMax = 4f; public float RelativeHpMin = 0.5f; public float RelativeHpMax = 2.5f; public float FleeDifficulty = 3f; public float WeightCoeff = 1.2E-05f; public float WeightExponent = 3f; public float WeightSpread = 0.35f; public float MinWeightKg = 0.01f; public float MaxMeanWeightKg = 1500f; public float WeightRollMin = 0.3f; public float WeightRollMax = 3f; public float SmallMaxCm = 40f; public float GiantMinCm = 500f; } public sealed class NativeCatch { public string Name; public int Id; public float Weight; public int Worth; public int MaxHp; public bool IsBoss; public bool Foreign; } public sealed class NativeBait { public int Index; public string Name; public int Cost; public bool Euro; public List<NativeCatch> Catches = new List<NativeCatch>(); } public sealed class LureStats { public int Tier; public NativeBait Bait; public float NativeWeightTotal; public float AvgWorth; public int MaxRegularWorth; public float AvgHp; } public static class NativeEconomy { public const int MinSpeciesForGeneralLure = 4; public static List<LureStats> GeneralLures(IEnumerable<NativeBait> baits) { List<NativeBait> list = (from b in baits where !b.Euro && b.Catches.Count((NativeCatch c) => !c.IsBoss && !c.Foreign) >= 4 group b by b.Name into g select g.First() into b orderby b.Cost, b.Index select b).ToList(); List<LureStats> list2 = new List<LureStats>(); for (int num = 0; num < list.Count; num++) { List<NativeCatch> list3 = list[num].Catches.Where((NativeCatch c) => !c.IsBoss && !c.Foreign && c.Weight > 0f).ToList(); float num2 = list3.Sum((NativeCatch c) => c.Weight); list2.Add(new LureStats { Tier = num, Bait = list[num], NativeWeightTotal = list[num].Catches.Where((NativeCatch c) => !c.Foreign).Sum((NativeCatch c) => Math.Max(0f, c.Weight)), AvgWorth = ((num2 > 0f) ? (list3.Sum((NativeCatch c) => (float)c.Worth * c.Weight) / num2) : 0f), MaxRegularWorth = ((list3.Count > 0) ? list3.Max((NativeCatch c) => c.Worth) : 0), AvgHp = ((num2 > 0f) ? (list3.Sum((NativeCatch c) => (float)c.MaxHp * c.Weight) / num2) : 0f) }); } return list2; } } [JsonConverter(typeof(StringEnumConverter))] public enum Quality { Normal, Shining, Glistening, Opulent, Radiant, Alpha } public static class QualityInfo { public const int Count = 6; public static readonly float[] WfWorth = new float[6] { 1f, 1.8f, 4f, 6f, 10f, 15f }; public static readonly string[] Tint = new string[6] { "#ffffff", "#e5f5f0", "#eafcf5", "#d5fcf5", "#fcf0d5", "#fcd5df" }; public static string Prefix(Quality q) { if (q != Quality.Normal) { return q.ToString() + " "; } return ""; } public static Quality Roll(float[] chances, Func<double> next01, float boost = 1f) { Quality result = Quality.Normal; if (chances == null) { return result; } for (int i = 0; i < chances.Length && i < 6; i++) { if (next01() < (double)(chances[i] * boost)) { result = (Quality)i; } } return result; } } public struct PixelRect { public int X; public int Y; public int W; public int H; public PixelRect(int x, int y, int w, int h) { X = x; Y = y; W = w; H = h; } } public static class SpriteRects { public static List<PixelRect> Build(byte[] alpha, int w, int h, byte cutoff = 128) { List<PixelRect> list = new List<PixelRect>(); Dictionary<long, int> dictionary = new Dictionary<long, int>(); Dictionary<long, int> dictionary2 = new Dictionary<long, int>(); for (int i = 0; i < h; i++) { dictionary2.Clear(); int num = i * w; int j = 0; while (j < w) { if (alpha[num + j] < cutoff) { j++; continue; } int num2 = j; for (; j < w && alpha[num + j] >= cutoff; j++) { } long key = ((long)num2 << 32) | (uint)j; if (dictionary.TryGetValue(key, out var value)) { PixelRect value2 = list[value]; value2.H++; list[value] = value2; dictionary2[key] = value; } else { list.Add(new PixelRect(num2, i, j - num2, 1)); dictionary2[key] = list.Count - 1; } } Dictionary<long, int> dictionary3 = dictionary; dictionary = dictionary2; dictionary2 = dictionary3; } return list; } } [JsonConverter(typeof(StringEnumConverter))] public enum WfPool { Freshwater, Saltwater, Rain, Misc } public sealed class WfFish { public string Id; public string Name; public string Description; public string Blurb; public string LootTable; public WfPool Pool; public int Tier; public float CatchDifficulty; public float CatchSpeed; public float LootWeight; public float AvgSizeCm; public int SellValue; public float SellMultiplier = 1f; public int ObtainXp; public bool GenerateWorth; public bool Rare; public WfSprite Sprite; } public sealed class WfSprite { public List<string> Frames = new List<string>(); public float Fps; public int Width; public int Height; public string Filter = "point"; } public sealed class WfCatalog { public const int CurrentSchema = 1; public int Schema = 1; public string ExtractorVersion; public string ExtractedUtc; public string SourceFile; public string SourceSha256; public string SteamBuildId; public List<WfFish> Fish = new List<WfFish>(); }
BepInEx/plugins/WFxHTF/WFxHTF.Extraction.dll
Decompiled a day agousing System; using System.Buffers.Binary; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.IO.Compression; using System.Linq; using System.Net; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security.Cryptography; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using WFxHTF.Data; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("WFxHTF")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.1.0.0")] [assembly: AssemblyInformationalVersion("0.1.0+166b9aae0d28865ba70c4894450aeb4db4b21aeb")] [assembly: AssemblyProduct("WFxHTF.Extraction")] [assembly: AssemblyTitle("WFxHTF.Extraction")] [assembly: AssemblyVersion("0.1.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 WFxHTF.Extraction { public static class ContactSheet { public static void Write(string path, WfCatalog catalog) { //IL_0087: Unknown result type (might be due to invalid IL or missing references) StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append("<!doctype html><html><head><meta charset=\"utf-8\"><title>WFxHTF contact sheet</title>\n<style>\nbody{font:13px system-ui,sans-serif;background:#1c1f24;color:#ddd;margin:16px}\nh2{margin:24px 0 8px;font-size:16px;color:#fff}\n.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(170px,1fr));gap:10px}\n.cell{background:#2a2e35;border-radius:6px;padding:8px;text-align:center}\n.cell.rare{outline:2px solid #e6b422}\n.img{height:128px;display:flex;align-items:center;justify-content:center;\n background:repeating-conic-gradient(#3a3f47 0 25%,#30343b 0 50%) 0 0/16px 16px;border-radius:4px}\n.img img{image-rendering:pixelated;max-width:150px;max-height:120px;min-width:64px}\n.name{font-weight:600;margin-top:6px;color:#fff}.meta{color:#9aa;font-size:11px}\n</style></head><body>"); stringBuilder.Append($"<p>{catalog.Fish.Count} fish · source build {WebUtility.HtmlEncode(catalog.SteamBuildId)} · extracted {catalog.ExtractedUtc}. Gold outline = rare flag. Check: no mirrored, cropped or missing sprites.</p>"); foreach (IGrouping<WfPool, WfFish> item in from f in catalog.Fish group f by f.Pool) { stringBuilder.Append($"<h2>{item.Key} ({item.Count()})</h2><div class=grid>"); foreach (WfFish item2 in item) { string value = string.Join(",", item2.Sprite.Frames.Select((string x) => "\"fish/" + x + "\"")); string text = ((item2.Sprite.Frames.Count > 1) ? $" data-frames=\"[{WebUtility.HtmlEncode(value)}]\" data-fps=\"{item2.Sprite.Fps}\"" : ""); stringBuilder.Append("<div class=\"cell" + (item2.Rare ? " rare" : "") + "\"><div class=img><img src=\"fish/" + item2.Sprite.Frames[0] + "\"" + text + "></div>").Append("<div class=name>" + WebUtility.HtmlEncode(item2.Name) + "</div>").Append($"<div class=meta>{item2.Id} · tier {item2.Tier} · {item2.AvgSizeCm:0}cm<br>{item2.Sprite.Width}×{item2.Sprite.Height}px") .Append((item2.Sprite.Frames.Count > 1) ? $" · {item2.Sprite.Frames.Count} frames" : "") .Append("</div></div>"); } stringBuilder.Append("</div>"); } stringBuilder.Append("<script>\ndocument.querySelectorAll('img[data-frames]').forEach(img=>{\n const fr=JSON.parse(img.dataset.frames),fps=+img.dataset.fps||10;let i=0;\n setInterval(()=>{i=(i+1)%fr.length;img.src=fr[i];},1000/fps);});\n</script></body></html>"); File.WriteAllText(path, stringBuilder.ToString(), new UTF8Encoding(encoderShouldEmitUTF8Identifier: false)); } } public sealed class ExtractOptions { public string WebfishingPath; public string GdrePath; public string Downloads; public string Cache; public bool KeepRaw; public bool ContactSheet; } public static class Extraction { public const string Version = "0.2.0"; private const int WebfishingAppId = 3146520; private const int HowToFishAppId = 4001890; public const int ExpectedFishCount = 70; private const string Marker = ".wfxhtf-cache"; private static readonly Dictionary<string, WfPool> Pools = new Dictionary<string, WfPool> { ["lake"] = (WfPool)0, ["ocean"] = (WfPool)1, ["rain"] = (WfPool)2, ["alien"] = (WfPool)3, ["void"] = (WfPool)3 }; public static string Root => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "WFxHTF"); public static string Run(ExtractOptions o, Action<string> log, Action<int, string> progress) { //IL_0a36: Unknown result type (might be due to invalid IL or missing references) //IL_0a3b: Unknown result type (might be due to invalid IL or missing references) //IL_0a46: Unknown result type (might be due to invalid IL or missing references) //IL_0a64: Unknown result type (might be due to invalid IL or missing references) //IL_0a71: Unknown result type (might be due to invalid IL or missing references) //IL_0a79: Unknown result type (might be due to invalid IL or missing references) //IL_0a83: Expected O, but got Unknown //IL_0afc: Unknown result type (might be due to invalid IL or missing references) //IL_0b01: Unknown result type (might be due to invalid IL or missing references) //IL_0b21: Expected O, but got Unknown //IL_0d7b: Unknown result type (might be due to invalid IL or missing references) //IL_060b: Unknown result type (might be due to invalid IL or missing references) //IL_0610: Unknown result type (might be due to invalid IL or missing references) //IL_061d: Unknown result type (might be due to invalid IL or missing references) //IL_0634: Unknown result type (might be due to invalid IL or missing references) //IL_064b: Unknown result type (might be due to invalid IL or missing references) //IL_0662: Unknown result type (might be due to invalid IL or missing references) //IL_066a: Unknown result type (might be due to invalid IL or missing references) //IL_066e: Unknown result type (might be due to invalid IL or missing references) //IL_0673: Unknown result type (might be due to invalid IL or missing references) //IL_0678: Unknown result type (might be due to invalid IL or missing references) //IL_068b: Unknown result type (might be due to invalid IL or missing references) //IL_06a2: Unknown result type (might be due to invalid IL or missing references) //IL_06b9: Unknown result type (might be due to invalid IL or missing references) //IL_06d0: Unknown result type (might be due to invalid IL or missing references) //IL_06e7: Unknown result type (might be due to invalid IL or missing references) //IL_06fa: Unknown result type (might be due to invalid IL or missing references) //IL_0711: Unknown result type (might be due to invalid IL or missing references) //IL_0725: Unknown result type (might be due to invalid IL or missing references) //IL_0738: Unknown result type (might be due to invalid IL or missing references) //IL_0752: Expected O, but got Unknown o = o ?? new ExtractOptions(); log = log ?? ((Action<string>)delegate { }); progress = progress ?? ((Action<int, string>)delegate { }); Dictionary<string, WfPool> pools = Pools; string text = null; try { string fullPath = Path.GetFullPath(o.Cache ?? Path.Combine(Root, "cache")); if (!Paths.IsUnder(fullPath, Root) && !Paths.IsUnder(fullPath, Path.GetTempPath())) { throw new Exception("the cache must be inside " + Root + " or " + Path.GetTempPath()); } if (Directory.Exists(fullPath) && Directory.EnumerateFileSystemEntries(fullPath).Any() && !File.Exists(Path.Combine(fullPath, ".wfxhtf-cache"))) { throw new Exception(fullPath + " exists and is not a WFxHTF cache; refusing to replace it"); } text = fullPath + ".staging"; string raw = Path.Combine(text, "raw"); string text2 = Path.Combine(text, "fish"); progress(2, "Looking for WEBFISHING"); string text3 = ""; string text4 = o.WebfishingPath; if (text4 == null) { SteamGame obj = SteamLocator.Find(3146520) ?? throw new Exception("WEBFISHING (Steam app 3146520) isn't installed on this PC. Install it in Steam (owning it isn't enough), then try again."); text4 = obj.InstallDir; text3 = obj.BuildId; } if (Directory.Exists(text4)) { string text5 = Path.Combine(text4, "webfishing.pck"); string text6 = Path.Combine(text4, "webfishing.exe"); string text7; if (!File.Exists(text5)) { if (!File.Exists(text6)) { throw new Exception("no webfishing.pck or webfishing.exe in " + text4); } text7 = text6; } else { text7 = text5; } text4 = text7; } if (!File.Exists(text4)) { throw new Exception("not found: " + text4); } text4 = Path.GetFullPath(text4); List<string> list = new List<string> { Path.GetDirectoryName(text4) }; SteamGame steamGame = SteamLocator.Find(4001890); if (steamGame != null) { list.Add(steamGame.InstallDir); } foreach (string item in list) { if (Paths.IsUnder(Paths.Resolve(fullPath), Paths.Resolve(item))) { throw new Exception("cache folder must not be inside a game folder (" + item + ")"); } } log("WEBFISHING: " + text4 + " (build " + ((text3 == "") ? "?" : text3) + ")"); progress(6, "Reading WEBFISHING"); string text8 = Win.Sha256(text4); log("sha256: " + text8); string text9 = Path.Combine(Root, "tools"); string text10 = Gdre.Locate(o.GdrePath, text9); if (text10 == null && o.GdrePath == null) { progress(10, "Looking for GDRE Tools in Downloads"); text10 = GdreLocal.FromDownloads(o.Downloads ?? GdreLocal.DownloadsFolder(), text9, log); if (text10 != null) { progress(40, "GDRE Tools checked and unpacked"); } } if (text10 == null) { throw new Exception("GDRE Tools not found: download GDRE_tools-v2.7.4-windows.zip from https://github.com/GDRETools/gdsdecomp/releases/tag/v2.7.4 into your Downloads folder and run this again (or unzip it into " + text9 + ", or pass --gdre <path to gdre_tools.exe>)."); } Gdre gdre = new Gdre(text10); log("GDRE Tools: " + text10); progress(45, "Finding the fish in WEBFISHING"); if (Directory.Exists(text)) { Directory.Delete(text, recursive: true); } Directory.CreateDirectory(raw); Directory.CreateDirectory(text2); gdre.Recover(text4, raw, new string[1] { "res://Resources/Creatures_Fish_*/**" }); if (gdre.Version != null && gdre.Version != "2.7.4") { log("WARNING: GDRE Tools " + gdre.Version + " differs from tested 2.7.4"); } List<(WfFish, string)> list2 = new List<(WfFish, string)>(); string path = Path.Combine(raw, "Resources"); List<string> obj2 = (Directory.Exists(path) ? Directory.GetDirectories(path, "Creatures_Fish_*").SelectMany((string d) => Directory.GetFiles(d, "*.tres")).OrderBy<string, string>((string f) => f, StringComparer.Ordinal) .ToList() : new List<string>()); if (obj2.Count == 0) { throw new Exception("GDRE recovered no fish resources; the pack layout may differ from WEBFISHING 1.x"); } List<string> list3 = new List<string>(); int num = 0; foreach (string item2 in obj2) { TresFile tresFile = TresFile.Parse(File.ReadAllText(item2)); string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(item2); string text11 = tresFile.Str("loot_table", "none"); string text12 = ((tresFile.Str("category") != "fish") ? "not category fish" : (tresFile.Bool("item_is_hidden", def: false) ? "hidden" : (tresFile.Bool("unobtainable", def: false) ? "unobtainable (test entry)" : ((!pools.ContainsKey(text11)) ? ("loot_table '" + text11 + "' excluded") : null)))); if (text12 != null) { log(" skip " + fileNameWithoutExtension + ": " + text12); continue; } num++; string id = "wf:" + (fileNameWithoutExtension.StartsWith("fish_") ? fileNameWithoutExtension.Substring(5) : fileNameWithoutExtension); string text13 = tresFile.ExtPath("icon"); if (text13 == null) { list3.Add(id + ": no icon texture"); continue; } if (list2.Any<(WfFish, string)>(((WfFish Fish, string IconRes) e) => e.Fish.Id == id)) { list3.Add(id + ": duplicate id (" + item2 + ")"); continue; } list2.Add((new WfFish { Id = id, Name = tresFile.Str("item_name"), Description = tresFile.Str("item_description"), Blurb = tresFile.Str("catch_blurb"), LootTable = text11, Pool = pools[text11], Tier = tresFile.Int("tier", 0), CatchDifficulty = tresFile.Float("catch_difficulty", 1f), CatchSpeed = tresFile.Float("catch_speed", 120f), LootWeight = tresFile.Float("loot_weight", 1f), AvgSizeCm = tresFile.Float("average_size", 75f), SellValue = tresFile.Int("sell_value", 5), SellMultiplier = tresFile.Float("sell_multiplier", 1f), ObtainXp = tresFile.Int("obtain_xp", 30), GenerateWorth = tresFile.Bool("generate_worth", def: true), Rare = tresFile.Bool("rare", def: false) }, text13)); } progress(65, "Reading the fish pictures"); Dictionary<string, (List<string>, float)> dictionary = new Dictionary<string, (List<string>, float)>(); Dictionary<string, string> dictionary2 = new Dictionary<string, string>(); HashSet<string> hashSet = new HashSet<string>(list2.Select<(WfFish, string), string>(((WfFish Fish, string IconRes) e) => e.IconRes)); HashSet<string> done = new HashSet<string>(); int num2 = 0; while (hashSet.Count > 0 && num2 < 4) { List<string> list4 = new List<string>(); foreach (string item3 in hashSet) { if (item3.EndsWith(".png", StringComparison.OrdinalIgnoreCase)) { list4.Add(item3 + ".import"); list4.Add("res://.import/" + item3.Substring(item3.LastIndexOf('/') + 1) + "-*"); } else { list4.Add(item3); } } gdre.Recover(text4, raw, list4); done.UnionWith(hashSet); HashSet<string> hashSet2 = new HashSet<string>(); foreach (string item4 in hashSet.Where((string r) => !r.EndsWith(".png", StringComparison.OrdinalIgnoreCase))) { try { if (!item4.EndsWith(".tres", StringComparison.OrdinalIgnoreCase)) { throw new Exception("unsupported texture file type"); } if (!File.Exists(RawPath(item4))) { throw new Exception("not recovered"); } TresFile t = TresFile.Parse(File.ReadAllText(RawPath(item4))); if (t.ResourceType != "AnimatedTexture") { throw new Exception("unsupported texture type " + t.ResourceType); } int count = t.Int("frames", 1); List<string> list5 = (from k in Enumerable.Range(0, count) select t.ExtPath($"frame_{k}/texture") ?? throw new Exception($"frame {k} missing")).ToList(); if (list5.Count == 0) { throw new Exception("no frames"); } dictionary[item4] = (list5, t.Float("fps", 4f)); hashSet2.UnionWith(list5.Where((string f) => !done.Contains(f))); } catch (Exception ex) { dictionary2[item4] = ex.Message; } } hashSet = hashSet2; num2++; } progress(85, "Saving the fish"); WfCatalog val = new WfCatalog { ExtractorVersion = "0.2.0", ExtractedUtc = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ", CultureInfo.InvariantCulture), SourceFile = Path.GetFileName(text4), SourceSha256 = text8, SteamBuildId = text3 }; foreach (var (val2, text14) in list2) { try { if (dictionary2.TryGetValue(text14, out var value)) { throw new Exception(text14 + ": " + value); } string text15 = val2.Id.Substring(3); object obj3; if (dictionary.TryGetValue(text14, out var value2)) { (obj3, _) = value2; } else { obj3 = new List<string> { text14 }; } List<string> list6 = (List<string>)obj3; WfSprite val3 = new WfSprite { Fps = ((list6.Count > 1) ? value2.Item2 : 0f) }; for (int num3 = 0; num3 < list6.Count; num3++) { string text16 = RawPath(list6[num3]); if (!File.Exists(text16)) { throw new Exception("texture not recovered: " + list6[num3]); } var (num4, num5) = PngCheck.Validate(text16); if (num3 == 0) { val3.Width = num4; val3.Height = num5; } else if (num4 != val3.Width || num5 != val3.Height) { throw new Exception($"frame {num3} is {num4}x{num5}, expected {val3.Width}x{val3.Height}"); } string text17 = ((list6.Count > 1) ? $"{text15}_{num3:00}.png" : (text15 + ".png")); File.Copy(text16, Path.Combine(text2, text17), overwrite: true); val3.Frames.Add(text17); } val2.Sprite = val3; val.Fish.Add(val2); } catch (Exception ex2) { list3.Add(val2.Id + ": " + ex2.Message); } } val.Fish = val.Fish.OrderBy((WfFish f) => f.Pool).ThenBy<WfFish, string>((WfFish f) => f.Id, StringComparer.Ordinal).ToList(); JsonFile.WriteAtomic<WfCatalog>(Path.Combine(text, "fishdefs.wf.json"), val); if (o.ContactSheet) { ContactSheet.Write(Path.Combine(text, "contact_sheet.html"), val); } foreach (IGrouping<WfPool, WfFish> item5 in from f in val.Fish group f by f.Pool) { log($" {item5.Key,-10} {item5.Count()}"); } log(" rare: " + string.Join(", ", from f in val.Fish where f.Rare select f.Name)); foreach (string item6 in list3) { log(" FAIL " + item6); } log($"{val.Fish.Count}/{num} converted"); if (list3.Count > 0 || val.Fish.Count != 70) { throw new Exception($"expected {70} fish to convert cleanly; existing cache left unchanged"); } if (!o.KeepRaw) { Directory.Delete(raw, recursive: true); } File.WriteAllText(Path.Combine(text, ".wfxhtf-cache"), "WFxHTF extractor cache. Safe to delete; re-run the extractor to rebuild.\n"); string text18 = fullPath + ".old"; if (Directory.Exists(text18)) { Directory.Delete(text18, recursive: true); } if (Directory.Exists(fullPath)) { Directory.Move(fullPath, text18); } Directory.Move(text, fullPath); text = null; if (Directory.Exists(text18)) { Directory.Delete(text18, recursive: true); } progress(100, $"{val.Fish.Count} WEBFISHING fish ready"); log("cache: " + fullPath); if (o.ContactSheet) { log("contact sheet: " + Path.Combine(fullPath, "contact_sheet.html")); } return fullPath; string RawPath(string res) { return Path.Combine(raw, res.Substring("res://".Length).Replace('/', Path.DirectorySeparatorChar)); } } finally { if (text != null && Directory.Exists(text) && !o.KeepRaw) { try { Directory.Delete(text, recursive: true); } catch (Exception ex3) { log("WARNING: could not delete " + text + ": " + ex3.Message); } } } } } public sealed class Gdre { public const string PinnedVersion = "2.7.4"; public readonly string Exe; public string Version { get; private set; } public Gdre(string exe) { Exe = exe; } public static string Locate(string explicitPath, string toolsDir) { if (!string.IsNullOrEmpty(explicitPath)) { if (!File.Exists(explicitPath)) { return null; } return Path.GetFullPath(explicitPath); } try { if (Directory.Exists(toolsDir)) { return Directory.EnumerateFiles(toolsDir, "gdre_tools.exe", SearchOption.AllDirectories).FirstOrDefault(); } } catch (IOException) { } catch (UnauthorizedAccessException) { } return null; } public void Recover(string source, string outDir, IEnumerable<string> includes) { List<string> list = new List<string> { "--headless", "--recover=" + source, "--output=" + outDir }; list.AddRange(includes.Select((string g) => "--include=" + g)); var (num, text) = Run(list); if (Version == null) { Match match = Regex.Match(text, "GDRE Tools v(\\d+\\.\\d+\\.\\d+)"); if (match.Success) { Version = match.Groups[1].Value; } } if (num != 0 || text.Contains("Recovery failed", StringComparison.OrdinalIgnoreCase)) { throw new Exception($"GDRE recover failed (exit {num}). Last output:\n{Tail(text, 20)}"); } } private (int, string) Run(List<string> args) { using Process process = Process.Start(new ProcessStartInfo(Exe) { RedirectStandardOutput = true, RedirectStandardError = true, UseShellExecute = false, CreateNoWindow = true, Arguments = string.Join(" ", args.Select(Quote)) }) ?? throw new Exception("could not start GDRE Tools"); Task<string> task = process.StandardOutput.ReadToEndAsync(); Task<string> task2 = process.StandardError.ReadToEndAsync(); if (!process.WaitForExit(600000)) { try { process.Kill(); } catch (InvalidOperationException) { } throw new Exception("GDRE Tools timed out after 10 minutes"); } process.WaitForExit(); return (process.ExitCode, task.Result + task2.Result); } private static string Quote(string a) { if (a.Length > 0 && a.IndexOfAny(new char[3] { ' ', '\t', '"' }) < 0) { return a; } StringBuilder stringBuilder = new StringBuilder("\""); int num = 0; foreach (char c in a) { switch (c) { case '\\': num++; continue; case '"': stringBuilder.Append('\\', num * 2 + 1).Append('"'); break; default: stringBuilder.Append('\\', num).Append(c); break; } num = 0; } return stringBuilder.Append('\\', num * 2).Append('"').ToString(); } private static string Tail(string s, int lines) { return string.Join("\n", s.Split('\n').TakeLast(lines)); } } public static class GdreLocal { public const string ReleasePage = "https://github.com/GDRETools/gdsdecomp/releases/tag/v2.7.4"; public const string ZipName = "GDRE_tools-v2.7.4-windows.zip"; public const string ZipSha256 = "af24c563eee501effb194d83595de64696c32547b2246034dbfb891a24bfa0ad"; public const string ExeSha256 = "dbcda88ded28b4c9cd0252ba5a4e9af416f91766d9067ca930876e7d4e07215a"; private static string FolderName => Path.GetFileNameWithoutExtension("GDRE_tools-v2.7.4-windows.zip"); public static string DownloadsFolder() { return Win.DownloadsFolder(); } public static string FromDownloads(string downloads, string toolsDir, Action<string> log) { if (string.IsNullOrEmpty(downloads) || !Directory.Exists(downloads)) { return null; } string text = Path.Combine(toolsDir, FolderName); foreach (FileInfo item in from f in new DirectoryInfo(downloads).GetFiles(FolderName + "*.zip") orderby f.LastWriteTimeUtc descending select f) { if (!SameHash(item.FullName, "af24c563eee501effb194d83595de64696c32547b2246034dbfb891a24bfa0ad")) { log(item.Name + " isn't the official GDRE Tools 2.7.4 release (fingerprint differs); skipped"); continue; } Directory.CreateDirectory(toolsDir); if (Directory.Exists(text)) { Directory.Delete(text, recursive: true); } ZipFile.ExtractToDirectory(item.FullName, text); string text2 = Path.Combine(text, "gdre_tools.exe"); if (File.Exists(text2) && SameHash(text2, "dbcda88ded28b4c9cd0252ba5a4e9af416f91766d9067ca930876e7d4e07215a")) { log("GDRE Tools: unpacked " + item.Name + " into " + text); return text2; } Directory.Delete(text, recursive: true); } DirectoryInfo[] directories = new DirectoryInfo(downloads).GetDirectories(FolderName + "*"); foreach (DirectoryInfo directoryInfo in directories) { string text3 = (File.Exists(Path.Combine(directoryInfo.FullName, "gdre_tools.exe")) ? directoryInfo.FullName : Path.Combine(directoryInfo.FullName, FolderName)); string path = Path.Combine(text3, "gdre_tools.exe"); if (File.Exists(path) && SameHash(path, "dbcda88ded28b4c9cd0252ba5a4e9af416f91766d9067ca930876e7d4e07215a")) { Directory.CreateDirectory(text); string[] files = Directory.GetFiles(text3); foreach (string text4 in files) { File.Copy(text4, Path.Combine(text, Path.GetFileName(text4)), overwrite: true); } log("GDRE Tools: copied " + directoryInfo.Name + " into " + text); return Path.Combine(text, "gdre_tools.exe"); } } return null; } private static bool SameHash(string path, string sha256) { return Win.Sha256(path).Equals(sha256, StringComparison.OrdinalIgnoreCase); } } public static class Paths { public static bool IsUnder(string path, string dir) { string text = Path.GetFullPath(path).TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar) + Path.DirectorySeparatorChar; string value = Path.GetFullPath(dir).TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar) + Path.DirectorySeparatorChar; return text.StartsWith(value, StringComparison.OrdinalIgnoreCase); } public static string Resolve(string path) { return Path.GetFullPath(path); } public static bool IsReparsePoint(string path) { try { for (DirectoryInfo directoryInfo = new DirectoryInfo(Path.GetFullPath(path)); directoryInfo != null; directoryInfo = directoryInfo.Parent) { if (directoryInfo.Exists && (directoryInfo.Attributes & FileAttributes.ReparsePoint) != FileAttributes.None) { return true; } } } catch (IOException) { } catch (UnauthorizedAccessException) { } return false; } } public static class PngCheck { private static readonly byte[] Signature = new byte[8] { 137, 80, 78, 71, 13, 10, 26, 10 }; private static readonly uint[] CrcTable = BuildCrcTable(); private static uint[] BuildCrcTable() { uint[] array = new uint[256]; for (uint num = 0u; num < 256; num++) { uint num2 = num; for (int i = 0; i < 8; i++) { num2 = (((num2 & 1) != 0) ? (0xEDB88320u ^ (num2 >> 1)) : (num2 >> 1)); } array[num] = num2; } return array; } private static uint Crc32(ReadOnlySpan<byte> bytes) { uint num = uint.MaxValue; ReadOnlySpan<byte> readOnlySpan = bytes; for (int i = 0; i < readOnlySpan.Length; i++) { byte b = readOnlySpan[i]; num = CrcTable[(num ^ b) & 0xFF] ^ (num >> 8); } return num ^ 0xFFFFFFFFu; } public static (int Width, int Height) Validate(string path) { byte[] array = File.ReadAllBytes(path); if (array.Length < 8 || !array.AsSpan(0, 8).SequenceEqual(Signature)) { throw new InvalidDataException("not a PNG"); } int i = 8; int num = 0; int num2 = 0; int num3 = 0; int num4 = 0; int num5 = 0; bool flag = false; using MemoryStream memoryStream = new MemoryStream(); int num6; for (; i + 12 <= array.Length; i += 12 + num6) { num6 = (int)BinaryPrimitives.ReadUInt32BigEndian(array.AsSpan(i)); if (num6 < 0 || i + 12 + num6 > array.Length) { throw new InvalidDataException("truncated chunk"); } Span<byte> span = array.AsSpan(i + 4, 4 + num6); uint num7 = BinaryPrimitives.ReadUInt32BigEndian(array.AsSpan(i + 8 + num6)); if (Crc32(span) != num7) { throw new InvalidDataException("bad chunk CRC"); } string text = Encoding.ASCII.GetString(span.Slice(0, 4)); Span<byte> span2 = span.Slice(4); switch (text) { case "IHDR": num = (int)BinaryPrimitives.ReadUInt32BigEndian(span2); num2 = (int)BinaryPrimitives.ReadUInt32BigEndian(span2.Slice(4)); num3 = span2[8]; num4 = span2[9]; num5 = span2[12]; continue; case "IDAT": memoryStream.Write(span2); continue; case "IEND": break; default: continue; } flag = true; break; } if (!flag) { throw new InvalidDataException("missing IEND"); } if (num <= 0 || num2 <= 0) { throw new InvalidDataException("empty image"); } if (memoryStream.Length < 2) { throw new InvalidDataException("empty image data"); } memoryStream.Position = 2L; using DeflateStream deflateStream = new DeflateStream(memoryStream, CompressionMode.Decompress); using MemoryStream memoryStream2 = new MemoryStream(); deflateStream.CopyTo(memoryStream2); if (num5 == 0) { long num8 = ((long)num * (long)(num4 switch { 0 => 1, 2 => 3, 3 => 1, 4 => 2, 6 => 4, _ => throw new InvalidDataException($"color type {num4}"), }) * num3 + 7) / 8; long num9 = num2 * (1 + num8); if (memoryStream2.Length != num9) { throw new InvalidDataException($"pixel data {memoryStream2.Length} bytes, expected {num9}"); } } return (Width: num, Height: num2); } } public sealed class SteamGame { public string InstallDir = ""; public string BuildId = ""; } public static class SteamLocator { public static IEnumerable<string> SteamRoots() { List<string> list = new List<string>(); (bool, string, string)[] array = new(bool, string, string)[2] { (false, "Software\\Valve\\Steam", "SteamPath"), (true, "SOFTWARE\\WOW6432Node\\Valve\\Steam", "InstallPath") }; for (int i = 0; i < array.Length; i++) { (bool, string, string) tuple = array[i]; bool item = tuple.Item1; string item2 = tuple.Item2; string item3 = tuple.Item3; string text = Win.RegistryString(item, item2, item3); if (!string.IsNullOrEmpty(text)) { list.Add(text); } } list.Add("C:\\Program Files (x86)\\Steam"); return list.Select((string p) => Path.GetFullPath(p.Replace('/', '\\'))).Where(Directory.Exists).Distinct<string>(StringComparer.OrdinalIgnoreCase); } public static IEnumerable<string> Libraries() { List<string> list = new List<string>(); foreach (string item in SteamRoots()) { list.Add(item); string path = Path.Combine(item, "steamapps", "libraryfolders.vdf"); if (!File.Exists(path)) { continue; } foreach (Match item2 in Regex.Matches(File.ReadAllText(path), "\"path\"\\s+\"((?:[^\"\\\\]|\\\\.)*)\"")) { list.Add(item2.Groups[1].Value.Replace("\\\\", "\\")); } } return list.Where(Directory.Exists).Select(Path.GetFullPath).Distinct<string>(StringComparer.OrdinalIgnoreCase); } public static SteamGame Find(int appId) { foreach (string item in Libraries()) { string path = Path.Combine(item, "steamapps", $"appmanifest_{appId}.acf"); if (!File.Exists(path)) { continue; } string acf = File.ReadAllText(path); string text = Field(acf, "installdir"); if (!(text == "")) { string text2 = Path.Combine(item, "steamapps", "common", text); if (Directory.Exists(text2)) { return new SteamGame { InstallDir = text2, BuildId = Field(acf, "buildid") }; } } } return null; } private static string Field(string acf, string name) { Match match = Regex.Match(acf, "\"" + name + "\"\\s+\"([^\"]*)\""); if (!match.Success) { return ""; } return match.Groups[1].Value; } } public sealed class TresFile { public string ResourceType = ""; public readonly Dictionary<int, (string Path, string Type)> ExtResources = new Dictionary<int, (string, string)>(); public readonly Dictionary<string, string> Props = new Dictionary<string, string>(StringComparer.Ordinal); private static readonly Regex HeaderRx = new Regex("^\\[gd_resource\\s+type=\"([^\"]+)\""); private static readonly Regex ExtRx = new Regex("^\\[ext_resource\\s+path=\"([^\"]+)\"\\s+type=\"([^\"]+)\"\\s+id=(\\d+)\\]"); private static readonly Regex ExtRefRx = new Regex("^ExtResource\\(\\s*(\\d+)\\s*\\)$"); public static TresFile Parse(string text) { TresFile tresFile = new TresFile(); bool flag = false; int num = 0; string[] array = text.Replace("\r\n", "\n").Split('\n'); while (num < array.Length) { string text2 = array[num]; string text3 = text2.Trim(); num++; if (text3.Length == 0 || text3.StartsWith(";")) { continue; } if (text3.StartsWith("[")) { Match match; if ((match = HeaderRx.Match(text3)).Success) { tresFile.ResourceType = match.Groups[1].Value; } else if ((match = ExtRx.Match(text3)).Success) { tresFile.ExtResources[int.Parse(match.Groups[3].Value, CultureInfo.InvariantCulture)] = (match.Groups[1].Value, match.Groups[2].Value); } flag = text3 == "[resource]"; } else { if (!flag) { continue; } int num2 = text2.IndexOf(" = ", StringComparison.Ordinal); if (num2 >= 0) { string key = text2.Substring(0, num2).Trim(); StringBuilder stringBuilder = new StringBuilder(text2.Substring(num2 + 3)); while (!IsComplete(stringBuilder.ToString()) && num < array.Length) { stringBuilder.Append('\n').Append(array[num++]); } tresFile.Props[key] = stringBuilder.ToString().Trim(); } } } return tresFile; } private static bool IsComplete(string v) { bool flag = false; bool flag2 = false; int num = 0; foreach (char c in v) { if (flag) { if (flag2) { flag2 = false; continue; } switch (c) { case '\\': flag2 = true; break; case '"': flag = false; break; } continue; } switch (c) { case '"': flag = true; break; case '(': case '[': case '{': num++; break; case ')': case ']': case '}': num--; break; } } if (!flag) { return num <= 0; } return false; } public bool Has(string key) { return Props.ContainsKey(key); } public string Str(string key, string def = "") { if (!Props.TryGetValue(key, out var value)) { return def; } if (value.Length >= 2 && value[0] == '"') { string text = value; if (text[text.Length - 1] == '"') { return Unescape(value.Substring(1, value.Length - 2)); } } return value; } public float Float(string key, float def) { if (!Props.TryGetValue(key, out var value) || !float.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) { return def; } return result; } public int Int(string key, int def) { if (!Props.TryGetValue(key, out var value) || !double.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) { return def; } return (int)Math.Round(result); } public bool Bool(string key, bool def) { if (!Props.TryGetValue(key, out var value)) { return def; } return value == "true"; } public string ExtPath(string key) { if (!Props.TryGetValue(key, out var value)) { return null; } Match match = ExtRefRx.Match(value); if (!match.Success) { return null; } if (!ExtResources.TryGetValue(int.Parse(match.Groups[1].Value, CultureInfo.InvariantCulture), out (string, string) value2)) { return null; } return value2.Item1; } private static string Unescape(string s) { StringBuilder stringBuilder = new StringBuilder(s.Length); for (int i = 0; i < s.Length; i++) { char c = s[i]; if (c != '\\' || i + 1 >= s.Length) { stringBuilder.Append(c); continue; } char c2 = s[++i]; StringBuilder stringBuilder2 = stringBuilder; stringBuilder2.Append(c2 switch { 'n' => '\n', 't' => '\t', '"' => '"', '\\' => '\\', _ => c2, }); } return stringBuilder.ToString(); } } public static class Win { private static readonly UIntPtr HKEY_CURRENT_USER = new UIntPtr(2147483649u); private static readonly UIntPtr HKEY_LOCAL_MACHINE = new UIntPtr(2147483650u); private const uint RRF_RT_REG_SZ = 2u; private const uint RRF_RT_REG_EXPAND_SZ = 4u; private const uint RRF_NOEXPAND = 268435456u; public static bool IsWindows => RuntimeInformation.IsOSPlatform(OSPlatform.Windows); [DllImport("advapi32.dll", CharSet = CharSet.Unicode)] private static extern int RegGetValueW(UIntPtr hkey, string subKey, string value, uint flags, out uint type, StringBuilder data, ref uint size); [DllImport("shell32.dll", CharSet = CharSet.Unicode)] private static extern int SHGetKnownFolderPath([MarshalAs(UnmanagedType.LPStruct)] Guid id, uint flags, IntPtr token, out IntPtr path); [DllImport("ole32.dll")] private static extern void CoTaskMemFree(IntPtr p); public static string RegistryString(bool machine, string subKey, string value) { if (!IsWindows) { return null; } try { StringBuilder stringBuilder = new StringBuilder(1024); uint size = (uint)(stringBuilder.Capacity * 2); uint type; return (RegGetValueW(machine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER, subKey, value, 268435462u, out type, stringBuilder, ref size) == 0) ? Environment.ExpandEnvironmentVariables(stringBuilder.ToString()) : null; } catch (Exception) { return null; } } public static string DownloadsFolder() { string result = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "Downloads"); if (!IsWindows) { return result; } try { if (SHGetKnownFolderPath(new Guid("374DE290-123F-4565-9164-39C4925E467B"), 0u, IntPtr.Zero, out var path) == 0) { string text = Marshal.PtrToStringUni(path); CoTaskMemFree(path); if (!string.IsNullOrEmpty(text) && Directory.Exists(text)) { return text; } } } catch (Exception) { } return result; } public static string Sha256(string path) { using FileStream s = File.OpenRead(path); return Sha256(s); } public static string Sha256(Stream s) { using SHA256 sHA = SHA256.Create(); return BitConverter.ToString(sHA.ComputeHash(s)).Replace("-", "").ToLowerInvariant(); } } }
BepInEx/plugins/WFxHTF/WFxHTF.Runtime.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.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using System.Text.RegularExpressions; using System.Threading; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.Events; using UnityEngine.Rendering; using UnityEngine.UI; using WFxHTF.Core; using WFxHTF.Data; using WFxHTF.Extraction; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("WFxHTF")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.1.0.0")] [assembly: AssemblyInformationalVersion("0.1.0+166b9aae0d28865ba70c4894450aeb4db4b21aeb")] [assembly: AssemblyProduct("WFxHTF.Runtime")] [assembly: AssemblyTitle("WFxHTF.Runtime")] [assembly: AssemblyVersion("0.1.0.0")] namespace WFxHTF.Runtime; public static class ConfigBinder { public static int Bind(ConfigFile file, object target, string section = null) { //IL_01bc: Unknown result type (might be due to invalid IL or missing references) //IL_01c6: Expected O, but got Unknown section = section ?? target.GetType().Name; int num = 0; FieldInfo[] fields = target.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public); foreach (FieldInfo fieldInfo in fields) { if (fieldInfo.IsInitOnly) { continue; } Type fieldType = fieldInfo.FieldType; object value = fieldInfo.GetValue(target); if (fieldType == typeof(float)) { fieldInfo.SetValue(target, file.Bind<float>(section, fieldInfo.Name, (float)value, (ConfigDescription)null).Value); } else if (fieldType == typeof(int)) { fieldInfo.SetValue(target, file.Bind<int>(section, fieldInfo.Name, (int)value, (ConfigDescription)null).Value); } else if (fieldType == typeof(bool)) { fieldInfo.SetValue(target, file.Bind<bool>(section, fieldInfo.Name, (bool)value, (ConfigDescription)null).Value); } else if (fieldType == typeof(double)) { fieldInfo.SetValue(target, file.Bind<double>(section, fieldInfo.Name, (double)value, (ConfigDescription)null).Value); } else if (fieldType == typeof(string)) { fieldInfo.SetValue(target, file.Bind<string>(section, fieldInfo.Name, ((string)value) ?? "", (ConfigDescription)null).Value); } else if (fieldType.IsEnum) { ConfigEntry<string> val = file.Bind<string>(section, fieldInfo.Name, value.ToString(), new ConfigDescription("One of: " + string.Join(", ", Enum.GetNames(fieldType)), (AcceptableValueBase)null, Array.Empty<object>())); fieldInfo.SetValue(target, Enum.IsDefined(fieldType, val.Value) ? Enum.Parse(fieldType, val.Value) : value); } else if (fieldType == typeof(int[])) { fieldInfo.SetValue(target, ParseArray(file.Bind<string>(section, fieldInfo.Name, Join((int[])value), (ConfigDescription)null).Value, int.Parse, (int[])value)); } else { if (!(fieldType == typeof(float[]))) { continue; } fieldInfo.SetValue(target, ParseArray(file.Bind<string>(section, fieldInfo.Name, Join((float[])value), (ConfigDescription)null).Value, (string s) => float.Parse(s, CultureInfo.InvariantCulture), (float[])value)); } num++; } return num; } private static string Join<T>(IEnumerable<T> xs) { return string.Join(", ", xs.Select((T x) => Convert.ToString(x, CultureInfo.InvariantCulture))); } private static T[] ParseArray<T>(string text, Func<string, T> parse, T[] fallback) { try { return (from s in text.Split(',') select parse(s.Trim())).ToArray(); } catch { return fallback; } } } public sealed class DebugConsole : MonoBehaviour { public KeyCode ToggleKey = (KeyCode)289; private readonly Dictionary<string, (string Help, Func<string[], string> Run)> _commands = new Dictionary<string, (string, Func<string[], string>)>(); private readonly List<string> _log = new List<string>(); private string _input = ""; private Vector2 _scroll; private Texture2D _bg; private CursorLockMode _prevLock; private bool _prevVisible; private int _lastToggleFrame = -100; public bool DebugAllowed; private const string DebugOff = "that's a cheat/test command: set [Debug] Commands = true in BepInEx\\config\\com.wfxhtf.mod.cfg to use it"; public bool Open { get; private set; } public static bool IsDebug(string help) { return help?.StartsWith("DEBUG") ?? false; } public void Register(string name, string help, Func<string[], string> run) { _commands[name] = (help, IsDebug(help) ? ((Func<string[], string>)((string[] a) => (!DebugAllowed) ? "that's a cheat/test command: set [Debug] Commands = true in BepInEx\\config\\com.wfxhtf.mod.cfg to use it" : run(a))) : run); } public void Print(string line) { _log.Add(line); if (_log.Count > 200) { _log.RemoveAt(0); } _scroll.y = float.MaxValue; } private void Awake() { Register("help", "list commands", (string[] _) => string.Join("\n", from c in _commands where DebugAllowed || !IsDebug(c.Value.Help) orderby c.Key select $"{c.Key,-12} {c.Value.Help}")); } private void Update() { //IL_0001: Unknown result type (might be due to invalid IL or missing references) if (Keys.Down(ToggleKey)) { Toggle(); } if (Open) { Cursor.lockState = (CursorLockMode)0; Cursor.visible = true; } } private void Toggle() { if (Time.frameCount - _lastToggleFrame >= 5) { _lastToggleFrame = Time.frameCount; SetOpen(!Open); } } private void SetOpen(bool open) { //IL_002c: 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) if (open != Open) { Open = open; if (open) { _prevLock = Cursor.lockState; _prevVisible = Cursor.visible; } else { GameHooks.ReleaseCursor(_prevLock, _prevVisible); } } } private void OnGUI() { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Invalid comparison between Unknown and I4 //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: 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_0136: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_0155: 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_008f: Expected O, but got Unknown //IL_00ab: 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) //IL_003e: Invalid comparison between Unknown and I4 //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Invalid comparison between Unknown and I4 //IL_019f: Unknown result type (might be due to invalid IL or missing references) //IL_01a5: Invalid comparison between Unknown and I4 //IL_01a8: Unknown result type (might be due to invalid IL or missing references) //IL_01af: Invalid comparison between Unknown and I4 //IL_01b2: Unknown result type (might be due to invalid IL or missing references) //IL_01bc: Invalid comparison between Unknown and I4 if (!Open) { return; } Event current = Event.current; if ((int)current.type == 4 && (current.keyCode == ToggleKey || (int)current.keyCode == 27)) { current.Use(); if ((int)current.keyCode == 27) { _lastToggleFrame = Time.frameCount; SetOpen(open: false); } else { Toggle(); } return; } float num = Mathf.Min(900, Screen.width - 40); float num2 = 320f; if ((Object)(object)_bg == (Object)null) { _bg = new Texture2D(1, 1); _bg.SetPixel(0, 0, new Color(0.04f, 0.05f, 0.07f, 0.95f)); _bg.Apply(); } GUI.DrawTexture(new Rect(20f, 20f, num, num2), (Texture)(object)_bg); GUI.Box(new Rect(20f, 20f, num, num2), "WFxHTF console (" + ((object)Unsafe.As<KeyCode, KeyCode>(ref ToggleKey)/*cast due to .constrained prefix*/).ToString() + " or Esc to close, 'help' for commands)"); GUILayout.BeginArea(new Rect(30f, 45f, num - 20f, num2 - 35f)); _scroll = GUILayout.BeginScrollView(_scroll, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(num2 - 75f) }); foreach (string item in _log) { GUILayout.Label(item, Array.Empty<GUILayoutOption>()); } GUILayout.EndScrollView(); Event current2 = Event.current; bool num3 = (int)current2.type == 4 && ((int)current2.keyCode == 13 || (int)current2.keyCode == 271); GUI.SetNextControlName("wf_input"); _input = GUILayout.TextField(_input ?? "", Array.Empty<GUILayoutOption>()); GUI.FocusControl("wf_input"); GUILayout.EndArea(); if (num3 && !string.IsNullOrWhiteSpace(_input)) { Execute(_input); _input = ""; current2.Use(); } } public void Execute(string line) { Print("> " + line); string[] array = line.Trim().Split((char[]?)null, StringSplitOptions.RemoveEmptyEntries); if (array.Length == 0) { return; } if (!_commands.TryGetValue(array[0].ToLowerInvariant(), out (string, Func<string[], string>) value)) { Print("unknown command; try 'help'"); return; } try { Print(value.Item2(array.Skip(1).ToArray()) ?? "ok"); } catch (Exception ex) { Print("error: " + ex.Message); Plugin.Log.LogError((object)$"console '{line}': {ex}"); } } } public sealed class DebugTank : MonoBehaviour { private SpriteCache _cache; private DebugConsole _console; public Aquarium Tank { get; private set; } public TankView View { get; private set; } public static DebugTank Spawn(SpriteCache cache, AquariumConfig cfg, DebugConsole console) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002a: 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_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005a: 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_0062: 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_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_0077: 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) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_0092: 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_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Expected O, but got Unknown Camera val = UnityUtil.ViewCamera(); if ((Object)(object)val == (Object)null) { return null; } Vector3 val2 = Vector3.ProjectOnPlane(((Component)val).transform.forward, Vector3.up); Vector3 val3 = ((Vector3)(ref val2)).normalized; if (((Vector3)(ref val3)).sqrMagnitude < 0.01f) { val3 = Vector3.forward; } Vector3 val4 = ((Component)val).transform.position + val3 * 4f; RaycastHit val5 = default(RaycastHit); val4.y = (Physics.Raycast(val4 + Vector3.up * 3f, Vector3.down, ref val5, 20f, -1, (QueryTriggerInteraction)1) ? ((RaycastHit)(ref val5)).point.y : (((Component)val).transform.position.y - 1.6f)); TankView tankView = TankView.Create(cache, val4, Quaternion.LookRotation(val3, Vector3.up)); DebugTank debugTank = ((Component)tankView).gameObject.AddComponent<DebugTank>(); debugTank._cache = cache; debugTank._console = console; debugTank.View = tankView; debugTank.Tank = new Aquarium(cfg, (AquariumState)null); return debugTank; } private void Update() { Handle(Tank.Advance((double)Time.deltaTime / 60.0 / (double)Tank.Config.TankDayRealMinutes, true)); View.Sync(Tank); } public int Handle(List<AqEvent> events) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Invalid comparison between Unknown and I4 foreach (AqEvent @event in events) { _console?.Print(string.Format("[tank] {0} {1} {2}{3}", @event.Kind, @event.SpeciesId, @event.Reason, ((int)@event.Kind != 2) ? "" : (@event.Defeated ? " (defeated)" : " (timed out)"))); } View.OnEvents(events); return events.Count; } public string Add(FishDef def, Quality q) { //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) float weightKgMean = def.Htf.WeightKgMean; int num = Mathf.Max(1, Mathf.RoundToInt(20f * def.Htf.RelativeWorth)); long num2 = default(long); string text = default(string); if (!Tank.TryDeposit(def.Id, weightKgMean, q, Identity.SteamId, Identity.Name, num, ref num2, ref text, false, false, 0f, 1f, 1f)) { return "refused: " + text; } return $"deposited {def.Name} ({q}) as #{num2}, value {num}"; } public string Dump() { //IL_0158: Unknown result type (might be due to invalid IL or missing references) AquariumState state = Tank.State; StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine($"seq {state.Seq} fish {state.Fish.Count}/{Tank.Capacity} tray {state.TrayCoins:0.00} zen {state.Zen} hopper {state.HopperFood}"); stringBuilder.AppendLine((state.Attack != null) ? $"ATTACK: {state.Attack.ElapsedDays:0.00}/{Tank.Config.AttackTimeoutDays} days, threat {state.Attack.ThreatHp}/{state.Attack.ThreatMaxHp}" : $"next attack in {state.NextAttackInDays:0.00} days"); foreach (AqFish item in state.Fish) { stringBuilder.AppendLine(string.Format(" #{0} {1,-20} {2,-10} hunger {3:0.00} health {4:0.00} value {5}{6}", item.Uid, GameHooks.SpeciesName(_cache, item.SpeciesId), item.Quality, item.Hunger, item.Health, item.Value, (item.StarvedDays > 0f) ? $" STARVING {item.StarvedDays:0.00}d" : "")); } stringBuilder.Append("hash " + Tank.Hash()); return stringBuilder.ToString(); } } public sealed class FirstRunSetup : MonoBehaviour { public bool Visible; private Thread _worker; private volatile bool _workerDone; private Exception _workerError; private readonly object _gate = new object(); private readonly Queue<string> _incoming = new Queue<string>(); private readonly List<string> _tail = new List<string>(); private int _pct; private string _status = ""; private string _error; private bool _finished; private float _closeAt = -1f; private GUIStyle _wrap; public const string GdrePage = "https://github.com/GDRETools/gdsdecomp/releases/tag/v2.7.4"; public const string GdreZip = "GDRE_tools-v2.7.4-windows.zip"; private float _gdreCheckAt; private bool _gdreReady; public bool Running => _worker != null; private static string ToolsDir => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "WFxHTF", "tools"); private static string DownloadsDir => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "Downloads"); private static bool GdreLooksReady() { try { if (Directory.Exists(ToolsDir) && Directory.GetFiles(ToolsDir, "gdre_tools.exe", SearchOption.AllDirectories).Length != 0) { return true; } return Directory.Exists(DownloadsDir) && Directory.GetFiles(DownloadsDir, "GDRE_tools-v2.7.4-windows*.zip").Length != 0; } catch (Exception) { return false; } } public string Run() { if (_worker != null) { return "the setup is already running"; } _error = null; _workerError = null; _workerDone = false; _finished = false; _pct = 0; _status = "Starting"; _closeAt = -1f; _tail.Clear(); _worker = new Thread((ThreadStart)delegate { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Expected O, but got Unknown try { Extraction.Run(new ExtractOptions(), (Action<string>)Enqueue, (Action<int, string>)delegate(int pct, string text) { Enqueue($"@progress {pct} {text}"); }); } catch (Exception workerError) { _workerError = workerError; } finally { _workerDone = true; } }) { IsBackground = true, Name = "WFxHTF WEBFISHING setup" }; _worker.Start(); Plugin.Log.LogInfo((object)"setup: reading the WEBFISHING fish"); return "setting up the WEBFISHING fish"; } private void Enqueue(string line) { if (line == null) { return; } lock (_gate) { _incoming.Enqueue(line); } } private void Update() { while (true) { string line; lock (_gate) { if (_incoming.Count == 0) { break; } line = _incoming.Dequeue(); goto IL_0038; } IL_0038: Handle(line); } if (_worker != null && _workerDone) { Finish(); } if (_worker != null && !Visible) { Hud.ShowSmall("wfsetup", $"Setting up WEBFISHING fish: {_pct}%"); } else if (_finished && !Visible && _closeAt > 0f) { Hud.ShowSmall("wfsetup", _status); } else { Hud.Hide("wfsetup"); } if (_closeAt > 0f && Time.unscaledTime >= _closeAt) { Visible = false; _closeAt = -1f; Hud.Hide("wfsetup"); } } private void Handle(string line) { if (line.StartsWith("@progress ")) { string[] array = line.Split(new char[1] { ' ' }, 3); if (array.Length >= 2 && int.TryParse(array[1], out var result)) { _pct = Mathf.Clamp(result, 0, 100); } if (array.Length == 3) { _status = array[2]; } } else if (!line.StartsWith(" skip ")) { _tail.Add(line); if (_tail.Count > 40) { _tail.RemoveAt(0); } } } private void Finish() { _worker = null; Update(); foreach (string item in _tail) { Plugin.Log.LogInfo((object)("setup> " + item)); } if (_workerError == null && (Object)(object)Plugin.Instance != (Object)null && Plugin.Instance.ReloadCache()) { _finished = true; _pct = 100; _status = $"Done: {Plugin.Instance.Cache.Defs.Count} WEBFISHING fish are ready."; _closeAt = Time.unscaledTime + 5f; } else { _error = ((_workerError != null) ? _workerError.Message : (Plugin.Instance?.Cache.Error ?? "the fish couldn't be loaded")); Plugin.Log.LogWarning((object)("setup failed: " + ((_workerError != null) ? _workerError.ToString() : _error))); } } private void OnGUI() { //IL_0065: 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_0034: Expected O, but got Unknown //IL_010c: Unknown result type (might be due to invalid IL or missing references) //IL_0164: Unknown result type (might be due to invalid IL or missing references) //IL_01b2: Unknown result type (might be due to invalid IL or missing references) //IL_01b7: Unknown result type (might be due to invalid IL or missing references) //IL_01bc: Unknown result type (might be due to invalid IL or missing references) //IL_01cd: 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_01e8: Unknown result type (might be due to invalid IL or missing references) //IL_02df: Unknown result type (might be due to invalid IL or missing references) //IL_0273: Unknown result type (might be due to invalid IL or missing references) //IL_0294: Unknown result type (might be due to invalid IL or missing references) //IL_0309: Unknown result type (might be due to invalid IL or missing references) //IL_02c9: Unknown result type (might be due to invalid IL or missing references) //IL_02b1: Unknown result type (might be due to invalid IL or missing references) //IL_033e: Unknown result type (might be due to invalid IL or missing references) //IL_0326: Unknown result type (might be due to invalid IL or missing references) if (!Visible) { return; } if (_wrap == null) { _wrap = new GUIStyle(GUI.skin.label) { wordWrap = true, richText = true }; } float num = 600f; float num2 = 230f; Rect val = default(Rect); ((Rect)(ref val))..ctor(((float)Screen.width - num) / 2f, ((float)Screen.height - num2) / 2f, num, num2); GUI.Box(val, "How to Webfish: WEBFISHING fish"); Rect val2 = default(Rect); ((Rect)(ref val2))..ctor(((Rect)(ref val)).x + 20f, ((Rect)(ref val)).y + 30f, num - 40f, 120f); Rect val3 = default(Rect); ((Rect)(ref val3))..ctor(((Rect)(ref val)).x + 20f, ((Rect)(ref val)).y + num2 - 48f, 220f, 32f); Rect val4 = default(Rect); ((Rect)(ref val4))..ctor(((Rect)(ref val)).x + num - 160f, ((Rect)(ref val)).y + num2 - 48f, 140f, 32f); if (_worker != null || _finished) { GUI.Label(val2, _status + (_finished ? "" : "\n\nThis takes about a minute. You can keep playing; the fish appear when it's done."), _wrap); Rect val5 = default(Rect); ((Rect)(ref val5))..ctor(((Rect)(ref val)).x + 20f, ((Rect)(ref val)).y + 150f, num - 40f, 18f); GUI.Box(val5, ""); Rect val6 = new Rect(((Rect)(ref val5)).x + 2f, ((Rect)(ref val5)).y + 2f, (((Rect)(ref val5)).width - 4f) * (float)_pct / 100f, ((Rect)(ref val5)).height - 4f); Color color = GUI.color; GUI.color = new Color(0.45f, 0.8f, 1f); GUI.DrawTexture(val6, (Texture)(object)Texture2D.whiteTexture); GUI.color = color; if (GUI.Button(val4, _finished ? "Close" : "Hide")) { Visible = false; } return; } Rect val7 = default(Rect); ((Rect)(ref val7))..ctor(((Rect)(ref val)).x + (num - 220f) / 2f, ((Rect)(ref val)).y + num2 - 48f, 220f, 32f); if (Time.unscaledTime >= _gdreCheckAt) { _gdreReady = GdreLooksReady(); _gdreCheckAt = Time.unscaledTime + 1f; } if (_error != null) { GUI.Label(val2, "<b>The setup didn't finish:</b> " + _error + "\n\nWEBFISHING must be installed in Steam. Details are in BepInEx\\LogOutput.log.", _wrap); if (GUI.Button(val3, "Try again")) { Run(); } if (!_gdreReady && GUI.Button(val7, "GDRE download page")) { Application.OpenURL("https://github.com/GDRETools/gdsdecomp/releases/tag/v2.7.4"); } if (GUI.Button(val4, "Close")) { Visible = false; } } else { GUI.Label(val2, "How to Webfish shows WEBFISHING's fish by reading them from <b>your own WEBFISHING</b> (it must be installed in Steam). This happens once, takes about a minute, and keeps them in %LOCALAPPDATA%\\WFxHTF.\n\n" + (_gdreReady ? "GDRE Tools found. Press <b>Set up now</b>." : "It needs GDRE Tools (free, open source). Press <b>GDRE download page</b>, download <b>GDRE_tools-v2.7.4-windows.zip</b> into your Downloads folder, then press <b>Set up now</b>."), _wrap); if (GUI.Button(val3, "Set up now")) { Run(); } if (!_gdreReady && GUI.Button(val7, "GDRE download page")) { Application.OpenURL("https://github.com/GDRETools/gdsdecomp/releases/tag/v2.7.4"); } if (GUI.Button(val4, "Later")) { Visible = false; } } } } public static class GameHooks { public static Func<bool> NativeJournalOpen; public static string NativeJournalHint; public static Action RestoreCursor; public static Func<string, bool, GameObject> NativeFishModel; public static Func<Light> MainLight; public static Func<ParticleSystem> CloudTemplate; public static Func<Vector3, float> WaterHeightAt; public static Func<string, string> NativeFishName; public static string SpeciesName(SpriteCache cache, string speciesId) { FishDef val = cache?.Find(speciesId); if (val != null) { return val.Name; } try { return NativeFishName?.Invoke(speciesId) ?? speciesId; } catch { return speciesId; } } public static void ReleaseCursor(CursorLockMode prevLock, bool prevVisible) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) Action restoreCursor = RestoreCursor; if (restoreCursor != null) { try { restoreCursor(); return; } catch { } } Cursor.lockState = prevLock; Cursor.visible = prevVisible; } } public sealed class GlowOrb : MonoBehaviour { private static Mesh _sphere; private float _phase; private Transform _core; public static GlowOrb Create(string name, float radius, Color colour) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Expected O, but got Unknown //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0056: 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_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject(name); GlowOrb glowOrb = val.AddComponent<GlowOrb>(); glowOrb._phase = Random.value * 10f; glowOrb._core = Ball(val.transform, "Core", radius, SpriteMaterial.CreatePlain(colour * 1.8f)); Ball(glowOrb._core, "Halo", 1.7f, SpriteMaterial.CreatePlain(colour * 0.35f, PlainBlend.Additive)); Sparkle.AttachBox(glowOrb._core, Vector3.zero, Vector3.one * 2.6f, radius * 4f); return glowOrb; } private static Transform Ball(Transform parent, string name, float scale, Material mat) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0013: 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_001f: 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_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject(name); val.transform.SetParent(parent, false); val.transform.localScale = Vector3.one * scale * 2f; val.AddComponent<MeshFilter>().sharedMesh = Sphere(); MeshRenderer obj = val.AddComponent<MeshRenderer>(); ((Renderer)obj).sharedMaterial = mat; ((Renderer)obj).shadowCastingMode = (ShadowCastingMode)0; ((Renderer)obj).receiveShadows = false; return val.transform; } private static Mesh Sphere() { if ((Object)(object)_sphere != (Object)null) { return _sphere; } GameObject obj = GameObject.CreatePrimitive((PrimitiveType)0); _sphere = obj.GetComponent<MeshFilter>().sharedMesh; Object.DestroyImmediate((Object)(object)obj); return _sphere; } private void Update() { //IL_003e: 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) if (!((Object)(object)_core == (Object)null)) { float num = Time.time + _phase; _core.localPosition = new Vector3(0f, Mathf.Sin(num * 1.7f) * 0.05f, 0f); _core.localRotation = Quaternion.Euler(0f, num * 40f, 0f); } } } public sealed class ShopStand : MonoBehaviour { public Transform Display { get; private set; } public Vector3 Size { get; private set; } public GameObject[] Outline { get; private set; } public static ShopStand Create(string name, Vector3 groundPos, Quaternion rot, GameObject display, float displayHeight = 1.05f) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Expected O, but got Unknown //IL_000d: 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) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_0105: 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_0187: Unknown result type (might be due to invalid IL or missing references) //IL_0161: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject(name); val.transform.SetPositionAndRotation(groundPos, rot); ShopStand shopStand = val.AddComponent<ShopStand>(); GameObject val2 = GameObject.CreatePrimitive((PrimitiveType)3); ((Object)val2).name = "Post"; val2.transform.SetParent(val.transform, false); val2.transform.localScale = new Vector3(0.45f, 0.9f, 0.45f); val2.transform.localPosition = new Vector3(0f, 0.45f, 0f); ((Renderer)val2.GetComponent<MeshRenderer>()).sharedMaterial = SpriteMaterial.CreatePlain(new Color(0.42f, 0.3f, 0.2f)); GameObject val3 = GameObject.CreatePrimitive((PrimitiveType)3); ((Object)val3).name = "Top"; Object.DestroyImmediate((Object)(object)val3.GetComponent<Collider>()); val3.transform.SetParent(val.transform, false); val3.transform.localScale = new Vector3(0.6f, 0.06f, 0.6f); val3.transform.localPosition = new Vector3(0f, 0.93f, 0f); ((Renderer)val3.GetComponent<MeshRenderer>()).sharedMaterial = SpriteMaterial.CreatePlain(new Color(0.3f, 0.21f, 0.14f)); if ((Object)(object)display != (Object)null) { display.transform.SetParent(val.transform, false); display.transform.localPosition = new Vector3(0f, displayHeight, 0f); shopStand.Display = display.transform; } shopStand.Size = new Vector3(0.7f, 1.5f, 0.7f); shopStand.Outline = (GameObject[])(object)new GameObject[2] { val2, val3 }; return shopStand; } private void Update() { if ((Object)(object)Display != (Object)null) { Display.Rotate(0f, 35f * Time.deltaTime, 0f, (Space)0); } } } public sealed class Hud : MonoBehaviour { private static readonly SortedDictionary<string, string> Lines = new SortedDictionary<string, string>(); private static readonly SortedDictionary<string, string> Small = new SortedDictionary<string, string>(); private GUIStyle _style; private GUIStyle _smallStyle; public static void Show(string key, string richText) { Small.Remove(key); Lines[key] = richText; } public static void ShowSmall(string key, string richText) { Lines.Remove(key); Small[key] = richText; } public static void Hide(string key) { Lines.Remove(key); Small.Remove(key); } private void OnGUI() { //IL_0080: 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_002f: 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) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Expected O, but got Unknown //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Expected O, but got Unknown //IL_01ba: Unknown result type (might be due to invalid IL or missing references) //IL_01d3: Unknown result type (might be due to invalid IL or missing references) //IL_0205: Unknown result type (might be due to invalid IL or missing references) //IL_0220: Unknown result type (might be due to invalid IL or missing references) if (Lines.Count > 0) { if (_style == null) { _style = new GUIStyle(GUI.skin.box) { alignment = (TextAnchor)4, fontSize = 20, richText = true, wordWrap = true }; } float num = 760f; float num2 = 34 * Lines.Count + 12; GUI.Box(new Rect(((float)Screen.width - num) / 2f, (float)Screen.height * 0.78f - num2, num, num2), string.Join("\n", Lines.Values), _style); } if (Small.Count > 0) { int num3 = Mathf.Max(12, Mathf.RoundToInt((float)Screen.height / 57f)); if (_smallStyle == null || _smallStyle.fontSize != num3) { _smallStyle = new GUIStyle(GUI.skin.label) { alignment = (TextAnchor)7, fontSize = num3, richText = true }; } string text = string.Join("\n", Small.Values); float num4 = Screen.height; float num5 = (float)Screen.width / 2f + 0.29f * num4; float num6 = (float)Screen.width - 0.13f * num4; if (num6 - num5 < 8f * (float)num3) { num5 = (float)Screen.width - 16f * (float)num3; num6 = (float)Screen.width - 8f; } float num7 = (float)num3 * 1.5f * (float)Small.Count + 4f; Rect val = default(Rect); ((Rect)(ref val))..ctor(num5, num4 - 0.022f * num4 - num7, num6 - num5, num7); Color color = GUI.color; GUI.color = new Color(0f, 0f, 0f, 0.8f); GUI.Label(new Rect(((Rect)(ref val)).x + 1f, ((Rect)(ref val)).y + 1f, ((Rect)(ref val)).width, ((Rect)(ref val)).height), StripColour(text), _smallStyle); GUI.color = color; GUI.Label(val, text, _smallStyle); } } private static string StripColour(string s) { return Regex.Replace(s, "</?color[^>]*>", ""); } } public static class Identity { private static ulong? _steamId; private static string _name; public static string SavesDir { get { string text = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "WFxHTF", "saves"); Directory.CreateDirectory(text); return text; } } public static ulong SteamId { get { if (_steamId.HasValue && _steamId.Value != 0L) { return _steamId.Value; } try { object obj = Type("Steamworks.SteamUser")?.GetMethod("GetSteamID")?.Invoke(null, null); _steamId = ((obj?.GetType().GetField("m_SteamID")?.GetValue(obj) is ulong num) ? num : 0); } catch { _steamId = 0uL; } return _steamId.Value; } } public static string Name { get { if (!string.IsNullOrEmpty(_name)) { return _name; } try { _name = Type("Steamworks.SteamFriends")?.GetMethod("GetPersonaName")?.Invoke(null, null) as string; } catch { _name = null; } return _name ?? "player"; } } private static Type Type(string fullName) { return (from a in AppDomain.CurrentDomain.GetAssemblies() select a.GetType(fullName)).FirstOrDefault((Type t) => t != null); } } public sealed class JournalService { public Journal Journal { get; private set; } public string Path { get; private set; } public string Status { get; private set; } = "not loaded"; public bool CanSave { get; private set; } = true; public event Action Changed; public void Load() { //IL_004d: Unknown result type (might be due to invalid IL or missing references) ulong steamId = Identity.SteamId; Path = System.IO.Path.Combine(Identity.SavesDir, WorldSidecar.JournalFileName(steamId)); LoadResult<Journal> val = SidecarStore.Load<Journal>(Path, Sidecars.PlayerJournal(steamId)); Journal = val.Value; CanSave = val.CanSave; Status = $"{val.Status} from {val.Source}" + ((val.Problem != null) ? (" (" + val.Problem + ")") : "") + $", {Journal.CaughtCount} species"; if (val.Problem != null) { Plugin.Log.LogWarning((object)("journal: " + Status)); } this.Changed?.Invoke(); } public bool RecordLocal(string speciesId, float weightKg, Quality q, string island, string bait, bool shiny = false) { //IL_002d: 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_0039: 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_0041: 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_0047: 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_005d: 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_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Expected O, but got Unknown if (Journal == null || Journal.OwnerId != Identity.SteamId.ToString()) { Load(); } CatchEvent val = new CatchEvent { SpeciesId = speciesId, WeightKg = weightKg, Quality = q, HookerId = Identity.SteamId, HookerName = Identity.Name, KillerId = Identity.SteamId, Island = island, Bait = bait, Shiny = shiny }; bool result = Journal.Record(val, DateTime.UtcNow); Save(); Action action = this.Changed; if (action != null) { action(); return result; } return result; } public void Save() { if (!CanSave || Journal == null) { return; } try { SidecarStore.Save<Journal>(Path, Journal, Sidecars.PlayerJournal(Identity.SteamId), (TimeSpan?)null); } catch (Exception ex) { Plugin.Log.LogError((object)("journal save failed: " + ex.Message)); } } } public sealed class JournalUI : MonoBehaviour { public KeyCode ToggleKey = (KeyCode)106; private SpriteCache _cache; private JournalService _journal; private Font _font; private GameObject _root; private GameObject _panel; private GameObject _opener; private float _hintUntil; private bool _managedCursor; private GUIStyle _hintStyle; private Transform _grid; private Text _header; private Text _detail; private RawImage _detailIcon; private readonly List<(WfPool? Pool, Image Fill, Text Label)> _bars = new List<(WfPool?, Image, Text)>(); private WfPool? _filter; private FishDef _selected; private CursorLockMode _prevLock; private bool _prevVisible; private static readonly Color Panel = new Color(0.07f, 0.09f, 0.12f, 1f); private static readonly Color Cell = new Color(0.16f, 0.19f, 0.24f, 1f); private static readonly Color Accent = new Color(0.55f, 0.78f, 1f, 1f); private static readonly Color Gold = new Color(0.9f, 0.71f, 0.13f, 1f); public bool Open { get { if ((Object)(object)_panel != (Object)null) { return _panel.activeSelf; } return false; } } private static bool Gated => GameHooks.NativeJournalOpen != null; public void Init(SpriteCache cache, JournalService journal) { ModUi.AddBlocker(() => Open && _managedCursor); _cache = cache; _journal = journal; _journal.Changed += delegate { if (Open) { Refresh(); } }; } private static bool Allowed() { Func<bool> nativeJournalOpen = GameHooks.NativeJournalOpen; if (nativeJournalOpen == null) { return true; } try { return nativeJournalOpen(); } catch (Exception ex) { GameHooks.NativeJournalOpen = null; Plugin.Log.LogWarning((object)("journal: native journal check failed, the panel now opens anywhere: " + ex.Message)); return true; } } private void Update() { //IL_0019: Unknown result type (might be due to invalid IL or missing references) bool flag = Allowed(); if (Open && !flag) { SetOpen(open: false); } if (Keys.Down(ToggleKey)) { DebugConsole console = Plugin.Instance.Console; if (console == null || !console.Open) { if (Open) { SetOpen(open: false); } else if (flag) { SetOpen(open: true); } else { _hintUntil = Time.unscaledTime + 3f; } } } if (Open && Keys.Down((KeyCode)27)) { SetOpen(open: false); } if (Open && _managedCursor) { Cursor.lockState = (CursorLockMode)0; Cursor.visible = true; } bool flag2 = Gated && flag && !Open && _cache != null && _cache.Loaded; if (flag2 && (Object)(object)_root == (Object)null) { TryBuild(); } if ((Object)(object)_opener != (Object)null && _opener.activeSelf != flag2) { _opener.SetActive(flag2); } } private void OnGUI() { //IL_006e: 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_0032: 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_0046: Expected O, but got Unknown if (!(Time.unscaledTime >= _hintUntil) && !string.IsNullOrEmpty(GameHooks.NativeJournalHint)) { if (_hintStyle == null) { _hintStyle = new GUIStyle(GUI.skin.box) { alignment = (TextAnchor)4, fontSize = 22 }; } GUI.Box(new Rect((float)Screen.width / 2f - 320f, (float)Screen.height * 0.8f, 640f, 44f), GameHooks.NativeJournalHint, _hintStyle); } } public void SetOpen(bool open) { //IL_00ee: 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) if (open == Open) { return; } if (open) { if (!_cache.Loaded) { Plugin.Instance.Console?.Print("journal: " + _cache.Error); } else if (!((Object)(object)_root == (Object)null) || TryBuild()) { if (_journal.Journal == null || _journal.Journal.OwnerId != Identity.SteamId.ToString()) { _journal.Load(); } _managedCursor = !Gated; if (_managedCursor) { _prevLock = Cursor.lockState; _prevVisible = Cursor.visible; } _panel.SetActive(true); Refresh(); } } else { _panel.SetActive(false); if (_managedCursor) { GameHooks.ReleaseCursor(_prevLock, _prevVisible); } _managedCursor = false; } } private bool TryBuild() { try { Build(); return true; } catch (Exception ex) { Plugin.Log.LogError((object)("journal: building the panel failed: " + ex)); Plugin.Instance.Console?.Print("journal: panel failed to build (see BepInEx log)"); if ((Object)(object)_root != (Object)null) { Object.Destroy((Object)(object)_root); } _root = (_panel = (_opener = null)); _bars.Clear(); return false; } } private void Build() { //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Expected O, but got Unknown //IL_00ca: 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_018d: Unknown result type (might be due to invalid IL or missing references) //IL_0197: Expected O, but got Unknown //IL_028f: Unknown result type (might be due to invalid IL or missing references) //IL_0299: Expected O, but got Unknown //IL_02c0: Unknown result type (might be due to invalid IL or missing references) //IL_02ef: Unknown result type (might be due to invalid IL or missing references) //IL_039f: Unknown result type (might be due to invalid IL or missing references) //IL_03fd: Unknown result type (might be due to invalid IL or missing references) //IL_0449: Unknown result type (might be due to invalid IL or missing references) //IL_046b: Unknown result type (might be due to invalid IL or missing references) //IL_0481: Unknown result type (might be due to invalid IL or missing references) //IL_0497: Unknown result type (might be due to invalid IL or missing references) //IL_04a5: Unknown result type (might be due to invalid IL or missing references) //IL_04aa: Unknown result type (might be due to invalid IL or missing references) //IL_04ab: Unknown result type (might be due to invalid IL or missing references) //IL_04b2: Unknown result type (might be due to invalid IL or missing references) //IL_04d0: Unknown result type (might be due to invalid IL or missing references) //IL_04e5: Unknown result type (might be due to invalid IL or missing references) //IL_04f7: Unknown result type (might be due to invalid IL or missing references) //IL_0501: Expected O, but got Unknown //IL_0532: Unknown result type (might be due to invalid IL or missing references) //IL_057f: Unknown result type (might be due to invalid IL or missing references) //IL_0649: Unknown result type (might be due to invalid IL or missing references) //IL_0653: Expected O, but got Unknown //IL_0677: Unknown result type (might be due to invalid IL or missing references) _font = Resources.GetBuiltinResource<Font>("LegacyRuntime.ttf") ?? Font.CreateDynamicFontFromOSFont("Arial", 16); if ((Object)(object)EventSystem.current == (Object)null) { Plugin.Log.LogWarning((object)"journal: no EventSystem in scene; buttons won't respond"); } _root = new GameObject("WFxHTF Journal", new Type[4] { typeof(RectTransform), typeof(Canvas), typeof(CanvasScaler), typeof(GraphicRaycaster) }); Object.DontDestroyOnLoad((Object)(object)_root); Canvas component = _root.GetComponent<Canvas>(); component.renderMode = (RenderMode)0; component.sortingOrder = 30000; CanvasScaler component2 = _root.GetComponent<CanvasScaler>(); component2.uiScaleMode = (ScaleMode)1; component2.referenceResolution = new Vector2(1920f, 1080f); component2.matchWidthOrHeight = 0.5f; RectTransform val = Box(_root.transform, "Panel", Panel); Stretch(val, 0.08f, 0.08f, 0.92f, 0.92f); _panel = ((Component)val).gameObject; _header = Label((Transform)(object)val, "Header", "", 34, (TextAnchor)3, (FontStyle)1); Place(((Graphic)_header).rectTransform, 0.02f, 0.92f, 0.6f, 0.99f); Place(Button((Transform)(object)val, "Close", "Close (" + ((object)Unsafe.As<KeyCode, KeyCode>(ref ToggleKey)/*cast due to .constrained prefix*/).ToString() + ")", (UnityAction)delegate { SetOpen(open: false); }), 0.86f, 0.93f, 0.98f, 0.985f); (string, WfPool?)[] array = new(string, WfPool?)[5] { ("All", null), ("Freshwater", (WfPool)0), ("Saltwater", (WfPool)1), ("Rain", (WfPool)2), ("Misc", (WfPool)3) }; for (int num = 0; num < array.Length; num++) { (string, WfPool?) tuple = array[num]; string item = tuple.Item1; WfPool? pool = tuple.Item2; float num2 = 0.02f + (float)num * 0.13f; Place(Button((Transform)(object)val, "Tab " + item, item, (UnityAction)delegate { _filter = pool; Refresh(); }), num2, 0.85f, num2 + 0.12f, 0.905f); RectTransform val2 = Box((Transform)(object)val, "Bar " + item, Cell); Place(val2, num2, 0.825f, num2 + 0.12f, 0.84f); RectTransform val3 = Box((Transform)(object)val2, "Fill", Accent); Place(val3, 0f, 0f, 0f, 1f); Text val4 = Label((Transform)(object)val, "Count " + item, "", 18, (TextAnchor)4, (FontStyle)0); Place(((Graphic)val4).rectTransform, num2, 0.79f, num2 + 0.12f, 0.825f); _bars.Add((pool, ((Component)val3).GetComponent<Image>(), val4)); } RectTransform val5 = Box((Transform)(object)val, "Scroll", new Color(0f, 0f, 0f, 0.25f)); Place(val5, 0.02f, 0.03f, 0.66f, 0.78f); ScrollRect val6 = ((Component)val5).gameObject.AddComponent<ScrollRect>(); val6.horizontal = false; val6.scrollSensitivity = 40f; RectTransform val7 = Box((Transform)(object)val5, "Viewport", new Color(0f, 0f, 0f, 0.01f)); Place(val7, 0f, 0f, 1f, 1f); ((Component)val7).gameObject.AddComponent<RectMask2D>(); RectTransform component3 = new GameObject("Grid", new Type[1] { typeof(RectTransform) }).GetComponent<RectTransform>(); ((Transform)component3).SetParent((Transform)(object)val7, false); component3.anchorMin = new Vector2(0f, 1f); component3.anchorMax = new Vector2(1f, 1f); component3.pivot = new Vector2(0.5f, 1f); Vector2 offsetMin = (component3.offsetMax = Vector2.zero); component3.offsetMin = offsetMin; GridLayoutGroup obj = ((Component)component3).gameObject.AddComponent<GridLayoutGroup>(); obj.cellSize = new Vector2(150f, 150f); obj.spacing = new Vector2(10f, 10f); ((LayoutGroup)obj).padding = new RectOffset(10, 10, 10, 10); ((Component)component3).gameObject.AddComponent<ContentSizeFitter>().verticalFit = (FitMode)2; val6.viewport = val7; val6.content = component3; _grid = (Transform)(object)component3; RectTransform val8 = Box((Transform)(object)val, "Detail", Cell); Place(val8, 0.68f, 0.03f, 0.98f, 0.78f); _detailIcon = new GameObject("Icon", new Type[2] { typeof(RectTransform), typeof(RawImage) }).GetComponent<RawImage>(); ((Transform)((Graphic)_detailIcon).rectTransform).SetParent((Transform)(object)val8, false); Place(((Graphic)_detailIcon).rectTransform, 0.1f, 0.55f, 0.9f, 0.97f); _detail = Label((Transform)(object)val8, "Text", "Select a fish.", 22, (TextAnchor)0, (FontStyle)0); Place(((Graphic)_detail).rectTransform, 0.06f, 0.03f, 0.96f, 0.53f); _panel.SetActive(false); RectTransform val9 = Button(_root.transform, "Opener", "WEBFISHING Journal (" + ((object)Unsafe.As<KeyCode, KeyCode>(ref ToggleKey)/*cast due to .constrained prefix*/).ToString() + ")", (UnityAction)delegate { SetOpen(open: true); }); Place(val9, 0.79f, 0.04f, 0.97f, 0.1f); ((Graphic)((Component)val9).GetComponentInChildren<Text>()).color = Gold; _opener = ((Component)val9).gameObject; _opener.SetActive(false); } private void Refresh() { //IL_0108: 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_0126: Unknown result type (might be due to invalid IL or missing references) //IL_0186: 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_01ae: Unknown result type (might be due to invalid IL or missing references) //IL_01a7: Unknown result type (might be due to invalid IL or missing references) //IL_027a: Unknown result type (might be due to invalid IL or missing references) //IL_02a8: Unknown result type (might be due to invalid IL or missing references) //IL_02b2: Expected O, but got Unknown //IL_02d7: Unknown result type (might be due to invalid IL or missing references) //IL_032e: Unknown result type (might be due to invalid IL or missing references) //IL_0327: Unknown result type (might be due to invalid IL or missing references) //IL_0396: Unknown result type (might be due to invalid IL or missing references) SortedDictionary<string, JournalEntry> entries = _journal.Journal?.Entries; int num = _cache.Defs.Count(Caught); _header.text = $"WEBFISHING Journal {num} / {_cache.Defs.Count}"; foreach (var bar in _bars) { List<FishDef> list = _cache.Defs.Where((FishDef d) => !bar.Pool.HasValue || (WfPool?)d.Wf.Pool == bar.Pool).ToList(); int num2 = list.Count(Caught); float num3 = ((list.Count == 0) ? 0f : ((float)num2 / (float)list.Count)); ((Graphic)bar.Fill).rectTransform.anchorMax = new Vector2(num3, 1f); ((Graphic)bar.Fill).color = ((num3 >= 1f) ? Gold : Accent); bar.Label.text = $"{num2} / {list.Count}"; ((Graphic)bar.Label).color = ((_filter == bar.Pool) ? Accent : Color.white); } for (int num4 = _grid.childCount - 1; num4 >= 0; num4--) { Object.Destroy((Object)(object)((Component)_grid.GetChild(num4)).gameObject); } foreach (FishDef item in _cache.Defs.Where((FishDef d) => !_filter.HasValue || (WfPool?)d.Wf.Pool == _filter)) { bool flag = Caught(item); RectTransform val = Box(_grid, item.Id, Cell); Button obj = ((Component)val).gameObject.AddComponent<Button>(); FishDef def = item; ((UnityEvent)obj.onClick).AddListener((UnityAction)delegate { _selected = def; ShowDetail(); }); RawImage component = new GameObject("Icon", new Type[2] { typeof(RectTransform), typeof(RawImage) }).GetComponent<RawImage>(); ((Transform)((Graphic)component).rectTransform).SetParent((Transform)(object)val, false); Place(((Graphic)component).rectTransform, 0.05f, 0.2f, 0.95f, 0.98f); component.texture = (Texture)(object)_cache.Frame(item, 0); ((Graphic)component).color = (flag ? Color.white : Color.black); ((Graphic)component).raycastTarget = false; Text val2 = Label((Transform)(object)val, "Name", flag ? item.Name : "???", 16, (TextAnchor)4, (FontStyle)0); Place(((Graphic)val2).rectTransform, 0.02f, 0f, 0.98f, 0.2f); if (item.Htf.UltraRare && flag) { ((Graphic)val2).color = Gold; } } ShowDetail(); bool Caught(FishDef d) { if (entries != null && entries.TryGetValue(d.Id, out var value)) { return value.Caught; } return false; } } private void ShowDetail() { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_009c: 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_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Expected I4, but got Unknown //IL_0222: Unknown result type (might be due to invalid IL or missing references) if (_selected == null) { _detailIcon.texture = null; ((Graphic)_detailIcon).color = Color.clear; _detail.text = "Select a fish."; return; } FishDef selected = _selected; JournalEntry value = null; bool flag = _journal.Journal != null && _journal.Journal.Entries.TryGetValue(selected.Id, out value) && value.Caught; _detailIcon.texture = (Texture)(object)_cache.Frame(selected, 0); ((Graphic)_detailIcon).color = (flag ? Color.white : Color.black); ZoneRule zone = selected.Htf.Zone; string text = (int)zone switch { 0 => "Freshwater islands", 1 => "The last two islands (saltwater)", 2 => "Anywhere, while it rains", _ => "Anywhere (very rare)", }; string text2 = (new string[4] { "Beginner", "Standard", "Professional", "Scientific" })[Mathf.Clamp(selected.Htf.MinBaitTier, 0, 3)]; string text3 = (flag ? ("<b>" + selected.Name + "</b>\n<i>" + selected.Wf.Description + "</i>\n\n") : "<b>???</b>\n\n"); text3 = text3 + "Where: " + text + "\nLure: " + text2 + " or better\n"; if (selected.Htf.UltraRare) { text3 += "<color=#e6b422>Ultra-rare: guarantees a Legendary skin at the slot machine</color>\n"; } if (flag) { text3 = text3 + string.Format("\nCaught: {0}{1}\nBest weight: {2:0.##} kg\nBest quality: {3}\n", value.Count, (value.ShinyCount > 0) ? $" ({value.ShinyCount} shiny!)" : "", value.BestWeightKg, value.BestQuality) + "First caught: " + LocalDate(value.FirstCatchUtc) + " by " + CatcherName(value) + "\n" + (string.IsNullOrEmpty(value.FirstIsland) ? "" : ("Island: " + value.FirstIsland + "\n")) + (string.IsNullOrEmpty(value.FirstBait) ? "" : ("Bait: " + value.FirstBait + "\n")) + "\n\"" + selected.Wf.Blurb + "\""; } _detail.text = text3; } private static string LocalDate(string utc) { if (!DateTime.TryParse(utc, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal, out var result)) { return utc; } return result.ToLocalTime().ToString("yyyy-MM-dd"); } private static string CatcherName(JournalEntry e) { return DisplayText.VisibleOr(e.FirstCatcherName, (e.FirstCatcherId == Identity.SteamId) ? "you" : $"player {e.FirstCatcherId % 10000:0000}"); } private RectTransform Box(Transform parent, string name, Color color) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0033: 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) GameObject val = new GameObject(name, new Type[2] { typeof(RectTransform), typeof(Image) }); val.transform.SetParent(parent, false); ((Graphic)val.GetComponent<Image>()).color = color; return val.GetComponent<RectTransform>(); } private Text Label(Transform parent, string name, string text, int size, TextAnchor anchor, FontStyle style = (FontStyle)0) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0055: 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) GameObject val = new GameObject(name, new Type[2] { typeof(RectTransform), typeof(Text) }); val.transform.SetParent(parent, false); Text component = val.GetComponent<Text>(); component.font = _font; component.fontSize = size; component.alignment = anchor; component.fontStyle = style; ((Graphic)component).color = Color.white; component.supportRichText = true; ((Graphic)component).raycastTarget = false; component.horizontalOverflow = (HorizontalWrapMode)0; component.verticalOverflow = (VerticalWrapMode)0; component.text = text; return component; } private RectTransform Button(Transform parent, string name, string text, UnityAction onClick) { //IL_0003: Unknown result type (might be due to invalid IL or missing references) RectTransform val = Box(parent, name, Cell); ((UnityEvent)((Component)val).gameObject.AddComponent<Button>().onClick).AddListener(onClick); Place(((Graphic)Label((Transform)(object)val, "Text", text, 22, (TextAnchor)4, (FontStyle)0)).rectTransform, 0f, 0f, 1f, 1f); return val; } private static void Stretch(RectTransform rt, float x0, float y0, float x1, float y1) { Place(rt, x0, y0, x1, y1); } private static void Place(RectTransform rt, float x0, float y0, float x1, float y1) { //IL_0003: 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_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0022: 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) rt.anchorMin = new Vector2(x0, y0); rt.anchorMax = new Vector2(x1, y1); Vector2 offsetMin = (rt.offsetMax = Vector2.zero); rt.offsetMin = offsetMin; } } public sealed class Lineup : MonoBehaviour { public sealed class Entry { public FishDef Def; public Quality Quality; public bool Shiny; public float Length; public string Label; public GameObject Body; } private const float SpanDegrees = 80f; private const float SlotPadding = 1.3f; private const float MinWidth = 0.5f; private const float LabelHeight = 0.5f; private const float LabelWiden = 1.7f; private static Lineup _current; public static bool Showing => (Object)(object)_current != (Object)null; public static void Clear() { if ((Object)(object)_current != (Object)null) { Object.Destroy((Object)(object)((Component)_current).gameObject); } _current = null; } public static float Show(SpriteCache cache, IList<Entry> entries, Vector3 feet, Vector3 forward, Func<Vector3, float?> floorAt) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_0101: 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_0108: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_0116: 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_013c: Unknown result type (might be due to invalid IL or missing references) //IL_0169: Unknown result type (might be due to invalid IL or missing references) //IL_0185: Unknown result type (might be due to invalid IL or missing references) //IL_018e: Unknown result type (might be due to invalid IL or missing references) //IL_0195: Unknown result type (might be due to invalid IL or missing references) //IL_01a1: Unknown result type (might be due to invalid IL or missing references) //IL_01a3: Unknown result type (might be due to invalid IL or missing references) //IL_0223: Unknown result type (might be due to invalid IL or missing references) //IL_0228: Unknown result type (might be due to invalid IL or missing references) //IL_022a: Unknown result type (might be due to invalid IL or missing references) //IL_022f: Unknown result type (might be due to invalid IL or missing references) //IL_0234: Unknown result type (might be due to invalid IL or missing references) //IL_032e: Unknown result type (might be due to invalid IL or missing references) //IL_0333: Unknown result type (might be due to invalid IL or missing references) //IL_033c: Unknown result type (might be due to invalid IL or missing references) //IL_0343: Unknown result type (might be due to invalid IL or missing references) //IL_025b: Unknown result type (might be due to invalid IL or missing references) //IL_0260: Unknown result type (might be due to invalid IL or missing references) //IL_0269: Unknown result type (might be due to invalid IL or missing references) //IL_026e: Unknown result type (might be due to invalid IL or missing references) //IL_0272: Unknown result type (might be due to invalid IL or missing references) //IL_0281: Unknown result type (might be due to invalid IL or missing references) //IL_0286: Unknown result type (might be due to invalid IL or missing references) //IL_028a: Unknown result type (might be due to invalid IL or missing references) //IL_0295: Unknown result type (might be due to invalid IL or missing references) //IL_029e: Unknown result type (might be due to invalid IL or missing references) //IL_02a3: Unknown result type (might be due to invalid IL or missing references) //IL_02a7: Unknown result type (might be due to invalid IL or missing references) //IL_02b2: Unknown result type (might be due to invalid IL or missing references) //IL_02b7: Unknown result type (might be due to invalid IL or missing references) //IL_02d1: Unknown result type (might be due to invalid IL or missing references) //IL_02d6: Unknown result type (might be due to invalid IL or missing references) //IL_02da: Unknown result type (might be due to invalid IL or missing references) //IL_0395: Unknown result type (might be due to invalid IL or missing references) //IL_039a: Unknown result type (might be due to invalid IL or missing references) //IL_03a3: Unknown result type (might be due to invalid IL or missing references) //IL_03a8: Unknown result type (might be due to invalid IL or missing references) //IL_03ac: Unknown result type (might be due to invalid IL or missing references) //IL_03b7: Unknown result type (might be due to invalid IL or missing references) //IL_03bc: Unknown result type (might be due to invalid IL or missing references) Clear(); GameObject val = new GameObject("WFxHTF Lineup"); _current = val.AddComponent<Lineup>(); forward.y = 0f; if (((Vector3)(ref forward)).sqrMagnitude < 0.0001f) { forward = Vector3.forward; } ((Vector3)(ref forward)).Normalize(); List<float> list = entries.Select((Entry e) => Mathf.Max(new float[3] { e.Length, 0.5f, BodyWidth(e.Body) }) * 1.3f).ToList(); float num = list.Sum(); float num2 = Mathf.Max(3.5f, num / 1.3962634f); float num3 = Mathf.Max(1f, num2 / 6f); float num4 = 0.5f * num3; Font builtinResource = Resources.GetBuiltinResource<Font>("LegacyRuntime.ttf"); float num5 = (0f - num) / 2f; for (int num6 = 0; num6 < entries.Count; num6++) { Entry entry = entries[num6]; float num7 = num5 + list[num6] / 2f; num5 += list[num6]; Vector3 val2 = Quaternion.AngleAxis(num7 / num2 * 57.29578f, Vector3.up) * forward; Vector3 val3 = feet + val2 * num2; float num8 = floorAt?.Invoke(val3) ?? feet.y; Transform transform = new GameObject((entry.Def != null) ? entry.Def.Name : "fish").transform; transform.SetParent(val.transform, false); transform.position = new Vector3(val3.x, num8, val3.z); transform.rotation = Quaternion.LookRotation(val2); float num9 = num8 + 2f * num4 + 0.2f; AddLabel(transform, builtinResource, entry.Label, list[num6] * 1.7f, num3, num8 + 0.05f + (float)(num6 % 2) * (num4 + 0.05f)); Bounds value; if ((Object)(object)entry.Body != (Object)null) { Transform transform2 = entry.Body.transform; transform2.SetParent(transform, true); transform2.rotation = Quaternion.LookRotation(Vector3.Cross(Vector3.up, val2), Vector3.up); Bounds? val4 = Bounds(entry.Body); if (val4.HasValue) { Vector3 position = transform2.position; float x = val3.x; value = val4.Value; float num10 = x - ((Bounds)(ref value)).center.x; float num11 = num9; value = val4.Value; float num12 = num11 - ((Bounds)(ref value)).min.y; float z = val3.z; value = val4.Value; transform2.position = position + new Vector3(num10, num12, z - ((Bounds)(ref value)).center.z); value = Bounds(entry.Body).Value; num9 = ((Bounds)(ref value)).max.y + 0.2f; } Renderer[] componentsInChildren = entry.Body.GetComponentsInChildren<Renderer>(); for (int num13 = 0; num13 < componentsInChildren.Length; num13++) { componentsInChildren[num13].shadowCastingMode = (ShadowCastingMode)0; } } if (entry.Def == null) { continue; } SpriteFish spriteFish = SpriteFish.Create(cache, entry.Def, entry.Quality, new Vector3(val3.x, num9, val3.z), entry.Length, entry.Shiny); if (!((Object)(object)spriteFish == (Object)null)) { ((Component)spriteFish).transform.SetParent(transform, true); Bounds? val5 = Bounds(((Component)spriteFish).gameObject); if (val5.HasValue) { Transform transform3 = ((Component)spriteFish).transform; Vector3 position2 = transform3.position; Vector3 up = Vector3.up; float num14 = num9; value = val5.Value; transform3.position = position2 + up * (num14 - ((Bounds)(ref value)).min.y); } } } return num2; } private static float BodyWidth(GameObject body) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0034: 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_0048: Unknown result type (might be due to invalid IL or missing references) Bounds? val = (((Object)(object)body != (Object)null) ? Bounds(body) : ((Bounds?)null)); if (!val.HasValue) { return 0f; } Bounds value = val.Value; float x = ((Bounds)(ref value)).size.x; value = val.Value; return Mathf.Max(x, ((Bounds)(ref value)).size.z); } private static Bounds? Bounds(GameObject go) { //IL_0031: 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_0047: 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) Bounds? result = null; Renderer[] componentsInChildren = go.GetComponentsInChildren<Renderer>(); foreach (Renderer val in componentsInChildren) { if (!result.HasValue) { result = val.bounds; continue; } Bounds value = result.Value; ((Bounds)(ref value)).Encapsulate(val.bounds); result = value; } return result; } private static void AddLabel(Transform slot, Font font, string text, float widthMeters, float scale, float bottomY) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //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: Expected O, but got Unknown //IL_0062: 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_007a: 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_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: 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_0139: Unknown result type (might be due to invalid IL or missing references) //IL_016b: Unknown result type (might be due to invalid IL or missing references) //IL_0185: Unknown result type (might be due to invalid IL or missing references) //IL_01c4: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("Label", new Type[2] { typeof(RectTransform), typeof(Canvas) }); RectTransform val2 = (RectTransform)val.transform; ((Transform)val2).SetParent(slot, false); val.GetComponent<Canvas>().renderMode = (RenderMode)2; float num = 0.5f * scale; val2.sizeDelta = new Vector2(widthMeters / scale * 400f, 200f); ((Transform)val2).localScale = Vector3.one * (scale / 400f); ((Transform)val2).position = new Vector3(slot.position.x, bottomY + num / 2f, slot.position.z); Image component = new GameObject("Bg", new Type[2] { typeof(RectTransform), typeof(Image) }).GetComponent<Image>(); ((Transform)((Graphic)component).rectTransform).SetParent((Transform)(object)val2, false); Stretch(((Graphic)component).rectTransform); ((Graphic)component).color = new Color(0.05f, 0.07f, 0.1f, 0.75f); Text component2 = new GameObject("Text", new Type[2] { typeof(RectTransform), typeof(Text) }).GetComponent<Text>(); ((Transform)((Graphic)component2).rectTransform).SetParent((Transform)(object)val2, false); Stretch(((Graphic)component2).rectTransform); ((Graphic)component2).rectTransform.offsetMin = new Vector2(8f, 4f); ((Graphic)component2).rectTransform.offsetMax = new Vector2(-8f, -4f); component2.font = font; component2.fontSize = 46; component2.resizeTextForBestFit = true; component2.resizeTextMinSize = 14; component2.resizeTextMaxSize = 46; component2.alignment = (TextAnchor)4; component2.supportRichText = true; ((Graphic)component2).color = Color.white; component2.text = text; } private static void Stretch(RectTransform r) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_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_001e: 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) r.anchorMin = Vector2.zero; r.anchorMax = Vector2.one; Vector2 offsetMin = (r.offsetMax = Vector2.zero); r.offsetMin = offsetMin; } } public static class ModUi { private static readonly List<Func<bool>> Blockers = new List<Func<bool>>(); public static bool BlocksGame { get { foreach (Func<bool> blocker in Blockers) { try { if (blocker()) { return true; } } catch { } } return false; } } public static void AddBlocker(Func<bool> isOpen) { Blockers.Add(isOpen); } } public sealed class PlacementRules { public LayerMask Ground = LayerMask.op_Implicit(-1); public LayerMask Blockers = LayerMask.op_Implicit(-1); public float MaxSlopeDeg = 25f; public float MaxStep = 0.35f; public float MinDistance = 1.5f; public float MaxDistance = 8f; public Func<Vector3, float> WaterHeightAt; public Func<Collider, bool> Ignore; } public sealed class PlacementGhost : MonoBehaviour { public KeyCode RotateKey = (KeyCode)114; private PlacementRules _rules; private Action<Vector3, float> _onConfirm; private Action _onCancel; private TankView _ghost; private Vector3 _size; private float _yaw; private bool _valid; private Vector3 _pos; private string _why = ""; private int _startFrame; public static PlacementGhost Current { get; private set; } public static PlacementGhost Begin(SpriteCache cache, PlacementRules rules, float startYaw, Action<Vector3, float> onConfirm, Action onCancel, float sizeScale = 1f) { //IL_0025: 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) //IL_003d: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)Current != (Object)null) { Current.Cancel(); } TankView tankView = TankView.CreateGhost(cache, sizeScale); PlacementGhost placementGhost = new GameObject("WFxHTF Placement").AddComponent<PlacementGhost>(); placementGhost._ghost = tankView; placementGhost._size = tankView.OuterSize; placementGhost._rules = rules ?? new PlacementRules(); placementGhost._yaw = startYaw; placementGhost._onConfirm = onConfirm; placementGhost._onCancel = onCancel; placementGhost._startFrame = Time.frameCount; Current = placementGhost; return placementGhost; } public void Cancel() { Action onCancel = _onCancel; Finish(); onCancel?.Invoke(); } private void Finish() { if ((Object)(object)Current == (Object)(object)this) { Current = null; } Hud.Hide("placement"); if ((Object)(object)_ghost != (Object)null) { Object.Destroy((Object)(object)((Component)_ghost).gameObject); } Object.Destroy((Object)(object)((Component)this).gameObject); } private void Update() { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: 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_015d: Unknown result type (might be due to invalid IL or missing references) //IL_0162: Unknown result type (might be due to invalid IL or missing references) //IL_0175: Unknown result type (might be due to invalid IL or missing references) Camera val = UnityUtil.ViewCamera(); bool blocksGame = ModUi.BlocksGame; ((Component)_ghost).gameObject.SetActive((Object)(object)val != (Object)null && !blocksGame); if ((Object)(object)val == (Object)null || blocksGame) { return; } if (Keys.Down(RotateKey)) { _yaw = (_yaw + (Input.GetKey((KeyCode)304) ? (-15f) : 15f) + 360f) % 360f; } _valid = Evaluate(((Component)val).transform, out _pos, out _why); ((Component)_ghost).transform.SetPositionAndRotation(_pos, Quaternion.Euler(0f, _yaw, 0f)); _ghost.SetGhostValid(_valid); Hud.Show("placement", "<b>Placing the aquarium</b> [Left click] place [" + ((object)Unsafe.As<KeyCode, KeyCode>(ref RotateKey)/*cast due to .constrained prefix*/).ToString() + "] rotate [Right click] cancel" + (_valid ? "" : ("\n<color=#ff8070>" + _why + "</color>"))); if (Time.frameCount > _startFrame + 1) { if (Input.GetMouseButtonDown(1) || Keys.Down((KeyCode)27)) { Cancel(); } else if (Input.GetMouseButtonDown(0) && _valid) { Action<Vector3, float> onConfirm = _onConfirm; Vector3 pos = _pos; float yaw = _yaw; Finish(); onConfirm?.Invoke(pos, yaw); } } } private bool Evaluate(Transform cam, out Vector3 pos, out string why) { //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_0012: 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_001c: 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_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0043: 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_0066: 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_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Unknown result type (might be due to invalid IL or missing references) //IL_0133: Unknown result type (might be due to invalid IL or missing references) //IL_0145: Unknown result type (might be due to invalid IL or missing references) //IL_014a: 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_0171: Unknown result type (might be due to invalid IL or missing references) //IL_0176: Unknown result type (might be due to invalid IL or missing references) //IL_0186: Unknown result type (might be due to invalid IL or missing references) //IL_018b: Unknown result type (might be due to invalid IL or missing references) //IL_019e: Unknown result type (might be due to invalid IL or missing references) //IL_01a3: Unknown result type (might be due to invalid IL or missing references) //IL_01a7: Unknown result type (might be due to invalid IL or missing references) //IL_01ac: Unknown result type (might be due to invalid IL or missing references) //IL_01ad: Unknown result type (might be due to invalid IL or missing references) //IL_01af: Unknown result type (might be due to invalid IL or missing references) //IL_01b4: Unknown result type (might be due to invalid IL or missing references) //IL_01b9: Unknown result type (might be due to invalid IL or missing references) //IL_01ca: Unknown result type (might be due to invalid IL or missing references) //IL_01cf: 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_01ee: Unknown result type (might be due to invalid IL or missing references) //IL_0247: 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) //IL_0264: Unknown result type (might be due to invalid IL or missing references) //IL_020d: 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_02e5: Unknown result type (might be due to invalid IL or missing references) //IL_02ea: Unknown result type (might be due to invalid IL or missing references) //IL_02f1: Unknown result type (might be due to invalid IL or missing references) //IL_02f9: Unknown result type (might be due to invalid IL or missing references) //IL_02fe: Unknown result type (might be due to invalid IL or missing references) //IL_0303: Unknown result type (might be due to invalid IL or missing references) //IL_0305: Unknown result type (might be due to invalid IL or missing references) //IL_0307: Unknown result type (might be due to invalid IL or missing references) //IL_0290: Unknown result type (might be due to invalid IL or missing references) pos = cam.position + cam.forward * 4f; PlacementRules rules = _rules; RaycastHit val = default(RaycastHit); if (!Physics.Raycast(cam.position, cam.forward, ref val, rules.MaxDistance + 3f, LayerMask.op_Implicit(rules.Ground), (QueryTriggerInteraction)1)) { why = "Aim at the ground"; return false; } pos = ((RaycastHit)(ref val)).point; Vector3 val2 = ((RaycastHit)(ref val)).point - cam.position; val2.y = 0f; if (((Vector3)(ref val2)).magnitude > rules.MaxDistance) { why = "Too far away"; return false; } if (((Vector3)(ref val2)).magnitude < rules.MinDistance) { why = "Too close to you"; return false; } if (Vector3.Angle(((RaycastHit)(ref val)).normal, Vector3.up) > rules.MaxSlopeDeg) { why = "Too steep"; return false; } Quaternion val3 = Quaternion.Euler(0f, _yaw, 0f); float num = _size.x / 2f; float num2 = _size.z / 2f; float num3 = float.MinValue; float num4 = float.MaxValue; Vector3[] array = (Vector3[])(object)new Vector3[5] { Vector3.zero, new Vector3(0f - num, 0f, 0f - num2), new Vector3(num, 0f, 0f - num2), new Vector3(0f - num, 0f, num2), new Vector3(num, 0f, num2) }; RaycastHit val5 = default(RaycastHit); foreach (Vector3 val4 in array) { if (!Physics.Raycast(((RaycastHit)(ref val)).point + val3 * val4 + Vector3.up * (rules.MaxStep + 1.5f), Vector3.down, ref val5, rules.MaxStep * 2f + 3f, LayerMask.op_Implicit(rules.Ground), (QueryTriggerInteraction)1)) { why = "Not enough ground under it"; return false; } num3 = Mathf.Max(num3, ((RaycastHit)(ref val5)).point.y); num4 = Mathf.Min(num4, ((RaycastHit)(ref val5)).point.y); } pos = new Vector3(((RaycastHit)(ref val)).point.x, num3, ((RaycastHit)(ref val)).point.z); if (num3 - num4 > rules.MaxStep) { why = "Ground too uneven"; return false; } if (rules.WaterHeightAt != null && num4 < rules.WaterHeightAt(pos) + 0.2f) { why = "Can't stand in water"; return false; } float num5 = rules.MaxStep * 0.5f; Vector3 val6 = default(Vector3); ((Vector3)(ref val6))..ctor(num - 0.03f, (_size.y - num5) / 2f, num2 - 0.03f); Collider[] array2 = Physics.OverlapBox(pos + Vector3.up * (num5 + val6.y), val6, val3, LayerMask.op_Implicit(rules.Blockers), (QueryTriggerInteraction)1); foreach (Collider val7 in array2) { if (!((Object)(object)val7 == (Object)null) && !((Component)val7).transform.IsChildOf(((Component)_ghost).transform) && (rules.Ignore == null || !rules.Ignore(val7))) { why = "Blocked by " + Nice(((Object)val7).name); return false; } } why = ""; return true; } private static string Nice(string name) { int num = name.IndexOf('('); return ((num > 0) ? name.Substring(0, num) : name).Trim(); } private void OnDestroy() { if ((Object)(object)_ghost != (Object)null) { Object.Destroy((Object)(object)((Component)_ghost).gameObject); } if ((Object)(object)Current == (Object)(object)this) { Current = null; Hud.Hide("placement"); } } } [BepInPlugin("com.wfxhtf.mod", "WFxHTF", "1.0.1")] public sealed class Plugin : BaseUnityPlugin { public const string Guid = "com.wfxhtf.mod"; public const string Version = "1.0.1"; public static ManualLogSource Log; public static Plugin Instance; public readonly MappingConfig Mapping = new MappingConfig(); public readonly EconomyConfig Economy = new EconomyConfig(); public readonly AquariumConfig Aquarium = new AquariumConfig(); public readonly RainConfig Rain = new RainConfig(); private DebugTank _tank; private ConfigEntry<KeyCode> _consoleKey; private ConfigEntry<KeyCode> _spawnKey; private ConfigEntry<KeyCode> _journalKey; private ConfigEntry<bool> _debugCommands; private ConfigEntry<float> _spriteScale; private readonly List<SpriteFish> _spawned = new List<SpriteFish>(); private int _spawnCursor; private readonly List<string> _report = new List<string>(); private static readonly (int Version, string Section, string Key, string OldDefault)[] DefaultChanges = new(int, string, string, string)[6] { (1, "AquariumConfig", "ZenToggleCooldownDays", "1"), (2, "AquariumConfig", "IncomeRate", "0.04"), (2, "AquariumConfig", "FoodUnitsPerItem", "4"), (2, "MappingConfig", "RainSpawnScale", "1"), (3, "MappingConfig", "RainSpawnScale", "8"), (4, "AquariumConfig", "IncomeRate", "0.18") }; public SpriteCache Cache { get; } = new SpriteCache(); public DebugConsole Console { get; private set; } public JournalService Journal { get; } = new JournalService(); public JournalUI JournalPanel { get; private set; } public TankMenu TankMenu { get; private set; } public RainVisuals RainFx { get; private set; } public FirstRunSetup Setup { get; private set; } public event Action CacheLoaded; private void Awake() { //IL_026c: Unknown result type (might be due to invalid IL or missing references) //IL_0271: Unknown result type (might be due to invalid IL or missing references) //IL_02a3: Unknown result type (might be due to invalid IL or missing references) //IL_0313: Unknown result type (might be due to invalid IL or missing references) //IL_0318: Unknown result type (might be due to invalid IL or missing references) //IL_0345: Unknown result type (might be due to invalid IL or missing references) Log = ((BaseUnityPlugin)this).Logger; Instance = this; int num = 0; object[] array = new object[4] { Mapping, Economy, Aquarium, Rain }; foreach (object target in array) { num += ConfigBinder.Bind(((BaseUnityPlugin)this).Config, target); } if (ApplyDefaultChanges() > 0) { array = new object[4] { Mapping, Economy, Aquarium, Rain }; foreach (object target2 in array) { ConfigBinder.Bind(((BaseUnityPlugin)this).Config, target2); } } _consoleKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Keys", "Console", (KeyCode)289, "Opens the WFxHTF debug console."); _spawnKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Keys", "DebugSpawnNext", (KeyCode)290, "Debug: spawn the next WEBFISHING fish sprite in front of you (needs Debug.Commands)."); _debugCommands = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "Commands", false, "Cheat and test console commands (wf.nextbite, shop.give, tank.fill, shiny.next, ...). Off for normal play."); _journalKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Keys", "Journal", (KeyCode)106, "Opens the WEBFISHING journal."); _spriteScale = ((BaseUnityPlugin)this).Config.Bind<float>("Visuals", "SpriteScale", 1f, "Size multiplier for WEBFISHING fish sprites."); Feature("config", $"{num} tunables bound"); bool visible = false; if (Cache.Load(Mapping)) { int num2 = Cache.Preload(); Feature("cache", $"{Cache.Defs.Count} fish, fishDefHash {Cache.FishDefHash}, source build {Cache.Catalog.SteamBuildId}" + ((num2 > 0) ? $", {num2} textures FAILED" : "")); } else { Feature("cache", "MISSING: " + Cache.Error); visible = true; } Setup = ((Component)this).gameObject.AddComponent<FirstRunSetup>(); Setup.Visible = visible; Console = ((Component)this).gameObject.AddComponent<DebugConsole>(); Console.ToggleKey = _consoleKey.Value; Console.DebugAllowed = _debugCommands.Value; RegisterCommands(); Feature("console", $"key {_consoleKey.Value}"); ModUi.AddBlocker(() => Console.Open); ((Component)this).gameObject.AddComponent<Hud>(); TankMenu = ((Component)this).gameObject.AddComponent<TankMenu>(); RainFx = ((Component)this).gameObject.AddComponent<RainVisuals>(); JournalPanel = ((Component)this).gameObject.AddComponent<JournalUI>(); JournalPanel.ToggleKey = _journalKey.Value; JournalPanel.Init(Cache, Journal); Feature("journal", $"key {_journalKey.Value}, saves in {Identity.SavesDir}"); Feature("adapter", "not loaded: no How to Fish hooks active"); Log.LogInfo((object)("WFxHTF 1.0.1 startup report:\n " + string.Join("\n ", _report))); } private int ApplyDefaultChanges() { //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Expected O, but got Unknown ConfigEntry<int> applied = ((BaseUnityPlugin)this).Config.Bind<int>("Meta", "DefaultsVersion", 0, "Internal: which updated default values have been applied. Leave as is."); int num = DefaultChanges.Max(((int Version, string Section, string Key, string OldDefault) c) => c.Version); int num2 = 0; if (applied.Value >= num) { return 0; } foreach (var item in DefaultChanges.Where(((int Version, string Section, string Key, string OldDefault) c) => c.Version > applied.Value)) { ConfigDefinition val = new ConfigDefinition(item.Section, item.Key); if (!((BaseUnityPlugin)this).Config.ContainsKey(val)) { continue; } ConfigEntryBase val2 = ((BaseUnityPlugin)this).Config[val]; string serializedValue = val2.GetSerializedValue(); if (SameValue(serializedValue, item.OldDefault)) { val2.BoxedValue = val2.DefaultValue; if (!SameValue(val2.GetSerializedValue(), serializedValue)) { num2++; ((BaseUnityPlugin)this).Logger.LogInfo((object)("config: " + item.Section + "." + item.Key + " " + item.OldDefault + " -> new default " + val2.GetSerializedValue())); } } } applied.Value = num; return num2; } private static bool SameValue(string a, string b) { if (!(a == b)) { if (double.TryParse(a, NumberStyles.Float, CultureInfo.InvariantCulture, out var result) && double.TryParse(b, NumberStyles.Float, CultureInfo.InvariantCulture, out var result2)) { return Math.Abs(result - result2) < 1E-09; } return false; } return true; } public bool ReloadCache() { if (!Cache.Load(Mapping)) { Log.LogWarning((object)("cache: " + Cache.Error)); return false; } int num = Cache.Preload(); Feature("cache", $"{Cache.Defs.Count} fish, fishDefHash {Cache.FishDefHash}, source build {Cache.Catalog.SteamBuildId}" + ((num > 0) ? $", {num} textures FAILED" : "") + " (set up in game)"); Log.LogInfo((object)$"cache: {Cache.Defs.Count} WEBFISHING fish loaded after the in-game setup"); try { this.CacheLoaded?.Invoke(); } catch (Exception ex) { Log.LogWarning((object)("after setup: " + ex.Message)); } return true; } public void Feature(string name, string detail) { string text = $"{name,-8} {detail}"; int num = _report.FindIndex((string l) => l.StartsWith(name + " ")); if (num >= 0) { _report[num] = text; } else { _report.Add(text); } } private void Update() { //IL_0013: Unknown result type (might be due to invalid IL or missing references) if (_debugCommands.Value && Keys.Down(_spawnKey.Value) && Cache.Loaded && !Console.Open) { FishDef def = Cache.Defs[_spawnCursor++ % Cache.Defs.Count]; Log.LogInfo((object)Spawn(def, (Quality)0)); } } private string Spawn(FishDef def, Quality q) { //IL_002d: 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) //IL_0044: 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_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0056: 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_00c9: Unknown result type (might be due to invalid IL or missing references) Camera val = UnityUtil.ViewCamera(); if ((Object)(object)val == (Object)null) { return "no camera"; } float num = SpriteFish.LengthMeters(def, _spriteScale.Value); Vector3 position = ((Component)val).transform.position + ((Component)val).transform.forward * (2f + num); SpriteFish spriteFish = SpriteFish.Create(Cache, def, q, position, num); if ((Object)(object)spriteFish == (Object)null) { return "could not create " + def.Id + " (texture or shader missing; shader = " + SpriteMaterial.ShaderName + ")"; } _spawned.Add(spriteFish); return $"spawned {def.Name} ({def.Id}, {q}) {num:0.0} m, shader {SpriteMaterial.ShaderName}"; } private FishDef ParseFish(string[] a, string usage, out Quality q, out double? number, out string error) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Expected I4, but got Unknown Quality? val = default(Quality?); string text = FishArgs.Split(a, ref val, ref number); q = (Quality)(int)val.GetValueOrDefault(); error = null; if (!Cache.Loaded) { error = Cache.Error; return null; } if (text.Length == 0) { error = "usage: " + usage; return null; } FishDef obj = Cache.Find(text); if (obj == null) { error = "no fish matches '" + text + "'"; } return obj; } private static double ParseNumber(string[] a, int i, double fallback) { if (a.Length <= i || !double.TryParse(a[i], NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) { return fallback; } return result; } private void RegisterCommands() { Console.Register("wf.firstrun", "show the WEBFISHING setup box (extract the fish from your WEBFISHING)", delegate { Setup.Visible = true; return "the setup box is open"; }); Console.Register("rain.clouds", "DEBUG: [height] [max size] [count] [darkness 0-1] tune the rain clouds live (shows the current values)", delegate(string[] a) { //IL_0144: 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) RainVisuals rainFx = RainFx; if ((Object)(object)rainFx == (Object)null) { return "no rain visuals"; } if (a.Length != 0) { rainFx.CloudHeight = Mathf.Clamp((float)ParseNumber(a, 0, rainFx.CloudHeight), 10f, 150f); rainFx.CloudMaxSize = Mathf.Clamp((float)ParseNumber(a, 1, rainFx.CloudMaxSize), 5f, 200f); rainFx.CloudMinSize = rainFx.CloudMaxSize * 0.5f; rainFx.CloudCount = Mathf.Clamp((int)ParseNumber(a, 2, rainFx.CloudCount), 0, 3000); float num = Mathf.Clamp01((float)ParseNumber(a, 3, 1f - rainFx.CloudColor.g / 0.6f)); float num2 = 0.6f * (1f - num); rainFx.CloudColor = new Color(num2 * 0.93f, num2, num2 * 1.2f, 1f); rainFx.RebuildClouds(); } return $"clouds: height {rainFx.CloudHeight:0}, size {rainFx.CloudMinSize:0}-{rainFx.CloudMaxSize:0} (flatness {rainFx.CloudFlatness:0.##}), count {rainFx.CloudCount}, colour {rainFx.CloudColor}" + ((rainFx.Amount <= 0f) ? " (not raining: rain.start to see them)" : ""); }); Console.Register("wf.catch", "DEBUG: <id|name> [quality] [kg] record a catch in your journal", delegate(string[] a) { //IL_004a: 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) Quality q; double? number; string error; FishDef val = ParseFish(a, "wf.catch <id|name> [quality] [kg]", out q, out number, out error); if (val == null) { return error; } float num = (float)(number ?? ((double)val.Htf.WeightKgMean)); bool flag = Journal.RecordLocal(val.Id, num, q, "debug", "debug"); return string.Format("{0}{1} {2} {3:0.##} kg -> {4}", flag ? "NEW! " : "", val.Name, q, num, Journal.Path); }); Console.Register("journal", "journal status (J opens it)", delegate { if (Journal.Journal == null) { Journal.Load(); } return $"{Journal.Status}\nsteam id {Identity.SteamId} ({Identity.Name})\nfile {Journal.Path}"; }); Console.Register("aq.spawn", "DEBUG: place a debug tank in front of you", delegate { if (!Cache.Loaded) { return Cache.Error; } if ((Object)(object)_tank != (Object)null) { Object.DestroyImmediate((Object)(object)((Component)_tank).gameObject); } _tank = DebugTank.Spawn(Cache, Aquarium, Console); return (!((Object)(object)_tank == (Object)null)) ? $"tank placed, capacity {_tank.Tank.Capacity}, tank day = {Aquarium.TankDayRealMinutes} real min" : "no camera"; }); Console.Register("aq.add", "DEBUG: <id|name> [quality] deposit a fish (debug tank)", delegate(string[] a) { //IL_0031: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_tank == (Object)null) { return "no tank: aq.spawn first"; } Quality q; double? number; string error; FishDef val = ParseFish(a, "aq.add <id|name> [quality]", out q, out number, out error); return (val != null) ? _tank.Add(val, q) : error; }); Console.Register("aq.fill", "DEBUG: fill the debug tank with random fish", delegate { if ((Object)(object)_tank == (Object)null) { return "no tank: aq.spawn first"; } Random rng = new Random(); List<FishDef> list = Cache.Defs.OrderBy((FishDef val) => rng.Next()).Take(_tank.Tank.Capacity - _tank.Tank.State.Fish.Count).ToList(); foreach (FishDef item in list) { _tank.Add(item, (Quality)rng.Next(0, 6)); } return $"added {list.Count}"; }); Console.Register("aq.dump", "DEBUG: print the debug tank's state", (string[] _) => (!((Object)(object)_tank == (Object)null)) ? _tank.Dump() : "no tank"); Console.Register("aq.tick", "DEBUG: <days> advance the debug tank", delegate(string[] a) { if ((Object)(object)_tank == (Object)null) { return "no tank"; } double num = ParseNumber(a, 0, 1.0); return $"advanced {num} days, {_tank.Handle(_tank.Tank.Advance(num, true))} events"; }); Console.Register("aq.attack", "DEBUG: start an attack now", (string[] _) => (!((Object)(object)_tank == (Object)null)) ? ((_tank.Handle(_tank.Tank.ForceAttack()) <= 0) ? "no attack (empty tank or one already running)" : "attack started (aq.defend to win, or wait it out)") : "no tank"); Console.Register("aq.defend", "DEBUG: [hp 0-1] hurt the threat (default: kil