using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using IL.RoR2;
using Microsoft.CodeAnalysis;
using Mono.Cecil;
using Mono.Cecil.Cil;
using MonoMod.Cil;
using On.RoR2.Projectile;
using R2API.Utils;
using RiskOfOptions;
using RiskOfOptions.Options;
using RoR2;
using RoR2.CharacterAI;
using RoR2.Projectile;
using UnityEngine;
using UnityEngine.Networking;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("Chinchi")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("No more losing your hard-earned drone army to a void seed/invasion.")]
[assembly: AssemblyFileVersion("1.1.0.0")]
[assembly: AssemblyInformationalVersion("1.1.0+f7880d24bb660a7c9c13108587b21997c10f41aa")]
[assembly: AssemblyProduct("AlliesAvoidImplosions")]
[assembly: AssemblyTitle("AlliesAvoidImplosions")]
[assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/schinchi/AlliesAvoidImplosions")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.1.0.0")]
[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 AlliesAvoidImplosions
{
[BepInPlugin("Chinchi.AlliesAvoidImplosions", "AlliesAvoidImplosions", "1.1.0")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
public class AlliesAvoidImplosions : BaseUnityPlugin
{
public const string PluginGUID = "Chinchi.AlliesAvoidImplosions";
public const string PluginAuthor = "Chinchi";
public const string PluginName = "AlliesAvoidImplosions";
public const string PluginVersion = "1.1.0";
internal const string RiskOfOptionsGUID = "com.rune580.riskofoptions";
internal static readonly string[] defaultAllies = new string[10] { "Drone1Master", "Drone2Master", "DroneBackupMaster", "DroneMissileMaster", "EmergencyDroneMaster", "EquipmentDroneMaster", "FlameDroneMaster", "MegaDroneMaster", "Turret1Master", "DevotedLemurianMaster" };
private void Awake()
{
Log.Init(((BaseUnityPlugin)this).Logger);
Configs.Init(((BaseUnityPlugin)this).Config, ((BaseUnityPlugin)this).Info.Location);
Hooks.Init();
RoR2Application.onLoad = (Action)Delegate.Combine(RoR2Application.onLoad, new Action(Hooks.LoadDataAndPatch));
}
}
internal class Configs
{
private const string RETREAT_SECTION = "1. Retreat";
private const string IMMUNITY_SECTION = "2. Immunity";
private const string GENERAL_SECTION = "3. General";
internal static ConfigEntry<string> AdditionalBackupEntries { get; private set; }
internal static ConfigEntry<string> BlacklistedBackupEntries { get; private set; }
internal static ConfigEntry<string> AdditionalImmuneEntries { get; private set; }
internal static ConfigEntry<string> BlacklistedImmuneEntries { get; private set; }
internal static ConfigEntry<bool> ImmuneToVoidDeath { get; private set; }
internal static ConfigEntry<float> EvasionDistance { get; private set; }
internal static void Init(ConfigFile config, string assemblyLocation)
{
AdditionalBackupEntries = config.Bind<string>("1. Retreat", "Additional Retreat Entities", "EngiWalkerTurretMaster", "Additional characters that back away from implosions. Use the master name of the desired entities separated by comma. You can get a list of all masters with `list_ai` from DebugToolkit.");
BlacklistedBackupEntries = config.Bind<string>("1. Retreat", "Blacklisted Retreat Entities", "EmergencyDroneMaster", "Any characters to be blacklisted from exhibiting this behaviour. Mostly because another mod also modifies the AISkillDrivers to use the custom target and it creates an incompatibility. Use the master name of the desired entities separated by comma. You can get a list of all masters with `list_ai` from DebugToolkit.");
ImmuneToVoidDeath = config.Bind<bool>("2. Immunity", "Immune To Void Death", false, "Allies affected by this mod are immune to void death. A sure-fire survival alternative.");
AdditionalImmuneEntries = config.Bind<string>("2. Immunity", "Additional Immune Entities", "", "Additional characters to exhibit this behaviour. Use the master name of the desired entities separated by comma. You can get a list of all masters with `list_ai` from DebugToolkit.");
BlacklistedImmuneEntries = config.Bind<string>("2. Immunity", "Blacklisted Immune Entries", "", "Any characters to be blacklisted from exhibiting this behaviour. Use the master name of the desired entities separated by comma. You can get a list of all masters with `list_ai` from DebugToolkit.");
EvasionDistance = config.Bind<float>("3. General", "Evasion Distance", 35f, "The distance to back up from an implosion. The longer the distance, the safer the minion but the longer it will override its behaviour and prevent it from engaging other enemies. For reference, a Void Devastator's implosion has a radius of 22.5 m.");
EvasionDistance.SettingChanged += UpdateEvasionDistance;
if (Chainloader.PluginInfos.ContainsKey("com.rune580.riskofoptions"))
{
RiskOfOptionsInit(assemblyLocation);
}
}
private static void UpdateEvasionDistance(object sender, EventArgs e)
{
Hooks.UpdateBackUpDistance();
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
private static void RiskOfOptionsInit(string assemblyLocation)
{
//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
//IL_00ae: Expected O, but got Unknown
//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
//IL_00bd: Expected O, but got Unknown
//IL_0037: Unknown result type (might be due to invalid IL or missing references)
//IL_003d: Expected O, but got Unknown
//IL_0070: Unknown result type (might be due to invalid IL or missing references)
//IL_007f: Unknown result type (might be due to invalid IL or missing references)
FileInfo fileInfo = null;
FileInfo[] files = new DirectoryInfo(Path.GetDirectoryName(assemblyLocation)).GetFiles("icon.png", SearchOption.TopDirectoryOnly);
if (files != null && files.Length != 0)
{
fileInfo = files[0];
}
if (fileInfo != null)
{
string name = "AlliesAvoidImplosionsIcon";
Texture2D val = new Texture2D(256, 256);
((Object)val).name = name;
if (ImageConversion.LoadImage(val, File.ReadAllBytes(fileInfo.FullName)))
{
Sprite obj = Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f));
((Object)obj).name = name;
ModSettingsManager.SetModIcon(obj, "Chinchi.AlliesAvoidImplosions", "AlliesAvoidImplosions");
}
}
ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(ImmuneToVoidDeath));
ModSettingsManager.AddOption((BaseOption)new FloatFieldOption(EvasionDistance));
}
}
internal class GTFOHController : MonoBehaviour
{
private static readonly List<GTFOHController> instancesList = new List<GTFOHController>();
private BaseAI ai;
private void Awake()
{
instancesList.Add(this);
ai = ((Component)this).GetComponent<BaseAI>();
}
private void OnDestroy()
{
instancesList.Remove(this);
}
private void FixedUpdate()
{
//IL_0050: Unknown result type (might be due to invalid IL or missing references)
//IL_005b: Unknown result type (might be due to invalid IL or missing references)
//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
if (Hooks.implosions.Count == 0)
{
((Behaviour)this).enabled = false;
return;
}
CharacterBody body = ai.body;
if (!Object.op_Implicit((Object)(object)body))
{
return;
}
GameObject val = null;
float num = float.MaxValue;
foreach (GameObject implosion in Hooks.implosions)
{
float num2 = Vector3.Distance(implosion.transform.position, body.transform.position);
if (num2 < Configs.EvasionDistance.Value && num2 < num)
{
num = num2;
val = implosion;
}
}
if ((Object)(object)ai.customTarget.gameObject != (Object)(object)val)
{
ai.customTarget.gameObject = val;
ai.BeginSkillDriver(ai.EvaluateSkillDrivers());
}
}
internal static void EnableAll()
{
foreach (GTFOHController instances in instancesList)
{
((Behaviour)instances).enabled = true;
}
}
}
internal class Hooks
{
[CompilerGenerated]
private static class <>O
{
public static hook_Awake <0>__OnSpawnProjectile;
public static hook_OnDestroy <1>__OnDestroyProjectile;
public static Manipulator <2>__IgnoreVoidDeathForAllies;
}
private const string BACKUP_DRIVER_NAME = "BackUpFromImplosion";
internal static readonly HashSet<GameObject> implosions = new HashSet<GameObject>();
private static readonly HashSet<int> implosionProjectiles = new HashSet<int>();
private static readonly HashSet<MasterIndex> backupMasterIndices = new HashSet<MasterIndex>();
private static readonly HashSet<MasterIndex> immuneMasterIndices = new HashSet<MasterIndex>();
internal static void Init()
{
//IL_0010: Unknown result type (might be due to invalid IL or missing references)
//IL_0015: Unknown result type (might be due to invalid IL or missing references)
//IL_001b: Expected O, but got Unknown
//IL_0030: Unknown result type (might be due to invalid IL or missing references)
//IL_0035: Unknown result type (might be due to invalid IL or missing references)
//IL_003b: 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_005b: Expected O, but got Unknown
object obj = <>O.<0>__OnSpawnProjectile;
if (obj == null)
{
hook_Awake val = OnSpawnProjectile;
<>O.<0>__OnSpawnProjectile = val;
obj = (object)val;
}
ProjectileController.Awake += (hook_Awake)obj;
object obj2 = <>O.<1>__OnDestroyProjectile;
if (obj2 == null)
{
hook_OnDestroy val2 = OnDestroyProjectile;
<>O.<1>__OnDestroyProjectile = val2;
obj2 = (object)val2;
}
ProjectileController.OnDestroy += (hook_OnDestroy)obj2;
object obj3 = <>O.<2>__IgnoreVoidDeathForAllies;
if (obj3 == null)
{
Manipulator val3 = IgnoreVoidDeathForAllies;
<>O.<2>__IgnoreVoidDeathForAllies = val3;
obj3 = (object)val3;
}
HealthComponent.TakeDamageProcess += (Manipulator)obj3;
}
private static void IgnoreVoidDeathForAllies(ILContext il)
{
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
//IL_0007: Expected O, but got Unknown
//IL_006f: Unknown result type (might be due to invalid IL or missing references)
ILCursor val = new ILCursor(il);
if (!val.TryGotoNext(new Func<Instruction, bool>[2]
{
(Instruction x) => ILPatternMatchingExt.MatchLdfld<CharacterBody>(x, "bodyFlags"),
(Instruction x) => ILPatternMatchingExt.MatchLdcI4(x, 2048)
}))
{
Log.PatchFail(il);
return;
}
val.Index += 1;
val.Emit(OpCodes.Ldarg_0);
val.EmitDelegate<Func<BodyFlags, HealthComponent, BodyFlags>>((Func<BodyFlags, HealthComponent, BodyFlags>)delegate(BodyFlags bodyFlags, HealthComponent healthComponent)
{
//IL_003b: Unknown result type (might be due to invalid IL or missing references)
//IL_0026: Unknown result type (might be due to invalid IL or missing references)
//IL_0032: Unknown result type (might be due to invalid IL or missing references)
//IL_0038: Unknown result type (might be due to invalid IL or missing references)
//IL_0039: Unknown result type (might be due to invalid IL or missing references)
CharacterMaster master = healthComponent.body.master;
if (Configs.ImmuneToVoidDeath.Value && Object.op_Implicit((Object)(object)master) && immuneMasterIndices.Contains(master.masterIndex))
{
bodyFlags = (BodyFlags)(bodyFlags | 0x800);
}
return bodyFlags;
});
}
private static void OnSpawnProjectile(orig_Awake orig, ProjectileController self)
{
orig.Invoke(self);
if (NetworkServer.active && implosionProjectiles.Contains(self.catalogIndex))
{
implosions.Add(((Component)self).gameObject);
GTFOHController.EnableAll();
}
}
private static void OnDestroyProjectile(orig_OnDestroy orig, ProjectileController self)
{
if (NetworkServer.active && implosions.Contains(((Component)self).gameObject))
{
implosions.Remove(((Component)self).gameObject);
}
orig.Invoke(self);
}
internal static void LoadDataAndPatch()
{
BuildImplosionProjectileList();
BuildAllyIDs();
PatchAllies();
}
private static void BuildImplosionProjectileList()
{
//IL_0005: Unknown result type (might be due to invalid IL or missing references)
//IL_000a: 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_002a: Unknown result type (might be due to invalid IL or missing references)
//IL_002b: Unknown result type (might be due to invalid IL or missing references)
//IL_0035: Unknown result type (might be due to invalid IL or missing references)
DamageTypeCombo val = DamageTypeCombo.op_Implicit((DamageType)65536);
ProjectileController[] projectilePrefabProjectileControllerComponents = ProjectileCatalog.projectilePrefabProjectileControllerComponents;
ProjectileDamage val3 = default(ProjectileDamage);
foreach (ProjectileController val2 in projectilePrefabProjectileControllerComponents)
{
if (((Component)val2).TryGetComponent<ProjectileDamage>(ref val3) && DamageTypeCombo.op_Implicit(val3.damageType & val) == DamageTypeCombo.op_Implicit(val))
{
implosionProjectiles.Add(val2.catalogIndex);
}
}
}
private static void BuildAllyIDs()
{
BuildMasterIndices(Configs.AdditionalBackupEntries, Configs.BlacklistedBackupEntries, backupMasterIndices);
BuildMasterIndices(Configs.AdditionalImmuneEntries, Configs.BlacklistedImmuneEntries, immuneMasterIndices);
}
private static void BuildMasterIndices(ConfigEntry<string> additionalConfig, ConfigEntry<string> blacklistedConfig, HashSet<MasterIndex> indices)
{
//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
HashSet<string> hashSet = new HashSet<string>(SplitConfig(blacklistedConfig));
HashSet<string> hashSet2 = new HashSet<string>();
string[] defaultAllies = AlliesAvoidImplosions.defaultAllies;
foreach (string item in defaultAllies)
{
if (!hashSet.Contains(item))
{
hashSet2.Add(item);
}
}
foreach (string item2 in SplitConfig(additionalConfig))
{
if (!hashSet.Contains(item2))
{
hashSet2.Add(item2);
}
}
foreach (string item3 in hashSet2)
{
GameObject val = MasterCatalog.FindMasterPrefab(item3);
if (Object.op_Implicit((Object)(object)val))
{
indices.Add(val.GetComponent<CharacterMaster>().masterIndex);
}
}
static IEnumerable<string> SplitConfig(ConfigEntry<string> config)
{
return from x in config.Value.Split(',')
select x.Trim();
}
}
private static void PatchAllies()
{
//IL_0012: Unknown result type (might be due to invalid IL or missing references)
//IL_007b: Unknown result type (might be due to invalid IL or missing references)
//IL_0092: Unknown result type (might be due to invalid IL or missing references)
//IL_0099: Unknown result type (might be due to invalid IL or missing references)
//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
CharacterMotor val = default(CharacterMotor);
RigidbodyMotor val2 = default(RigidbodyMotor);
foreach (MasterIndex backupMasterIndex in backupMasterIndices)
{
GameObject masterPrefab = MasterCatalog.GetMasterPrefab(backupMasterIndex);
if (!Object.op_Implicit((Object)(object)masterPrefab))
{
continue;
}
CharacterMaster component = masterPrefab.GetComponent<CharacterMaster>();
if (!Object.op_Implicit((Object)(object)component.bodyPrefab) || (!component.bodyPrefab.TryGetComponent<CharacterMotor>(ref val) && !component.bodyPrefab.TryGetComponent<RigidbodyMotor>(ref val2)))
{
continue;
}
AISkillDriver[] components = masterPrefab.GetComponents<AISkillDriver>();
AISkillDriver obj = masterPrefab.AddComponent<AISkillDriver>();
obj.customName = "BackUpFromImplosion";
obj.skillSlot = (SkillSlot)(-1);
obj.maxDistance = Configs.EvasionDistance.Value;
obj.moveTargetType = (TargetType)3;
obj.aimType = (AimType)1;
obj.movementType = (MovementType)3;
obj.shouldSprint = true;
obj.ignoreNodeGraph = true;
obj.driverUpdateTimerOverride = 1f;
AISkillDriver[] array = components;
foreach (AISkillDriver val3 in array)
{
Component obj2 = masterPrefab.AddComponent(((object)val3).GetType());
FieldInfo[] fields = ((object)val3).GetType().GetFields();
foreach (FieldInfo fieldInfo in fields)
{
fieldInfo.SetValue(obj2, fieldInfo.GetValue(val3));
}
Object.DestroyImmediate((Object)(object)val3);
}
masterPrefab.AddComponent<GTFOHController>();
}
}
internal static void UpdateBackUpDistance()
{
//IL_000f: Unknown result type (might be due to invalid IL or missing references)
//IL_005c: Unknown result type (might be due to invalid IL or missing references)
foreach (MasterIndex backupMasterIndex in backupMasterIndices)
{
GameObject masterPrefab = MasterCatalog.GetMasterPrefab(backupMasterIndex);
if (Object.op_Implicit((Object)(object)masterPrefab))
{
Update(masterPrefab);
}
}
foreach (CharacterMaster instances in CharacterMaster.instancesList)
{
if (backupMasterIndices.Contains(instances.masterIndex))
{
Update(((Component)instances).gameObject);
}
}
static void Update(GameObject master)
{
AISkillDriver[] components = master.GetComponents<AISkillDriver>();
foreach (AISkillDriver val in components)
{
if (val.customName == "BackUpFromImplosion")
{
val.maxDistance = Configs.EvasionDistance.Value;
break;
}
}
}
}
}
internal static class Log
{
private static ManualLogSource _logger;
internal static void Init(ManualLogSource logger)
{
_logger = logger;
}
internal static void Info(object data)
{
_logger.LogInfo(data);
}
internal static void Debug(object data)
{
_logger.LogDebug(data);
}
internal static void Message(object data)
{
_logger.LogMessage(data);
}
internal static void Warning(object data)
{
_logger.LogWarning(data);
}
internal static void Error(object data)
{
_logger.LogError(data);
}
internal static void Fatal(object data)
{
_logger.LogFatal(data);
}
internal static void PatchFail(string message)
{
_logger.LogError((object)("Failed to patch " + message));
}
internal static void PatchFail(ILContext il)
{
PatchFail(((MemberReference)il.Method).Name);
}
}
}