using System;
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.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BetterFlashlight_Fixed.Patches;
using GameNetcodeStuff;
using HarmonyLib;
using UnityEngine;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyVersion("0.0.0.0")]
namespace BetterFlashlight_Fixed
{
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", 2f, "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", "Enemy 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:3:30:120:20:30: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", "Player 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] { ',' });
for (int i = 0; i < array.Length; i++)
{
string[] array2 = array[i].Split(new char[1] { ':' });
if (array2.Length == 8)
{
list.Add(new FlashlightStun(array2[0], float.Parse(array2[1], CultureInfo.InvariantCulture), float.Parse(array2[2], CultureInfo.InvariantCulture), float.Parse(array2[3], CultureInfo.InvariantCulture), float.Parse(array2[4], CultureInfo.InvariantCulture), int.Parse(array2[5]), float.Parse(array2[6], CultureInfo.InvariantCulture), float.Parse(array2[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("Azx.BetterFlashlight_Fixed", "BetterFlashlight Fixed", "1.0.0")]
internal class BetterFlashlight : BaseUnityPlugin
{
private const string modGUID = "Azx.BetterFlashlight_Fixed";
private const string modName = "BetterFlashlight Fixed";
private const string modVersion = "1.0.0";
private readonly Harmony harmony = new Harmony("Azx.BetterFlashlight_Fixed");
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_Fixed.Patches
{
internal class EnemyAIPatch
{
[CompilerGenerated]
private sealed class <ImmuneCoroutine>d__5 : IEnumerator<object>, IDisposable, IEnumerator
{
private int <>1__state;
private object <>2__current;
public EnemyAI enemy;
public float immunityTime;
object IEnumerator<object>.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
[DebuggerHidden]
public <ImmuneCoroutine>d__5(int <>1__state)
{
this.<>1__state = <>1__state;
}
[DebuggerHidden]
void IDisposable.Dispose()
{
<>1__state = -2;
}
private bool MoveNext()
{
//IL_002f: Unknown result type (might be due to invalid IL or missing references)
//IL_0039: Expected O, but got Unknown
switch (<>1__state)
{
default:
return false;
case 0:
<>1__state = -1;
FlashlightItemPatch.blindableEnemies.Remove(enemy);
<>2__current = (object)new WaitForSeconds(immunityTime);
<>1__state = 1;
return true;
case 1:
<>1__state = -1;
FlashlightItemPatch.blindableEnemies.Add(enemy);
return false;
}
}
bool IEnumerator.MoveNext()
{
//ILSpy generated this explicit interface implementation from .override directive in MoveNext
return this.MoveNext();
}
[DebuggerHidden]
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
}
[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);
}
}
[IteratorStateMachine(typeof(<ImmuneCoroutine>d__5))]
private static IEnumerator ImmuneCoroutine(EnemyAI enemy, float immunityTime)
{
//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
return new <ImmuneCoroutine>d__5(0)
{
enemy = enemy,
immunityTime = immunityTime
};
}
}
internal class FlashlightItemPatch
{
[CompilerGenerated]
private sealed class <StunCoroutine>d__5<T> : IEnumerator<object>, IDisposable, IEnumerator
{
private int <>1__state;
private object <>2__current;
public FlashlightItem flashlightItem;
public float flashTime;
public T entity;
public float lightAngle;
public float angle;
public int distance;
public float stunTime;
public float batteryConsumption;
private float <timePassed>5__2;
private float <minSpotAngle>5__3;
object IEnumerator<object>.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
[DebuggerHidden]
public <StunCoroutine>d__5(int <>1__state)
{
this.<>1__state = <>1__state;
}
[DebuggerHidden]
void IDisposable.Dispose()
{
<>1__state = -2;
}
private bool MoveNext()
{
//IL_0049: Unknown result type (might be due to invalid IL or missing references)
//IL_0053: Expected O, but got Unknown
//IL_01b1: Unknown result type (might be due to invalid IL or missing references)
//IL_01bb: Expected O, but got Unknown
switch (<>1__state)
{
default:
return false;
case 0:
<>1__state = -1;
<timePassed>5__2 = 0f;
<minSpotAngle>5__3 = maxSpotAngle / 3f;
goto IL_00f8;
case 1:
{
<>1__state = -1;
<timePassed>5__2 += 0.1f;
flashlightItem.flashlightBulb.spotAngle = Mathf.Lerp(maxSpotAngle, <minSpotAngle>5__3, <timePassed>5__2 / flashTime);
object obj = entity;
PlayerControllerB val = (PlayerControllerB)((obj is PlayerControllerB) ? obj : null);
if (val != null && (Object)(object)GameNetworkManager.Instance.localPlayerController == (Object)(object)val && HUDManager.Instance.flashFilter < <timePassed>5__2)
{
HUDManager.Instance.flashFilter = <timePassed>5__2;
}
if (!(<timePassed>5__2 >= flashTime))
{
goto IL_00f8;
}
goto IL_0120;
}
case 2:
{
<>1__state = -1;
isFlashing = false;
break;
}
IL_00f8:
if (MeetConditions(entity, ref flashlightItem, lightAngle, angle, distance))
{
<>2__current = (object)new WaitForSeconds(0.1f);
<>1__state = 1;
return true;
}
goto IL_0120;
IL_0120:
if (isFlashing)
{
flashlightItem.flashlightBulb.spotAngle = maxSpotAngle * 2f;
object obj2 = entity;
EnemyAI val2 = (EnemyAI)((obj2 is EnemyAI) ? obj2 : null);
if (val2 != null)
{
val2.SetEnemyStunned(true, stunTime, ((GrabbableObject)flashlightItem).playerHeldBy);
}
if (batteryConsumption > 0f)
{
((GrabbableObject)flashlightItem).insertedBattery.charge = ((GrabbableObject)flashlightItem).insertedBattery.charge * batteryConsumption / 100f;
}
<>2__current = (object)new WaitForSeconds(0.1f);
<>1__state = 2;
return true;
}
break;
}
flashlightItem.flashlightBulb.spotAngle = maxSpotAngle;
return false;
}
bool IEnumerator.MoveNext()
{
//ILSpy generated this explicit interface implementation from .override directive in MoveNext
return this.MoveNext();
}
[DebuggerHidden]
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
}
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 || !((GrabbableObject)__instance).isHeld || isFlashing)
{
return;
}
FlashlightItem flashlightItem = __instance;
foreach (EnemyAI enemy in from e in blindableEnemies.ToArray()
where (Object)(object)e.enemyType != (Object)null && e.enemyType.canBeStunned && (Object)(object)e.eye != (Object)null
select e)
{
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.BatteryConsumption;
}
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;
}
}
[IteratorStateMachine(typeof(<StunCoroutine>d__5<>))]
private static IEnumerator StunCoroutine<T>(Transform eye, FlashlightItem flashlightItem, T entity, float flashTime, float lightAngle, float angle, int distance, float batteryConsumption, float stunTime = 0f)
{
//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
return new <StunCoroutine>d__5<T>(0)
{
flashlightItem = flashlightItem,
entity = entity,
flashTime = flashTime,
lightAngle = lightAngle,
angle = angle,
distance = distance,
batteryConsumption = batteryConsumption,
stunTime = stunTime
};
}
private static bool MeetConditions<T>(T entity, ref FlashlightItem flashlightItem, float lightAngle, float angle, int distance)
{
//IL_0037: Unknown result type (might be due to invalid IL or missing references)
//IL_003c: Unknown result type (might be due to invalid IL or missing references)
//IL_0046: Unknown result type (might be due to invalid IL or missing references)
//IL_004b: Unknown result type (might be due to invalid IL or missing references)
//IL_0122: 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_0079: Unknown result type (might be due to invalid IL or missing references)
//IL_008a: Unknown result type (might be due to invalid IL or missing references)
//IL_008f: 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_014e: Unknown result type (might be due to invalid IL or missing references)
//IL_015f: Unknown result type (might be due to invalid IL or missing references)
//IL_0164: Unknown result type (might be due to invalid IL or missing references)
//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
if (((GrabbableObject)flashlightItem).isBeingUsed && ((GrabbableObject)flashlightItem).isHeld)
{
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_017e;
}
}
isFlashing = true;
return true;
}
goto IL_017e;
IL_017e:
flashlightItem.flashlightBulb.spotAngle = maxSpotAngle;
isFlashing = false;
return false;
}
}
internal class RoundManagerPatch
{
[HarmonyPatch(typeof(RoundManager), "DetectElevatorIsRunning")]
[HarmonyPostfix]
private static void EndGame()
{
FlashlightItemPatch.blindableEnemies.Clear();
}
}
}