using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BetterFlashlight.Patches;
using GameNetcodeStuff;
using HarmonyLib;
using UnityEngine;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("BetterFlashlight")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("BetterFlashlight")]
[assembly: AssemblyCopyright("Copyright © 2024")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("aba20aae-1860-4f89-8642-9dbb57af9525")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace BetterFlashlight
{
internal class ConfigManager
{
public static ConfigEntry<float> enemyFlashTime;
public static ConfigEntry<float> enemyStunTime;
public static ConfigEntry<float> enemyLightAngle;
public static ConfigEntry<float> enemyAngle;
public static ConfigEntry<int> enemyDistance;
public static ConfigEntry<float> enemyImmunityTime;
public static ConfigEntry<float> enemyBatteryConsumption;
public static ConfigEntry<string> enemyValues;
public static ConfigEntry<string> exclusions;
public static ConfigEntry<bool> isPlayerBlind;
public static ConfigEntry<float> playerFlashTime;
public static ConfigEntry<float> playerLightAngle;
public static ConfigEntry<float> playerAngle;
public static ConfigEntry<float> playerHeadAngle;
public static ConfigEntry<int> playerDistance;
public static ConfigEntry<float> playerBatteryConsumption;
internal static void Load()
{
enemyFlashTime = BetterFlashlight.configFile.Bind<float>("Enemy values", "Flash Time", 2f, "Default time required to blind an enemy.");
enemyStunTime = BetterFlashlight.configFile.Bind<float>("Enemy values", "Stun Time", 3f, "Default duration of the stun.");
enemyLightAngle = BetterFlashlight.configFile.Bind<float>("Enemy values", "Light Angle", 15f, "Default angle of the light in relation to the enemy's eyes.\nIncreasing this value makes aiming easier.");
enemyAngle = BetterFlashlight.configFile.Bind<float>("Enemy values", "Light Angle", 120f, "Default angle of the enemy in relation to the flashlight.\nIncreasing this value makes blinding from an angle easier.");
enemyDistance = BetterFlashlight.configFile.Bind<int>("Enemy values", "Distance", 10, "Default distance between the enemy and the flashlight.");
enemyImmunityTime = BetterFlashlight.configFile.Bind<float>("Enemy values", "Immunity Time", 10f, "Default duration of the immunity.");
enemyBatteryConsumption = BetterFlashlight.configFile.Bind<float>("Enemy values", "Battery Consumption", 0f, "Default battery consumption rate for blinding an enemy.");
enemyValues = BetterFlashlight.configFile.Bind<string>("Enemy values", "Values", "ForestGiant:2:4:30:120:20:20:0", "Values per enemy, the format is EnemyName:FlashTime:StunTime:LightAngle:EnemyAngle:EnemyDistance:ImmunityTime:BatteryConsumption.");
exclusions = BetterFlashlight.configFile.Bind<string>("Enemy values", "Exclusion list", "MouthDog", "List of creatures that will not be affected by the stun.");
isPlayerBlind = BetterFlashlight.configFile.Bind<bool>("Player values", "Enable", true, "Can the player be blinded?");
playerFlashTime = BetterFlashlight.configFile.Bind<float>("Player values", "Flash Time", 2f, "Time required to blind a player.");
playerLightAngle = BetterFlashlight.configFile.Bind<float>("Player values", "Light Angle", 10f, "Angle of the light in relation to the player's eyes.\nIncreasing this value makes aiming easier.");
playerAngle = BetterFlashlight.configFile.Bind<float>("Player values", "Light Angle", 60f, "Angle of the player in relation to the flashlight.\nIncreasing this value makes blinding from an angle easier.");
playerHeadAngle = BetterFlashlight.configFile.Bind<float>("Player values", "Head Angle", 40f, "Angle of the player head in relation to the flashlight (when the player is looking up or down).\nIncreasing this value makes blinding from an angle easier.");
playerDistance = BetterFlashlight.configFile.Bind<int>("Player values", "Distance", 10, "Default distance between the aimed player and the flashlight.");
playerBatteryConsumption = BetterFlashlight.configFile.Bind<float>("Player values", "Battery Consumption", 0f, "Default battery consumption rate for blinding a player.");
}
internal static List<FlashlightStun> GetFlashlightStunsFromConfig()
{
List<FlashlightStun> list = new List<FlashlightStun>();
string[] array = enemyValues.Value.Split(new char[1] { ',' });
string[] array2 = array;
foreach (string text in array2)
{
string[] array3 = text.Split(new char[1] { ':' });
if (array3.Length == 8)
{
list.Add(new FlashlightStun(array3[0], float.Parse(array3[1], CultureInfo.InvariantCulture), float.Parse(array3[2], CultureInfo.InvariantCulture), float.Parse(array3[3], CultureInfo.InvariantCulture), float.Parse(array3[4], CultureInfo.InvariantCulture), int.Parse(array3[5]), float.Parse(array3[6], CultureInfo.InvariantCulture), float.Parse(array3[7], CultureInfo.InvariantCulture)));
}
}
return list;
}
}
internal class FlashlightStun
{
public string EnemyName { get; private set; }
public float FlashTime { get; private set; }
public float StunTime { get; private set; }
public float LightAngle { get; private set; }
public float EnemyAngle { get; private set; }
public int EnemyDistance { get; private set; }
public float ImmunityTime { get; private set; }
public float BatteryConsumption { get; private set; }
public FlashlightStun(string enemyName, float flashTime, float stunTime, float lightAngle, float enemyAngle, int enemyDistance, float immunityTime, float batteryConsumption)
{
EnemyName = enemyName;
FlashTime = flashTime;
StunTime = stunTime;
LightAngle = lightAngle;
EnemyAngle = enemyAngle;
EnemyDistance = enemyDistance;
ImmunityTime = immunityTime;
BatteryConsumption = batteryConsumption;
}
}
[BepInPlugin("Lega.BetterFlashlight", "Better Flashlight", "1.0.6")]
internal class BetterFlashlight : BaseUnityPlugin
{
private const string modGUID = "Lega.BetterFlashlight";
private const string modName = "Better Flashlight";
private const string modVersion = "1.0.6";
private readonly Harmony harmony = new Harmony("Lega.BetterFlashlight");
public static ConfigFile configFile;
public static List<FlashlightStun> flashlightStuns = new List<FlashlightStun>();
private void Awake()
{
configFile = ((BaseUnityPlugin)this).Config;
ConfigManager.Load();
flashlightStuns = ConfigManager.GetFlashlightStunsFromConfig();
harmony.PatchAll(typeof(BetterFlashlight));
harmony.PatchAll(typeof(FlashlightItemPatch));
harmony.PatchAll(typeof(EnemyAIPatch));
harmony.PatchAll(typeof(RoundManagerPatch));
}
}
}
namespace BetterFlashlight.Patches
{
internal class EnemyAIPatch
{
[HarmonyPatch(typeof(EnemyAI), "Start")]
[HarmonyPostfix]
private static void StartEnemy(ref EnemyAI __instance)
{
AddBlindableEnemy(ref __instance);
}
[HarmonyPatch(typeof(MaskedPlayerEnemy), "Start")]
[HarmonyPostfix]
private static void StartMaskedEnemy(ref MaskedPlayerEnemy __instance)
{
EnemyAI enemy = (EnemyAI)(object)__instance;
AddBlindableEnemy(ref enemy);
}
[HarmonyPatch(typeof(EnemyAI), "KillEnemy")]
[HarmonyPostfix]
private static void EndEnemy(ref EnemyAI __instance)
{
FlashlightItemPatch.blindableEnemies.Remove(__instance);
}
[HarmonyPatch(typeof(EnemyAI), "SetEnemyStunned")]
[HarmonyPostfix]
private static void PostSetEnemyStunned(ref EnemyAI __instance)
{
if (!__instance.isEnemyDead && __instance.enemyType.canBeStunned)
{
float immunityTime = ConfigManager.enemyImmunityTime.Value;
EnemyAI enemy = __instance;
FlashlightStun flashlightStun = BetterFlashlight.flashlightStuns.Where((FlashlightStun v) => v.EnemyName.Equals(enemy.enemyType.enemyName)).FirstOrDefault();
if (flashlightStun != null)
{
immunityTime = flashlightStun.ImmunityTime;
}
((MonoBehaviour)__instance).StartCoroutine(ImmuneCoroutine(__instance, immunityTime));
}
}
private static void AddBlindableEnemy(ref EnemyAI enemy)
{
if ((Object)(object)enemy.enemyType != (Object)null && enemy.enemyType.canBeStunned && (Object)(object)enemy.eye != (Object)null && (string.IsNullOrEmpty(ConfigManager.exclusions.Value) || !ConfigManager.exclusions.Value.Contains(enemy.enemyType.enemyName)))
{
FlashlightItemPatch.blindableEnemies.Add(enemy);
}
}
private static IEnumerator ImmuneCoroutine(EnemyAI enemy, float immunityTime)
{
FlashlightItemPatch.blindableEnemies.Remove(enemy);
yield return (object)new WaitForSeconds(immunityTime);
FlashlightItemPatch.blindableEnemies.Add(enemy);
}
}
internal class FlashlightItemPatch
{
internal static List<EnemyAI> blindableEnemies = new List<EnemyAI>();
private static bool isFlashing = false;
private static float maxSpotAngle;
[HarmonyPatch(typeof(FlashlightItem), "Start")]
[HarmonyPostfix]
private static void StartPatch(ref FlashlightItem __instance)
{
maxSpotAngle = __instance.flashlightBulb.spotAngle;
}
[HarmonyPatch(typeof(FlashlightItem), "Update")]
[HarmonyPostfix]
private static void StunFlash(ref FlashlightItem __instance)
{
if (!((GrabbableObject)__instance).isBeingUsed || isFlashing)
{
return;
}
FlashlightItem flashlightItem = __instance;
foreach (EnemyAI enemy in blindableEnemies.Where((EnemyAI e) => (Object)(object)e.enemyType != (Object)null && e.enemyType.canBeStunned && (Object)(object)e.eye != (Object)null))
{
float flashTime = ConfigManager.enemyFlashTime.Value;
float stunTime = ConfigManager.enemyStunTime.Value;
float lightAngle = ConfigManager.enemyLightAngle.Value;
float angle = ConfigManager.enemyAngle.Value;
int distance = ConfigManager.enemyDistance.Value;
float batteryConsumption = ConfigManager.enemyBatteryConsumption.Value;
FlashlightStun flashlightStun = BetterFlashlight.flashlightStuns.Where((FlashlightStun v) => v.EnemyName.Equals(enemy.enemyType.enemyName)).FirstOrDefault();
if (flashlightStun != null)
{
flashTime = flashlightStun.FlashTime;
stunTime = flashlightStun.StunTime;
lightAngle = flashlightStun.LightAngle;
angle = flashlightStun.EnemyAngle;
distance = flashlightStun.EnemyDistance;
batteryConsumption = flashlightStun.EnemyDistance;
}
if (MeetConditions<EnemyAI>(enemy, ref flashlightItem, lightAngle, angle, distance))
{
((MonoBehaviour)__instance).StartCoroutine(StunCoroutine<EnemyAI>(enemy.eye, flashlightItem, enemy, flashTime, lightAngle, angle, distance, batteryConsumption, stunTime));
break;
}
}
if (ConfigManager.isPlayerBlind.Value && !isFlashing)
{
foreach (PlayerControllerB item in StartOfRound.Instance.allPlayerScripts.Where((PlayerControllerB p) => p.isPlayerControlled))
{
float value = ConfigManager.playerFlashTime.Value;
float value2 = ConfigManager.playerLightAngle.Value;
float value3 = ConfigManager.playerAngle.Value;
int value4 = ConfigManager.playerDistance.Value;
float value5 = ConfigManager.playerBatteryConsumption.Value;
if (MeetConditions<PlayerControllerB>(item, ref flashlightItem, value2, value3, value4))
{
((MonoBehaviour)__instance).StartCoroutine(StunCoroutine<PlayerControllerB>(item.playerEye, flashlightItem, item, value, value2, value3, value4, value5));
break;
}
}
}
if (__instance.flashlightBulb.spotAngle != maxSpotAngle)
{
__instance.flashlightBulb.spotAngle = maxSpotAngle;
}
}
private static IEnumerator StunCoroutine<T>(Transform eye, FlashlightItem flashlightItem, T entity, float flashTime, float lightAngle, float angle, int distance, float batteryConsumption, float stunTime = 0f)
{
float timePassed = 0f;
float minSpotAngle = maxSpotAngle / 3f;
while (MeetConditions(entity, ref flashlightItem, lightAngle, angle, distance))
{
yield return (object)new WaitForSeconds(0.1f);
timePassed += 0.1f;
flashlightItem.flashlightBulb.spotAngle = Mathf.Lerp(maxSpotAngle, minSpotAngle, timePassed / flashTime);
object obj = entity;
PlayerControllerB player = (PlayerControllerB)((obj is PlayerControllerB) ? obj : null);
if (player != null && (Object)(object)GameNetworkManager.Instance.localPlayerController == (Object)(object)player && HUDManager.Instance.flashFilter < timePassed)
{
HUDManager.Instance.flashFilter = timePassed;
}
if (timePassed >= flashTime)
{
break;
}
}
if (isFlashing)
{
flashlightItem.flashlightBulb.spotAngle = maxSpotAngle * 2f;
object obj2 = entity;
EnemyAI enemy = (EnemyAI)((obj2 is EnemyAI) ? obj2 : null);
if (enemy != null)
{
enemy.SetEnemyStunned(true, stunTime, ((GrabbableObject)flashlightItem).playerHeldBy);
}
if (batteryConsumption > 0f)
{
((GrabbableObject)flashlightItem).insertedBattery.charge = ((GrabbableObject)flashlightItem).insertedBattery.charge * batteryConsumption / 100f;
}
yield return (object)new WaitForSeconds(0.1f);
isFlashing = false;
}
flashlightItem.flashlightBulb.spotAngle = maxSpotAngle;
}
private static bool MeetConditions<T>(T entity, ref FlashlightItem flashlightItem, float lightAngle, float angle, int distance)
{
//IL_002c: Unknown result type (might be due to invalid IL or missing references)
//IL_0031: Unknown result type (might be due to invalid IL or missing references)
//IL_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_0117: Unknown result type (might be due to invalid IL or missing references)
//IL_0063: Unknown result type (might be due to invalid IL or missing references)
//IL_006e: Unknown result type (might be due to invalid IL or missing references)
//IL_007f: Unknown result type (might be due to invalid IL or missing references)
//IL_0084: Unknown result type (might be due to invalid IL or missing references)
//IL_0138: Unknown result type (might be due to invalid IL or missing references)
//IL_0143: Unknown result type (might be due to invalid IL or missing references)
//IL_0154: Unknown result type (might be due to invalid IL or missing references)
//IL_0159: Unknown result type (might be due to invalid IL or missing references)
//IL_00a1: 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_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_00d6: Unknown result type (might be due to invalid IL or missing references)
//IL_00db: Unknown result type (might be due to invalid IL or missing references)
//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
if (((GrabbableObject)flashlightItem).isBeingUsed)
{
object obj = entity;
PlayerControllerB val = (PlayerControllerB)((obj is PlayerControllerB) ? obj : null);
if (val == null || !val.HasLineOfSightToPosition(((Component)flashlightItem.flashlightBulb).transform.position + Vector3.up * 0.4f, angle, distance, -1f) || !(Mathf.Abs(Vector3.Angle(((Component)flashlightItem.flashlightBulb).transform.forward, val.playerEye.position - ((Component)flashlightItem.flashlightBulb).transform.position)) < lightAngle) || !(Mathf.Abs(Vector3.Angle(((Component)val.gameplayCamera).transform.forward, -1f * (val.playerEye.position - ((Component)flashlightItem.flashlightBulb).transform.position + Vector3.up * 0.4f))) <= ConfigManager.playerHeadAngle.Value))
{
object obj2 = entity;
EnemyAI val2 = (EnemyAI)((obj2 is EnemyAI) ? obj2 : null);
if (val2 == null || !val2.CheckLineOfSightForPosition(((Component)flashlightItem.flashlightBulb).transform.position, angle, distance, -1f, (Transform)null) || !(Mathf.Abs(Vector3.Angle(((Component)flashlightItem.flashlightBulb).transform.forward, val2.eye.position - ((Component)flashlightItem.flashlightBulb).transform.position)) < lightAngle))
{
goto IL_0183;
}
}
isFlashing = true;
return true;
}
goto IL_0183;
IL_0183:
flashlightItem.flashlightBulb.spotAngle = maxSpotAngle;
isFlashing = false;
return false;
}
}
internal class RoundManagerPatch
{
[HarmonyPatch(typeof(RoundManager), "DetectElevatorIsRunning")]
[HarmonyPostfix]
private static void EndGame()
{
FlashlightItemPatch.blindableEnemies.Clear();
}
}
}