using System; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using CG; using CG.Game; using CG.Game.Player; using CG.Network; using CG.Objects; using CG.Ship.Hull; using CG.Ship.Modules; using CG.Space; using Gameplay.Carryables; using Gameplay.CompositeWeapons; using Gameplay.Tags; using Gameplay.Utilities; using HarmonyLib; using Microsoft.CodeAnalysis; using Photon.Realtime; using UnityEngine; using VoidManager; using VoidManager.CustomGUI; using VoidManager.MPModChecks; using VoidManager.ModMessages; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = "")] [assembly: AssemblyCompany("BalancedSentries")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyFileVersion("1.1.0.0")] [assembly: AssemblyInformationalVersion("1.1.0+c756262cd81dde7848a10752e393250438ed6d5c")] [assembly: AssemblyProduct("BalancedSentries")] [assembly: AssemblyTitle("Automatically adjusts B.R.A.I.N. Turret damage and power consumption based on the number of players in the lobby.")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.1.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace BalancedSentries { internal class BlessedHomunculusPatcher { private static GUIDUnion destroyerShipGUID = new GUIDUnion("4bc2ff9e1d156c94a9c94286a7aaa79b"); private static GUIDUnion strikerShipGUID = new GUIDUnion("d872500325a2d54498189cdfd4b788e7"); private static GUIDUnion frigateShipGUID = new GUIDUnion("ac98ae5eb3b940747b0fbb7bd616b019"); private static PPlayerCondition PPlayerCondition = new PPlayerCondition(); public static void ApplyPatch(CarryableMod homunculus) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0027: 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_003d: 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) GUIDUnion assetGuid = ((AbstractCloneStarObject)ClientGame.Current.PlayerShip).assetGuid; bool flag = assetGuid == destroyerShipGUID && Configs.ActiveEnableOnDestroyer; bool flag2 = assetGuid == strikerShipGUID && Configs.ActiveEnableOnStriker; bool flag3 = assetGuid == frigateShipGUID && Configs.ActiveEnableOnFrigate; if (!flag && !flag2 && !flag3) { return; } StatMod val = null; StatMod val2 = null; foreach (StatMod modifier in homunculus.Modifiers) { if (modifier.DynamicCondition is PPlayerCondition) { return; } if (modifier.DynamicCondition is SinglePlayerModRule) { if (modifier.Type == ((Enumeration)StatType.Damage).Id) { val = modifier; } else if (modifier.Type == ((Enumeration)StatType.PowerWanted).Id) { val2 = modifier; } } } if (val2 != null) { BepinPlugin.Log.LogInfo((object)"Applying P Power Wanted Mod to Blessed Homunculus"); StatMod val3 = (StatMod)(object)new PPowerWantedStatMod(val2.TagConfiguration); val3.Mod.Source = (IModifierSource)(object)homunculus; val3.Mod.InformationSource = (IModifierSource)(object)homunculus; val3.DynamicCondition = (ModDynamicCondition)(object)PPlayerCondition; homunculus.Modifiers.Add(val3); } if (val != null) { BepinPlugin.Log.LogInfo((object)"Applying P Damage Mod to Blessed Homunculus"); StatMod val4 = (StatMod)(object)new PDamageStatMod(val.TagConfiguration); val4.Mod.Source = (IModifierSource)(object)homunculus; val4.Mod.InformationSource = (IModifierSource)(object)homunculus; val4.DynamicCondition = (ModDynamicCondition)(object)PPlayerCondition; homunculus.Modifiers.Add(val4); } } } internal class Configs { internal static bool IsHost = true; internal static ConfigEntry LocalEnableOnDestroyer; internal static ConfigEntry LocalEnableOnStriker; internal static ConfigEntry LocalEnableOnFrigate; internal static ConfigEntry LocalDamage2P; internal static ConfigEntry LocalDamage3to4P; internal static ConfigEntry LocalDamage5to6P; internal static ConfigEntry LocalDamage7Plus; internal static ConfigEntry LocalPower2P; internal static ConfigEntry LocalPower3to4P; internal static ConfigEntry LocalPower5to6P; internal static ConfigEntry LocalPower7Plus; internal static bool ActiveEnableOnDestroyer; internal static bool ActiveEnableOnStriker; internal static bool ActiveEnableOnFrigate; internal static float ActiveDamage2P; internal static float ActiveDamage3to4P; internal static float ActiveDamage5to6P; internal static float ActiveDamage7Plus; internal static int ActivePower2P; internal static int ActivePower3to4P; internal static int ActivePower5to6P; internal static int ActivePower7Plus; internal static void Load(BaseUnityPlugin plugin) { LocalEnableOnDestroyer = plugin.Config.Bind("Ships", "Destroyer", true, "Enable on Destroyer"); LocalEnableOnStriker = plugin.Config.Bind("Ships", "Striker", true, "Enable on Striker"); LocalEnableOnFrigate = plugin.Config.Bind("Ships", "Frigate", true, "Enable on Frigate"); LocalDamage2P = plugin.Config.Bind("Damage", "2_Players", 1f, "Damage for Duo"); LocalDamage3to4P = plugin.Config.Bind("Damage", "3-4_Players", 0.75f, "Damage for Standard Crew"); LocalDamage5to6P = plugin.Config.Bind("Damage", "5-6_Players", 0.5f, "Damage for Large Crew"); LocalDamage7Plus = plugin.Config.Bind("Damage", "7+_Players", 0.25f, "Damage for More Crew lobbies"); LocalPower2P = plugin.Config.Bind("Power", "2_Players", -2, "Power for Duo"); LocalPower3to4P = plugin.Config.Bind("Power", "3-4_Players", -1, "Power for Standard Crew"); LocalPower5to6P = plugin.Config.Bind("Power", "5-6_Players", 0, "Power for Large Crew"); LocalPower7Plus = plugin.Config.Bind("Power", "7+_Players", 1, "Power for More Crew lobbies"); SyncLocalToActive(); } internal static void SyncLocalToActive() { ActiveEnableOnDestroyer = LocalEnableOnDestroyer.Value; ActiveEnableOnStriker = LocalEnableOnStriker.Value; ActiveEnableOnFrigate = LocalEnableOnFrigate.Value; ActiveDamage2P = LocalDamage2P.Value; ActiveDamage3to4P = LocalDamage3to4P.Value; ActiveDamage5to6P = LocalDamage5to6P.Value; ActiveDamage7Plus = LocalDamage7Plus.Value; ActivePower2P = LocalPower2P.Value; ActivePower3to4P = LocalPower3to4P.Value; ActivePower5to6P = LocalPower5to6P.Value; ActivePower7Plus = LocalPower7Plus.Value; } internal static float GetCurrentDamageMultiplier() { if (ClientGame.Current?.Players == null) { return 1f; } int count = ClientGame.Current.Players.Count; if (count <= 1) { return 0f; } if (count == 2) { return ActiveDamage2P; } if (count <= 4) { return ActiveDamage3to4P; } if (count <= 6) { return ActiveDamage5to6P; } return ActiveDamage7Plus; } internal static int GetCurrentPowerWanted() { if (ClientGame.Current?.Players == null) { return 0; } int count = ClientGame.Current.Players.Count; if (count <= 1) { return 0; } if (count == 2) { return ActivePower2P; } if (count <= 4) { return ActivePower3to4P; } if (count <= 6) { return ActivePower5to6P; } return ActivePower7Plus; } } public class ConfigSyncMessage : ModMessage { public static void SendToClients() { if (Configs.IsHost && GameSessionManager.ActiveSession != null) { object[] array = new object[11] { Configs.ActiveEnableOnDestroyer, Configs.ActiveEnableOnStriker, Configs.ActiveEnableOnFrigate, Configs.ActiveDamage2P, Configs.ActiveDamage3to4P, Configs.ActiveDamage5to6P, Configs.ActiveDamage7Plus, Configs.ActivePower2P, Configs.ActivePower3to4P, Configs.ActivePower5to6P, Configs.ActivePower7Plus }; ModMessage.Send("HeX.BalancedSentries", "ConfigSyncMessage", (Player)null, array, true); } } public override void Handle(object[] arguments, Player sender) { if (Configs.IsHost) { return; } try { Configs.ActiveEnableOnDestroyer = (bool)arguments[0]; Configs.ActiveEnableOnStriker = (bool)arguments[1]; Configs.ActiveEnableOnFrigate = (bool)arguments[2]; Configs.ActiveDamage2P = (float)arguments[3]; Configs.ActiveDamage3to4P = (float)arguments[4]; Configs.ActiveDamage5to6P = (float)arguments[5]; Configs.ActiveDamage7Plus = (float)arguments[6]; Configs.ActivePower2P = (int)arguments[7]; Configs.ActivePower3to4P = (int)arguments[8]; Configs.ActivePower5to6P = (int)arguments[9]; Configs.ActivePower7Plus = (int)arguments[10]; } catch { } } } internal class HostSettingsMenu : ModSettingsMenu { public override string Name() { return "Balanced Sentries (Host)"; } public override void Draw() { GUILayout.Label("CRITICAL SYSTEM NOTICE", Array.Empty()); GUILayout.BeginVertical(GUIStyle.op_Implicit("box"), Array.Empty()); GUILayout.Label("Modifications require a hardware reset to apply visually.", Array.Empty()); GUILayout.Label("Unequip/Re-equip the Blessed Homunculus after changes.", Array.Empty()); GUILayout.EndVertical(); GUILayout.Space(15f); GUILayout.Label("MOD LOGIC:", Array.Empty()); GUILayout.Label("Disabled in Solo (1 Player) to preserve the original Solo Buff.", Array.Empty()); GUILayout.Space(10f); GUI.changed = false; GUILayout.Label("--- Ship Compatibility ---", Array.Empty()); Configs.LocalEnableOnDestroyer.Value = GUILayout.Toggle(Configs.LocalEnableOnDestroyer.Value, " Enable on Destroyer Class", Array.Empty()); Configs.LocalEnableOnStriker.Value = GUILayout.Toggle(Configs.LocalEnableOnStriker.Value, " Enable on Striker Class", Array.Empty()); Configs.LocalEnableOnFrigate.Value = GUILayout.Toggle(Configs.LocalEnableOnFrigate.Value, " Enable on Frigate Class", Array.Empty()); GUILayout.Space(15f); GUILayout.Label("--- Damage Scaling Multipliers ---", Array.Empty()); DrawRow("Duo (2P) [Default: 1.00]", Configs.LocalDamage2P, 0.05f, 0.25f, 1f, isFloat: true); DrawRow("Standard (3-4P) [Default: 0.75]", Configs.LocalDamage3to4P, 0.05f, 0.25f, 1f, isFloat: true); DrawRow("Large (5-6P) [Default: 0.50]", Configs.LocalDamage5to6P, 0.05f, 0.25f, 1f, isFloat: true); DrawRow("More Crew (7+P) [Default: 0.25]", Configs.LocalDamage7Plus, 0.05f, 0.25f, 1f, isFloat: true); GUILayout.Space(15f); GUILayout.Label("--- Power Consumption Demand ---", Array.Empty()); DrawRow("Duo (2P) [Default: -2]", Configs.LocalPower2P, 1f, -2f, 1f, isFloat: false); DrawRow("Standard (3-4P) [Default: -1]", Configs.LocalPower3to4P, 1f, -2f, 1f, isFloat: false); DrawRow("Large (5-6P) [Default: 0]", Configs.LocalPower5to6P, 1f, -2f, 1f, isFloat: false); DrawRow("More Crew (7+P) [Default: 1]", Configs.LocalPower7Plus, 1f, -2f, 1f, isFloat: false); if (GUI.changed && Configs.IsHost) { Configs.SyncLocalToActive(); ConfigSyncMessage.SendToClients(); } } private void DrawRow(string label, ConfigEntry config, float step, float min, float max, bool isFloat) { GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label(label, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(180f) }); string text = (isFloat ? $"{Convert.ToSingle(config.Value):F2}" : config.Value.ToString()); GUILayout.Label("" + text + "", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(50f) }); if (GUILayout.Button("-", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(30f) })) { if (isFloat) { ((ConfigEntryBase)config).BoxedValue = Mathf.Clamp(Convert.ToSingle(config.Value) - step, min, max); } else { ((ConfigEntryBase)config).BoxedValue = (int)Mathf.Clamp((float)(Convert.ToInt32(config.Value) - (int)step), min, max); } } if (GUILayout.Button("+", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(30f) })) { if (isFloat) { ((ConfigEntryBase)config).BoxedValue = Mathf.Clamp(Convert.ToSingle(config.Value) + step, min, max); } else { ((ConfigEntryBase)config).BoxedValue = (int)Mathf.Clamp((float)(Convert.ToInt32(config.Value) + (int)step), min, max); } } GUILayout.EndHorizontal(); } } internal class ServerSettingsMenu : ModSettingsMenu { public override string Name() { return "Balanced Sentries (Server Status)"; } public override void Draw() { GUILayout.Label("CRITICAL SYSTEM NOTICE", Array.Empty()); GUILayout.BeginVertical(GUIStyle.op_Implicit("box"), Array.Empty()); GUILayout.Label("Modifications require a hardware reset to apply visually.", Array.Empty()); GUILayout.Label("Unequip/Re-equip the Blessed Homunculus after changes.", Array.Empty()); GUILayout.EndVertical(); GUILayout.Space(15f); GUILayout.Label("--- Active Server Profile ---", Array.Empty()); GUILayout.Label("Ships: " + (Configs.ActiveEnableOnDestroyer ? "DES " : "") + (Configs.ActiveEnableOnStriker ? "STR " : "") + (Configs.ActiveEnableOnFrigate ? "FRI" : ""), Array.Empty()); GUILayout.Space(10f); GUILayout.BeginVertical(GUIStyle.op_Implicit("box"), Array.Empty()); DrawInfo("Duo (2P)", Configs.ActiveDamage2P, Configs.ActivePower2P); DrawInfo("Standard (3-4P)", Configs.ActiveDamage3to4P, Configs.ActivePower3to4P); DrawInfo("Large (5-6P)", Configs.ActiveDamage5to6P, Configs.ActivePower5to6P); DrawInfo("Massive (7+P)", Configs.ActiveDamage7Plus, Configs.ActivePower7Plus); GUILayout.EndVertical(); } private void DrawInfo(string l, float d, int p) { GUILayout.Label($"{l}: {d:F2}x Dmg | {p} Pwr", Array.Empty()); } } public class MyPluginInfo { public const string PLUGIN_GUID = "HeX.BalancedSentries"; public const string PLUGIN_NAME = "BalancedSentries"; public const string USERS_PLUGIN_NAME = "Balanced Sentries"; public const string PLUGIN_VERSION = "1.1.0"; public const string PLUGIN_DESCRIPTION = "Automatically adjusts B.R.A.I.N. Turret damage and power consumption based on the number of players in the lobby."; public const string PLUGIN_ORIGINAL_AUTHOR = "HeX"; public const string PLUGIN_AUTHORS = "HeX, Airborne"; public const string PLUGIN_THUNDERSTORE_ID = "ShinyChiefs/BalancedSentries"; } [HarmonyPatch(typeof(HomunculusAndBiomassSocket), "DispenseHomunculusNow")] internal class HomunculusAndBiomassSocket_DispenseHomunculusNow_Patch { private static void Postfix(HomunculusAndBiomassSocket __instance) { if (!((Object)(object)((CarryablesSocket)__instance).Payload == (Object)null)) { CarryableObject payload = ((CarryablesSocket)__instance).Payload; CarryableMod val = (CarryableMod)(object)((payload is CarryableMod) ? payload : null); if (val != null) { BlessedHomunculusPatcher.ApplyPatch(val); } } } } [HarmonyPatch(typeof(ModSocket), "Start")] internal class ModSocket_Start_Patch { private static void Postfix(ModSocket __instance) { ClientGame current = ClientGame.Current; object obj; if (current == null) { obj = null; } else { PlayerControlledShip playerShip = current.PlayerShip; obj = ((playerShip != null) ? playerShip.GetModule() : null); } CentralShipComputerModule val = (CentralShipComputerModule)obj; if (!((Object)(object)val == (Object)null) && !((Object)(object)__instance != (Object)(object)val.HomunculusSocket)) { ((CarryablesSocket)__instance).OnAcquireCarryable += OnCarriableAcquired; } } private static void OnCarriableAcquired(ICarrier carrier, CarryableObject carryable, ICarrier previousCarrier) { CarryableMod val = (CarryableMod)(object)((carryable is CarryableMod) ? carryable : null); if (val != null) { BlessedHomunculusPatcher.ApplyPatch(val); } } } internal class PDamageStatMod : StatMod, IDescriptiveModifierSource, IModifierSource { public PDamageStatMod(ModTagConfiguration tagConfig) : base((PrimitiveModifier)new FloatModifier(Configs.GetCurrentDamageMultiplier(), (ModifierType)0, (IModifierSource)null), ((Enumeration)StatType.Damage).Id, tagConfig) { }//IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Expected O, but got Unknown public string GetDescription() { return ""; } public string GetHeader() { int num = ((ClientGame.Current?.Players == null) ? 1 : ClientGame.Current.Players.Count); return $"Dynamic Sentry Damage Active ({num}P)"; } } [BepInPlugin("HeX.BalancedSentries", "Balanced Sentries", "1.1.0")] [BepInProcess("Void Crew.exe")] [BepInDependency(/*Could not decode attribute arguments.*/)] public class BepinPlugin : BaseUnityPlugin { internal static ManualLogSource Log; internal static readonly GUIDUnion SENTRY_FRIGATE_AUTO_BRAIN_WEAPON_GUID = new GUIDUnion("bc2dcf53afe3a90478b5d9fcffb1f523"); private void Awake() { Log = ((BaseUnityPlugin)this).Logger; Harmony.CreateAndPatchAll(Assembly.GetExecutingAssembly(), (string)null); Configs.Load((BaseUnityPlugin)(object)this); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Plugin HeX.BalancedSentries is loaded!"); } } public class VoidManagerPlugin : VoidPlugin { public override MultiplayerType MPType => (MultiplayerType)30; public override string Author => "HeX, Airborne"; public override string Description => "Automatically adjusts B.R.A.I.N. Turret damage and power consumption based on the number of players in the lobby."; public override string ThunderstoreID => "ShinyChiefs/BalancedSentries"; public override SessionChangedReturn OnSessionChange(SessionChangedInput input) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) Configs.IsHost = input.IsHost; SessionChangedReturn result = default(SessionChangedReturn); result.SetMod_Session = true; return result; } } [Serializable] public class PPlayerCondition : ModDynamicCondition { public override void OnInitialize() { ClientGame current = ClientGame.Current; if ((Object)(object)((current != null) ? current.ModelEventBus : null) != (Object)null) { ClientGame.Current.ModelEventBus.OnPlayerAdded.Subscribe((Action)OnPlayerAdded, (AutoUnsubscribeCollection)null); ClientGame.Current.ModelEventBus.OnPlayerRemoved.Subscribe((Action)OnPlayerRemoved, (AutoUnsubscribeCollection)null); } } public override void OnDestroy() { ClientGame current = ClientGame.Current; if ((Object)(object)((current != null) ? current.ModelEventBus : null) != (Object)null) { ClientGame.Current.ModelEventBus.OnPlayerAdded.Unsubscribe((Action)OnPlayerAdded); ClientGame.Current.ModelEventBus.OnPlayerRemoved.Unsubscribe((Action)OnPlayerRemoved); } } private void OnPlayerRemoved(Player obj) { ((ModDynamicCondition)this).CheckIfActive(); } private void OnPlayerAdded(Player obj) { ((ModDynamicCondition)this).CheckIfActive(); if (Configs.IsHost) { ConfigSyncMessage.SendToClients(); } } public override bool ShouldApply() { return ClientGame.Current.Players.Count >= 2; } public override string Description() { int num = ((ClientGame.Current?.Players == null) ? 1 : ClientGame.Current.Players.Count); if (num <= 1) { return "Condition: Dynamic Scaling [Idle - Solo Mode]"; } return $"Condition: Dynamic Player Scaling [{num} Players Active]"; } } internal class PPowerWantedStatMod : StatMod, IDescriptiveModifierSource, IModifierSource { public PPowerWantedStatMod(ModTagConfiguration tagConfig) : base((PrimitiveModifier)new IntModifier(Configs.GetCurrentPowerWanted(), (ModifierType)2), ((Enumeration)StatType.PowerWanted).Id, tagConfig) { }//IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Expected O, but got Unknown public string GetDescription() { return ""; } public string GetHeader() { int num = ((ClientGame.Current?.Players == null) ? 1 : ClientGame.Current.Players.Count); return $"Dynamic Sentry Scaling Active ({num}P)"; } } }