Decompiled source of FishVsHunters v0.8.8
FishVsHunters.dll
Decompiled 20 hours ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Runtime.Serialization.Json; using System.Runtime.Versioning; using System.Security.Cryptography; using System.Text; using System.Xml; using BepInEx; using BepInEx.Configuration; using FishNet; using FishNet.Broadcast; using FishNet.Connection; using FishNet.Managing.Client; using FishNet.Managing.Object; using FishNet.Managing.Server; using FishNet.Object; using FishNet.Object.Synchronizing; using FishNet.Serializing; using FishNet.Transporting; using FishVsHunters.debug; using FishVsHunters.fish; using FishVsHunters.match; using FishVsHunters.net; using FishVsHunters.ui; using HarmonyLib; using Steamworks; using UnityEngine; using UnityEngine.InputSystem; using UnityEngine.InputSystem.Controls; using UnityEngine.SceneManagement; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: InternalsVisibleTo("IslandRetentionChecks")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyVersion("0.0.0.0")] namespace FishVsHunters { [BepInPlugin("com.fishvshunters.mod", "Fish vs Hunters", "0.8.8")] public class Plugin : BaseUnityPlugin { public const string Guid = "com.fishvshunters.mod"; public const string Name = "Fish vs Hunters"; public const string Version = "0.8.8"; private Harmony _harmony; private void OnGUI() { MatchHud.Draw(); } private void OnApplicationQuit() { RoundDirector.Flush(); } private void Awake() { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Expected O, but got Unknown //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) Config.Init(((BaseUnityPlugin)this).Config); MutationCatalog.ApplyConfig(); SkinCatalog.Validate(); CustomSerializers.RegisterAll(); _harmony = new Harmony("com.fishvshunters.mod"); _harmony.PatchAll(); ((BaseUnityPlugin)this).Logger.LogInfo((object)string.Format("{0} v{1} loaded. Role key: {2}; host round-mode key: {3}.", "Fish vs Hunters", "0.8.8", Config.BecomeFishKey.Value, Config.ToggleRestartKey.Value)); } private void Update() { //IL_0048: 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) NetHub.EnsureRegistered(); MatchController.ClientPresentationTick(); IslandRetention.Tick((MonoBehaviour)(object)this); if ((Object)(object)Server.Instance != (Object)null && ((NetworkBehaviour)Server.Instance).IsServerInitialized) { FishRegistry.HostTick(); PreySpawner.HostTick(); MatchController.HostMatchTick(); } Keyboard current = Keyboard.current; if (current != null) { if (WasPressed(current, Config.ToggleRestartKey.Value)) { RoundDirector.ToggleMode(); } if (WasPressed(current, Config.BecomeFishKey.Value)) { MatchController.RequestToggleFish(); } else if (Config.DebugToolsEnabled.Value) { HandleDebugKeys(current); } } } private static void HandleDebugKeys(Keyboard kb) { //IL_0006: 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_004f: 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_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_0135: Unknown result type (might be due to invalid IL or missing references) //IL_015c: 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_0196: 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_01dc: Unknown result type (might be due to invalid IL or missing references) //IL_0203: Unknown result type (might be due to invalid IL or missing references) //IL_0266: 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_024b: Unknown result type (might be due to invalid IL or missing references) //IL_02bd: Unknown result type (might be due to invalid IL or missing references) //IL_023a: Unknown result type (might be due to invalid IL or missing references) //IL_0229: Unknown result type (might be due to invalid IL or missing references) if (WasPressed(kb, Config.DebugGiveXp100Key.Value)) { MatchController.DebugForceEvolve(); Debug.Log((object)"[FishVsHunters] DEBUG F7 -> next level/growth"); } else if (WasPressed(kb, Config.DebugGiveXp500Key.Value)) { MatchController.DebugGiveXp(500); Debug.Log((object)"[FishVsHunters] DEBUG +500 biomass"); } else if (WasPressed(kb, Config.DebugForceEvolveKey.Value)) { MatchController.DebugForceEvolve(); Debug.Log((object)"[FishVsHunters] DEBUG force evolve"); } else if (WasPressed(kb, Config.DebugSetLevelKey.Value)) { MatchController.DebugSetLevel(Config.DebugSetLevelTarget.Value); Debug.Log((object)$"[FishVsHunters] DEBUG set level -> {Config.DebugSetLevelTarget.Value}"); } else if (WasPressed(kb, Config.DebugHealKey.Value)) { MatchController.DebugHeal(); Debug.Log((object)"[FishVsHunters] DEBUG heal"); } else if (WasPressed(kb, Config.DebugDamageKey.Value)) { MatchController.DebugDamage(Config.DebugDamageAmount.Value); Debug.Log((object)$"[FishVsHunters] DEBUG damage -{Config.DebugDamageAmount.Value}"); } else if (WasPressed(kb, Config.DebugClearMutationsKey.Value)) { MatchController.DebugClearMutations(); Debug.Log((object)"[FishVsHunters] DEBUG mutations cleared"); } else if (WasPressed(kb, Config.DebugPrintStatsKey.Value)) { Debug.Log((object)("[FishVsHunters] DEBUG stats: " + MatchController.DebugStatsSummary())); } else if (WasPressed(kb, Config.DebugPrintXpTableKey.Value)) { DebugTools.PrintXpTable(); } else if (WasPressed(kb, Config.DebugForcePathChoiceKey.Value)) { MatchController.DebugForcePathChoice(); Debug.Log((object)"[FishVsHunters] DEBUG force path choice"); } else if (WasPressed(kb, Config.DebugChoosePrimalKey.Value)) { MatchController.DebugChoosePath(EvolutionPath.Primal); Debug.Log((object)"[FishVsHunters] DEBUG choose PRIMAL"); } else if (WasPressed(kb, Config.DebugChooseIntelligenceKey.Value)) { MatchController.DebugChoosePath(EvolutionPath.Intelligence); Debug.Log((object)"[FishVsHunters] DEBUG choose INTELLIGENCE"); } else if (WasPressed(kb, Config.DebugSpawnPreyKey.Value)) { PreySpawner.DebugSpawnOne(MatchController.GetHostLocalFishRuntime()); Debug.Log((object)"[FishVsHunters] DEBUG spawn prey"); } else if (WasPressed(kb, Config.DebugNearestHunterHpKey.Value)) { DebugTools.LogNearestHunterHp(((Object)(object)MatchController.LocalFishBody != (Object)null) ? ((Component)MatchController.LocalFishBody).transform.position : (((Object)(object)Player.LocalPlayer != (Object)null) ? Player.LocalPlayer.Transform.position : Vector3.zero), Config.DebugNearestRange.Value); } else if (WasPressed(kb, Config.DebugTestBiteHunterKey.Value)) { MatchController.LocalFishController?.DebugForceBite(); } else if (WasPressed(kb, Config.DebugTankBuildKey.Value)) { ApplyBuildPreset(new MutationId[4] { MutationId.ThickSkin, MutationId.Armor, MutationId.Growth, MutationId.Ram }, 6, 3); Debug.Log((object)"[FishVsHunters] DEBUG Tank build applied (ThickSkin+Armor+Growth+Ram x3, level 6)"); } else if (WasPressed(kb, Config.DebugSpeedBuildKey.Value)) { ApplyBuildPreset(new MutationId[5] { MutationId.FastFins, MutationId.Dash, MutationId.SharpTeeth, MutationId.Bleed, MutationId.Sonar }, 6, 3); Debug.Log((object)"[FishVsHunters] DEBUG Speed/Damage build applied (FastFins+Dash+SharpTeeth+Bleed+Sonar x3, level 6)"); } } private static void ApplyBuildPreset(MutationId[] ids, int level, int stacksEach) { MatchController.DebugSetLevel(level); for (int i = 0; i < stacksEach; i++) { MatchController.DebugApplyPreset(ids); } } private static bool WasPressed(Keyboard kb, Key key) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) KeyControl val = kb[key]; if (val != null) { return ((ButtonControl)val).wasPressedThisFrame; } return false; } } } namespace FishVsHunters.ui { internal class FishHud : MonoBehaviour { private Creature _body; private FishController _controller; private float _evolutionBannerUntil; private int _evolutionBannerLevel; private GUIStyle _panelStyle; private GUIStyle _titleStyle; private GUIStyle _lineStyle; private GUIStyle _boldLineStyle; private GUIStyle _bannerStyle; private GUIStyle _crossStyle; private GUIStyle _crossLabelStyle; private GUIStyle _cardStyle; private GUIStyle _cardNumberStyle; public void Initialize(Creature body, FishController controller) { _body = body; _controller = controller; _controller.OnLevelUp += OnLevelUp; } private void OnDestroy() { if ((Object)(object)_controller != (Object)null) { _controller.OnLevelUp -= OnLevelUp; } } private void OnLevelUp(int level) { _evolutionBannerLevel = level; _evolutionBannerUntil = Time.time + 2.5f; } private void EnsureStyles() { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Expected O, but got Unknown //IL_0050: 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) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Expected O, but got Unknown //IL_007b: 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_0093: 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_00a6: Expected O, but got Unknown //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Expected O, but got Unknown //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: 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_0105: 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_0113: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Expected O, but got Unknown //IL_0139: Unknown result type (might be due to invalid IL or missing references) //IL_0144: Unknown result type (might be due to invalid IL or missing references) //IL_0149: Unknown result type (might be due to invalid IL or missing references) //IL_0151: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Unknown result type (might be due to invalid IL or missing references) //IL_0164: Expected O, but got Unknown //IL_0165: Unknown result type (might be due to invalid IL or missing references) //IL_016a: Unknown result type (might be due to invalid IL or missing references) //IL_0172: Unknown result type (might be due to invalid IL or missing references) //IL_0179: Unknown result type (might be due to invalid IL or missing references) //IL_0185: Expected O, but got Unknown //IL_0187: Unknown result type (might be due to invalid IL or missing references) //IL_018d: Expected O, but got Unknown //IL_01a4: Unknown result type (might be due to invalid IL or missing references) //IL_01bb: Unknown result type (might be due to invalid IL or missing references) //IL_01c0: Unknown result type (might be due to invalid IL or missing references) //IL_01c9: Unknown result type (might be due to invalid IL or missing references) //IL_01d3: Expected O, but got Unknown //IL_01d8: Expected O, but got Unknown //IL_01f0: Unknown result type (might be due to invalid IL or missing references) //IL_01f5: Unknown result type (might be due to invalid IL or missing references) //IL_01fd: Unknown result type (might be due to invalid IL or missing references) //IL_0209: Expected O, but got Unknown //IL_0223: Unknown result type (might be due to invalid IL or missing references) if (_panelStyle == null) { Texture2D val = new Texture2D(1, 1); val.SetPixel(0, 0, new Color(0f, 0f, 0f, 0.6f)); val.Apply(); GUIStyle val2 = new GUIStyle(); val2.normal.background = val; _panelStyle = val2; _titleStyle = new GUIStyle { fontSize = 18, fontStyle = (FontStyle)1, alignment = (TextAnchor)3 }; _titleStyle.normal.textColor = Color.white; _lineStyle = new GUIStyle { fontSize = 15, alignment = (TextAnchor)3, wordWrap = false }; _lineStyle.normal.textColor = Color.white; _boldLineStyle = new GUIStyle(_lineStyle) { fontStyle = (FontStyle)1 }; _boldLineStyle.normal.textColor = new Color(1f, 0.85f, 0.3f); _bannerStyle = new GUIStyle { fontSize = 30, fontStyle = (FontStyle)1, alignment = (TextAnchor)4, wordWrap = true }; _bannerStyle.normal.textColor = new Color(1f, 0.85f, 0.2f); _crossStyle = new GUIStyle { fontSize = 26, fontStyle = (FontStyle)1, alignment = (TextAnchor)4 }; _crossLabelStyle = new GUIStyle { fontSize = 14, fontStyle = (FontStyle)1, alignment = (TextAnchor)4 }; Texture2D val3 = new Texture2D(1, 1); val3.SetPixel(0, 0, new Color(0.035f, 0.12f, 0.16f, 0.96f)); val3.Apply(); _cardStyle = new GUIStyle(_panelStyle) { padding = new RectOffset(16, 16, 14, 14) }; _cardStyle.normal.background = val3; _cardNumberStyle = new GUIStyle(_titleStyle) { fontSize = 30, alignment = (TextAnchor)1 }; _cardNumberStyle.normal.textColor = new Color(1f, 0.78f, 0.18f); } } private void OnGUI() { //IL_0023: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_body == (Object)null) && !((Object)(object)_controller == (Object)null)) { EnsureStyles(); GUI.color = Color.white; DrawStatsPanel(); if (!DrawPathChoice()) { DrawPerkChoice(); DrawSkinChoice(); } DrawSonarReadout(); DrawBloodScent(); DrawEvolutionBanner(); DrawCrosshair(); } } private void DrawStatsPanel() { //IL_0066: Unknown result type (might be due to invalid IL or missing references) CalculatedFishStats stats = _controller.Stats; bool flag = stats.DashUnlocked || stats.SonarUnlocked; int num = 11 + (flag ? (1 + (stats.DashUnlocked ? 1 : 0) + (stats.SonarUnlocked ? 1 : 0)) : 0); float num2 = 24f * (float)num + 16f; GUI.Box(new Rect(20f, 20f, 290f, num2), GUIContent.none, _panelStyle); float ly = 28f; Line("РЫБА", _titleStyle); Line($"Уровень: {_controller.Level} / {100}"); Line("Путь: " + PathName(_controller.Path), _controller.AwaitingPathChoice ? _boldLineStyle : _lineStyle); Line("Форма: " + _controller.SkinDisplayName, _controller.AwaitingSkinChoice ? _boldLineStyle : _lineStyle); string text = ((_controller.Level >= 100) ? $"Биомасса: {_controller.Xp} (МАКС)" : (_controller.AwaitingSkinChoice ? $"Биомасса: {_controller.Xp} (нужен выбор формы)" : $"Биомасса: {_controller.Xp} / {_controller.NextThreshold}")); Line(text); Line($"ХП: {_body._hp.Value} / {_body.MaxHp}"); Line($"Броня: {stats.ArmorPercent:0}%"); Line($"Урон укуса: {stats.BiteDamage:0}"); float biteCooldownRemaining = _controller.BiteCooldownRemaining; Line((biteCooldownRemaining > 0f) ? $"Укус: {biteCooldownRemaining:0.0}с" : "Укус: ГОТОВ", (biteCooldownRemaining > 0f) ? _lineStyle : _boldLineStyle); Line($"Скорость: {stats.SwimSpeed:0.0}"); Line($"Размер: {stats.Scale:0.00}x"); if (flag) { Line("АКТИВНЫЕ:", _boldLineStyle); if (stats.DashUnlocked) { float dashCooldownRemaining = _controller.DashCooldownRemaining; Line((dashCooldownRemaining > 0f) ? $"[Q] Рывок — {dashCooldownRemaining:0.0}с" : "[Q] Рывок — ГОТОВ"); } if (stats.SonarUnlocked) { float sonarCooldownRemaining = _controller.SonarCooldownRemaining; Line((sonarCooldownRemaining > 0f) ? $"[E] Сонар — {sonarCooldownRemaining:0.0}с" : "[E] Сонар — ГОТОВ"); } } void Line(string text2, GUIStyle style = null) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) GUI.Label(new Rect(32f, ly, 266f, 24f), text2, style ?? _lineStyle); ly += 24f; } } private void DrawPerkChoice() { //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0077: 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_0087: Expected O, but got Unknown //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Expected O, but got Unknown //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_0112: 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_0120: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Expected O, but got Unknown //IL_01dd: Unknown result type (might be due to invalid IL or missing references) //IL_021b: 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_02ab: Unknown result type (might be due to invalid IL or missing references) MutationId[] pendingChoice = _controller.PendingChoice; if (pendingChoice == null) { return; } float num = Mathf.Min(900f, (float)Screen.width - 40f); Rect val = default(Rect); ((Rect)(ref val))..ctor((float)Screen.width / 2f - num / 2f, (float)Screen.height * 0.56f, num, 250f); GUI.Box(val, GUIContent.none, _panelStyle); GUIStyle val2 = new GUIStyle(_titleStyle) { alignment = (TextAnchor)4, wordWrap = true, fontSize = 22 }; GUI.Label(new Rect(((Rect)(ref val)).x + 18f, ((Rect)(ref val)).y + 8f, num - 36f, 42f), "ЭВОЛЮЦИЯ! ВЫБЕРИ МУТАЦИЮ", val2); GUIStyle val3 = new GUIStyle(_lineStyle) { fontStyle = (FontStyle)1, wordWrap = true, alignment = (TextAnchor)1, fontSize = 17 }; val3.normal.textColor = new Color(0.65f, 0.93f, 1f); GUIStyle val4 = new GUIStyle(_lineStyle) { wordWrap = true, alignment = (TextAnchor)1, fontSize = 14 }; float num2 = (num - 36f - 28f) / 3f; float num3 = ((Rect)(ref val)).y + 42f + 10f; float num4 = ((Rect)(ref val)).height - 42f - 24f; Rect val5 = default(Rect); for (int i = 0; i < pendingChoice.Length; i++) { MutationDefinition mutationDefinition = MutationCatalog.Get(pendingChoice[i]); if (mutationDefinition != null) { int arg = _controller.StackOf(pendingChoice[i]) + 1; string text = ((mutationDefinition.Describe != null) ? mutationDefinition.Describe(arg) : ""); ((Rect)(ref val5))..ctor(((Rect)(ref val)).x + 18f + (float)i * (num2 + 14f), num3, num2, num4); GUI.Box(val5, GUIContent.none, _cardStyle); GUI.Label(new Rect(((Rect)(ref val5)).x + 8f, ((Rect)(ref val5)).y + 8f, ((Rect)(ref val5)).width - 16f, 38f), (i + 1).ToString(), _cardNumberStyle); GUI.Label(new Rect(((Rect)(ref val5)).x + 12f, ((Rect)(ref val5)).y + 50f, ((Rect)(ref val5)).width - 24f, 48f), mutationDefinition.DisplayName, val3); GUI.Label(new Rect(((Rect)(ref val5)).x + 12f, ((Rect)(ref val5)).y + 105f, ((Rect)(ref val5)).width - 24f, ((Rect)(ref val5)).height - 116f), text, val4); } } } private static string PathName(EvolutionPath p) { return p switch { EvolutionPath.Primal => "ИНСТИНКТ", EvolutionPath.Intelligence => "РАЗУМ", _ => "—", }; } private bool DrawPathChoice() { //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) //IL_0046: 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_0076: Expected O, but got Unknown //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_0088: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Expected O, but got Unknown //IL_00a6: 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_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Expected O, but got Unknown //IL_00ca: 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_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Expected O, but got Unknown //IL_00f5: Unknown result type (might be due to invalid IL or missing references) if (!_controller.AwaitingPathChoice) { return false; } float num = 664f; Rect panel = new Rect((float)Screen.width / 2f - 360f, (float)Screen.height * 0.16f, 720f, num); GUI.Box(panel, GUIContent.none, _panelStyle); GUIStyle style = new GUIStyle(_titleStyle) { alignment = (TextAnchor)4, fontSize = 22 }; GUIStyle val = new GUIStyle(_lineStyle) { fontStyle = (FontStyle)1, fontSize = 17 }; val.normal.textColor = new Color(1f, 0.85f, 0.3f); GUIStyle style2 = new GUIStyle(_lineStyle) { wordWrap = true }; GUIStyle val2 = new GUIStyle(_lineStyle) { fontStyle = (FontStyle)1, alignment = (TextAnchor)4 }; val2.normal.textColor = new Color(1f, 0.5f, 0.4f); float ly = ((Rect)(ref panel)).y + 20f; L("ВЫБЕРИ ПУТЬ ЭВОЛЮЦИИ", style); ly += 13f; L("[1] ИНСТИНКТ", val); L("Совершенствуй тело. Стань вершиной пищевой цепи.", style2, 20f); L("• огромный размер • здоровье • природная броня", style2, 20f); L("• укус • таран • регенерация • чудовищные формы", style2, 20f); ly += 13f; L("[2] РАЗУМ", val); L("Совершенствуй разум. Используй мир против своих врагов.", style2, 20f); L("• передвижение по суше • взаимодействие с предметами", style2, 20f); L("• использование экипировки • оружие • техника", style2, 20f); ly += 13f; L("ВЫБОР НЕОБРАТИМ", val2); return true; void L(string text, GUIStyle val3, float indent = 0f) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) GUI.Label(new Rect(((Rect)(ref panel)).x + 20f + indent, ly, 680f - indent, 26f), text, val3); ly += 26f; } } private void DrawSkinChoice() { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0025: 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_0034: Expected O, but got Unknown //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: 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_0116: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Expected O, but got Unknown //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_013f: Expected O, but got Unknown //IL_01eb: 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_02fb: 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) if (!_controller.AwaitingSkinChoice) { return; } IReadOnlyList<int> pendingSkinIndices = _controller.PendingSkinIndices; GUIStyle val = new GUIStyle(_titleStyle) { alignment = (TextAnchor)4, wordWrap = true }; if (pendingSkinIndices == null || pendingSkinIndices.Count == 0) { Rect val2 = new Rect((float)Screen.width / 2f - 320f, (float)Screen.height * 0.5f, 640f, 60f); GUI.Box(val2, GUIContent.none, _panelStyle); GUI.Label(val2, "ЭВОЛЮЦИЯ ДОСТУПНА — жди варианты...", val); return; } float num = 96f; float num2 = 64f + num * (float)pendingSkinIndices.Count; Rect val3 = default(Rect); ((Rect)(ref val3))..ctor((float)Screen.width / 2f - 320f, (float)Screen.height * 0.4f, 640f, num2); GUI.Box(val3, GUIContent.none, _panelStyle); GUI.Label(new Rect(((Rect)(ref val3)).x + 16f, ((Rect)(ref val3)).y + 16f, 608f, 32f), "ЭВОЛЮЦИЯ! ВЫБЕРИ НОВУЮ ФОРМУ", val); GUIStyle val4 = new GUIStyle(_lineStyle) { fontStyle = (FontStyle)1, wordWrap = true }; GUIStyle val5 = new GUIStyle(_lineStyle) { wordWrap = true }; for (int i = 0; i < pendingSkinIndices.Count; i++) { float num3 = ((Rect)(ref val3)).y + 16f + 32f + num * (float)i; SkinDefinition byIndex = SkinCatalog.GetByIndex(pendingSkinIndices[i]); if (byIndex == null) { GUI.Label(new Rect(((Rect)(ref val3)).x + 16f, num3, 608f, 32f), $"[{i + 1}] (неизвестная форма)", val4); continue; } SkinBaseStats skinBaseStats = byIndex.Evaluate(byIndex.MinLevel); SkinBaseStats skinBaseStats2 = byIndex.Evaluate(byIndex.MaxLevel); GUI.Label(new Rect(((Rect)(ref val3)).x + 16f, num3, 608f, 28.8f), $"[{i + 1}] {byIndex.DisplayName} (ур. {byIndex.MinLevel}–{byIndex.MaxLevel})", val4); GUI.Label(new Rect(((Rect)(ref val3)).x + 16f + 24f, num3 + 27.2f, 584f, 28.8f), $"ХП {skinBaseStats.MaxHp:0}→{skinBaseStats2.MaxHp:0} · Урон {skinBaseStats.BiteDamage:0}→{skinBaseStats2.BiteDamage:0} · Скор {skinBaseStats.SwimSpeed:0.0} · Броня {skinBaseStats.ArmorPercent:0}%", val5); GUI.Label(new Rect(((Rect)(ref val3)).x + 16f + 24f, num3 + 54.4f, 584f, 28.8f), $"Размер {skinBaseStats.Scale:0.0}→{skinBaseStats2.Scale:0.0}x · Поворот {skinBaseStats.TurnSpeedDegPerSec:0}°/с · Масса {skinBaseStats.Mass:0}", val5); } } private void DrawSonarReadout() { IReadOnlyList<string> sonarLines = _controller.SonarLines; if (sonarLines != null && sonarLines.Count != 0) { DrawSidePanel("СОНАР", sonarLines, 210f); } } private void DrawBloodScent() { IReadOnlyList<string> bloodScentLines = _controller.BloodScentLines; if (bloodScentLines != null && bloodScentLines.Count != 0) { float yPos = ((_controller.SonarLines != null && _controller.SonarLines.Count > 0) ? 340f : 210f); DrawSidePanel("КРОВАВЫЙ НЮХ", bloodScentLines, yPos); } } private void DrawSidePanel(string title, IReadOnlyList<string> lines, float yPos) { //IL_0029: 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_00a2: Unknown result type (might be due to invalid IL or missing references) float num = 22f * (float)(lines.Count + 1) + 20f; Rect val = default(Rect); ((Rect)(ref val))..ctor(20f, yPos, 280f, num); GUI.Box(val, GUIContent.none, _panelStyle); GUI.Label(new Rect(((Rect)(ref val)).x + 10f, ((Rect)(ref val)).y + 10f, 260f, 22f), title, _boldLineStyle); for (int i = 0; i < lines.Count; i++) { GUI.Label(new Rect(((Rect)(ref val)).x + 10f, ((Rect)(ref val)).y + 10f + 22f * (float)(i + 1), 260f, 22f), lines[i], _lineStyle); } } private void DrawEvolutionBanner() { //IL_0036: Unknown result type (might be due to invalid IL or missing references) if (!(Time.time >= _evolutionBannerUntil)) { GUI.Label(new Rect((float)Screen.width / 2f - 300f, (float)Screen.height * 0.18f, 600f, 80f), $"ЭВОЛЮЦИЯ!\nУРОВЕНЬ {_evolutionBannerLevel}", _bannerStyle); } } private void DrawCrosshair() { //IL_005c: 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_0061: 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_008b: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: 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) string aimLabel = _controller.AimLabel; bool num = !string.IsNullOrEmpty(aimLabel); float num2 = (float)Screen.width / 2f; float num3 = (float)Screen.height / 2f; Color textColor = (num ? new Color(1f, 0.25f, 0.2f) : new Color(1f, 1f, 1f, 0.85f)); _crossStyle.normal.textColor = textColor; GUI.Label(new Rect(num2 - 16f, num3 - 16f, 32f, 32f), "+", _crossStyle); if (num) { _crossLabelStyle.normal.textColor = textColor; GUI.Label(new Rect(num2 - 150f, num3 + 14f, 300f, 22f), aimLabel, _crossLabelStyle); } } } internal static class MatchHud { internal static void Draw() { //IL_005c: 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_00ba: 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_01d7: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Expected O, but got Unknown //IL_014c: Unknown result type (might be due to invalid IL or missing references) if (InstanceFinder.IsClientStarted && !((Object)(object)Player.LocalPlayer == (Object)null) && NetHub.HasRoster) { float num = Mathf.Max(10, Screen.width - 420); string arg = (NetHub.ClientAutoRestart ? "Победы и новый раунд" : "Без побед · постоянный респавн"); string arg2 = (InstanceFinder.IsServerStarted ? $"{Config.ToggleRestartKey.Value} — переключить режим" : "Режим меняет хост"); GUI.Box(new Rect(num, 12f, 408f, 64f), $"Раунд {NetHub.ClientRound} · {arg}\n{arg2}"); if (InstanceFinder.IsServerStarted && PauseManager.IsPaused && GUI.Button(new Rect(num, 80f, 408f, 32f), NetHub.ClientAutoRestart ? "Отключить победы: включить респавн" : "Включить победы и новый раунд")) { RoundDirector.ToggleMode(); } byte clientWinner = NetHub.ClientWinner; if (clientWinner != 0) { string text = clientWinner switch { 2 => "ОХОТНИКИ ПОБЕДИЛИ!", 1 => "РЫБЫ ПОБЕДИЛИ!", _ => "НИЧЬЯ — ОБЕ КОМАНДЫ ПОГИБЛИ", }; GUIStyle val = new GUIStyle(GUI.skin.box) { fontSize = 28, fontStyle = (FontStyle)1, alignment = (TextAnchor)4 }; GUI.Box(new Rect((float)Screen.width * 0.5f - 340f, 100f, 680f, 78f), text, val); } if (NetHub.ClientCountdown >= 0f) { GUI.Box(new Rect((float)Screen.width * 0.5f - 200f, 182f, 400f, 36f), $"Новый раунд через {Mathf.CeilToInt(NetHub.ClientCountdown)} сек."); } else if (NetHub.LocalDead) { GUI.Box(new Rect((float)Screen.width * 0.5f - 220f, (float)(Screen.height - 110), 440f, 46f), NetHub.ClientAutoRestart ? "Вы погибли. Ожидайте окончания раунда." : "Вы погибли. Возрождение через несколько секунд."); } } } } } namespace FishVsHunters.patches { [HarmonyPatch(typeof(Creature), "ServerChangeHp")] internal static class ArmorPatch { private static void Prefix(Creature __instance, ref int damage) { if (damage <= 0 || !((NetworkBehaviour)__instance).IsServerInitialized) { return; } FishRuntime byBody = FishRegistry.GetByBody(__instance); if (byBody != null) { byBody.LastDamageTime = Time.time; float armorPercent = byBody.Stats.ArmorPercent; if (armorPercent > 0f) { damage = Mathf.Max(1, Mathf.RoundToInt((float)damage * (1f - armorPercent / 100f))); } } } } [HarmonyPatch] internal static class CaughtFishPatch { [HarmonyPostfix] [HarmonyPatch(typeof(Item), "SetAttachedRod")] private static void RodPostfix(Item __instance) { PreySpawner.PreserveCatch(__instance); } [HarmonyPostfix] [HarmonyPatch(typeof(Item), "SetSyncedHolder")] private static void HolderPostfix(Item __instance) { PreySpawner.PreserveCatch(__instance); } [HarmonyPostfix] [HarmonyPatch(typeof(ItemManager), "SpawnSavedItem")] private static void LoadedPostfix(Item __result) { PreySpawner.RestoreCatch(__result); } } [HarmonyPatch(/*Could not decode attribute arguments.*/)] internal static class FishInputBlockPatch { private static void Postfix(Player __instance, ref bool __result) { if (MatchController.IsHumanHidden(__instance) && !MatchController.IsIntelligenceFish(__instance)) { __result = true; } } } [HarmonyPatch(typeof(PlayerVitals), "TakeDamage")] internal static class HiddenHumanDamagePatch { private static bool Prefix(PlayerVitals __instance) { Player val = (((Object)(object)__instance != (Object)null) ? ((Component)__instance).GetComponentInParent<Player>() : null); if ((Object)(object)val == (Object)null) { return true; } FishRuntime byOwner = FishRegistry.GetByOwner(((NetworkBehaviour)val).Owner); if (byOwner != null) { return (Object)(object)byOwner.Body == (Object)null; } return true; } } [HarmonyPatch(typeof(PlayerUnderwater), "SetSoundsPlaying")] internal static class HiddenHumanUnderwaterPatch { private static void Prefix(PlayerUnderwater __instance, ref bool shouldPlay) { if (shouldPlay && MatchController.IsHumanHidden(((Component)__instance).GetComponentInParent<Player>())) { shouldPlay = false; } } } [HarmonyPatch] internal static class IslandRetentionPatch { [HarmonyPrefix] [HarmonyPatch(typeof(IslandManager), "QueueRequest")] private static bool QueuePrefix(byte islandId) { return IslandRetention.Queue(islandId); } [HarmonyPrefix] [HarmonyPatch(typeof(IslandManager), "ProcessRequest")] private static void RequestPrefix(byte islandId) { if (islandId == byte.MaxValue) { IslandRetention.Reset(); } IslandRetention.Request(islandId); } [HarmonyPostfix] [HarmonyPatch(typeof(IslandManager), "ProcessRequest")] private static void RequestPostfix(ref IEnumerator __result) { __result = IslandRetention.TrackTravel(__result); } [HarmonyPostfix] [HarmonyPatch(typeof(IslandManager), "OnDestroy")] private static void DestroyPostfix() { IslandRetention.Reset(); } [HarmonyPrefix] [HarmonyPatch(typeof(IslandManager), "UnloadAllIslandsRoutine")] private static bool UnloadPrefix(ref IEnumerator __result) { if (!IslandRetention.KeepDuringTravel) { return true; } __result = KeepIslands(); return false; } private static IEnumerator KeepIslands() { if ((Object)(object)Server.Instance != (Object)null && ((NetworkBehaviour)Server.Instance).IsServerInitialized) { ItemManager.OnIslandUnloaded(); } yield break; } [HarmonyPostfix] [HarmonyPatch(typeof(Island), "Awake")] private static void IslandPostfix(Island __instance) { IslandRetention.Record(__instance); } [HarmonyPostfix] [HarmonyPatch(typeof(Island), "Start")] private static void IslandStartPostfix(Island __instance) { IslandRetention.Record(__instance); } [HarmonyPostfix] [HarmonyPatch(typeof(SpawnManager), "Awake")] private static void SpawnPostfix(SpawnManager __instance) { IslandRetention.Record(__instance); } [HarmonyPostfix] [HarmonyPatch(typeof(IslandManager), "OnIslandChange")] private static void ChangedPostfix() { IslandRetention.SelectCurrent(); } [HarmonyPrefix] [HarmonyPatch(typeof(ItemManager), "RemoveItemsFarAwar")] private static void CleanupPrefix(List<Item> toRemove) { toRemove.RemoveAll(IslandRetention.ProtectFromCleanup); } } [HarmonyPatch] internal static class IslandLoadOncePatch { private static MethodBase TargetMethod() { return AccessTools.EnumeratorMoveNext((MethodBase)AccessTools.Method(typeof(IslandManager), "ProcessRequest", (Type[])null, (Type[])null)); } private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) { MethodInfo original = AccessTools.Method(typeof(SceneManager), "LoadSceneAsync", new Type[2] { typeof(int), typeof(LoadSceneMode) }, (Type[])null); MethodInfo replacement = AccessTools.Method(typeof(IslandRetention), "LoadOnce", (Type[])null, (Type[])null); int replacements = 0; foreach (CodeInstruction instruction in instructions) { if (CodeInstructionExtensions.Calls(instruction, original)) { instruction.operand = replacement; replacements++; } yield return instruction; } if (replacements != 1) { throw new InvalidOperationException("Island loading patch: expected exactly one LoadSceneAsync call."); } } } [HarmonyPatch(typeof(DazedUtils), "AllPlayersNearbyCheck")] internal static class IslandTravelPatch { [HarmonyPostfix] private static void Postfix(Vector3 point, float dist, ref bool __result) { //IL_006e: 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_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) if (__result) { return; } bool flag = false; float num = dist * dist; foreach (Player player in PlayerManager.Players) { if ((Object)(object)player == (Object)null) { return; } if (MatchController.IsHumanHidden(player) || FishRegistry.GetByOwner(((NetworkBehaviour)player).Owner) != null) { flag = true; continue; } Vector3 val = (player.Dying.IsDead ? ((Component)player.Dying.DeadPlayer).transform.position : player.Transform.position) - point; if (((Vector3)(ref val)).sqrMagnitude < num) { continue; } return; } if (flag) { __result = true; } } } [HarmonyPatch] internal static class SessionPatch { [HarmonyPrefix] [HarmonyPatch(typeof(Server), "OnStopClient")] private static void StopClient() { NetHub.ResetClient(); } [HarmonyPrefix] [HarmonyPatch(typeof(Server), "OnStopServer")] private static void BeforeStopServer() { RoundDirector.Flush(); } [HarmonyPostfix] [HarmonyPatch(typeof(Server), "OnStopServer")] private static void StopServer() { RoundDirector.StopServer(); } [HarmonyPrefix] [HarmonyPatch(typeof(SaveManager), "SaveServer")] private static void Save() { RoundDirector.Flush(); } [HarmonyPrefix] [HarmonyPatch(typeof(Server), "RpcLogic___RespawnPlayer___2210451296")] private static bool RespawnPrefix() { return !RoundDirector.LockRespawn; } [HarmonyPrefix] [HarmonyPatch(typeof(PlayerDying), "LocalRespawn")] private static bool LocalRespawnPrefix() { return !NetHub.ClientRoundLocked; } [HarmonyPostfix] [HarmonyPatch(typeof(SaveManager), "CanSaveWorldItem")] private static void WorldItemPostfix(Item item, ref bool __result) { if (__result && (Object)(object)item != (Object)null && (Object)(object)item.Creature != (Object)null && (FishRegistry.GetByBody(item.Creature) != null || PreySpawner.IsWild(item.Creature))) { __result = false; } } } [HarmonyPatch(typeof(SteamManager), "LeaveLobby")] internal static class SteamShutdownPatch { private static void LeaveIfAvailable(CSteamID lobby) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) try { SteamMatchmaking.LeaveLobby(lobby); } catch (InvalidOperationException ex) when (ex.Message == "Steamworks is not initialized.") { Debug.Log((object)"[FishVsHunters] Steam already shut down; finishing local lobby cleanup."); } } private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) { MethodInfo original = AccessTools.Method(typeof(SteamMatchmaking), "LeaveLobby", (Type[])null, (Type[])null); MethodInfo replacement = AccessTools.Method(typeof(SteamShutdownPatch), "LeaveIfAvailable", (Type[])null, (Type[])null); int count = 0; foreach (CodeInstruction instruction in instructions) { if (CodeInstructionExtensions.Calls(instruction, original)) { instruction.operand = replacement; count++; } yield return instruction; } if (count != 1) { throw new InvalidOperationException("Steam shutdown patch expected one LeaveLobby call."); } } } [HarmonyPatch] internal static class VanillaCrabMovementPatch { [HarmonyPrefix] [HarmonyPatch(typeof(Crab), "UpdateMovement")] private static bool CrabPrefix(Crab __instance) { if (!((Object)(object)__instance == (Object)null)) { return (Object)(object)((Component)__instance).GetComponent<FishController>() == (Object)null; } return true; } [HarmonyPrefix] [HarmonyPatch(typeof(Spidercrab), "UpdateMovement")] private static bool SpidercrabPrefix(Spidercrab __instance) { if (!((Object)(object)__instance == (Object)null)) { return (Object)(object)((Component)__instance).GetComponent<FishController>() == (Object)null; } return true; } [HarmonyPrefix] [HarmonyPatch(typeof(BowheadWhale), "UpdateMovement")] private static bool WhalePrefix(BowheadWhale __instance) { if (!((Object)(object)__instance == (Object)null)) { return (Object)(object)((Component)__instance).GetComponent<FishController>() == (Object)null; } return true; } [HarmonyPrefix] [HarmonyPatch(typeof(BowheadWhale), "FixedUpdate")] private static bool WhaleFixedPrefix(BowheadWhale __instance) { if (!((Object)(object)__instance == (Object)null)) { return (Object)(object)((Component)__instance).GetComponent<FishController>() == (Object)null; } return true; } [HarmonyPostfix] [HarmonyPatch(typeof(CrabLegs), "GetHorizontalVelocity")] private static void LegVelocityPostfix(CrabLegs __instance, ref Vector3 __result) { //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) FishController fishController = (((Object)(object)__instance != (Object)null) ? ((Component)__instance).GetComponentInParent<FishController>() : null); if (!((Object)(object)fishController == (Object)null) && !((Object)(object)fishController.Body == (Object)null) && !((Object)(object)((Item)fishController.Body).Rig == (Object)null)) { __result = ((Item)fishController.Body).Rig.linearVelocity; __result.y = 0f; } } } [HarmonyPatch(typeof(Fish), "UpdateMovement")] internal static class VanillaFishFlapPatch { private static bool Prefix(Fish __instance) { if (!((Object)(object)__instance == (Object)null)) { return (Object)(object)((Component)__instance).GetComponent<FishController>() == (Object)null; } return true; } } } namespace FishVsHunters.net { public struct C2S_BecomeFishRequest : IBroadcast { public bool Leave; } public struct S2C_FishAssigned : IBroadcast { public int BodyObjectId; public bool Removed; } public struct S2C_FishPrivateState : IBroadcast { public FishStatsWire Stats; public int NextXp; public int BodyObjectId; public int Level; public int Xp; public byte SkinIndex; public bool AwaitingSkinChoice; public bool AwaitingMutationChoice; public byte Path; public bool AwaitingPathChoice; public byte ThickSkin; public byte Armor; public byte Devourer; public byte Regeneration; public byte SharpTeeth; public byte FastJaw; public byte Bleed; public byte Predator; public byte FastFins; public byte Maneuverability; public byte Dash; public byte ImprovedDash; public byte Growth; public byte Gigantism; public byte Ram; public byte Sonar; public byte ImprovedSonar; public byte BloodScent; public byte TidalWave; public byte Manipulation; } public struct S2C_MutationOffer : IBroadcast { public int BodyObjectId; public byte Count; public byte Option0; public byte Option1; public byte Option2; } public struct C2S_MutationChoice : IBroadcast { public byte OptionIndex; } public struct C2S_BiteReport : IBroadcast { public int XpAmount; public bool WasPreyEaten; } [StructLayout(LayoutKind.Sequential, Size = 1)] public struct C2S_BiteVisual : IBroadcast { } public struct S2C_BiteVisual : IBroadcast { public int BodyObjectId; } [StructLayout(LayoutKind.Sequential, Size = 1)] public struct C2S_Wave : IBroadcast { } public struct S2C_IslandResidency : IBroadcast { public byte Current; public string Builds; } public struct S2C_Wave : IBroadcast { public int BodyObjectId; public byte Stack; } public struct S2C_SkinOffer : IBroadcast { public int BodyObjectId; public byte Count; public byte Option0; public byte Option1; public byte Option2; } public struct C2S_SkinChoice : IBroadcast { public byte OptionIndex; } public struct S2C_PathOffer : IBroadcast { public int BodyObjectId; } public struct C2S_PathChoice : IBroadcast { public byte Path; } public struct S2C_FishBodySwapped : IBroadcast { public int OldObjectId; public int NewObjectId; public byte SkinIndex; } public struct FishEvolutionBroadcast : IBroadcast { public int BodyObjectId; public byte Level; public float Scale; } internal static class CustomSerializers { public static void RegisterAll() { RosterWire.Register(); GenericWriter<S2C_IslandResidency>.SetWrite((Action<Writer, S2C_IslandResidency>)delegate(Writer writer, S2C_IslandResidency v) { writer.WriteUInt8Unpacked(v.Current); writer.WriteString(v.Builds); }); GenericReader<S2C_IslandResidency>.SetRead((Func<Reader, S2C_IslandResidency>)((Reader reader) => new S2C_IslandResidency { Current = reader.ReadUInt8Unpacked(), Builds = reader.ReadStringAllocated() })); GenericWriter<C2S_BecomeFishRequest>.SetWrite((Action<Writer, C2S_BecomeFishRequest>)WriteBecomeFishRequest); GenericReader<C2S_BecomeFishRequest>.SetRead((Func<Reader, C2S_BecomeFishRequest>)ReadBecomeFishRequest); GenericWriter<S2C_FishAssigned>.SetWrite((Action<Writer, S2C_FishAssigned>)WriteFishAssigned); GenericReader<S2C_FishAssigned>.SetRead((Func<Reader, S2C_FishAssigned>)ReadFishAssigned); GenericWriter<S2C_FishPrivateState>.SetWrite((Action<Writer, S2C_FishPrivateState>)WriteFishPrivateState); GenericReader<S2C_FishPrivateState>.SetRead((Func<Reader, S2C_FishPrivateState>)ReadFishPrivateState); GenericWriter<S2C_MutationOffer>.SetWrite((Action<Writer, S2C_MutationOffer>)WriteMutationOffer); GenericReader<S2C_MutationOffer>.SetRead((Func<Reader, S2C_MutationOffer>)ReadMutationOffer); GenericWriter<C2S_MutationChoice>.SetWrite((Action<Writer, C2S_MutationChoice>)WriteMutationChoice); GenericReader<C2S_MutationChoice>.SetRead((Func<Reader, C2S_MutationChoice>)ReadMutationChoice); GenericWriter<C2S_BiteReport>.SetWrite((Action<Writer, C2S_BiteReport>)WriteBiteReport); GenericReader<C2S_BiteReport>.SetRead((Func<Reader, C2S_BiteReport>)ReadBiteReport); GenericWriter<C2S_BiteVisual>.SetWrite((Action<Writer, C2S_BiteVisual>)delegate { }); GenericReader<C2S_BiteVisual>.SetRead((Func<Reader, C2S_BiteVisual>)((Reader reader) => default(C2S_BiteVisual))); GenericWriter<S2C_BiteVisual>.SetWrite((Action<Writer, S2C_BiteVisual>)delegate(Writer writer, S2C_BiteVisual v) { writer.WriteInt32(v.BodyObjectId); }); GenericReader<S2C_BiteVisual>.SetRead((Func<Reader, S2C_BiteVisual>)((Reader reader) => new S2C_BiteVisual { BodyObjectId = reader.ReadInt32() })); GenericWriter<C2S_Wave>.SetWrite((Action<Writer, C2S_Wave>)delegate { }); GenericReader<C2S_Wave>.SetRead((Func<Reader, C2S_Wave>)((Reader reader) => default(C2S_Wave))); GenericWriter<S2C_Wave>.SetWrite((Action<Writer, S2C_Wave>)delegate(Writer writer, S2C_Wave v) { writer.WriteInt32(v.BodyObjectId); writer.WriteUInt8Unpacked(v.Stack); }); GenericReader<S2C_Wave>.SetRead((Func<Reader, S2C_Wave>)((Reader reader) => new S2C_Wave { BodyObjectId = reader.ReadInt32(), Stack = reader.ReadUInt8Unpacked() })); GenericWriter<FishEvolutionBroadcast>.SetWrite((Action<Writer, FishEvolutionBroadcast>)WriteFishEvolution); GenericReader<FishEvolutionBroadcast>.SetRead((Func<Reader, FishEvolutionBroadcast>)ReadFishEvolution); GenericWriter<S2C_SkinOffer>.SetWrite((Action<Writer, S2C_SkinOffer>)WriteSkinOffer); GenericReader<S2C_SkinOffer>.SetRead((Func<Reader, S2C_SkinOffer>)ReadSkinOffer); GenericWriter<C2S_SkinChoice>.SetWrite((Action<Writer, C2S_SkinChoice>)WriteSkinChoice); GenericReader<C2S_SkinChoice>.SetRead((Func<Reader, C2S_SkinChoice>)ReadSkinChoice); GenericWriter<S2C_FishBodySwapped>.SetWrite((Action<Writer, S2C_FishBodySwapped>)WriteFishBodySwapped); GenericReader<S2C_FishBodySwapped>.SetRead((Func<Reader, S2C_FishBodySwapped>)ReadFishBodySwapped); GenericWriter<S2C_PathOffer>.SetWrite((Action<Writer, S2C_PathOffer>)delegate(Writer writer, S2C_PathOffer v) { writer.WriteInt32(v.BodyObjectId); }); GenericReader<S2C_PathOffer>.SetRead((Func<Reader, S2C_PathOffer>)((Reader reader) => new S2C_PathOffer { BodyObjectId = reader.ReadInt32() })); GenericWriter<C2S_PathChoice>.SetWrite((Action<Writer, C2S_PathChoice>)delegate(Writer writer, C2S_PathChoice v) { writer.WriteUInt8Unpacked(v.Path); }); GenericReader<C2S_PathChoice>.SetRead((Func<Reader, C2S_PathChoice>)((Reader reader) => new C2S_PathChoice { Path = reader.ReadUInt8Unpacked() })); } private static void WriteBecomeFishRequest(Writer writer, C2S_BecomeFishRequest v) { writer.WriteBoolean(v.Leave); } private static C2S_BecomeFishRequest ReadBecomeFishRequest(Reader reader) { return new C2S_BecomeFishRequest { Leave = reader.ReadBoolean() }; } private static void WriteFishAssigned(Writer writer, S2C_FishAssigned v) { writer.WriteInt32(v.BodyObjectId); writer.WriteBoolean(v.Removed); } private static S2C_FishAssigned ReadFishAssigned(Reader reader) { return new S2C_FishAssigned { BodyObjectId = reader.ReadInt32(), Removed = reader.ReadBoolean() }; } private static void WriteFishPrivateState(Writer writer, S2C_FishPrivateState v) { FishStatsWire.Write(writer, v.Stats); writer.WriteInt32(v.NextXp); writer.WriteInt32(v.BodyObjectId); writer.WriteInt32(v.Level); writer.WriteInt32(v.Xp); writer.WriteUInt8Unpacked(v.SkinIndex); writer.WriteBoolean(v.AwaitingSkinChoice); writer.WriteBoolean(v.AwaitingMutationChoice); writer.WriteUInt8Unpacked(v.Path); writer.WriteBoolean(v.AwaitingPathChoice); writer.WriteUInt8Unpacked(v.ThickSkin); writer.WriteUInt8Unpacked(v.Armor); writer.WriteUInt8Unpacked(v.Devourer); writer.WriteUInt8Unpacked(v.Regeneration); writer.WriteUInt8Unpacked(v.SharpTeeth); writer.WriteUInt8Unpacked(v.FastJaw); writer.WriteUInt8Unpacked(v.Bleed); writer.WriteUInt8Unpacked(v.Predator); writer.WriteUInt8Unpacked(v.FastFins); writer.WriteUInt8Unpacked(v.Maneuverability); writer.WriteUInt8Unpacked(v.Dash); writer.WriteUInt8Unpacked(v.ImprovedDash); writer.WriteUInt8Unpacked(v.Growth); writer.WriteUInt8Unpacked(v.Gigantism); writer.WriteUInt8Unpacked(v.Ram); writer.WriteUInt8Unpacked(v.Sonar); writer.WriteUInt8Unpacked(v.ImprovedSonar); writer.WriteUInt8Unpacked(v.BloodScent); writer.WriteUInt8Unpacked(v.TidalWave); writer.WriteUInt8Unpacked(v.Manipulation); } private static S2C_FishPrivateState ReadFishPrivateState(Reader reader) { return new S2C_FishPrivateState { Stats = FishStatsWire.Read(reader), NextXp = reader.ReadInt32(), BodyObjectId = reader.ReadInt32(), Level = reader.ReadInt32(), Xp = reader.ReadInt32(), SkinIndex = reader.ReadUInt8Unpacked(), AwaitingSkinChoice = reader.ReadBoolean(), AwaitingMutationChoice = reader.ReadBoolean(), Path = reader.ReadUInt8Unpacked(), AwaitingPathChoice = reader.ReadBoolean(), ThickSkin = reader.ReadUInt8Unpacked(), Armor = reader.ReadUInt8Unpacked(), Devourer = reader.ReadUInt8Unpacked(), Regeneration = reader.ReadUInt8Unpacked(), SharpTeeth = reader.ReadUInt8Unpacked(), FastJaw = reader.ReadUInt8Unpacked(), Bleed = reader.ReadUInt8Unpacked(), Predator = reader.ReadUInt8Unpacked(), FastFins = reader.ReadUInt8Unpacked(), Maneuverability = reader.ReadUInt8Unpacked(), Dash = reader.ReadUInt8Unpacked(), ImprovedDash = reader.ReadUInt8Unpacked(), Growth = reader.ReadUInt8Unpacked(), Gigantism = reader.ReadUInt8Unpacked(), Ram = reader.ReadUInt8Unpacked(), Sonar = reader.ReadUInt8Unpacked(), ImprovedSonar = reader.ReadUInt8Unpacked(), BloodScent = reader.ReadUInt8Unpacked(), TidalWave = reader.ReadUInt8Unpacked(), Manipulation = reader.ReadUInt8Unpacked() }; } private static void WriteMutationOffer(Writer writer, S2C_MutationOffer v) { writer.WriteInt32(v.BodyObjectId); writer.WriteUInt8Unpacked(v.Count); writer.WriteUInt8Unpacked(v.Option0); writer.WriteUInt8Unpacked(v.Option1); writer.WriteUInt8Unpacked(v.Option2); } private static S2C_MutationOffer ReadMutationOffer(Reader reader) { return new S2C_MutationOffer { BodyObjectId = reader.ReadInt32(), Count = reader.ReadUInt8Unpacked(), Option0 = reader.ReadUInt8Unpacked(), Option1 = reader.ReadUInt8Unpacked(), Option2 = reader.ReadUInt8Unpacked() }; } private static void WriteMutationChoice(Writer writer, C2S_MutationChoice v) { writer.WriteUInt8Unpacked(v.OptionIndex); } private static C2S_MutationChoice ReadMutationChoice(Reader reader) { return new C2S_MutationChoice { OptionIndex = reader.ReadUInt8Unpacked() }; } private static void WriteBiteReport(Writer writer, C2S_BiteReport v) { writer.WriteInt32(v.XpAmount); writer.WriteBoolean(v.WasPreyEaten); } private static C2S_BiteReport ReadBiteReport(Reader reader) { return new C2S_BiteReport { XpAmount = reader.ReadInt32(), WasPreyEaten = reader.ReadBoolean() }; } private static void WriteFishEvolution(Writer writer, FishEvolutionBroadcast v) { writer.WriteInt32(v.BodyObjectId); writer.WriteUInt8Unpacked(v.Level); writer.WriteSingle(v.Scale); } private static FishEvolutionBroadcast ReadFishEvolution(Reader reader) { return new FishEvolutionBroadcast { BodyObjectId = reader.ReadInt32(), Level = reader.ReadUInt8Unpacked(), Scale = reader.ReadSingle() }; } private static void WriteSkinOffer(Writer writer, S2C_SkinOffer v) { writer.WriteInt32(v.BodyObjectId); writer.WriteUInt8Unpacked(v.Count); writer.WriteUInt8Unpacked(v.Option0); writer.WriteUInt8Unpacked(v.Option1); writer.WriteUInt8Unpacked(v.Option2); } private static S2C_SkinOffer ReadSkinOffer(Reader reader) { return new S2C_SkinOffer { BodyObjectId = reader.ReadInt32(), Count = reader.ReadUInt8Unpacked(), Option0 = reader.ReadUInt8Unpacked(), Option1 = reader.ReadUInt8Unpacked(), Option2 = reader.ReadUInt8Unpacked() }; } private static void WriteSkinChoice(Writer writer, C2S_SkinChoice v) { writer.WriteUInt8Unpacked(v.OptionIndex); } private static C2S_SkinChoice ReadSkinChoice(Reader reader) { return new C2S_SkinChoice { OptionIndex = reader.ReadUInt8Unpacked() }; } private static void WriteFishBodySwapped(Writer writer, S2C_FishBodySwapped v) { writer.WriteInt32(v.OldObjectId); writer.WriteInt32(v.NewObjectId); writer.WriteUInt8Unpacked(v.SkinIndex); } private static S2C_FishBodySwapped ReadFishBodySwapped(Reader reader) { return new S2C_FishBodySwapped { OldObjectId = reader.ReadInt32(), NewObjectId = reader.ReadInt32(), SkinIndex = reader.ReadUInt8Unpacked() }; } } public struct FishStatsWire { public int Level; public float MaxHp; public float ArmorPercent; public float BiteDamage; public float BiteCooldown; public float BiteRange; public float BiteRadius; public float SwimSpeed; public float TurnSpeedDegPerSec; public float Acceleration; public float Scale; public float Mass; public float MassMultiplier; public bool DashUnlocked; public float DashCooldown; public float DashForce; public bool SonarUnlocked; public float SonarCooldown; public float SonarDuration; public bool RamUnlocked; public bool BleedUnlocked; public float BleedDamagePerTick; public bool PredatorUnlocked; public float PredatorBonusDamagePercent; public bool DevourerUnlocked; public float DevourerHealPercent; public bool RegenerationUnlocked; public float RegenerationHpPerTick; public bool BloodScentUnlocked; internal static FishStatsWire From(CalculatedFishStats value) { return new FishStatsWire { Level = value.Level, MaxHp = value.MaxHp, ArmorPercent = value.ArmorPercent, BiteDamage = value.BiteDamage, BiteCooldown = value.BiteCooldown, BiteRange = value.BiteRange, BiteRadius = value.BiteRadius, SwimSpeed = value.SwimSpeed, TurnSpeedDegPerSec = value.TurnSpeedDegPerSec, Acceleration = value.Acceleration, Scale = value.Scale, Mass = value.Mass, MassMultiplier = value.MassMultiplier, DashUnlocked = value.DashUnlocked, DashCooldown = value.DashCooldown, DashForce = value.DashForce, SonarUnlocked = value.SonarUnlocked, SonarCooldown = value.SonarCooldown, SonarDuration = value.SonarDuration, RamUnlocked = value.RamUnlocked, BleedUnlocked = value.BleedUnlocked, BleedDamagePerTick = value.BleedDamagePerTick, PredatorUnlocked = value.PredatorUnlocked, PredatorBonusDamagePercent = value.PredatorBonusDamagePercent, DevourerUnlocked = value.DevourerUnlocked, DevourerHealPercent = value.DevourerHealPercent, RegenerationUnlocked = value.RegenerationUnlocked, RegenerationHpPerTick = value.RegenerationHpPerTick, BloodScentUnlocked = value.BloodScentUnlocked }; } internal CalculatedFishStats ToStats() { return new CalculatedFishStats { Level = Level, MaxHp = MaxHp, ArmorPercent = ArmorPercent, BiteDamage = BiteDamage, BiteCooldown = BiteCooldown, BiteRange = BiteRange, BiteRadius = BiteRadius, SwimSpeed = SwimSpeed, TurnSpeedDegPerSec = TurnSpeedDegPerSec, Acceleration = Acceleration, Scale = Scale, Mass = Mass, MassMultiplier = MassMultiplier, DashUnlocked = DashUnlocked, DashCooldown = DashCooldown, DashForce = DashForce, SonarUnlocked = SonarUnlocked, SonarCooldown = SonarCooldown, SonarDuration = SonarDuration, RamUnlocked = RamUnlocked, BleedUnlocked = BleedUnlocked, BleedDamagePerTick = BleedDamagePerTick, PredatorUnlocked = PredatorUnlocked, PredatorBonusDamagePercent = PredatorBonusDamagePercent, DevourerUnlocked = DevourerUnlocked, DevourerHealPercent = DevourerHealPercent, RegenerationUnlocked = RegenerationUnlocked, RegenerationHpPerTick = RegenerationHpPerTick, BloodScentUnlocked = BloodScentUnlocked }; } internal static void Write(Writer writer, FishStatsWire value) { writer.WriteInt32(value.Level); writer.WriteSingle(value.MaxHp); writer.WriteSingle(value.ArmorPercent); writer.WriteSingle(value.BiteDamage); writer.WriteSingle(value.BiteCooldown); writer.WriteSingle(value.BiteRange); writer.WriteSingle(value.BiteRadius); writer.WriteSingle(value.SwimSpeed); writer.WriteSingle(value.TurnSpeedDegPerSec); writer.WriteSingle(value.Acceleration); writer.WriteSingle(value.Scale); writer.WriteSingle(value.Mass); writer.WriteSingle(value.MassMultiplier); writer.WriteBoolean(value.DashUnlocked); writer.WriteSingle(value.DashCooldown); writer.WriteSingle(value.DashForce); writer.WriteBoolean(value.SonarUnlocked); writer.WriteSingle(value.SonarCooldown); writer.WriteSingle(value.SonarDuration); writer.WriteBoolean(value.RamUnlocked); writer.WriteBoolean(value.BleedUnlocked); writer.WriteSingle(value.BleedDamagePerTick); writer.WriteBoolean(value.PredatorUnlocked); writer.WriteSingle(value.PredatorBonusDamagePercent); writer.WriteBoolean(value.DevourerUnlocked); writer.WriteSingle(value.DevourerHealPercent); writer.WriteBoolean(value.RegenerationUnlocked); writer.WriteSingle(value.RegenerationHpPerTick); writer.WriteBoolean(value.BloodScentUnlocked); } internal static FishStatsWire Read(Reader reader) { return new FishStatsWire { Level = reader.ReadInt32(), MaxHp = reader.ReadSingle(), ArmorPercent = reader.ReadSingle(), BiteDamage = reader.ReadSingle(), BiteCooldown = reader.ReadSingle(), BiteRange = reader.ReadSingle(), BiteRadius = reader.ReadSingle(), SwimSpeed = reader.ReadSingle(), TurnSpeedDegPerSec = reader.ReadSingle(), Acceleration = reader.ReadSingle(), Scale = reader.ReadSingle(), Mass = reader.ReadSingle(), MassMultiplier = reader.ReadSingle(), DashUnlocked = reader.ReadBoolean(), DashCooldown = reader.ReadSingle(), DashForce = reader.ReadSingle(), SonarUnlocked = reader.ReadBoolean(), SonarCooldown = reader.ReadSingle(), SonarDuration = reader.ReadSingle(), RamUnlocked = reader.ReadBoolean(), BleedUnlocked = reader.ReadBoolean(), BleedDamagePerTick = reader.ReadSingle(), PredatorUnlocked = reader.ReadBoolean(), PredatorBonusDamagePercent = reader.ReadSingle(), DevourerUnlocked = reader.ReadBoolean(), DevourerHealPercent = reader.ReadSingle(), RegenerationUnlocked = reader.ReadBoolean(), RegenerationHpPerTick = reader.ReadSingle(), BloodScentUnlocked = reader.ReadBoolean() }; } } internal static class NetHub { private static bool _registered; private static ClientManager _clientManager; private static ServerManager _serverManager; private static readonly List<Action> Unregister = new List<Action>(); private static S2C_Roster? _roster; private static int _revision; private static float _countdownUntil = -1f; private static float _winnerUntil; private static readonly Dictionary<int, S2C_FishPrivateState> _pendingPrivateStates = new Dictionary<int, S2C_FishPrivateState>(); private static readonly Dictionary<int, S2C_MutationOffer> _pendingMutationOffers = new Dictionary<int, S2C_MutationOffer>(); private static readonly Dictionary<int, S2C_SkinOffer> _pendingSkinOffers = new Dictionary<int, S2C_SkinOffer>(); private static readonly Dictionary<int, S2C_PathOffer> _pendingPathOffers = new Dictionary<int, S2C_PathOffer>(); private static readonly Dictionary<int, FishEvolutionBroadcast> _pendingEvolutions = new Dictionary<int, FishEvolutionBroadcast>(); private static readonly Dictionary<int, S2C_BiteVisual> _pendingBiteVisuals = new Dictionary<int, S2C_BiteVisual>(); public static byte ClientWinner { get { if (!_roster.HasValue || !_roster.Value.AutoRestart || !(Time.time < _winnerUntil)) { return 0; } return _roster.Value.Winner; } } public static bool ClientRoundLocked { get { if (_roster.HasValue) { return _roster.Value.RoundLocked; } return false; } } public static bool ClientAutoRestart => _roster?.AutoRestart ?? true; public static int ClientRound => _roster?.Round ?? 1; public static float ClientCountdown { get { if (!(_countdownUntil < 0f)) { return Mathf.Max(0f, _countdownUntil - Time.time); } return -1f; } } public static bool HasRoster => _roster.HasValue; public static bool LocalDead { get { if (!_roster.HasValue || (Object)(object)Player.LocalPlayer == (Object)null || ((NetworkBehaviour)Player.LocalPlayer).Owner == (NetworkConnection)null) { return false; } FishRosterEntry[] fish = _roster.Value.Fish; for (int i = 0; i < fish.Length; i++) { FishRosterEntry fishRosterEntry = fish[i]; if (fishRosterEntry.OwnerId == ((NetworkBehaviour)Player.LocalPlayer).Owner.ClientId) { return fishRosterEntry.Dead; } } return Player.LocalPlayer.Vitals.Health <= 0; } } private static void RegisterClient<T>(Action<T, Channel> handler) where T : struct, IBroadcast { ClientManager manager = _clientManager; manager.RegisterBroadcast<T>(handler); Unregister.Add(delegate { if ((Object)(object)manager != (Object)null) { manager.UnregisterBroadcast<T>(handler); } }); } private static void RegisterServer<T>(Action<NetworkConnection, T, Channel> handler) where T : struct, IBroadcast { ServerManager manager = _serverManager; manager.RegisterBroadcast<T>(handler, true); Unregister.Add(delegate { if ((Object)(object)manager != (Object)null) { manager.UnregisterBroadcast<T>(handler); } }); } internal static void ResetClient() { MatchController.ResetClient(); _roster = null; _countdownUntil = -1f; ClearPending(); IslandRetention.StopSession(); OceanSwell.Reset(); } private static void ClearPending() { _pendingPrivateStates.Clear(); _pendingMutationOffers.Clear(); _pendingSkinOffers.Clear(); _pendingPathOffers.Clear(); _pendingEvolutions.Clear(); _pendingBiteVisuals.Clear(); } private static S2C_Roster BuildRoster() { List<FishRosterEntry> list = new List<FishRosterEntry>(); foreach (FishRuntime item in FishRegistry.All) { if (!(item.OwnerConn == (NetworkConnection)null) && item.OwnerConn.IsActive) { CalculatedFishStats stats = item.Stats; list.Add(new FishRosterEntry { OwnerId = item.OwnerConn.ClientId, BodyId = (((Object)(object)item.Body != (Object)null) ? ((NetworkBehaviour)item.Body).ObjectId : (-1)), Dead = !item.Alive, Scale = stats.Scale, MaxHp = Mathf.RoundToInt(stats.MaxHp), Mass = stats.Mass }); } } return new S2C_Roster { Session = RoundDirector.Session, Revision = ++_revision, Round = RoundDirector.Round, AutoRestart = RoundDirector.AutoRestart, RoundLocked = RoundDirector.LockRespawn, Countdown = RoundDirector.Countdown, Winner = RoundDirector.Winner, WinnerSeconds = RoundDirector.WinnerSeconds, Fish = list.ToArray() }; } internal static void BroadcastRoster() { if (InstanceFinder.IsServerStarted && RoundDirector.Running) { InstanceFinder.ServerManager.Broadcast<S2C_Roster>(BuildRoster(), true, (Channel)0); } } private static void OnAuthenticationResult(NetworkConnection conn, bool authenticated) { if (authenticated && !(conn == (NetworkConnection)null) && RoundDirector.Running) { InstanceFinder.ServerManager.Broadcast<S2C_Roster>(conn, BuildRoster(), true, (Channel)0); FishRuntime byOwner = FishRegistry.GetByOwner(conn); if (byOwner != null) { SendPrivateState(byOwner); } } } private static void OnRoster(S2C_Roster msg, Channel channel) { if (!string.IsNullOrEmpty(msg.Session) && msg.Fish != null && (!_roster.HasValue || !(_roster.Value.Session == msg.Session) || msg.Revision > _roster.Value.Revision)) { if (!_roster.HasValue || _roster.Value.Session != msg.Session) { MatchController.ResetClient(); ClearPending(); } _roster = msg; _countdownUntil = ((msg.Countdown < 0f) ? (-1f) : (Time.time + msg.Countdown)); _winnerUntil = Time.time + msg.WinnerSeconds; HashSet<int> hashSet = new HashSet<int>(); FishRosterEntry[] fish = msg.Fish; for (int i = 0; i < fish.Length; i++) { FishRosterEntry fishRosterEntry = fish[i]; hashSet.Add(fishRosterEntry.BodyId); } Creature localFishBody = MatchController.LocalFishBody; if ((Object)(object)localFishBody != (Object)null && _pendingPrivateStates.ContainsKey(((NetworkBehaviour)localFishBody).ObjectId) && !hashSet.Contains(((NetworkBehaviour)localFishBody).ObjectId)) { Debug.LogWarning((object)("[FishVsHunters] A newer roster (revision " + msg.Revision + ") arrived and discarded our own still-unapplied private state (body=" + ((NetworkBehaviour)localFishBody).ObjectId + ") - bite/movement stay locked until the next one arrives.")); } Prune(_pendingPrivateStates, hashSet); Prune(_pendingMutationOffers, hashSet); Prune(_pendingSkinOffers, hashSet); Prune(_pendingPathOffers, hashSet); Prune(_pendingEvolutions, hashSet); _pendingBiteVisuals.Clear(); ProcessPending(); } } private static void Prune<T>(Dictionary<int, T> queue, HashSet<int> valid) { foreach (int item in new List<int>(queue.Keys)) { if (!valid.Contains(item)) { queue.Remove(item); } } } private static void OnIslandResidency(S2C_IslandResidency msg, Channel channel) { IslandRetention.Receive(msg); } private static void OnPrepareRespawn(S2C_PrepareRespawn msg, Channel channel) { RoundDirector.PrepareRespawn(msg.OwnerId); } private static void OnClientState(ClientConnectionStateArgs args) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Invalid comparison between Unknown and I4 if ((int)args.ConnectionState == 1) { ResetClient(); } } public static void EnsureRegistered() { if (_registered && (_clientManager != InstanceFinder.ClientManager || _serverManager != InstanceFinder.ServerManager)) { foreach (Action item in Unregister) { item(); } Unregister.Clear(); _registered = false; ResetClient(); } if (_registered) { ProcessPending(); } else { if ((Object)(object)InstanceFinder.ClientManager == (Object)null || (Object)(object)InstanceFinder.ServerManager == (Object)null) { return; } _clientManager = InstanceFinder.ClientManager; _serverManager = InstanceFinder.ServerManager; ClientManager subscribedClient = _clientManager; subscribedClient.OnClientConnectionState += OnClientState; Unregister.Add(delegate { if ((Object)(object)subscribedClient != (Object)null) { subscribedClient.OnClientConnectionState -= OnClientState; } }); ServerManager subscribedServer = _serverManager; subscribedServer.OnAuthenticationResult += OnAuthenticationResult; Unregister.Add(delegate { if ((Object)(object)subscribedServer != (Object)null) { subscribedServer.OnAuthenticationResult -= OnAuthenticationResult; } }); RegisterClient<FishEvolutionBroadcast>((Action<FishEvolutionBroadcast, Channel>)OnFishEvolutionBroadcast); RegisterClient<S2C_FishPrivateState>((Action<S2C_FishPrivateState, Channel>)OnFishPrivateState); RegisterClient<S2C_MutationOffer>((Action<S2C_MutationOffer, Channel>)OnMutationOffer); RegisterClient<S2C_SkinOffer>((Action<S2C_SkinOffer, Channel>)OnSkinOffer); RegisterClient<S2C_PathOffer>((Action<S2C_PathOffer, Channel>)OnPathOffer); RegisterClient<S2C_BiteVisual>((Action<S2C_BiteVisual, Channel>)OnBiteVisual); RegisterClient<S2C_Wave>((Action<S2C_Wave, Channel>)OnWaveVisual); RegisterClient<S2C_IslandResidency>((Action<S2C_IslandResidency, Channel>)OnIslandResidency); RegisterServer<C2S_BecomeFishRequest>((Action<NetworkConnection, C2S_BecomeFishRequest, Channel>)OnBecomeFishRequest); RegisterServer<C2S_MutationChoice>((Action<NetworkConnection, C2S_MutationChoice, Channel>)OnMutationChoice); RegisterServer<C2S_BiteReport>((Action<NetworkConnection, C2S_BiteReport, Channel>)OnBiteReport); RegisterServer<C2S_SkinChoice>((Action<NetworkConnection, C2S_SkinChoice, Channel>)OnSkinChoice); RegisterServer<C2S_PathChoice>((Action<NetworkConnection, C2S_PathChoice, Channel>)OnPathChoice); RegisterServer<C2S_BiteVisual>((Action<NetworkConnection, C2S_BiteVisual, Channel>)OnBiteVisualRequest); RegisterServer<C2S_Wave>((Action<NetworkConnection, C2S_Wave, Channel>)OnWaveRequest); RegisterClient<S2C_Roster>((Action<S2C_Roster, Channel>)OnRoster); RegisterClient<S2C_PrepareRespawn>((Action<S2C_PrepareRespawn, Channel>)OnPrepareRespawn); _registered = true; ProcessPending(); } } private static void ProcessPending() { if (_roster.HasValue) { MatchController.ReconcileRoster(_roster.Value.Fish); } ProcessControllerQueue(_pendingPrivateStates, delegate(FishController fc, S2C_FishPrivateState msg) { fc.ApplyPrivateState(msg); }); ProcessControllerQueue(_pendingMutationOffers, delegate(FishController fc, S2C_MutationOffer msg) { fc.ApplyMutationOffer(msg); }); ProcessControllerQueue(_pendingSkinOffers, delegate(FishController fc, S2C_SkinOffer msg) { fc.ApplySkinOffer(msg); }); ProcessControllerQueue(_pendingPathOffers, delegate(FishController fc, S2C_PathOffer msg) { fc.ApplyPathOffer(); }); ProcessBodyQueue(_pendingEvolutions, delegate(Creature body, FishEvolutionBroadcast msg) { //IL_0006: 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) ((Component)body).transform.localScale = Vector3.one * msg.Scale; }); ProcessBodyQueue(_pendingBiteVisuals, PlayBiteVisual); } private static void ProcessControllerQueue<T>(Dictionary<int, T> queue, Action<FishController, T> apply) { if (queue.Count == 0) { return; } List<int> list = new List<int>(); foreach (KeyValuePair<int, T> item in queue) { Creature val = ResolveBody(item.Key); FishController fishController = (((Object)(object)val != (Object)null) ? ((Component)val).GetComponent<FishController>() : null); if (!((Object)(object)fishController == (Object)null)) { apply(fishController, item.Value); list.Add(item.Key); } } foreach (int item2 in list) { queue.Remove(item2); } } private static void ProcessBodyQueue<T>(Dictionary<int, T> queue, Action<Creature, T> apply) { if (queue.Count == 0) { return; } List<int> list = new List<int>(); foreach (KeyValuePair<int, T> item in queue) { Creature val = ResolveBody(item.Key); if (!((Object)(object)val == (Object)null)) { apply(val, item.Value); list.Add(item.Key); } } foreach (int item2 in list) { queue.Remove(item2); } } internal static Creature ResolveBody(int objectId) { ClientManager clientManager = InstanceFinder.ClientManager; if (((clientManager != null) ? clientManager.Objects : null) == null) { return null; } if (!((ManagedObjects)InstanceFinder.ClientManager.Objects).Spawned.TryGetValue(objectId, out var value) || (Object)(object)value == (Object)null) { return null; } return ((Component)value).GetComponent<Creature>(); } public static void SendBecomeFishRequest(bool leave) { if ((Object)(object)InstanceFinder.ClientManager != (Object)null && InstanceFinder.IsClientStarted) { InstanceFinder.ClientManager.Broadcast<C2S_BecomeFishRequest>(new C2S_BecomeFishRequest { Leave = leave }, (Channel)0); } } public static void SendMutationChoice(byte optionIndex) { if ((Object)(object)InstanceFinder.ClientManager != (Object)null && InstanceFinder.IsClientStarted) { InstanceFinder.ClientManager.Broadcast<C2S_MutationChoice>(new C2S_MutationChoice { OptionIndex = optionIndex }, (Channel)0); } } public static void SendBiteReport(int xpAmount, bool wasPreyEaten) { if ((Object)(object)InstanceFinder.ClientManager != (Object)null && InstanceFinder.IsClientStarted) { InstanceFinder.ClientManager.Broadcast<C2S_BiteReport>(new C2S_BiteReport { XpAmount = xpAmount, WasPreyEaten = wasPreyEaten }, (Channel)0); } } public static void SendBiteVisual() { if ((Object)(object)InstanceFinder.ClientManager != (Object)null && InstanceFinder.IsClientStarted) { InstanceFinder.ClientManager.Broadcast<C2S_BiteVisual>(default(C2S_BiteVisual), (Channel)0); } } public static void SendWave() { if ((Object)(object)InstanceFinder.ClientManager != (Object)null && InstanceFinder.IsClientStarted) { InstanceFinder.ClientManager.Broadcast<C2S_Wave>(default(C2S_Wave), (Channel)0); } } public static void SendSkinChoice(byte optionIndex) { if ((Object)(object)InstanceFinder.ClientManager != (Object)null && InstanceFinder.IsClientStarted) { InstanceFinder.ClientManager.Broadcast<C2S_SkinChoice>(new C2S_SkinChoice { OptionIndex = optionIndex }, (Channel)0); } } public static void SendPathChoice(byte path) { if ((Object)(object)InstanceFinder.ClientManager != (Object)null && InstanceFinder.IsClientStarted) { InstanceFinder.ClientManager.Broadcast<C2S_PathChoice>(new C2S_PathChoice { Path = path }, (Channel)0); } } public static void SendEvolution(Creature body, byte level, float scale) { if (!((Object)(object)body == (Object)null) && InstanceFinder.IsServerStarted) { InstanceFinder.ServerManager.Broadcast<FishEvolutionBroadcast>(new FishEvolutionBroadcast { BodyObjectId = ((NetworkBehaviour)body).ObjectId, Level = level, Scale = scale }, true, (Channel)0); } } public static void SendFishAssigned(int bodyObjectId, bool removed) { BroadcastRoster(); } public static void SendPrivateState(FishRuntime runtime) { if (!((Object)(object)runtime?.Body == (Object)null) && InstanceFinder.IsServerStarted && !(runtime.OwnerConn == (NetworkConnection)null)) { RoundDirector.Capture(runtime); S2C_FishPrivateState s2C_FishPrivateState = new S2C_FishPrivateState { Stats = FishStatsWire.From(runtime.Stats), NextXp = runtime.ThresholdForNextLevel, BodyObjectId = ((NetworkBehaviour)runtime.Body).ObjectId, Level = runtime.Level, Xp = runtime.Xp, SkinIndex = (byte)Mathf.Max(0, SkinCatalog.IndexOf(runtime.SkinId)), AwaitingSkinChoice = runtime.AwaitingSkinChoice, AwaitingMutationChoice = (runtime.PendingChoice != null), Path = (byte)runtime.Path, AwaitingPathChoice = runtime.AwaitingPathChoice, ThickSkin = (byte)runtime.StackOf(MutationId.ThickSkin), Armor = (byte)runtime.StackOf(MutationId.Armor), Devourer = (byte)runtime.StackOf(MutationId.Devourer), Regeneration = (byte)runtime.StackOf(MutationId.Regeneration), SharpTeeth = (byte)runtime.StackOf(MutationId.SharpTeeth), FastJaw = (byte)runtime.StackOf(MutationId.FastJaw), Bleed = (byte)runtime.StackOf(MutationId.Bleed), Predator = (byte)runtime.StackOf(MutationId.Predator), FastFins = (byte)runtime.StackOf(MutationId.FastFins), Maneuverability = (byte)runtime.StackOf(MutationId.Maneuverability), Dash = (byte)runtime.StackOf(MutationId.Dash), ImprovedDash = (byte)runtime.StackOf(MutationId.ImprovedDash), Growth = (byte)runtime.StackOf(MutationId.Growth), Gigantism = (byte)runtime.StackOf(MutationId.Gigantism), Ram = (byte)runtime.StackOf(MutationId.Ram), Sonar = (byte)runtime.StackOf(MutationId.Sonar), ImprovedSonar = (byte)runtime.StackOf(MutationId.ImprovedSonar), BloodScent = (byte)runtime.StackOf(MutationId.BloodScent), TidalWave = (byte)runtime.StackOf(MutationId.TidalWave), Manipulation = (byte)runtime.StackOf(MutationId.Manipulation) }; InstanceFinder.ServerManager.Broadcast<S2C_FishPrivateState>(runtime.OwnerConn, s2C_FishPrivateState, true, (Channel)0); } } public static void SendMutationOffer(FishRuntime runtime) { if (!((Object)(object)runtime?.Body == (Object)null) && runtime.PendingChoice != null && InstanceFinder.IsServerStarted && !(runtime.OwnerConn == (NetworkConnection)null)) { MutationId[] pendingChoice = runtime.PendingChoice; InstanceFinder.ServerManager.Broadcast<S2C_MutationOffer>(runtime.OwnerConn, new S2C_MutationOffer { BodyObjectId = ((NetworkBehaviour)runtime.Body).ObjectId, Count = (byte)pendingChoice.Length, Option0 = (byte)((pendingChoice.Length != 0) ? pendingChoice[0] : MutationId.ThickSkin), Option1 = (byte)((pendingChoice.Length > 1) ? pendingChoice[1] : MutationId.ThickSkin), Option2 = (byte)((pendingChoice.Length > 2) ? pendingChoice[2] : MutationId.ThickSkin) }, true, (Channel)0); } } public static void SendSkinOffer(FishRuntime runtime) { if (!((Object)(object)runtime?.Body == (Object)null) && runtime.PendingSkinChoice != null && InstanceFinder.IsServerStarted && !(runtime.OwnerConn == (NetworkConnection)null)) { string[] pendingSkinChoice = runtime.PendingSkinChoice; InstanceFinder.ServerManager.Broadcast<S2C_SkinOffer>(runtime.OwnerConn, new S2C_SkinOffer { BodyObjectId = ((NetworkBehaviour)runtime.Body).ObjectId, Count = (byte)Mathf.Min(pendingSkinChoice.Length, 3), Option0 = (byte)((pendingSkinChoice.Length != 0) ? ((byte)Mathf.Max(0, SkinCatalog.IndexOf(pendingSkinChoice[0]))) : 0), Option1 = (byte)((pendingSkinChoice.Length > 1) ? ((byte)Mathf.Max(0, SkinCatalog.IndexOf(pendingSkinChoice[1]))) : 0), Option2 = (byte)((pendingSkinChoice.Length > 2) ? ((byte)Mathf.Max(0, SkinCatalog.IndexOf(pendingSkinChoice[2]))) : 0) }, true, (Channel)0); } } public static void SendFishBodySwapped(int oldObjectId, int newObjectId, byte skinIndex) { BroadcastRoster(); } public static void SendPathOffer(FishRuntime runtime) { if (!((Object)(object)runtime?.Body == (Object)null) && runtime.PendingPathChoice && InstanceFinder.IsServerStarted && !(runtime.OwnerConn == (NetworkConnection)null)) { InstanceFinder.ServerManager.Broadcast<S2C_PathOffer>(runtime.OwnerConn, new S2C_PathOffer { BodyObjectId = ((NetworkBehaviour)runtime.Body).ObjectId }, true, (Channel)0); } } private static void OnFishEvolutionBroadcast(FishEvolutionBroadcast msg, Channel channel) { //IL_001b: 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) Creature val = ResolveBody(msg.BodyObjectId); if ((Object)(object)val != (Object)null) { ((Component)val).transform.localScale = Vector3.one * msg.Scale; } else { _pendingEvolutions[msg.BodyObjectId] = msg; } } private static void OnFishPrivateState(S2C_FishPrivateState msg, Channel channel) { Creature val = ResolveBody(msg.BodyObjectId); FishController fishController = (((Object)(object)val != (Object)null) ? ((Component)val).GetComponent<FishController>() : null); if ((Object)(object)fishController != (Object)null) { fishController.ApplyPrivateState(msg); } else { _pendingPrivateStates[msg.BodyObjectId] = msg; } } private static void OnMutationOffer(S2C_MutationOffer msg, Channel channel) { Creature val = ResolveBody(msg.BodyObjectId); FishController fishController = (((Object)(object)val != (Object)null) ? ((Component)val).GetComponent<FishController>() : null); if ((Object)(object)fishController != (Object)null) { fishController.ApplyMutationOffer(msg); } else { _pendingMutationOffers[msg.BodyObjectId] = msg; } } private static void OnSkinOffer(S2C_SkinOffer msg, Channel channel) { Creature val = ResolveBody(msg.BodyObjectId); FishController fishController = (((Object)(object)val != (Object)null) ? ((Component)val).GetComponent<FishController>() : null); if ((Object)(object)fishController != (Object)null) { fishController.ApplySkinOffer(msg); } else { _pendingSkinOffers[msg.BodyObjectId] = msg; } } private static void OnPathOffer(S2C_PathOffer msg, Channel channel) { Creature val = ResolveBody(msg.BodyObjectId); FishController fishController = (((Object)(object)val != (Object)null) ? ((Component)val).GetComponent<FishController>() : null); if ((Object)(object)fishController != (Object)null) { fishController.ApplyPathOffer(); } else { _pendingPathOffers[msg.BodyObjectId] = msg; } } private static void OnBiteVisual(S2C_BiteVisual msg, Channel channel) { Creature val = ResolveBody(msg.BodyObjectId); if ((Object)(object)val == (Object)null) { _pendingBiteVisuals[msg.BodyObjectId] = msg; } else { PlayBiteVisual(val, msg); } } private static void PlayBiteVisual(Creature body, S2C_BiteVisual msg) { (((Component)body).GetComponent<FishBiteVisual>() ?? ((Component)body).gameObject.AddComponent<FishBiteVisual>()).Play(); } private static void OnWaveVisual(S2C_Wave msg, Channel channel) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) Creature val = ResolveBody(msg.BodyObjectId); if (!((Object)(object)val == (Object)null)) { TidalWaveEffect.Launch(((Component)val).transform.position, ((Component)val).transform.forward, msg.Stack); } } private static void OnBecomeFishRequest(NetworkConnection conn, C2S_BecomeFishRequest msg, Channel channel) { MatchController.HandleBecomeFishRequest(conn, msg.Leave); } private static void OnMutationChoice(NetworkConnection conn, C2S_MutationChoice msg, Channel channel) { MatchController.HandleMutationChoice(conn, msg.OptionIndex); } private static void OnBiteReport(NetworkConnection conn, C2S_BiteReport msg, Channel channel) { MatchController.HandleBiteReport(conn, msg.XpAmount, msg.WasPreyEaten); } private static void OnSkinChoice(NetworkConnection conn, C2S_SkinChoice msg, Channel channel) { MatchController.HandleSkinChoice(conn, msg.OptionIndex); } private static void OnPathChoice(NetworkConnection conn, C2S_PathChoice msg, Channel channel) { MatchController.HandlePathChoice(conn, msg.Path); } private static void OnBiteVisualRequest(NetworkConnection conn, C2S_BiteVisual msg, Channel channel) { FishRuntime byOwner = FishRegistry.GetByOwner(conn); if ((Object)(object)byOwner?.Body != (Object)null) { InstanceFinder.ServerManager.Broadcast<S2C_BiteVisual>(new S2C_BiteVisual { BodyObjectId = ((NetworkBehaviour)byOwner.Body).ObjectId }, true, (Channel)0); } } private static void OnWaveRequest(NetworkConnection conn, C2S_Wave msg, Channel channel) { //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: 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_00fc: 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) //IL_0117: Unknown result type (might be due to invalid IL or missing references) FishRuntime byOwner = FishRegistry.GetByOwner(conn); int num = byOwner?.StackOf(MutationId.TidalWave) ?? 0; if (byOwner == null || !byOwner.Alive || byOwner.Path != EvolutionPath.Primal || num <= 0 || Time.time < byOwner.NextWaveTime) { return; } byOwner.NextWaveTime = Time.time + 12f; float num2 = 12f + (float)num * 4f; int num3 = 15 + num * 10; foreach (Player alivePlayer in PlayerManager.AlivePlayers) { if (!((Object)(object)alivePlayer == (Object)null) && !(((NetworkBehaviour)alivePlayer).Owner == conn) && FishRegistry.GetByOwner(((NetworkBehaviour)alivePlayer).Owner) == null) { Vector3 val = alivePlayer.Transform.position - ((Component)byOwner.Body).transform.position; if (!(((Vector3)(ref val)).sqrMagnitude > num2 * num2)) { Server.Instance.HitPlayer(alivePlayer, num3, ((Vector3)(ref val)).normalized * (12f + (float)num * 4f) + Vector3.up * 5f, alivePlayer.Transform.position, (byte)3, (Player)null); } } } InstanceFinder.ServerManager.Broadcast<S2C_Wave>(new S2C_Wave { BodyObjectId = ((NetworkBehaviour)byOwner.Body).ObjectId, Stack = (byte)num }, true, (Channel)0); Debug.Log((object)$"[FishVsHunters] TIDAL WAVE conn#{conn.ClientId}: stack={num}, radius={num2}, damage={num3}."); } } public struct FishRosterEntry { public int OwnerId; public int BodyId; public bool Dead; public float Scale; public int MaxHp; public float Mass; } public struct S2C_Roster : IBroadcast { public string Session; public int Revision; public int Round; public bool AutoRestart; public bool RoundLocked; public float Countdown; public byte Winner; public float WinnerSeconds; public FishRosterEntry[] Fish; } public struct S2C_PrepareRespawn : IBroadcast { public int OwnerId; } internal static class RosterWire { internal static void Register() { GenericWriter<S2C_Roster>.SetWrite((Action<Writer, S2C_Roster>)Write); GenericReader<S2C_Roster>.SetRead((Func<Reader, S2C_Roster>)Read); GenericWriter<S2C_PrepareRespawn>.SetWrite((Action<Writer, S2C_PrepareRespawn>)delegate(Writer w, S2C_PrepareRespawn v) { w.WriteInt32(v.OwnerId); }); GenericReader<S2C_PrepareRespawn>.SetRead((Func<Reader, S2C_PrepareRespawn>)((Reader r) => new S2C_PrepareRespawn { OwnerId = r.ReadInt32() })); } internal static void Write(Writer w, S2C_Roster v) { w.WriteString(v.Session); w.WriteInt32(v.Revision); w.WriteInt32(v.Round); w.WriteBoolean(v.AutoRestart); w.WriteSingle(v.Countdown); w.WriteBoolean(v.RoundLocked); w.WriteUInt8Unpacked(v.Winner); w.WriteSingle(v.WinnerSeconds); w.WriteInt32(v.Fish.Length); FishRosterEntry[] fish = v.Fish; for (int i = 0; i < fish.Length; i++) { FishRosterEntry fishRosterEntry = fish[i]; w.WriteInt32(fishRosterEntry.OwnerId); w.WriteInt32(fishRosterEntry.BodyId); w.WriteBoolean(fishRosterEntry.Dead); w.WriteSingle(fishRosterEntry.Scale); w.WriteInt32(fishRosterEntry.MaxHp); w.WriteSingle(fishRosterEntry.Mass); } } internal static S2C_Roster Read(Reader r) { S2C_Roster result = new S2C_Roster { Session = r.ReadStringAllocated(), Revision = r.ReadInt32(), Round = r.ReadInt32(), AutoRestart = r.ReadBoolean(), Countdown = r.ReadSingle() }; result.RoundLocked = r.ReadBoolean(); result.Winner = r.ReadUInt8Unpacked(); result.WinnerSeconds = r.ReadSingle(); int num = r.ReadInt32(); if (num < 0 || num > 128) { throw new InvalidDataException("Invalid fish roster length."); } result.Fish = new FishRosterEntry[num]; for (int i = 0; i < num; i++) { result.Fish[i] = new FishRosterEntry { OwnerId = r.ReadInt32(), BodyId = r.ReadInt32(), Dead = r.ReadBoolean(), Scale = r.ReadSingle(), MaxHp = r.ReadInt32(), Mass = r.ReadSingle() }; } return result; } } } namespace FishVsHunters.match { internal struct CalculatedFishStats { public int Level; public float MaxHp; public float ArmorPercent; public float BiteDamage; public float BiteCooldown; public float BiteRange; public float BiteRadius; public float SwimSpeed; public float TurnSpeedDegPerSec; public float Acceleration; public float Scale; public float Mass; public float MassMultiplier; public bool DashUnlocked; public float DashCooldown; public float DashForce; public bool SonarUnlocked; public float SonarCooldown; public float SonarDuration; public bool RamUnlocked; public bool BleedUnlocked; public float BleedDamagePerTick; public bool PredatorUnlocked; public float PredatorBonusDamagePercent; public bool DevourerUnlocked; public float DevourerHealPercent; public bool RegenerationUnlocked; public float RegenerationHpPerTick; public bool BloodScentUnlocked; public static int StackOf(IReadOnlyDictionary<MutationId, int> stacks, MutationId id) { if (stacks == null || !stacks.TryGetValue(id, out var value)) { return 0; } return value; } private static SkinBaseStats FallbackBand() { return new SkinBaseStats { Scale = 1f, MaxHp = 40f, BiteDamage = 12f, SwimSpeed = Config.SwimSpeed.Value, ArmorPercent = 0f, BiteRange = Config.BiteForwardOffset.Value, Mass = SkinCatalog.BaselineMass, TurnSpeedDegPerSec = Config.TurnSpeedDegPerSec.Value }; } public static CalculatedFishStats Compute(SkinDefinition skin, int level, IReadOnlyDictionary<MutationId, int> stacks) { level = Mathf.Clamp(level, 1, 100); SkinBaseStats skinBaseStats = skin?.Evaluate(level) ?? FallbackBand(); int num = StackOf(stacks, MutationId.ThickSkin); int num2 = StackOf(stacks, MutationId.Armor); int num3 = StackOf(stacks, MutationId.Devourer); int num4 = StackOf(stacks, MutationId.Regeneration); int num5 = StackOf(stacks, MutationId.SharpTeeth); int num6 = StackOf(stacks, MutationId.FastJaw); int num7 = StackOf(stacks, MutationId.Bleed); int num8 = StackOf(stacks, MutationId.Predator); int num9 = StackOf(stacks, MutationId.FastFins); int num10 = StackOf(stacks, MutationId.Maneuverability); int num11 = StackOf(stacks, MutationId.Dash); int num12 = StackOf(stacks, MutationId.ImprovedDash); int num13 = StackOf(stacks, MutationId.Growth); int num14 = StackOf(stacks, MutationId.Gigantism); int num15 = StackOf(stacks, MutationId.Ram); int num16 = StackOf(stacks, MutationId.Sonar); int num17 = StackOf(stacks, MutationId.ImprovedSonar); int num18 = StackOf(stacks, MutationId.BloodScent); float num19 = skinBaseStats.Scale + (float)num13 * Config.GrowthScalePerStack.Value + (float)num14 * Config.GigantismScaleBonus.Value; float num20 = skinBaseStats.TurnSpeedDegPerSec + (float)num10 * Config.ManeuverabilityTurnPerStack.Value - (float)num13 * Config.GrowthTurnPenaltyPerStack.Value - (float)num14 * Config.GigantismTurnPenalty.Value; float num21 = skinBaseStats.ArmorPercent + (float)num2 * Config.ArmorPercentPerStack.Value; CalculatedFishStats result = new CalculatedFishStats { Level = level, MaxHp = skinBaseStats.MaxHp + (float)num * Config.ThickSkinHpPerStack.Value, ArmorPercent = Mathf.Min(num21, Config.ArmorCapPercent.Value), BiteDamage = skinBaseStats.BiteDamage + (float)num5 * Config.SharpTeethDamagePerStack.Value, BiteCooldown = Mathf.Max(Config.BiteCooldownMinimum.Value, Config.BiteCooldown.Value - (float)num6 * Config.FastJawCooldownReductionPerStack.Value), BiteRange = skinBaseStats.BiteRange * (1f + (float)num13 * 0.15f + (float)num14 * 0.3f), BiteRadius = Config.BiteRadius.Value, SwimSpeed = Mathf.Max(1f, skinBaseStats.SwimSpeed + (float)num9 * Config.FastFinsSpeedPerStack.Value), TurnSpeedDegPerSec = Mathf.Max(30f, num20), Acceleration = Config.SwimAcceleration.Value * Mathf.Clamp(1f / Mathf.Max(1f, Mathf.Sqrt(Mathf.Max(0.1f, num19))), 0.3f, 1f), Scale = Mathf.Max(0.1f, num19), Mass = Mathf.Max(0.1f, skinBaseStats.Mass), MassMultiplier = 1f, DashUnlocked = (num11 > 0), DashCooldown = Mathf.Max(0.5f, Config.DashCooldown.Value - (float)num12 * Config.ImprovedDashCooldownReductionPerStack.Value), DashForce = Config.DashForce.Value, SonarUnlocked = (num16 > 0), SonarCooldown = Mathf.Max(2f, Config.SonarCooldown.Value - (float)num17 * Config.ImprovedSonarCooldownReductionPerStack.Value), SonarDuration = Config.SonarDuration.Value + (float)num17 * Config.ImprovedSonarDurationBonusPerStack.Value, RamUnlocked = (num15 > 0), BleedUnlocked = (num7 > 0), BleedDamagePerTick = (float)num7 * Config.BleedDamagePerTick.Value, PredatorUnlocked = (num8 > 0), PredatorBonusDamagePercent = (float)num8 * Config.PredatorBonusDamagePercentPerStack.Value, DevourerUnlocked = (num3 > 0), DevourerHealPercent = (float)num3 * Config.DevourerHealPercentPerStack.Value, RegenerationUnlocked = (num4 > 0 && level >= Config.RegenerationMinLevel.Value), RegenerationHpPerTick = (float)num4 * Config.Regeneration